OpenCV实战

您所在的位置:网站首页 叠加函数求导 OpenCV实战

OpenCV实战

2023-06-03 01:52| 来源: 网络整理| 查看: 265

点击上方“小白学视觉”,选择加"星标"或“置顶”

重磅干货,第一时间送达

术语解释

- 由于本文代码基于OpenCV基础库,所以题目中添加了“OpenCV实现”字样。

- 由于图像的二维特性,所以下文中所有“Hessian矩阵”都特指“二维Hessian矩阵”。

Hessian矩阵等相关理论基础

这里的基础理论有点多,你可以先过一遍,然后在读代码的时候再回过头来加深理解,这样效果比较好。

1. Hessian矩阵的由来及定义

由高等数学知识可知,若一元函数f(x)

b20bfeef8c778c1013bafa37632a2ff8.png

点的某个邻域内具有任意阶导数,则

 aaefd113ffb71173482a2d4343c1a055.png在 352829ff7ac0c793f1a161ae72de33ec.png点处的泰勒展开式为: 

030d61c2e7f33fbb4bbfc044b3343eb6.png

其中c29394ffd87dd7f7f2d5dc8946f7413d.png39d9393e4d82270c502f1eafe465b42b.png

二元函数138da0526258784cd269d25e9b0e50f6.png374d8c2f8e3445ad775c117f31aa7529.png点处的泰勒展开式为:

81b4cd51a36edb3e4d790fc26eef42e3.png

其中

6b4c6d2e85346d3691ca487913725b86.png

将上述展开式写成矩阵形式,则有:

085cd2758b9ac353a7c36627f6c2e867.png

即为

cba17896212b0ecbe65f373f3986790e.png

其中:

272777dfabfce9557d6b95a97ad03687.png

 4d8a80257b5f15fa8401d8f34eec88a9.png 是 ffcfc8ec585aa41084c1011b4ca4ca86.png 在 f34ca373f83d7e00dfffd25b1d651eab.png 点处的Hessian矩阵。它是由函数39aeb1aefc7098e060b7ce220cdd4834.png在 92946b401ed364cffa507447d36eba9a.png点处的二阶偏导数所组成的方阵。我们一般将其表示为:

b934f58e4928837054053e2c233d5aca.jpeg

我们也可以将其简写为:

3b558aba15bd76faf556e058fbe77fc0.png

2.数字图像处理之尺度空间理论

尺度空间理论的基本思想是:在图像信息处理模型中引入一个被视为尺度的参数,通过连续变化尺度参数获得多尺度下的尺度空间表示序列,对这些序列进行尺度空间主轮廓的提取,并以该主轮廓作为一种特征向量,实现边缘、角点检测和不同分辨率上的特征提取等。

尺度空间理论的特点是:将传统的单尺度图像信息处理技术纳入尺度不断变化的动态分析框架中,更容易获取图像的本质特征。尺度空间中各尺度图像的模糊程度逐渐变大,能够模拟人在距离目标由近到远时目标在视网膜上的形成过程。

尺度空间理论的一个直观理解:我们人在远近不同距离上对同一物体,都可以实现辨识。

高斯卷积核是实现尺度变换的唯一线性核(高斯函数可以称为最伟大和最重要的函数)。一幅图像的尺度空间可以定义为:

0f60bca0c551c161bf67ff1c01e287e5.png

其中符号"*"表示卷积操作。 

σ是尺度空间因子,值越小表示图像被平滑的越少,相应的尺度也就越小。大尺度对应于图像的概貌特征,小尺度对应于图像的细节特征。

3.基于尺度理论的Hessian简化算法

对于二维图像IHessian矩阵描述每个像素在主方向上的二维导数为:

2b74a14568bfaa7e15917882e15d8a10.png

根据尺度空间理论,二阶导数可以通过图像与高斯函数的卷积获得,例如,在点(x,y)处有

98a9ff0a2411cee6e1b919e534071ca4.png

其中89c6a46abd5a706781a14c602d961022.png,为尺度f56e43d4811d4999099aa977069844ff.png为的高斯函数。

如果我们接受这个理论,那么就可以得到推论:

对全图直接做偏导操作  = 对原图以特定的高斯核做卷积

基于这个推论,对于图的Hessian运算将极大地降低计算量,同时提高运算速度。

