Matlab数字图像处理学习记录【8】——图像分割

2023-05-16

图像分割

  • 一.点、线和边缘检测
    • 1.1 点检测
    • 1.2 线检测
    • 1.3 使用edge函数进行边缘检测
  • 二.使用Hough(霍夫)变换的线检测
    • 2.1 使用Hough变换做峰值检测
    • 2.2 使用Hough变换做线检测和连接
  • 三.阈值处理
    • 3.1 全局阈值处理
    • 3.2 局部阈值处理
  • 四.基于区域的分割
    • 4.1 基础公式
    • 4.2 区域生长
    • 4.3 区域的分离与合并
  • 五.使用分水岭变换分割
    • 5.1 使用距离变换的分水岭分割
    • 5.2 使用梯度的分水岭分割
    • 5.3 用控制标记符的分水岭分割

一.点、线和边缘检测

用于查找不连续的最常用的方法是对图像运行掩膜计算。就是将掩膜区与所包含的图像的灰度级乘积的和。

1.1 点检测

在该模板下,给定一个非负阈值,设|R|≥T则可以说该点被检测出来:
在这里插入图片描述
对于掩膜最重要的要求是,在一个孤立点的时候,掩膜的响应必须最强,而在亮度不变的区域中,响应为零。
可以通过该方法实现g = abs(imfilter(double(f), w)) >= T
为了便于观察,图片被放大:
在这里插入图片描述

f = imread('./point.tif');
w = [-1 -1 -1; -1 8 -1; -1 -1 -1];
subplot(1, 2, 1);
imshow(f);
g = abs(imfilter(double(f), w));
T = max(g(:));
g = g >= T;
subplot(1, 2, 2);
imshow(g);

当然还有的点检测方法是在大小为m*n的所有领域中寻找一些点,他们的最大值和最小值之差超过了T,该方法可以通过g = imsubtract(ordfilt2(f, m*n, ones(m, n)), ordfilt2(f, 1, ones(m*n))); g = g >= T实现。

1.2 线检测

也是通过掩膜计算,其掩膜为:
在这里插入图片描述
这个效果就不展示了。

1.3 使用edge函数进行边缘检测

图像中的边缘检测最好的方法还是检测亮度值的不连续性,不连续性意味着一阶二阶导数变化的比较大。那么二维函数的梯度可以写为:
∇ f = [ G x G y ] = [ ∂ f ∂ x ∂ f ∂ x ] ∇f = \left[ \frac{G_x}{Gy} \right]=\left[ \frac{\frac{∂f}{∂x}}{\frac{∂f}{∂x}} \right] f=[GyGx]=[xfxf]
其幅值为:
∇ f = m a g ( ∇ f ) = [ G x 2 + G y 2 ] 1 2 ∇f = mag(∇f) = [G_x^2 + G_y^2]^{\frac{1}{2}} f=mag(f)=[Gx2+Gy2]21
为了简化计算可以直接近似于其绝对值或平方和。
二阶导数一般用于拉普拉斯算子计算。但拉普拉斯算子自身很少被直接用做边缘检测,因为二阶导数对噪声具有无法接受的敏感性,它的幅度会产生双边缘,而且它不能检测边缘的方向。然而,正像本节稍后讨论的那样,当与其他边缘检测技术组合使用时,拉普拉斯算子是一种有效的补充方法。例如,虽然它的双边缘使得它不适合直接用于边缘检测,但该性质可用于边缘定位。
所以 边缘检测的方法为:

  1. 找到亮度的一阶导数在幅度上比指定的阈值大的地方。
  2. 找到亮度的二阶导数有零交叉的地方。

所以edge函数就基于该方法,其中语法为:[g, t] = edge(f, 'method', parameters)
其中一些边缘检测器为:
在这里插入图片描述
其掩膜为:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Sobel算子为例, 其调用语法为:[g, t] = edge(f, 'sobel', T, dir)
其中,f是输入图像,T是一个指定的阈值,dir指定检测边缘的首选方向: ‘horizontal’、‘vertical’或’ both’(默认值)。如先前说明的那样,g是在被检测到边缘的位置处为1而在其他位置为0的逻辑类图像。输出参数t是可选的,它是函数edge所用的阈值。若指定了T的值,则t=T。否则,若T未被赋值(或为空,[]),则函数edge会令t等于它自动确定的一个阈值,然后用于边缘检测。
在输出参量中要包括t的主要原因之一是为了得到一个阈值的初始值。若使用语法g = edge(f)[g,t] = egde (f),则函数edge会默认使用Sobel检测器。

二.使用Hough(霍夫)变换的线检测

大一的时候,我自己给出的一个理解,在这里:PythonCV学习记录9——霍夫变换
然后hough变换得通过自己的代码实现:

function [h, theta, rho] = hough(f, dtheta, drho)
%HOUGH Hough transform.
%   [H, THETA, RHO] = HOUGH(F, DTHETA, DRHO) computes the Hough
%   transform of the image F.  DTHETA specifies the spacing (in
%   degrees) of the Hough transform bins along the theta axis.  DRHO
%   specifies the spacing of the Hough transform bins along the rho
%   axis.  H is the Hough transform matrix.  It is NRHO-by-NTHETA,
%   where NRHO = 2*ceil(norm(size(F))/DRHO) - 1, and NTHETA =
%   2*ceil(90/DTHETA).  Note that if 90/DTHETA is not an integer, the
%   actual angle spacing will be 90 / ceil(90/DTHETA).
%
%   THETA is an NTHETA-element vector containing the angle (in
%   degrees) corresponding to each column of H.  RHO is an
%   NRHO-element vector containing the value of rho corresponding to
%   each row of H. 
%
%   [H, THETA, RHO] = HOUGH(F) computes the Hough transform using
%   DTHETA = 1 and DRHO = 1. 

%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.4 $  $Date: 2003/10/26 22:33:44 $

if nargin < 3
   drho = 1;
end
if nargin < 2
   dtheta = 1;
end

f = double(f);
[M,N] = size(f);
theta = linspace(-90, 0, ceil(90/dtheta) + 1);
theta = [theta -fliplr(theta(2:end - 1))];
ntheta = length(theta);

