草庐IT

【图像处理】海森矩阵(Hessian Matrix)及用例(基于Steger的中心提取_含代码)

三维成像魔法 2024-02-03 原文

Hess矩阵是一个多元函数的二阶偏导数构成的方阵,描述了函数的局部曲率。Hess矩阵经常用在牛顿法中求多元函数的极值问题,将目标函数在某点领域内进行二阶泰勒展开,其中的二阶导数就是Hess矩阵。

海森矩阵的意义

应用在图像中,将图像中在某点领域内进行泰勒展开:
  F ( x 1 + Δ x ) = F ( x 1 ) + J ( x 1 ) T Δ x + 1 2 Δ x T H ( x 1 ) Δ x   \ F(x_1+\Delta x) = F(x_1) + J(x_1)^\mathrm{T} {\Delta x} + \frac{1}{2} \Delta x^\mathrm{T}H(x_1) \Delta x \,  F(x1+Δx)=F(x1)+J(x1)TΔx+21ΔxTH(x1)Δx
其中   J ( x 1 )   \ J(x_1)\,  J(x1)是F(x)在   x 1   \ x_1\,  x1处的一阶导数(梯度),   H ( x 1 )   \ H(x1) \,  H(x1)是二阶导数,图像   x 1   \ x_1\,  x1领域内增量是   Δ x   \ \Delta x \,  Δx
求图像   x 1   \ x_1\,  x1点领域的极值,对上述等式右侧等式关于   Δ x   \ \Delta x \,  Δx求导,并令求导后等于0,得到关系式:
  J ( x 1 ) + H ( x 1 ) Δ x = 0 ; H ( x 1 ) Δ x = − J ( x 1 ) ;   \ J(x_1) + H(x_1) \Delta x = 0; H(x_1) \Delta x = -J(x_1);\,  J(x1)+H(x1)Δx=0H(x1)Δx=J(x1)
那么对于图像处理中,图像   x 1   \ x_1\,  x1领域内增量   Δ x = [ 1 , 1 ] T   \ \Delta x = [1,1]^\mathrm{T} \,  Δx=[1,1]T是像素,那么Hess矩阵的特征向量就是反向一阶梯度;特征值正负表示的是局部领域内函数的凹凸性,大小表示凹凸的程度;特征值绝对值最大的特征向量表示一阶梯度的变化快慢,乘以增量   Δ x = [ 1 , 1 ] T   \ \Delta x = [1,1]^\mathrm{T}\,  Δx=[1,1]T像素,表示的一阶梯度;

如下图所示,线条处两个特征值表示了图像在两个特征向量所指的方向上图像变化的各向异性。相似的,圆具有各项同性;线性结构越强的越是各项异性

海森矩阵的应用(Steger算法)


其中   r x x   \ r_{xx} \,  rxx表示图像沿x的二阶偏导,其他类似;   λ   \ \lambda \,  λ则表示特征向量的长度。

Hess矩阵按照变量求二阶偏导后按顺序排列,因各二阶导连续故原函数的混合偏导数全相等,导致其是对称的,这时就可以与正负定矩阵知识联系起来。若是正负定矩阵,二阶导为系数的多项式符号确定(前提在该领域内一阶导单调),证明一阶导有零点,故配合二阶导的符号即可确定是极小还是极大

如何判断一个矩阵是否是正定的,负定的,还是不定的呢?
一个最常用的方法就是顺序主子式。实对称矩阵为正定矩阵的充要条件是的各顺序主子式都大于零。
另一个判定方法:
矩阵正定的充分必要条件是它的特征值都大于零。矩阵负定的充分必要条件是它的特征值都小于零。否则是不定的

因此在算法中求解处Hess矩阵特征值后,可以依照图像线某一方向极大值点添加约束条件:
两个约束条件(1、起码有一个特征值小于0;2、其中一个特征值的绝对值 >(另一个特征值的绝对值 + 1));
另一个约束条件,中心点处图像的灰度值要大于某一阈值(结合图像的特性)。

中心点位置亚像素求解,沿着一阶梯度方向,利用下述泰勒展开获得亚像素的值,求极值让下式一阶导为0,得到偏差值距离值
  F ( x 0 + t n x , y 0 + t n y ) = F ( x 0 , y 0 ) + J ( x 1 ) T [ t n x , t n y ] T + 1 2 [ t n x , t n y ] ∗ H ( x 1 ) ∗ [ t n x , t n y ] T   \ F(x_0 + tn_x, y_0 + tn_y) = F(x_0, y_0) + J(x_1)^\mathrm{T} [tn_x, tn_y]^\mathrm{T} + \frac{1}{2}[tnx, tny]*H(x1)*[tn_x, tn_y]^\mathrm{T} \,  F(x0+tnx,y0+tny)=F(x0,y0)+J(x1)T[tnx,tny]T+21[tnx,tny]H(x1)[tnx,tny]T;