4.Hessian矩阵特征值的求解方法

首先回忆本科知识,根据定义求二阶矩阵的特征值:

根据定义,对于矩阵A,它的特征值满足

|λE-A|=0

其中

E是二阶对角阵

(1 0) (0 1)

我们表示A为

|a   b| |c   d|

则λE-A|

= (λ-a)(λ-d)-bc

= λ^2-(a+d)λ+ad-bc = 0这个是一元二次方程,可以计算得到有两个解,分别为λ1=(a+d+√((a-d)^2+4bc))/2λ2=(a+d-√((a-d)^2+4bc))/2 

由前面的资料,我们已经得到了Hessian矩阵的定义:

3487f7046cb54f4ab7226192e090214e.png

根据二维矩阵特征值的定义:“设A是n阶方阵,如果存在数m和非零n维列向量 x,使得 Ax=mx 成立,则称 m 是矩阵A的一个特征值(characteristic value)或本征值(eigenvalue)”,可以得到等式

c2ffce38953b92ffb997e19cf25f4d83.png

并且根据图像的特性,可以得到

a6a6ea12728cdcef57bbe040c7c01205.jpeg=10d53f6fb0bf07caf63983e42c059db3.jpeg

带入以上方程得到Hessian的特征值的解:

f126252f2a7cacc2c7241cac6efae682.jpeg

请记住这个结论,我们在代码部分将再一次提及。

5.Hessian矩阵特征值的图像性质

一个Hessian矩阵可以分解为两个特征值以及定义的特征向量。79a308b1e77ae7d1af5f44fada3bfde4.png1d539f6ff7c01dd067f46704cbe348f0.png

其中最大的绝对特征值8dbff617d1ba2b50d2c44aea0b67de1d.png表示最大的局部灰度变化,其特征向量则代表它方向,可以认为是切线方向;而较小的那个代表垂直方向,也就是法线方向。

这张图可以很好地表明切线和法线的概念。

e8f1f384cb0bcf7bf6f7ec51dfd0648e.jpeg

这些都将在下面的算法中得到利用。

6.高斯方程及二阶导数

前面提到了高斯函数,这里补充一些知识,下面有用。

高斯分布即正态分布,其曲线图如下:

0cdaedf90df458c690878038c7f89632.jpeg

二维高斯分布,其曲线图如下:

48a451b71270ad1a8c197898fd9f8def.jpeg

根据定义,我们可以求得一下内容。

二维高斯函数的一阶偏导数:

7e446069ed6745375dec9b2a169915db.jpeg

二维高斯函数的二阶偏导数

596e88ae3094b3ec0949497ad684f20e.jpeg

这里从原函数到二阶偏导的推断都是本科的知识,建议大家自己推一下,很简单,对于这个问题的深入认识很有帮助,后面我们在代码部分将再一次提及。

血管增强”算法(Frangi算法)原理

ujkHessian矩阵及其特征值能够很好地描述常见的几何形状的信息,我们将利用它进行血管增强;Hessian矩阵的简化算法将为我们代码化提供可能方法。我们主要基于最著名的"Frangi滤波"论文。

Frangi A F, Niessen W J, Vincken K L, et al. 《Multiscale vessel enhancement filtering》[C]//International Conference on Medical Image Computing and Computer-Assisted Intervention. Springer Berlin Heidelberg, 1998: 130-137. 

1.认识血管及其增强

在采集到的图片中,血管应该呈现“管状网络”结构,这为我们进行图像处理提供基本依据。上面提高的"Frangi"算法本身就是用来识别管线的。

2.Frangi论文基本原理

基于前面我们说明的”加速算法“,首先将血管在多尺度下进行Gaussian滤波处理,然后计算每个像素点的二阶导数构造Hessian矩阵,并且计算出两个特征值(这个地方在代码实现的时候有技巧)。

虽然我们已经得到了Hessian矩阵及其特征值,从图像上已经能够看出增强的效果,但是这还不够。接下来

将求得的特征值带入事先建立好的血管相似性函数中获取在不同尺度下的滤波响应。

64171c541c1bb3c2a2189fc48b45a167.png

当尺度和局部结构匹配时计算得到最大滤波响应,从而判断当前像素点是否属于血管结构。