D = sqrt((M - 1)^2 + (N - 1)^2);
q = ceil(D/drho);
nrho = 2*q - 1;
rho = linspace(-q*drho, q*drho, nrho);

[x, y, val] = find(f);
x = x - 1;  y = y - 1;

% Initialize output.
h = zeros(nrho, length(theta));

% To avoid excessive memory usage, process 1000 nonzero pixel
% values at a time.
for k = 1:ceil(length(val)/1000)
   first = (k - 1)*1000 + 1;
   last  = min(first+999, length(x));
   
   x_matrix     = repmat(x(first:last), 1, ntheta);
   y_matrix     = repmat(y(first:last), 1, ntheta);
   val_matrix   = repmat(val(first:last), 1, ntheta);
   theta_matrix = repmat(theta, size(x_matrix, 1), 1)*pi/180;
   
   rho_matrix = x_matrix.*cos(theta_matrix) + ...
       y_matrix.*sin(theta_matrix);
   slope = (nrho - 1)/(rho(end) - rho(1));
   rho_bin_index = round(slope*(rho_matrix - rho(1)) + 1);
   
   theta_bin_index = repmat(1:ntheta, size(x_matrix, 1), 1);
   
   % Take advantage of the fact that the SPARSE function, which
   % constructs a sparse matrix, accumulates values when input
   % indices are repeated.  That抯 the behavior we want for the
   % Hough transform.  We want the output to be a full (nonsparse)
   % matrix, however, so we call function FULL on the output of
   % SPARSE.
   h = h + full(sparse(rho_bin_index(:), theta_bin_index(:), ...
                       val_matrix(:), nrho, ntheta));
end

画出每一个点的关于ρθ的函数线:

f = zeros(101, 101);
f(1, 1) = 1;
f(101, 1) = 1;
f(1, 101) = 1;
f(101, 101) = 1;
f(51, 51) = 1;
[H, theta, rho] = hough(f);
imshow(H, [ ]);

最终的效果是这样:
在这里插入图片描述
其中曲线的交点,就是图像中的点。

2.1 使用Hough变换做峰值检测

其原理为:

  1. 找到包含有最大值的Hough变换单元并记下它的位置。
  2. 把第一步中找到的最大值点的邻域中的Hough变换单元设为零。
  3. 重复该步骤,直到找到需要的峰值数时为止,或者达到一-个指定的阈值时为止。

自定义函数:houghpeaks

function [r, c, hnew] = houghpeaks(h, numpeaks, threshold, nhood)
%HOUGHPEAKS Detect peaks in Hough transform.
%   [R, C, HNEW] = HOUGHPEAKS(H, NUMPEAKS, THRESHOLD, NHOOD) detects
%   peaks in the Hough transform matrix H.  NUMPEAKS specifies the
%   maximum number of peak locations to look for.  Values of H below
%   THRESHOLD will not be considered to be peaks.  NHOOD is a
%   two-element vector specifying the size of the suppression
%   neighborhood.  This is the neighborhood around each peak that is
%   set to zero after the peak is identified.  The elements of NHOOD
%   must be positive, odd integers.  R and C are the row and column
%   coordinates of the identified peaks.  HNEW is the Hough transform
%   with peak neighborhood suppressed. 
%
%   If NHOOD is omitted, it defaults to the smallest odd values >=
%   size(H)/50.  If THRESHOLD is omitted, it defaults to
%   0.5*max(H(:)).  If NUMPEAKS is omitted, it defaults to 1. 

%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.5 $  $Date: 2003/11/21 13:34:50 $

if nargin < 4
   nhood = size(h)/50;
   % Make sure the neighborhood size is odd.
   nhood = max(2*ceil(nhood/2) + 1, 1);
end
if nargin < 3
   threshold = 0.5 * max(h(:));
end
if nargin < 2
   numpeaks = 1;
end

done = false;
hnew = h; r = []; c = [];
while ~done
   [p, q] = find(hnew == max(hnew(:)));
   p = p(1); q = q(1);
   if hnew(p, q) >= threshold
      r(end + 1) = p; c(end + 1) = q;

      % Suppress this maximum and its close neighbors.
      p1 = p - (nhood(1) - 1)/2; p2 = p + (nhood(1) - 1)/2;
      q1 = q - (nhood(2) - 1)/2; q2 = q + (nhood(2) - 1)/2;
      [pp, qq] = ndgrid(p1:p2,q1:q2);
      pp = pp(:); qq = qq(:);

      % Throw away neighbor coordinates that are out of bounds in
      % the rho direction.
      badrho = find((pp < 1) | (pp > size(h, 1)));
      pp(badrho) = []; qq(badrho) = [];

      % For coordinates that are out of bounds in the theta
      % direction, we want to consider that H is antisymmetric
      % along the rho axis for theta = +/- 90 degrees.
      theta_too_low = find(qq < 1);
      qq(theta_too_low) = size(h, 2) + qq(theta_too_low);
      pp(theta_too_low) = size(h, 1) - pp(theta_too_low) + 1;
      theta_too_high = find(qq > size(h, 2));
      qq(theta_too_high) = qq(theta_too_high) - size(h, 2);
      pp(theta_too_high) = size(h, 1) - pp(theta_too_high) + 1;

      % Convert to linear indices to zero out all the values.
      hnew(sub2ind(size(hnew), pp, qq)) = 0;

      done = length(r) == numpeaks;
   else
      done = true;
   end
end

2.2 使用Hough变换做线检测和连接

一旦在Hough变换中识别出了一组候选的峰被,则它们还要留待确定是否存在与这些峰值相关的线段以及它们的起始和结束位置。对每一个峰值来说,第一步是找到图像中影响到峰值的每一个非零值点的位置。为达到这一目的,我们编写了函数houghpixels

function [r, c] = houghpixels(f, theta, rho, rbin, cbin)
%HOUGHPIXELS Compute image pixels belonging to Hough transform bin.
%   [R, C] = HOUGHPIXELS(F, THETA, RHO, RBIN, CBIN) computes the
%   row-column indices (R, C) for nonzero pixels in image F that map
%   to a particular Hough transform bin, (RBIN, CBIN).  RBIN and CBIN
%   are scalars indicating the row-column bin location in the Hough
%   transform matrix returned by function HOUGH.  THETA and RHO are
%   the second and third output arguments from the HOUGH function.

