Hessian矩阵以及在血管增强中的应用—OpenCV实现
目录: 一、 Hessian矩阵等相关 理论基础 1.Hessian矩阵的由来及定义 2.数字图像处理之尺度空间理论 3.基于尺度理论的Hessian简化算法 4.Hessian矩阵特征值的求解方法 5.Hessian矩阵特征值的图像性质 6.高斯方程及二阶导数 二、“血管增强”算法 (Frangi算法) 原理 1.认识血管及其增强 2.Frangi论文基本原理 3.Frangi论文优缺点 三、编写代码进行具体实现 1.项目结构 2.计算Hessian矩阵 3.Hessian特征值的计算 4.frangi2d主要过程 1.项目结构 2.计算Hessian矩阵 3.Hessian特征值的计算 4.frangi2d主要过程
其中
尺度空间理论的基本思想是:在图像信息处理模型中引入一个被视为尺度的参数,通过连续变化尺度参数获得多尺度下的尺度空间表示序列,对这些序列进行尺度空间主轮廓的提取,并以该主轮廓作为一种特征向量,实现边缘、角点检测和不同分辨率上的特征提取等。
尺度空间理论的特点是:将传统的单尺度图像信息处理技术纳入尺度不断变化的动态分析框架中,更容易获取图像的本质特征。尺度空间中各尺度图像的模糊程度逐渐变大,能够模拟人在距离目标由近到远时目标在视网膜上的形成过程。
尺度空间理论的一个直观理解:我们人在远近不同距离上对同一物体,都可以实现辨识。高斯卷积核是实现尺度变换的唯一线性核(高斯函数可以称为最伟大和最重要的函数)。
一幅图像的尺度空间可以定义为:
<br>
对全图直接做偏导操作 = 对原图以特定的高斯核做卷积
基于这个推论,对于图的Hessian运算将极大地降低计算量,同时提高运算速度。
|a b|
|c d|
这个是一元二次方程,可以计算得到有两个解,分别为
λ1=(a+d+√((a-d)^2+4bc))/2
λ2=(a+d-√((a-d)^2+4bc))/2
二维高斯分布,其曲线图如下:
根据定义,我们可以求得一下内容。
二维高斯函数的一阶偏导数:
二维高斯函数的二阶偏导数
这里从原函数到二阶偏导的推断都是本科的知识,建议大家自己推一下,很简单,对于这个问题的深入认识很有帮助,后面我们在代码部分将再一次提及。
二、“血管增强”算法的原理
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.
int main( int argc, char * argv[]) { //使用默认参数设定Frangi frangi2d_opts_t opts; frangi2d_createopts( & opts); //读取图片,进行处理 Mat input_img = imread( "E:/template/51.bmp" , 0 ); Mat input_img_fl; //转换为单通道,浮点运算 input_img.convertTo(input_img_fl, CV_32FC1); //进行处理 Mat vesselness, scale, angles; frangi2d(input_img_fl, vesselness, scale, angles, opts); //显示结果 vesselness.convertTo(vesselness, CV_8UC1, 255 ); scale.convertTo(scale, CV_8UC1, 255 ); angles.convertTo(angles, CV_8UC1, 255 ); imshow( "result" , vesselness); }
frangi2d(input_img_fl, vesselness, scale, angles, opts);
//frangi滤波主要过程 void frangi2d( const cv : : Mat & src, cv : : Mat & J, cv : : Mat & scale, cv : : Mat & directions, frangi2d_opts_t opts); //Helper functions// //计算Hessian矩阵 with parameter sigma on src, save to Dxx, Dxy and Dyy. void frangi2d_hessian( const cv : : Mat & src, cv : : Mat & Dxx, cv : : Mat & Dxy, cv : : Mat & Dyy, float sigma); //参数设置 (sigma_start = 3, sigma_end = 7, sigma_step = 1, BetaOne = 1.6, BetaTwo 0.08) void frangi2d_createopts(frangi2d_opts_t * opts); //计算特征值 from Dxx, Dxy, Dyy. Save results to lambda1, lambda2, Ix, Iy. void 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函数的过程。
//计算Hessian矩阵 with parameter sigma on src, save to Dxx, Dxy and Dyy. void frangi2d_hessian( const cv : : Mat & src, cv : : Mat & Dxx, cv : : Mat & Dxy, cv : : Mat & Dyy, float sigma);
for ( int x = - round( 3 * sigma); x < = round( 3 * sigma); x ++ ){ j = 0 ; for ( int y = - round( 3 * sigma); y < = round( 3 * sigma); y ++ ){ kern_xx_f[i * n_kern_y + j] = 1 .0f / ( 2 .0f * M_PI * sigma * sigma * sigma * sigma) * (x * x / (sigma * sigma) - 1 ) * exp( - (x * x + y * y) / ( 2 .0f * sigma * sigma)); kern_xy_f[i * n_kern_y + j] = 1 .0f / ( 2 .0f * M_PI * sigma * sigma * sigma * sigma * sigma * sigma) * (x * y) * exp( - (x * x + y * y) / ( 2 .0f * sigma * sigma)); j ++ ; } i ++ ; } for ( int j = 0 ; j < n_kern_y; j ++ ){ for ( int i = 0 ; i < n_kern_x; i ++ ){ kern_yy_f[j * n_kern_x + i] = kern_xx_f[i * n_kern_x + j]; } }
这里我们就是首先把“特定的高斯核”计算出来。然后我们回忆当时介绍的二维高斯函数的二阶偏导数
那么它翻译成代码是什么样子的了?
matlab版本
//生成卷积核 //DGaussxx = 1/(2*pi*Sigma^4) * (X.^2/Sigma^2 - 1) .* exp(-(X.^2 + Y.^2)/(2*Sigma^2)); //DGaussxy = 1/(2*pi*Sigma^6) * (X .* Y) .* exp(-(X.^2 + Y.^2)/(2*Sigma^2)); //DGaussyy = DGaussxx';
cv : : Mat tmp1 = 1 / ( 2 * PI * Sigma * Sigma * Sigma * Sigma) * (EWM(X,X) / (Sigma * Sigma) - 1 ); cv : : Mat tmp2 = - 1 * ((EWM(X,X) + EWM(Y,Y)) / ( 2 * Sigma * Sigma)); exp(tmp2,tmp2);
flip(Mat(n_kern_y, n_kern_x, CV_32FC1, kern_xx_f), kern_xx, - 1 );
//calculate eigenvectors of J, v1 and v2 Mat tmp, tmp2; tmp2 = Dxx - Dyy; sqrt(tmp2.mul(tmp2) + 4 * Dxy.mul(Dxy), tmp); Mat v2x = 2 * Dxy; Mat v2y = Dyy - Dxx + tmp; //normalize Mat mag; sqrt((v2x.mul(v2x) + v2y.mul(v2y)), mag); Mat v2xtmp = v2x.mul( 1 .0f / mag); v2xtmp.copyTo(v2x, mag != 0 ); Mat v2ytmp = v2y.mul( 1 .0f / mag); v2ytmp.copyTo(v2y, mag != 0 ); //eigenvectors are orthogonal Mat v1x, v1y; v2y.copyTo(v1x); v1x = - 1 * v1x; v2x.copyTo(v1y); //compute eigenvalues Mat mu1 = 0 . 5 * (Dxx + Dyy + tmp); Mat mu2 = 0 . 5 * (Dxx + Dyy - tmp);
tmp2 = Dxx - Dyy; sqrt(tmp2.mul(tmp2) + 4 * Dxy.mul(Dxy), tmp);
for (float sigma = opts.sigma_start; sigma <= opts.sigma_end; sigma += opts.sigma_step){……} //多尺度叠加 for (int i=1; i < ALLfiltered.size(); i++){ maxVals = max(maxVals, ALLfiltered[i]); whatScale.setTo(sigma, ALLfiltered[i] == maxVals); ALLangles[i].copyTo(outAngles, ALLfiltered[i] == maxVals); sigma += opts.sigma_step; }
程序最外面的框架告诉我们,整个程序是多次运算(尺度)的叠加。
//根据论文定义,对结果进行修正 Dxx = Dxx * sigma * sigma; Dyy = Dyy * sigma * sigma; Dxy = Dxy * sigma * sigma; //calculate (abs sorted) eigenvalues and vectors Mat lambda1, lambda2, Ix, Iy; frangi2_eig2image(Dxx, Dxy, Dyy, lambda1, lambda2, Ix, Iy);
在每次计算中,首先计算出的值,代码中对于的变量叫做lambda1,lambda2。
//根据定义,计算“血管增强响应函数” lambda2.setTo(nextafterf( 0 , 1 ), lambda2 == 0 ); Mat Rb = lambda1.mul( 1 . 0 / lambda2); Rb = Rb.mul(Rb); Mat S2 = lambda1.mul(lambda1) + lambda2.mul(lambda2); Mat tmp1, tmp2; exp( - Rb / beta, tmp1); exp( - S2 / c, tmp2); //保存单尺度结果 Mat Ifiltered = tmp1.mul(Mat::ones(src.rows, src.cols, src.type()) - tmp2);
float beta = 2 * opts.BetaOne * opts.BetaOne; float c = 2 * opts.BetaTwo * opts.BetaTwo;

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!