注:在求上述hessian矩阵之前,根据Steger文章中,设计高斯方差   σ ⩽ w 3   \ \sigma \leqslant \frac{w}{\sqrt3} \,  σ3 w;核过大,高斯分布顶部满足条件的点变多,选择中心点的精度不够,提取到的中心线会小范围的波动;核过小,中心点在二阶法线长度局部最大值处,非最小点处。

基于Steger改善算法示例代码

clc; clear; close all;
tic;
%% Extraction of normal binarization center based on Steger algorithm
image = imread(['.\Precision\stair\8\a.bmp']); 
Sz = size(image);

% 自动提取ROI区域阈值
Img_index = zeros(1, 245);
for i = 1:Sz(1)
    for j = 1:Sz(2)
        if image(i, j) > 10
           Img_index(image(i, j) - 10) = Img_index(image(i, j) - 10) + 1;
        end
    end
end
Img_index(245) = 0;
ind_x = Img_index(1:end-1) - Img_index(2:end);
ind_x = imfilter(ind_x, [1 3 6 9 10 9 6 3 1]./48, 'replicate');% ;
for i = 2:245
    if ind_x(i) < 10
       Intensity_threshold = i + 10;%20
       break;
    end
end

% choose the region of interest
BC = roicolor(image, Intensity_threshold, 256);
[r, c] = find(BC == 1);
Range_vertical = lineboundary(r, c);
global num
nn = ones(1, Sz(2));
nn_sz = size(nn(Range_vertical(1, :) ~= 0));
num = sum(Range_vertical(2, :) - Range_vertical(1, :)) + nn_sz(2);

%对所有ROI区域执行平滑操作
[img_filter, Index_c] = filter_gauss_1(image, Range_vertical);
% Guass smoothing: Notices: sigma >= w/sqrt(3);first w = 8; 

DIR = [ [-1; -1], [-1;  0], [-1;  1] ...
        [ 0; -1], [ 0;  0], [ 0;  1]... 
        [ 1; -1], [ 1;  0], [ 1;  1] ];