%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.4 $  $Date: 2003/10/26 22:35:03 $

[x, y, val] = find(f);
x = x - 1; y = y - 1;

theta_c = theta(cbin) * pi / 180;
rho_xy = x*cos(theta_c) + y*sin(theta_c);
nrho = length(rho);
slope = (nrho - 1)/(rho(end) - rho(1));
rho_bin_index = round(slope*(rho_xy - rho(1)) + 1);

idx = find(rho_bin_index == rbin);

r = x(idx) + 1; c = y(idx) + 1;

然后通过这段代码找到的相关像素组合成线段,所以得再编写一个houghlines,其过程为:

  1. 将像素位置旋转908-0,以便它们大概位于一条垂直线上。
  2. 按旋转的x值来对这些像素位置分排序。
  3. 使用函数diff找到裂口。忽视掉小裂口;这将合并被小空白分离的相邻线段。
  4. 返回比最小阈值长的线段的信息。
function lines = houghlines(f,theta,rho,rr,cc,fillgap,minlength)
%HOUGHLINES Extract line segments based on the Hough transform.
%   LINES = HOUGHLINES(F, THETA, RHO, RR, CC, FILLGAP, MINLENGTH)
%   extracts line segments in the image F associated with particular
%   bins in a Hough transform.  THETA and RHO are vectors returned by
%   function HOUGH.  Vectors RR and CC specify the rows and columns
%   of the Hough transform bins to use in searching for line
%   segments.  If HOUGHLINES finds two line segments associated with
%   the same Hough transform bin that are separated by less than
%   FILLGAP pixels, HOUGHLINES merges them into a single line
%   segment.  FILLGAP defaults to 20 if omitted.  Merged line
%   segments less than MINLENGTH pixels long are discarded.
%   MINLENGTH defaults to 40 if omitted. 
%
%   LINES is a structure array whose length equals the number of
%   merged line segments found.  Each element of the structure array
%   has these fields: 
%
%      point1    End-point of the line segment; two-element vector
%      point2    End-point of the line segment; two-element vector
%      length    Distance between point1 and point2
%      theta     Angle (in degrees) of the Hough transform bin
%      rho       Rho-axis position of the Hough transform bin

%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.4 $  $Date: 2003/10/26 22:34:10 $

if nargin < 6 
   fillgap = 20; 
end
if nargin < 7 
   minlength = 40; 
end

numlines = 0; lines = struct;
for k = 1:length(rr)
   rbin = rr(k); cbin = cc(k);
   
   % Get all pixels associated with Hough transform cell.
   [r, c] = houghpixels(f, theta, rho, rbin, cbin);
   if isempty(r) 
      continue 
   end
   
   % Rotate the pixel locations about (1,1) so that they lie
   % approximately along a vertical line.
   omega = (90 - theta(cbin)) * pi / 180;
   T = [cos(omega) sin(omega); -sin(omega) cos(omega)];
   xy = [r - 1  c - 1] * T;
   x = sort(xy(:,1));
   
   % Find the gaps larger than the threshold.
   diff_x = [diff(x); Inf];
   idx = [0; find(diff_x > fillgap)];
   for p = 1:length(idx) - 1
      x1 = x(idx(p) + 1); x2 = x(idx(p + 1));
      linelength = x2 - x1;
      if linelength >= minlength
         point1 = [x1 rho(rbin)]; point2 = [x2 rho(rbin)];
         % Rotate the end-point locations back to the original
         % angle.
         Tinv = inv(T);
         point1 = point1 * Tinv; point2 = point2 * Tinv;
         
         numlines = numlines + 1;
         lines(numlines).point1 = point1 + 1;
         lines(numlines).point2 = point2 + 1;
         lines(numlines).length = linelength;
         lines(numlines).theta = theta(cbin);
         lines(numlines).rho = rho(rbin);
      end
   end
end

举个例子:

f = imread('buliding.tif');
[H, theta, rho] = hough(f);
[r, c, hnew] = houghpeaks(H, 5);
lines = houghlines(f, theta, rho, r, c);
imshow(f), hold on
for k = 1:length(lines)
    xy = [lines(k).point1; lines(k).point2];
    plot(xy(:, 2), xy(:, 1), 'Linewidth', 4, 'Color', [.6 .6 .6]);
end

在这里插入图片描述

三.阈值处理

除了我们手动指定阈值,还可以将阈值算法写入程序中,让他自定义计算阈值

3.1 全局阈值处理

选取阈值的一种方法是目视检查直方图。所示的直方图有两个不同的模式;正如结果所示,我们可很容易地选取一个阈值T来分开它们。另一种选择T的方法是反复试验,挑选不同的阈值,直到观测者觉得产生了较好的结果时为止。这在交互式环境下特别有效,例如,这种方法允许用户使用一个图形控件(如滑动条)来改变阈值并可立即看到结果。比如图示这样的时候:
在这里插入图片描述
当然算法可以这样迭代:

  1. 为T选一个初始估计值(建议初始估计值为图像中最大亮度值和最小亮度值的中间值)。
  2. 使用T分割图像。这会产生两组像素:亮度值≥T的所有像素组成的G1,亮度值<T的所有像素组成的G0
  3. 计算G1和G0,范围内的像素的平均亮度值 u1和u0
  4. 计算新阈值: T = 0.5*(u1+u0)
  5. 重复步骤2到步骤4,直到连续迭代中T的差比预先指定的参数To小为止。
    当然,工具箱提供函数graythresh函数,通过Otsu方法计算阈值。具体过程可以百度,是计算选择最大类间方差σB2的阈值k实现的,调用方法:T = graythresh(f)

比如这个图,它的直方图为:
在这里插入图片描述
我们通过全局阈值算法或者Otsu算法来计算阈值:

f = imread('calc.tif');
T = 0.5*(double(min(f(:))) + double(max(f(:))));
done = false;
while ~done
    g = f >= T;
    Tnext = 0.5*(mean(f(g)) + mean(f(~g)));
    done = abs(T - Tnext) < 0.5;
    T = Tnext;
end
T2 = graythresh(f) * 255;
display([T, T2]);
% 140.5727  140.0000