为了尽可能地得到增强的效果,在论文中采用的是“多尺度”叠加的方法,具体来说就是采用不同的卷积核同时进行处理,得到多张处理效果,而后对结果中“着色”效果比较好的部分进行叠加。

3.Frangi论文优缺点

该方法得到了一种有效的血管增强方法,但是可以看到,算法中引入了较多需要认为定义的因素;同时本身较大较多的浮点运算难以在嵌入式系统上实时运行;关于”血管相似性函数“的定义缺乏理论依据,更多像是认为定义的结果,最后该算法不能够直接分割得到血管,因此该步骤往往用于血管分割的预处理阶段。

光看理论很难搞清楚,这里我们边讲解代码边继续理解。

编写代码进行具体实现

下面开始讲解具体代码,整个可以运行的项目我会付到文章最后。在实现过程中,我们参考libfrangi 

https://ntnu-bioopt.github.io/software/libfrangi.html

提供的优质代码进行讲解,过程中我做了必要的精简和注释。

必须要多说一句的是,这个代码从内容到风格上,很大程度上参考了frangi的matlab版本代码

https://www.mathworks.com/matlabcentral/fileexchange/24409-hessian-based-frangi-vesselness-filter,

如果你也擅长matlab,建议对比学习。

1.项目结构

首先从结构开始说明,这样如果你有一定基础就可以自己先进行研究,然后对比我的讲解,对于学习有帮助。

8bf8896e65bb6f9f5a1215148e7ff937.png

这个项目很简单,除了main.cpp外,frangi算法封装到了frangi.h+frangi.cpp中,以函数形式直接提供。

1int main(int argc, char *argv[]) { 2    //使用默认参数设定Frangi 3    frangi2d_opts_t opts; 4    frangi2d_createopts(&opts); 5    //读取图片,进行处理 6    Mat input_img = imread("E:/template/51.bmp", 0); 7    Mat input_img_fl; 8    //转换为单通道,浮点运算 9    input_img.convertTo(input_img_fl, CV_32FC1); 10    //进行处理 11    Mat vesselness, scale, angles; 12    frangi2d(input_img_fl, vesselness, scale, angles, opts); 13    //显示结果 14    vesselness.convertTo(vesselness, CV_8UC1, 255); 15    scale.convertTo(scale, CV_8UC1, 255); 16    angles.convertTo(angles, CV_8UC1, 255); 17    imshow("result", vesselness);     18}

而main.cpp也尽可能简单,除了必要的图片格式转换外,主要过程封装在

frangi2d(input_img_fl, vesselness, scale, angles, opts);

打开frangi.h,我们可以看见以下内容:

1/ 2//Frangi filter// 3/ 4//frangi滤波主要过程 5void frangi2d(const cv::Mat &src, cv::Mat &J, cv::Mat &scale, cv::Mat &directions, frangi2d_opts_t opts); 6 7//Helper functions// 8 9//计算Hessian矩阵 with parameter sigma on src, save to Dxx, Dxy and Dyy.  10void frangi2d_hessian(const cv::Mat &src, cv::Mat &Dxx, cv::Mat &Dxy, cv::Mat &Dyy, float sigma); 11//参数设置 (sigma_start = 3, sigma_end = 7, sigma_step = 1, BetaOne = 1.6, BetaTwo 0.08) 12void frangi2d_createopts(frangi2d_opts_t *opts); 13//计算特征值 from Dxx, Dxy, Dyy. Save results to lambda1, lambda2, Ix, Iy.  14void frangi2_eig2image(const cv::Mat &Dxx, const cv::Mat &Dxy, const cv::Mat &Dyy, cv::Mat &lambda1, cv::Mat &lambda2, cv::Mat &Ix, cv::Mat &Iy);

保持的是一个主要函数+三个Helper函数的过程。

2.计算Hessian矩阵

我们来看frangi2d_hessian这个函数,正如注释说明,它就是Hessian运算的具体实现:

1//计算Hessian矩阵 with parameter sigma on src, save to Dxx, Dxy and Dyy.    2void frangi2d_hessian(const cv::Mat &src, cv::Mat &Dxx, cv::Mat &Dxy, cv::Mat &Dyy, float sigma);

其中比较难以理解的是这段,似乎做了较多的数值计算

1for (int x = -round(3*sigma); x 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3