% 找到对应的中心点与线宽大小
CenterCord = zeros(2, Sz(2)); 
img_2filter = zeros(1, num);
LineW = zeros(1, Sz(2));
for i = 2:Sz(2)-1   
    if i == 1110
        i = 1110;
    end
    % 判断初始检索位置
    y0 = (Range_vertical(2, i-1) + Range_vertical(1, i-1))/2;
    y1 = (Range_vertical(2, i) + Range_vertical(1, i))/2;
    y2 = (Range_vertical(2, i+1) + Range_vertical(1, i+1))/2;
    if (y0==0 || y1==0 || y2==0)|| abs(y1 - y0) > 5 || abs(y1 - y2) > 5
        continue;
    else
        tempInd = find(img_filter(Index_c(i):Index_c(i+1) - 1) > 220);
        if length(tempInd) > 1
            temp = tempInd(1) + floor(length(tempInd)/2);            
        else
            [~, temp] = max(img_filter(Index_c(i):Index_c(i+1) - 1));
        end            
        tempy = Range_vertical(1, i) + temp - 1;        
    end  
    CenterCord(:, i) = [i; tempy];
    
    if image(tempy, i) > 150
       % 计算有限个纵向滤波      
       img_2f_array = Block_filter(img_filter, img_2filter, [i; tempy], Index_c, Range_vertical, gausFilter);    
       for k = [1 2 4 5 6 8]  % 开始检索([1 2 *; 4 5 6; * 8 *]')  
           m = Index_c(i + DIR(1, k)) + tempy + DIR(2, k) - Range_vertical(1, i + DIR(1, k));
           img_2filter(m) = img_2f_array(k);           
       end               
      [norm, lamda, t] = StegerHess(img_2f_array);        
      if lamda(2) < 0 && abs(lamda(2))>(4 + abs(lamda(1)))         
          W = Linewidth([i, tempy], norm, Range_vertical); %    
          if W <= 40
             LineW(i) = W;
          end
      end
   end
end  
se = strel('line', 5, 0);
LineWtemp = imopen(LineW, se);
LineW = round(imfilter(LineWtemp, [1 3 6 9 10 9 6 3 1]./48, 'replicate'));
LineW(LineW <= 5) = 0; LineW(1) = 0;


% 构建高斯二阶偏导数模板
W = 5;
sigma = 2.5;
xxGauKernel = zeros(2*W + 1, W + 1);
yyGauKernel = zeros(2*W + 1, W + 1); 
xyGauKernel = zeros(2*W + 1, W + 1);  
xGauKernel = zeros(2*W + 1, W + 1);
yGauKernel = zeros(2*W + 1, W + 1);
for i = -W:W	
	for j = -W:W		
		yyGauKernel(i + W + 1, j + W + 1) = (1 - (i*i) / (sigma*sigma))*exp(-1 * (i*i + j*j) / (2 * sigma*sigma))*(-1 / (2 * pi*sigma^4));
		xxGauKernel(i + W + 1, j + W + 1) = (1 - (j*j) / (sigma*sigma))*exp(-1 * (i*i + j*j) / (2 * sigma*sigma))*(-1 / (2 * pi*sigma^4));
		xyGauKernel(i + W + 1, j + W + 1) = ((i*j))*exp(-1 * (i*i + j*j) / (2 * sigma*sigma))*(1 / (2 * pi*sigma^6));
        yGauKernel(i + W + 1, j + W + 1) = ( - i / (sigma*sigma))*exp(-1 * (i*i + j*j) / (2 * sigma*sigma))*(1 / (2 * pi*sigma^2));
        xGauKernel(i + W + 1, j + W + 1) = ( - j / (sigma*sigma))*exp(-1 * (i*i + j*j) / (2 * sigma*sigma))*(1 / (2 * pi*sigma^2));
    end
end

image = double(image);
xxG = imfilter(image, xxGauKernel, 'replicate');% ;
xyG = imfilter(image, xyGauKernel, 'replicate');% ;
yyG = imfilter(image, yyGauKernel, 'replicate');% ;
xG = imfilter(image, xGauKernel, 'replicate');% ;
yG = imfilter(image, yGauKernel, 'replicate');% 

line = zeros(2, Sz(2));
% 精细中心提取
for i = 2:Sz(2)-1   
    if i == 37
       i = 37; 
    end
    y0 = (Range_vertical(2, i-1) + Range_vertical(1, i-1))/2;
    y1 = (Range_vertical(2, i) + Range_vertical(1, i))/2;
    y2 = (Range_vertical(2, i+1) + Range_vertical(1, i+1))/2;
    if (y0==0 || y1==0 || y2==0)|| abs(y1 - y0) > 5 || abs(y1 - y2) > 5 || LineW(i) == 0
        continue;
    else  
        tempx = round(CenterCord(1, i)); 
        tempy = round(CenterCord(2, i));
    if tempx == 0 && tempy == 0 
        continue;
    end
        
    mx_1order = xG(tempy, tempx);
    my_1order = yG(tempy, tempx);
    % 二阶偏导
    mx2_2order = xxG(tempy, tempx);
    mxy_2order = xyG(tempy, tempx);
    my2_2order = yyG(tempy, tempx); 
    hess = [mx2_2order mxy_2order;
            mxy_2order my2_2order;];
    [V, D] = eig(hess);   
    if abs(D(1, 1)) >= abs(D(2, 2))
       norm = V(:, 1) ;
       lamda1 = D(2, 2);        
       lamda2 = D(1, 1);     
    else
       norm = V(:, 2) ; 
       lamda1 = D(1, 1);     
       lamda2 = D(2, 2);     
    end
    lamda = [lamda1; lamda2];  
    t = -(norm(1)*mx_1order + norm(2)*my_1order)/ ...
         (norm(1)^2*mx2_2order + 2*norm(1)*norm(2)*mxy_2order + norm(2)^2*my2_2order); 
               
    if lamda(2) < 0 && abs(lamda(2))>(1 + abs(lamda(1)))        
          line(:, i) = [tempx - t*norm(1); tempy - t*norm(2)];  
    end              
    end
end 

% 检查
toc;
figure;
imshow(image, []);
hold on
plot(line(1, :), line(2, :));

主要参考有两个,一个是综述,一个是法线法的算法实现代码;都具有一定的参考意义,这里也贴出来仅供大家参考。
1:线结构光中心提取综述
2:光条中心线提取-Steger算法(基于Hessian矩阵)

有关【图像处理】海森矩阵(Hessian Matrix)及用例(基于Steger的中心提取_含代码)的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  4. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  5. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  6. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  7. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  8. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  9. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  10. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

随机推荐