3.2 局部阈值处理

有些情况,全局阈值处理方法在背景照明不均匀时有可能无效。在这种情况下,一种常用的处理方法是针对照明问题做预处理以补偿图像,然后再对预处理后的图像采用全局阈值处理。通过应用一个形态学顶帽算子并对得到的结果使用函数graythresh来计算的。我们可以证明这种处理等同于使用局部变化的阈值函数T(x,y)对f(x, y)进行阈值处理:
g ( x , y ) = x = { 1 若 f ( x , y ) ≥ T ( x , y ) 0 若 f ( x , y ) < T ( x , y ) g(x,y) = x = \begin{cases} 1 &\text{若} f(x,y)≥T(x,y) \\ 0 &\text{若} f(x,y)<T(x,y) \end{cases} g(x,y)=x={10f(x,y)T(x,y)f(x,y)<T(x,y)
其中:
T ( x , y ) = f o ( x , y ) + T o T(x,y)=f_o(x,y)+T_o T(x,y)=fo(x,y)+To
图像fo(x,y)是形态学开运算,常数To是对fo应用函数garythresh后的结果。

四.基于区域的分割

前面的区域分割是基于灰度级的不连续性来查找区域之间的边界。而该节则是使用像素的属性分布(亮度值等)的阈值来完成的。

4.1 基础公式

若用R表示整个图像区域。则可以将分割看成R的n个子区域,记为R1,R2……Rn,且他们满足:

  1. R1∪R2∪……∪Rn = R
  2. Ri是连接区域,i = 1,2,……,n
  3. Ri∩Rj=∅,i≠j
  4. P(Ri) = True,i = 1,2,……,n
  5. P(Ri∪Rj) = False, 对于任何邻接区域的Ri和Rj

这些条件指明:
条件1指出分割必须是完全的,即每个点都必须在一个区域中。条件2要求区域中的点应该是按预先定义好的方式(如4连接或8连接)连接的。条件3说明区域之间不相交。条件4说明分割区域中的像素点必须满足的性质——例如,若所有Ri中的像素有相同的灰度级,则P(Ri)=TRUE。最后,条件5指出,邻近区域Ri和Rj在谓词P上的意义是不-样的

4.2 区域生长

顾名思义,区域生长是根据预先定义的生长准则来把像素或子区域集合成较大区域的处理方法。基本处理方法是以–组“种子”点开始来形成生长区域,即将那些预定义属性类似于种子的邻域像素附加到每个种子上(如指定的灰度级或颜色)。那么就类似于前面的形态学的重构。那么我们就需要知道开始成长的条件,以及结束成长的条件和成长过程中的迭代算法。
由一个或多个开始点组成的集合的选择通常可基于问题的性质。当没有先验的信息可用时,一种处理方法是:在每一个像素上计算网一组属性,在生长过程期间,这些属性最终将用于把像素分配到区域中。若这些计算的结果显示了一簇值,则应把具有这些特性的像系放在可以作为种子的这些簇的质心。
若在区域生长处理中未使用连通性(邻接)信息,则描述符会严生销误的错误。例知,仅使用三个不同的亮度值来显示像素的一个随机排列。若不考虑连通性而组合有着相同灰度级的像素以形成一个“区域”,则会产生无意义的分割结果。
停止规则的公式表达。一般来说,当不再有像素满足该区域所包含的准则时,生长区域的过程就会停止。准则(如亮度值、纹理、色彩)实际上是局部的,在区域生长的“历史”中不予考虑。增加区域生长算法能力的附加准则利用了大小的概念,如被生长像素和候选像素之间的相似性(如候选像素的亮度与生长区域的平均亮度的比较),正生长区域的形状等。这些类型的描述符的应用基于这样一个假设,即期望结果的模型至少是部分可用的。
所以书上写个一个regiongrow函数:
[g, NR, SI, TI] = regiongrow(f, S, T)
其中f是被分割的图像,S是一个与f大小相同的数组或是一个标量。数组中的1则是种子。若是标量,则表示一个亮度值,所有等于该亮度值的像素点都会成为种子。T也是一个与f相同大小的数组或者标量。若T是数组,则是为每一个像素点确定一个阈值,若是标量,则是全局阈值。
在输出中, g是分割后的图像,每个区域的成员都用整数标出。参数NR是不同区域的数目。参数SI是一幅包含有种子点的图像,参数TI是一幅图像,该图像中包含在经过连通性处理前通过阈值测试的像素。SI和TI的大小均与f相同。

function [g, NR, SI, TI] = regiongrow(f, S, T)
%REGIONGROW Perform segmentation by region growing.
%   [G, NR, SI, TI] = REGIONGROW(F, SR, T).  S can be an array (the
%   same size as F) with a 1 at the coordinates of every seed point
%   and 0s elsewhere.  S can also be a single seed value. Similarly,
%   T can be an array (the same size as F) containing a threshold
%   value for each pixel in F. T can also be a scalar, in which
%   case it becomes a global threshold.   
%
%   On the output, G is the result of region growing, with each
%   region labeled by a different integer, NR is the number of
%   regions, SI is the final seed image used by the algorithm, and TI
%   is the image consisting of the pixels in F that satisfied the
%   threshold test. 

%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.4 $  $Date: 2003/10/26 22:35:37 $

f = double(f);
% If S is a scalar, obtain the seed image.
if numel(S) == 1
   SI = f == S;
   S1 = S;
else
   % S is an array. Eliminate duplicate, connected seed locations 
   % to reduce the number of loop executions in the following 
   % sections of code.
   SI = bwmorph(S, 'shrink', Inf);  
   J = find(SI);
   S1 = f(J); % Array of seed values.
end
 
TI = false(size(f));
for K = 1:length(S1)
   seedvalue = S1(K);
   S = abs(f - seedvalue) <= T;
   TI = TI | S;
end

% Use function imreconstruct with SI as the marker image to
% obtain the regions corresponding to each seed in S. Function
% bwlabel assigns a different integer to each connected region.
[g, NR] = bwlabel(imreconstruct(SI, TI));

该例子是通过全局阈值法来设置种子点,然后在进行生长的结果:
在这里插入图片描述

4.3 区域的分离与合并

我们刚刚讨论过从一组种子点来生长区域的过程。另一种方法是再把图像细分为一组任意且互不相连的区域,然后在试图满足基础公式节规定的条件下合并或分离这些区域。
令R代表整个图像区域并选择一个谓词P。分割R的一种方法是把它再细分为更小的象限区域,以便对任何区域Ri都有P(Ri) = TRUE。我们从整个区域开始。若P(Ri)=FALSE,则把图像分成象限。若对任何一个象限来说Р都是FALSE,则再把象限细分为子象限,依次类推。这种特别的分离技术有一种方便的表示方法——四叉树,即一棵树,树中的每一个节点都恰好有四个后代,如图10.16所示(对应于四叉树的节点有时称为四叉区域或四叉图像)。注意,树的根对应于整个图像,每个节点对应于一个再分为四个后代节点的节点分支。在这种情况下,只有R4被进一步细分了。
若仅使用了分离,则最终的部分通常包含具有相同性质的邻近区域。这种缺点可通过合并以及分离来弥补。若要满足10.4.1节中的约束条件,则要求只合并邻近区域,组合的像素满足谓词Р。换言之,仅当P(Rj∪Rk)= TRUE时,两个邻近区域Rj和Rk才会合并。
在这里插入图片描述
即过程为:

  1. 把任意区域Ri分为4个不相连的象限,其中满足P(Ri)=False
  2. 当不可能再分时,合并任何满足P(Rj∪Rk)=True的区域Rj和Rk
  3. 发现不能进一步合并时,停止

IPT中实现四叉树分解的函数是qtdecomp。其语法为:s = qtdecomp (f, split_test, parameters)

其中,f是输人图像,s是包含四叉树结构的稀疏矩阵。若s ( k , m)非零,则(k, m)是分解中的一个左上角块,且该块的大小是s (k, m)。函数split_test(见下例中的函数splitmerge)用来确定一个区域是否被分离,parameters(用逗号分开)是函数split_test所要求的附加参数这种机理和前面所讨论的函数coltfilt相似。
为了在四叉树分解中得到实际的四叉区域像素值,我们使用函数qtgetblk,其语法为[ vals, r, c] - qtgetblk (f, s, m)
其中,vals是一个数组,它包含f的四叉树分解中大小为m x m的块的值,s是由qtdecomp返回的稀疏矩阵。参数r和c是包含左上角块的行坐标和列坐标的向量。
我们通过编写一个基本的分离和合并M函数来说明函数qtdecomp的用法,该函数使用前面讨论的简化,若其中的两个区域均满足谓词,则这两个区域将被合并。我们所调用的函数splitmerge的语法为:g splitmerge (f, mindim, predicate)
其中,f是输入图像,g是输出图像,其中的每一个连接区域都用不同的整数来标注。参数mindim定义分解中所允许的最小块;该参数必须是2的正整数次幂。
函数predicate是一个用户定义的函数,它必须包括在MATLAB路径中。其语法为flag =spredicate (region)
region中的像素满足函数中由代码所定义的谓词,则函数就必须被写成返回true(逻辑1);否则,flag 的值就必须为false(逻辑0)。

function g = splitmerge(f, mindim, fun)
%SPLITMERGE Segment an image using a split-and-merge algorithm.
%   G = SPLITMERGE(F, MINDIM, @PREDICATE) segments image F by using a
%   split-and-merge approach based on quadtree decomposition. MINDIM
%   (a positive integer power of 2) specifies the minimum dimension
%   of the quadtree regions (subimages) allowed. If necessary, the
%   program pads the input image with zeros to the nearest square  
%   size that is an integer power of 2. This guarantees that the  
%   algorithm used in the quadtree decomposition will be able to 
%   split the image down to blocks of size 1-by-1. The result is  
%   cropped back to the original size of the input image. In the  
%   output, G, each connected region is labeled with a different
%   integer.
%
%   Note that in the function call we use @PREDICATE for the value of 
%   fun.  PREDICATE is a function in the MATLAB path, provided by the
%   user. Its syntax is
%
%       FLAG = PREDICATE(REGION) which must return TRUE if the pixels
%       in REGION satisfy the predicate defined by the code in the
%       function; otherwise, the value of FLAG must be FALSE.
% 
%   The following simple example of function PREDICATE is used in 
%   Example 10.9 of the book.  It sets FLAG to TRUE if the 
%   intensities of the pixels in REGION have a standard deviation  
%   that exceeds 10, and their mean intensity is between 0 and 125. 
%   Otherwise FLAG is set to false. 
%
%       function flag = predicate(region)
%       sd = std2(region);
%       m = mean2(region);
%       flag = (sd > 10) & (m > 0) & (m < 125);

%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.6 $  $Date: 2003/10/26 22:36:01 $

% Pad image with zeros to guarantee that function qtdecomp will
% split regions down to size 1-by-1.
Q = 2^nextpow2(max(size(f)));
[M, N] = size(f);
f = padarray(f, [Q - M, Q - N], 'post');

%Perform splitting first. 
S = qtdecomp(f, @split_test, mindim, fun);

% Now merge by looking at each quadregion and setting all its 
% elements to 1 if the block satisfies the predicate.

% Get the size of the largest block. Use full because S is sparse.
Lmax = full(max(S(:)));
% Set the output image initially to all zeros.  The MARKER array is
% used later to establish connectivity.
g = zeros(size(f));
MARKER = zeros(size(f));
% Begin the merging stage.
for K = 1:Lmax 
   [vals, r, c] = qtgetblk(f, S, K);
   if ~isempty(vals)
      % Check the predicate for each of the regions
      % of size K-by-K with coordinates given by vectors
      % r and c.
      for I = 1:length(r)
         xlow = r(I); ylow = c(I);
         xhigh = xlow + K - 1; yhigh = ylow + K - 1;
         region = f(xlow:xhigh, ylow:yhigh);
         flag = feval(fun, region);
         if flag 
            g(xlow:xhigh, ylow:yhigh) = 1;
            MARKER(xlow, ylow) = 1;
         end
      end
   end
end

% Finally, obtain each connected region and label it with a
% different integer value using function bwlabel.
g = bwlabel(imreconstruct(MARKER, g));

% Crop and exit
g = g(1:M, 1:N);

%-------------------------------------------------------------------%
function v = split_test(B, mindim, fun)
% THIS FUNCTION IS PART OF FUNCTION SPLIT-MERGE. IT DETERMINES 
% WHETHER QUADREGIONS ARE SPLIT. The function returns in v 
% logical 1s (TRUE) for the blocks that should be split and 
% logical 0s (FALSE) for those that should not.

% Quadregion B, passed by qtdecomp, is the current decomposition of
% the image into k blocks of size m-by-m.

% k is the number of regions in B at this point in the procedure.
k = size(B, 3);

% Perform the split test on each block. If the predicate function
% (fun) returns TRUE, the region is split, so we set the appropriate
% element of v to TRUE. Else, the appropriate element of v is set to
% FALSE.
v(1:k) = false;
for I = 1:k
   quadregion = B(:, :, I);
   if size(quadregion, 1) <= mindim
      v(I) = false;
      continue
   end
   flag = feval(fun, quadregion);
   if flag
      v(I) = true;
   end
end

用该函数实现的效果如下:
在这里插入图片描述

五.使用分水岭变换分割

理解分水岭变换要求我们把灰度级图像看做是一个拓扑表面,其中f (x,y)的值是被解释为高度。例如,我们可以把图10.18(a)所示的简单图像看成是图10.18(b)所示的三维表面。若雨水降落在该表面上,则雨水将流向标注为汇水盆地的两个区域中。若雨水恰好降落在标注的分水岭脊线上,则雨水流向两个汇水盆地的概率是相同的。分水岭变换会在灰度级图像中找到汇水盆地和脊线。在求解图像问题方面,关键概念是将初始图像变成另一幅图像,在变换后的图像中,汇水盆地就是我们想要识别的对象或区域。
在这里插入图片描述

5.1 使用距离变换的分水岭分割

在分割中,与分水岭变换配合使用的常用工具是距离变换。二值图像的距离变换是一个相对简单的概念:它是每一个像素到最近非零值像素的距离。图10.19示例了距离变换。图10.19(a)显示了一个小的二值图像矩阵。图10.19(b)显示了相应的距离变换。注意,值为1的像素的距离变换为0。距离变换可以使用IPT 函数bwdist加以计算,该函数的调用语法为D=bwdist(f),效果为这样:
在这里插入图片描述
这里举出一个分水岭分割二值图像的例子:

f = imread('disk.tif');
se = strel('disk', 4);
f = imopen(f, se);
subplot(2,3,1);
imshow(f);
g = im2bw(f, graythresh(f));
subplot(2,3,2);
imshow(g);
subplot(2,3,3);
gc = ~g;
imshow(gc);
subplot(2,3,4);
D = bwdist(gc);
imshow(D);
subplot(2,3,5);
L = watershed(-D);
imshow(L);
subplot(2,3,6);
w = L == 0;
g2 = g & ~w;
imshow(g2);

在这里插入图片描述
可能是图的原因,并没有书中的效果:
在这里插入图片描述

5.2 使用梯度的分水岭分割

在为分割使用分水岭变换之间,通常要使用梯度幅度来预处理图像。梯度幅度图像在沿对象的边缘处有较高的像素值,而在其他地方则有较低的像素值。理想情况下,分水岭变换会在沿对象边缘处产生分水岭脊线。下例说明了这一概念。
但并没有例图,所以这个例子并不好实现,思路即为先通过形态学梯度的方法求出梯度,然后再和上面的例子一样,求出分水岭山脊线即可。
在这里插入图片描述

5.3 用控制标记符的分水岭分割

分水岭变换直接用于梯度图像时,噪声和梯度的其他局部不规则性常常会导致过分割。其导致的问题可能会非常严重,以至于产生不可用的结果。按照现在的思路,这将意味着具有大量的分割区域。解决该问题的一种实际方法是把加人一个预处理阶段,以将其他知识带到分割过程中,从而限制允许的区域数目。
用于控制过分割的一种方法基于标记符的概念。标记符是一个属于一幅图像的连接分量。我们希望有一个内部标记符集合(处在每一个感兴趣对象的内部)和一个外部标记符集合(包含在背景中)。这些标记符然后使用下例中描述的过程来修改梯度图像。用于计算内部和外部标记符的方法有许多,其中包括前面描述的线性滤波、非线性滤波及形态学处理。对于特定的应用,我们选择的方法高度依赖于与应用相关的图像的特性。
效果:
在这里插入图片描述

代码:

f = imread('bubble.tif');
fd = double(f);
h = fspecial('sobel');
t = imfilter(fd, h, 'replicate');
g = sqrt(t .^2 + t .^2);
L = watershed(g);
wr = L == 0;
rm = imregionalmin(g);
im = imextendedmin(f, 2);
Lim = watershed(bwdist(im));
fim = f;
fim(im) = 175;
em = Lim == 0;
g2 = imimposemin(g, im | em);
L2 = watershed(g2);
f2 = f;
f2(L2 == 0) = 255;
subplot(2,4,1);
imshow(f);
title('f');
subplot(2,4,2);
imshow(wr);
title('wr');
subplot(2,4,3);
imshow(rm);
title('rm');
subplot(2,4,4);
imshow(fim);
title('fim');
subplot(2,4,5);
imshow(em);
title('em');
subplot(2,4,6);
imshow(L2);
title('L2');
subplot(2,4,7);
imshow(f2);
title('f2');

解释:
rm = imregionalmin(g);该函数计算图像中大量局部最小区域的位置,从而了解为什么函数watershed会产生如此多的小汇水盆地。
图rm中显示的多数局部最小区域位置非常浅,并且表示了与我们的分割问题不相关的细节。为消除这些无关的小区域,我们使用IPT函数imextendedmin,该函数可计算图像中的“低点”集合,即比周围点更深的点的集合(通过某个高度阈值)。
其中,f是一幅灰度级图像,h是高度阙值,im是一幅二值图像,该二值图像的前景像素标记了深局部最小区域的位置。fim = f; fim(im) = 175; em = Lim == 0;
最后两行将在原图像上以灰色气泡的形式叠加扩展的局部最小区域位置,如图fm所示。我们看到,得到的气泡确实很合理地标记了我们想要分割的对象。
图em显显示了二值图像em中的分水岭脊线。因为这些脊线位于由im标记的暗气泡间的中间位置,所以它们应该是很好的外部标记符。
给出内部和外部标记符后,然后就可使用它们来修改梯度图像,方法是使用称为强制最小( minima imposition )的过程。强制最小技术修改了灰度级图像,以便局部最小区域仅出现在标记的位置。其他像素值将按需要“上推”,以便删除其他的局部最小区域。IPT函数imimposemin可实现这种技术。
g2 = imimposemin(f, mask);其中,f是一幅灰度级图像,mask是一幅二值图像,该二值图像的前景像素标记出了输出图像mp中局部最小区域的期望位置。通过在内部和外部标记符的位置覆盖局部最小区域,我们可修改梯度图像:g2 = imimposemin(g, im | em);
然后再对g2 进行分水岭操作,即可得到最后的f2.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Matlab数字图像处理学习记录【8】——图像分割 的相关文章

  • 分页中遇到的一个传值问题

    文章目录 前言一 传入Integer值参数二 传入String值参数 前言 目的 xff1a 在前端传入一个参数对所选的结果进行分页过滤 xff0c 这应该是非常常见的一个需求吧 xff0c 但是如何传Integer值使用mybatis p
  • Python2.7升级版本记录

    文章目录 前言一 环境二 升级步骤1 安装各类依赖库2 编译3 编译安装4 添加软连接5 修改yum安装6 升级pip7 验证 参考 前言 python2 x版本已经废弃了 xff0c 有些软件安装的是会遇到如下提示 xff0c Sorry
  • MySQL笔记

    MySQL Version 5 7 25 一 常见面试问题汇总 1 select for update会锁表还是锁行 如果是纯select是不会加锁的 xff0c 但是这里会加锁 xff0c 而且还是悲观锁 xff0c 但是具体锁表还是锁行
  • mybatis-plus最好用的逻辑删除

    文章目录 前言一 逻辑删除1 添加全局配置2 设置实体中的字段 总结 前言 为了防止人为的因素导致误删除无法恢复的情况所以我们需要逻辑意义上的删除 xff0c 而通常最简便的方式就是打标记 xff0c 这个步骤可以由mybatis plus
  • IDEA Git常用操作

    前言 Git的操作可以使用命令行 xff0c 当然为了偷懒熟练使用IDEA的git未尝不是一个好办法 xff01 一 当前修改的分支想要暂存怎么办 xff1f 这个需要是因为当前修改的分支还没有修改完 xff0c 突然有另外的需求需要去处理
  • Ubuntu14.04安装build-essential失败,包依赖问题如何解决?

    正在读取软件包列表 完成 正在分析软件包的依赖关系树 正在读取状态信息 完成 有一些软件包无法被安装 如果您用的是 unstable 发行版 xff0c 这也许是 因为系统无法达到您要求的状态造成的 该版本中可能会有一些您需要的软件 包尚未
  • 关于需求沟通的一点思考

    作为一名程序员 xff0c 可能会来自各方的需求沟通问题 xff0c 而且更多的时候是横插进来的任务 xff0c 每个人都说这个任务优先级高 xff0c 尽快做 xff0c 是不是有点焦头烂额了 xff0c 马上就开始啪啪敲代码了吗 xff
  • Go同目录下多个main声明会导致编译失败的问题

    问题 xff1a Go同目录下多个main声明会导致编译失败的问题 main redeclared in this block 表示在同级目录下main重复声明 xff0c 在学习中可以依照不同的文件夹进行分割 xff0c 也可以按照如下方
  • 嵌入式debian没有lsusb命令解决

    问题 bash lsusb command not found 解决
  • Python学习笔记-PyQt6状态栏

    QMainWindow有自带的状态栏 xff0c 可以通过statusBar 方法获取自身的状态栏 xff0c 或者通过实例化QStatusBar类 xff0c 然后使用QMainWindow setStatusBar 方法将状态栏添加到主
  • 未完成的IT路停在回车键---2014年末总结篇

    时间都去哪儿了 xff1f 一晃而过 xff0c 越来越能体会到这个词的真实感 特别是过了二十岁 xff0c 这种感觉越来越深刻 xff0c 越来越强烈 xff0c 犹如小编做公交车的时候一直向后排排倒的香樟树 xff0c 还记得有首歌叫时
  • 这一次,VR离我们真的很近

    从高考作文开始 今年号称是VR元年 xff0c 虽然目前VR还没能像手机一样走进千家万户 xff0c 但关于VR设备的关讨论是层出不穷 而今年高考 xff0c 浙江省的作文题就与VR相关 网上购物 视频聊天等在我们生活中越来越普及 有人预言
  • 补.从零开始学习C语言--scanf的%c前为什么加空格

    include lt stdio h gt int main void int i char ch scanf 34 d 34 amp i scanf 34 c 34 amp ch 这行的 C前有个空格 printf 34 i 61 d n
  • svn status 返回值详解

    转http blog linuxphp org archives 652 svn 是在提交前查看本地文本和版本库里面的文件的区别 返回值有许多种具体含义如下 xff1a url 61 L abc c svn已经在 svn目录锁定了abc c
  • ubuntu杀毒软件clamAV运维笔记

    1 安装 xff1a apt get install clamav 2 守护进程安装 xff1a apt get install clamav daemon 3 更新病毒库 xff1a freshclam 或手动下载安装 cvd文件 备注
  • shell 教程一:变量,字符串,传参

    一 xff0c hello shell strong span class pln style color rgb 72 72 76 vi hello span span class pun style color rgb 147 161
  • 树莓派Ubuntu20.04创建虚拟内存文件并设置开机自动启用

    目录 一 检查有没有虚拟内存 二 创建虚拟内存文件并设置权限 三 设置并激活虚拟内存文件 四 设置开机自动启用虚拟内存 五 重启后检测虚拟内存是否正常启用 一 检查有没有虚拟内存 树莓派Ubuntu20 04默认没有虚拟内存 xff0c 可
  • 随着稻香河流继续奔跑 ——致2016

    写在前面 xff0c 2016于我而言 xff0c 是丰收的一年 这一年 xff0c 我收获了能力与本领 xff0c 收获了美丽与自信 xff0c 收获了欣赏和肯定 2017 xff0c 我会不忘来时路 xff0c 继续前行 2016的驿站
  • 浅谈strtok函数的使用心得

    经常使用strtok函数进行文本操作 xff0c 其实他是一个很好用的函数 xff0c 很方便 xff0c 能够简单的实现一行文本的切分操作 xff0c 总结一下使用心得 函数原型char strtok char s const char
  • 局域网内Windows使用RealVNC远程连接CentOS6.5桌面

    1 进入root终端 xff0c 检查是否安装VNC server xff1a rpm q tigervnctigervnc server 2 如果未安装VNC server xff0c 则 xff1a yum install ytiger

随机推荐

  • CentOS7安装tigerVNC

    一 首先系统是已经安装了图形界面 并默认是启动到图形界面 xff0c 如果你的系统没安装图形界面 xff0c 就请给系统安装图形界面 xff1a yum y span class token function groups span spa
  • openstack主要版本亮点

    openstack主要版本亮点 1 Stein 在Stein新增的几十项功能特性中 xff0c 主要亮点有三 xff1a 容器功能的强化 用于支持5G 边缘计算和网络功能虚拟化 xff08 NFV xff09 用例的网络升级功能 资源管理和
  • OpenStack 学习之 OVN : L2网络 ( Logical switches 逻辑交换机)

    OVN Manual install amp Configuration Open vSwitch 官网 参考 OVN学习 xff08 一 xff09 OVN实战一之GNS3操作指南及OVN入门 简单理解和知识 按照 OVN Manual
  • linux vncserver设置及配置自动启动

    VNC 服务端 vncserver 启动VNC vncserver kill num num一般从1开始 因为0被x server占用了 vncpasswd 设置vnc连接密码 要使用VNC图形界面修改 vnc xstartup配置文件中末
  • 【125】Linux 中 ps -ef|grep和ps、grep详解

    一 ps ef grep详解 xff08 原文见公众号python宝 xff09 ps命令将某个进程显示出来 PS是LINUX下最常用的也是非常强大的进程查看命令 grep命令是查找 xff0c 是一种强大的文本搜索工具 xff0c 它能使
  • Pycharm Debug调试(纯干货)

    内容目录 xff08 原文见公众号python宝或www xmmup com xff09 一 打断点二 代码调试三 界面小图标介绍四 控制台介绍 数字转换为大写人民币 import sys import io sys stdout 61 i
  • 【217】#!/usr/bin/env 的意义

    题目部分 xff08 原文见公众号 xff1a python宝 xff09 python宝 https mp weixin qq com mp profile ext action 61 home amp biz 61 MzU5NjIyOT
  • 使用Scrum进行敏捷项目管理

    Scrum是一种敏捷方法 xff0c 旨在指导团队进行产品的迭代和增量交付 通常被称为 敏捷项目管理框架 xff0c 其重点是使用经验过程 xff0c 使团队能够快速 xff0c 有效 xff0c 有效地做出改变 传统的项目管理方法确定了需
  • 【246】Python -继承(父类、子类、super)

    题目部分 xff08 原文见公众号 xff1a python宝 xff09 python宝 xff1a https mp weixin qq com mp profile ext action 61 home amp biz 61 MzU5
  • 【250】Python 的基本数据类型

    题目部分 xff08 原文见公众号 xff1a python宝 xff09 python宝 xff1a https mp weixin qq com mp profile ext action 61 home amp biz 61 MzU5
  • 【252】Python3 常见异常和处理方法

    题目部分 xff08 原文见公众号 xff1a python宝 xff09 python宝 xff1a https mp weixin qq com mp profile ext action 61 home amp biz 61 MzU5
  • 500 : Internal Server Error(jupyter)

    如需转发 xff0c 请注明出处 xff1a 小婷儿的python https www cnblogs com xxtalhr p 10739036 html 一 报错 jupyter notebook能打开目录页 xff0c 但是打不开i
  • 聚类总结(二)聚类性能评估、肘部法则、轮廓系数

    文章目录 一 聚类K的选择规则1 1 肘部法则 Elbow Method1 2 轮廓系数 Silhouette Coefficient 二 聚类性能评估2 1 外部评估 xff08 external evaluation xff09 2 1
  • keil工程的文件

    打开工程前 tree span class token punctuation span project span class token punctuation span uvoptx project span class token p
  • vmware 中减少硬盘vmdk大小

    一般的话 span class token punctuation span 我用一个文件代表所有的磁盘上 span class token punctuation span xxx span class token punctuation
  • 各种 RTOS 对比

    商业解读 RTOS种类是否开源是否免费厂家官网uclinux 并入linux mainline 是是linux基金会linux orgucosII是是Micriumweston embeddeducosIII是是Micriumweston
  • 使用adb命令取出手机中已安装的apk

    1 查看手机中安装的apk列表 xff1a adb shell pm list package 2 根据包名找出apk在内部存储空间的路径 xff1a adb shell pm path com baicells voip 3 使用adb
  • STM32学习过程记录8——蜂鸣器

    零之前言 最近想用无源蜂鸣器来播放曲子 xff0c 但是看了好多博客讲的都是马马虎虎 xff0c 没有讲的太清楚 xff0c 所以我只好自己重新学习了一下 xff0c 音乐发声的原理 xff08 因为硬件基础够啦QAQ xff09 和简谱
  • 软件工程:软件开发生命周期 (SDLC)

    软件构建的基本概念之一 软件开发生命周期模型 或者只是SDLC模型 SDLC 是一个连续的过程 xff0c 从决定启动项目的那一刻开始 xff0c 并在它完全从开发中移除的那一刻结束 没有一个单一的SDLC模型 它们分为主要组 xff0c
  • Matlab数字图像处理学习记录【8】——图像分割

    图像分割 一 点 线和边缘检测1 1 点检测1 2 线检测1 3 使用edge函数进行边缘检测 二 使用Hough 霍夫 变换的线检测2 1 使用Hough变换做峰值检测2 2 使用Hough变换做线检测和连接 三 阈值处理3 1 全局阈值