基于改进区域生长算法的图像分割方法及实现

一、题目与要求

1.1课程设计题目
采用灰度图像(该图像由高分二号卫星拍摄,是武汉市江夏区矿区的部分灰度图像)分别实现几种图像分割方法:
(1) 经典区域生长方法;
(2) 改进的区域生长方法;
(3) 分水岭方法。
1.2题目具体要求
(1)首先,基于经典区域生长方法,实现图像分割;
(2)然后,基于经典区域生长方法,自己查阅文献,设计改进的区域生长算法,实现图像分割;
(3)目视比较分析2种方法的分割效果,概述改进方法分割效果的优点和不足之处;
(4)实现分水岭分割。

二、需求分析

2.1问题分析
图像分割(image segmentation)是按照某些特性(如灰度值、频谱、纹理等)将图像划分成一些区域,在这些区域内其特性是相同的,或者说是均匀的,两个相邻区域彼此特性则是不同的,其间存在着边缘或边界。图像分割从本质上来说是将图象按照区域内的一致性和区域内的不一致性进行分类的过程,可视为一种特殊的图像识别问题。
图像分割主要分为以下几种方法:
(1)特征空间聚类:包括阈值化、尺度空间聚类、均值迁移等;
(2)边缘检测:包括梯度法、Snake模型等;
(3)区域跟踪:包括区域生长法、分裂合并法等;
(4)全局优化:包括Bayes方法、最小描述长度、最小割算法等;
(5)混合方法:包括形态学方法、表面拟合法、边缘-区域法等;
(6)基于识别的方法。
本次研究区域生长(region growing)方法。所谓区域生长法就是从满足检测准则的点(或者已知点)开始,在各个方向上把具有相似性质的像素集合起来构成一个区域。即在每个分割区域找一个种子像素作为生长的起点,再将种子像素周围邻域中与种子像素有相同或相似性质的像素(根据某种事先确定的生长或相似准则判定)合并到种子像素所在区域中。将这些新像素当做新的种子像素继续进行上面的过程,直到再没有满足条件的像素可被包括进来,这样的区域就生长成了。
综上,要解决该问题,通过区域生长方法实现图像分割,即要通过编程确定种子点、生长准则和停止条件。
2.2运行环境
我是在matlab(R2018a版)下完成的。
MATLAB是用于数值计算的软件产品,主要包括以下几个部分:MATLAB程序设计语言、MATLAB工作环境、MATLAB工具箱、MATLAB的应用程序接口等。MATLAB语言既是一种编程环境,又是一种程序设计语言。它的规则更接近于数学表示,因此集成性更好,应用更为简便。MATLAB超强的矩阵计算能力和图像处理工具箱相结合,使得MATLAB在数字图形、图像的处理领域获得广泛的应用。主要图像处理应用领域有:亮度变换与空间滤波、频域处理、图像复原、图像小波处理、图像压缩、形态学图像处理、图像分割、图像表示与描述、对象识别等。
基于此,matlab是能方便完成目标的不错选择。
2.3预处理
观察原图像,可发现图像上较均匀分散有噪声,影响观察及后续操作,故先对图像进行预处理,使用中值滤波去噪。中值滤波能达到噪声被消除,而边缘还完好保留的效果。

A=imread('jx_3.jpg');
imshow(A);
title('原图像');
B=rgb2gray(A);
C=medfilt2(B,[3,3]);      %利用中值滤波消除噪声
figure,imshow(C);
title('去噪后图像');

在这里插入图片描述

三、经典区域生长方法

3.1理论基础
区域生长算法是从单个像素出发,按照一定的生长准则,将具有相似性质的像素合并起来构成同一区域。具体做法如下:
(1) 选出图像中要分割的目标物体内的一点或者说生长起始点;
(2) 在种子区域的基础上不断将其周围的像素点以一定的规则加入其中;
(3) 最终将代表该物体的所有像素点结合成一个区域。
在所有图像中,同一个子区域内部像素会有相似的特性,不同子区域之间像素特性差异一般较大。区域生长算法从种子点开始生长,直到所有符合条件的像素点加入为止。区域生长算法原理很简单,其中最为核心的是生长准则,一般可以表示为一个灰度区间。如果邻域中像素点的灰度值处于这个区间中则被接纳,否则就被拒绝。
整幅图像中所有的像素点分为两类:己检测过的像素点和未被检测过的像素点。首先选择目标区域中一个或多个点(种子区域)加入到已生长区域,并以此作为生长起点,计算已生长区域中所有像素点灰度值平均值和标准差,接着判断目前区域的邻域中是否有符合生长准则的像素点,如果存在就将其划分入已生长区域,这样就完成一次迭代。第一次迭代完成后,重复上述步骤,直到没有满足条件的邻域像素点划分入己生长区域为止,区域生长算法结束。
区域生长算法程序流程图如下:
在这里插入图片描述
3.2算法设计
3.2.1鼠标选取单种子点

I=double(C);              %转换为灰度值是0-1的双精度
[M,N]=size(I);
J=zeros(M,N);             %定义输出图像矩阵,初始为零矩阵
msgbox('请用鼠标单击选择一种子点并按回车键', '操作','modal');
[y,x]=getpts;             %通过鼠标获得区域生长起始点
xr=round(x); 
yr=round(y);
seed=I(xr,yr);
J(xr,yr)=1;               %将J中与所取点相对应位置的点设置为白点
graysum=seed;             %储存符合区域生长条件的点的灰度值的总和
pointsum=1;               %储存符合区域生长条件的点的总个数
count=1;                  %每次判断一点周围八点符合条件的新点的数目
threshold=13;             %阈值
while count>0             %判断是否有新的符合生长条件的点,若没有,则结束
 s=0;                     %记录判断一点周围八点时,符合条件的新点的灰度值之和
 count=0;
 for i=1:M
     for j=1:N
       if J(i,j)==1
        if (i-1)>0 && (i+1)<(M+1) && (j-1)>0 && (j+1)<(N+1)       %判断此点是否为图像边界上的点
         for u= -1:1                                              %判断点周围八点是否符合阈值条件
          for v= -1:1                                             %u,v为偏移量
            if  J(i+u,j+v)==0 && abs(I(i+u,j+v)-seed)<=threshold  %判断是否未存在于输出矩阵J,并且为符合阈值条件的点
               J(i+u,j+v)=1;                                      %将符合以上两条件的点在J中与之位置对应的点设置为白点
               count=count+1;   
               s=s+I(i+u,j+v);
            end
          end  
         end
        end
       end
     end
 end
 pointsum=pointsum+count;
 graysum=graysum+s;
 seed=graysum/pointsum;                                     %计算新的灰度平均值
end
figure,imshow(J);
title('分割后图像');

3.2.2鼠标选取多种子点

I=double(C);              %转换为灰度值是0-1的双精度
[M,N]=size(I);
J=zeros(M,N);             %定义输出图像矩阵,初始为零矩阵
msgbox('请用鼠标单击选择多个种子点并按回车键', '操作','modal');
[y,x]=getpts;             %通过鼠标获得区域生长起始点(getpts函数本身就可以获得多个点,并返回多个点的坐标)
xr=round(x); 
yr=round(y);
threshold=13;             %阈值
T=length(xr);
for k=1:T                 %循环,依次以当选取的各点为种子点进行区域分割
 seed=I(xr(k),yr(k));
 J(xr(k),yr(k))=1;         %将J中与所取点相对应位置的点设置为白点
 graysum=seed;             %储存符合区域生长条件的点的灰度值的总和
 pointsum=1;               %储存符合区域生长条件的点的总个数
 count=1;                  %每次判断一点周围八点符合条件的新点的数目   
 while count>0             %判断是否有新的符合生长条件的点,若没有,则结束
  s=0;                     %记录判断一点周围八点时,符合条件的新点的灰度值之和
  count=0;
  for i=1:M
      for j=1:N
        if J(i,j)==1
         if (i-1)>0 && (i+1)<(M+1) && (j-1)>0 && (j+1)<(N+1)       %判断此点是否为图像边界上的点
          for u= -1:1                                              %判断点周围八点是否符合阈值条件
           for v= -1:1                                             %u,v为偏移量
             if  J(i+u,j+v)==0 && abs(I(i+u,j+v)-seed)<=threshold  %判断是否未存在于输出矩阵J,并且为符合阈值条件的点
                J(i+u,j+v)=1;                                       %将符合以上两条件的点在J中与之位置对应的点设置为白点
                count=count+1;   
                s=s+I(i+u,j+v);
             end
           end  
          end
         end
        end
      end
  end
  pointsum=pointsum+count;
  graysum=graysum+s;
  seed=graysum/pointsum;                                     %计算新的灰度平均值
 end
end
figure,imshow(J);
title('分割后图像');

3.3调试分析
最为经典的区域生长方法是单个种子点的,运行该程序,用鼠标选择种子点。
在这里插入图片描述在这里插入图片描述
由以上两个测试例可以看出,区域分割效果较好,但结果与种子点的选取位置有关,同时不连通的区域无法被一起分割出来。
以上两测试例中,我们给定阈值13,下面将测试不同阈值对区域生长效果的影响。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
由上面三个同一种子点、不同阈值的测试例(分别取10、20、30)可以看出,阈值的选取对图像分割效果有巨大影响。阈值选取过小会造成分割不完全,得到的图像不能将预期部分完整展现,而阈值选取过大会造成分割过度,得到的图像中包括不期望部分。故在手动选取阈值时,需多次尝试,得到最符合预期的分割情况。
在单个种子点的图形分割中,不连通的部分无法分割出,这时就需用多个种子点。(以下测试均在阈值恒定为13的情况下进行)
在这里插入图片描述在这里插入图片描述在这里插入图片描述
上述对多种子区域生长的测试例,选取不同种子点,得到的分割结果存在差异。同时,可以看出,种子点位置选取恰当,即可分割出不连通区域。从而,选取多个种子点,可实现将原图像中预期的多部分同时分割出来的效果。

四、区域生长改进方法

4.1改进思路
从上部分对经典区域生长方法的测试中,可以看出该方法存在的一个较大问题,是通过鼠标手动选取种子点,较为繁琐,且起始点的位置选择随机,完全依靠操作人员判断,图像复现性差。理想的图像分割是输入图像后,无需人手动选择,可程序自动确定种子点,得到分割效果较好的一个结果。基于此,对经典方法进行改进。
观察原图像,可发现预期分割出来的部分灰度低、亮度大,对应的图像像素值就要普遍较大。针对图像的这一特征,我们就可以用一个 3x3矩阵去遍历整个图片,然后计算矩阵内像素点的平均值,取平均值最大的区域中间值坐标点为生长起始点进行区域生长。便可实现程序自动选择较理想种子点的目的,称此方法为局部平均值判定法。
4.2算法设计

I=double(C);              %转换为灰度值是0-1的双精度
[M,N]=size(I);
J=zeros(M,N);             %定义输出图像矩阵,初始为零矩阵
for k=1:6					 %一次循环即为一个种子点
x=0;
for i=2:3:(M-mod(M,3)-1)                   %3x3 矩阵将整幅图像遍历 
    for j=2:3:(N-mod(N,3)-1) 
        Q=0; 
        for u=-1:1:1 
            for v=-1:1:1                   %计算每一个小矩阵的均值 
                Q=Q+I(i+u,j+v)./9;          
            end 
        end
        if Q>x&&J(i,j)==0                  %将均值最大且未存在于输出矩阵J的矩阵中心点作为生长起始点 
            x=Q;  
            xr=i;                              
            yr=j;                              
        end 
    end 
end 
seed=I(xr,yr);
J(xr,yr)=1;               %将J中与所取点相对应位置的点设置为白点
graysum=seed;             %储存符合区域生长条件的点的灰度值的总和
pointsum=1;               %储存符合区域生长条件的点的总个数
count=1;                  %每次判断一点周围八点符合条件的新点的数目
threshold=13;             %阈值
while count>0             %判断是否有新的符合生长条件的点,若没有,则结束
 s=0;                     %记录判断一点周围八点时,符合条件的新点的灰度值之和
 count=0;
 for i=1:M
     for j=1:N
       if J(i,j)==1
        if (i-1)>0 && (i+1)<(M+1) && (j-1)>0 && (j+1)<(N+1)       %判断此点是否为图像边界上的点
         for u= -1:1                                              %判断点周围八点是否符合阈值条件
          for v= -1:1                                             %u,v为偏移量
            if  J(i+u,j+v)==0 && abs(I(i+u,j+v)-seed)<=threshold  %判断是否未存在于输出矩阵J,并且为符合阈值条件的点
               J(i+u,j+v)=1;                                      %将符合以上两条件的点在J中与之位置对应的点设置为白点
               count=count+1;   
               s=s+I(i+u,j+v);
            end
          end  
         end
        end
       end
     end
 end
 pointsum=pointsum+count;
 graysum=graysum+s;
 seed=graysum/pointsum;                                     %计算新的灰度平均值
end
end
figure,imshow(J);
title('分割后图像');

4.3调试分析
在阈值给定为13的情况下进行测试,调整循环次数(一次循环即为一个种子点)。
1次循环:
在这里插入图片描述
2次循环:
在这里插入图片描述
3次循环:
在这里插入图片描述
4次循环:
在这里插入图片描述
5次循环:
在这里插入图片描述
6次循环:
在这里插入图片描述
4.4优点与不足
由上述测试例可以看出,该改进的区域生长方法较好地实现了区域分割,且能同时分割出不连通的几部分。执行一次循环,即为程序自动确定一种子点并进行分割,通过控制循环次数,即可控制种子点数。程序自动确定种子点,使得阈值和循环次数确定的情况下,图像分割效果一定,结果可复现。从上述1次循环递增至6次循环,可以看出循环次数较少时,可能会分割不完全,得到的图像不能将预期部分完整展现,而循环次数较多会造成分割过度,得到的图像中包括不期望部分。故需多次尝试,确定理想的循环次数,得到最符合预期的分割情况。此改进后的区域生长方法仍存在一较大不足,无法实现阈值自适应,还需人工给定阈值。下一步可从此处着手,寻找解决方案,实现程序自动确定阈值,获得更好的效果。

五、分水岭方法

5.1理论基础
传统分水岭算法(watershed)的基本思想是将图像看做为地理学的拓扑地貌,将图像想象成一个立体的地形表面。图像中的每一个像素的灰度值作为该点的海拔高度,每一个局部极小值及它影响的区域被称为集水盆,则集水盆的边界就形成了分水岭。分水岭的概念和形成能够通过模拟浸入的过程来说明,假设在每个区域极小值的位置刺穿一个小孔,从孔中向该区域不断注水,则地势较低的区域会首先被淹没,在浸入加深的过程中,每一个局部极小值的影响域都处在慢慢扩展过程中,当水淹没到一定高度时两个集水盆会相连合并为同一个区域,此时应在两个集水盆汇合处构筑“大坝”来阻止区域合并,最终形成分水岭,这些“大坝”的边界对应于分水岭的分割线,同时也是由分水岭算法提取出的连续边界轮廓。要实现分水岭方法,可分为以下几步进行:读入图像,求取图像的边界,应用分割算法。

5.2算法设计

I = C;
hy = fspecial('sobel');   %sobel算子
hx = hy';
Iy = imfilter(double(I), hy, 'replicate');   %滤波求y方向边缘
Ix = imfilter(double(I), hx, 'replicate');   %滤波求x方向边缘
gradmag = sqrt(Ix.^2 + Iy.^2);               %求摸
figure,imshow(gradmag,[]),                   %显示梯度
title('Gradient magnitude (gradmag)')
L = watershed(gradmag);                      %直接应用分水岭算法
Lrgb = label2rgb(L);                         %转化为彩色图像
figure,imshow(Lrgb),                         %显示分割后的图像
title('Watershed transform of gradient magnitude (Lrgb)')

5.3调试分析
在这里插入图片描述
通过测试可以看出,传统的分水岭方法对该图像分割效果较差,过分割现象明显。传统分水岭算法虽对微弱的边缘具有良好的响应,但对图像中噪声,物体表面细微的灰度变化比较敏感,噪声会恶化图像梯度进而造成分割结果轮廓偏移,通常的分割结果会出现严重的“过分割”现象。因此要想通过分水岭方法得到更好的图像分割效果,必须对其算法进行改进。
PS:暂未进行对传统分水岭方法的改进~

参考文献
【1】《数字图像处理与分析基础——MATLAB和VC++实现》 孙明主编 电子工业出版社
【2】李政国.基于区域生长法的高空间分辨率遥感图像分割与实现[D].南宁:广西大学,2008.
【3】曹彪.基于区域生长的OTC 图像分割算法研究[D].北京:北京理工大学,2014.
【4】许宪法.基于显著性检测模型的图像分割算法研究[D].兰州:兰州理工大学,2018.
【5】闫东阳,明东萍.基于自动多种子区域生长的遥感影像面向对象分割方法[J].工程科学学报,2017.
【6】施兢业.基于改进区域生长法的电力设备红外图像分割[J].光学技术,2017.
【7】李云红,张秋铭,周小计,贾凯莉.基于形态学及区域合并的分水岭图像分割算法[J].计算机工程与应用,2019.

### 回答1: 下面是一个使用基于改进区域生长算法图像分割的python代码,用于在一张图片中切割多个物体: ```python import cv2 import numpy as np # 读取图片 img = cv2.imread("image.jpg") # 转化为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 阈值处理 ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) # 形态学处理 kernel = np.ones((3,3),np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 定义确定背景的区域 sure_bg = cv2.dilate(opening, kernel, iterations=3) # 定义确定前景的区域 dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, sure_fg = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0) # 获取未知区域 sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg, sure_fg) # 连通域标记 ret, markers = cv2.connectedComponents(sure_fg) # 将未知区域标记为0 markers = markers+1 markers[unknown==255] = 0 # 区域生长 markers = cv2.watershed(img, markers) img[markers == -1] = [255,0,0] # 显示结果 cv2.imshow("Result", img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 请注意,此代码只是一个示例,在实际应用中可能需要根据需求进行修改。 ### 回答2: 基于改进区域生长算法图像分割方法可以通过以下步骤实现: 1. 导入必要的库和模块,如OpenCV和numpy。 2. 读取待分割的图像文件。 3. 定义改进区域生长函数,接受图像和种子点作为参数。 4. 在改进区域生长函数中,首先定义一个空的分割结果矩阵,初始化为与原始图像大小相同,并且所有像素值均为0。 5. 然后创建一个待处理点集合,并将种子点添加到这个集合中。 6. 在循环中,从待处理点集合中取出一个像素点。然后对该像素点的相邻像素进行判断,如果符合条件(如灰度相似度、颜色距离等),则将其添加到待处理点集合中,并且将对应位置在分割结果矩阵中的像素值设为1。 7. 当待处理点集合为空时,分割结束。 8. 调用改进区域生长函数,分割出多个物体。 9. 将分割结果可视化,可以使用不同的颜色对每个物体进行区分。 下面是基于改进区域生长算法图像分割方法的Python代码示例: ```python import cv2 import numpy as np def region_growing(image, seed): # 初始化分割结果矩阵 segmented = np.zeros_like(image) # 定义待处理点集合并添加种子点 to_process = [] to_process.append(seed) # 开始区域生长 while len(to_process) > 0: # 取出一个待处理点 current_point = to_process.pop(0) # 获取当前点的坐标 x, y = current_point # 判断当前点是否已经处理过 if segmented[x, y] == 1: continue # 将当前点标记为已处理 segmented[x, y] = 1 # 对当前点的相邻像素进行判断 for i in range(-1, 2): for j in range(-1, 2): # 排除边界点 if i == 0 and j == 0: continue # 计算相邻点的坐标 nx, ny = x + i, y + j # 判断相邻点是否符合条件 if is_similar(image[x, y], image[nx, ny]): # 将符合条件的相邻点添加到待处理点集合中 to_process.append((nx, ny)) return segmented def is_similar(pixel1, pixel2): # 定义相似性判断条件,根据实际应用进行调整 threshold = 10 difference = abs(int(pixel1) - int(pixel2)) if difference < threshold: return True return False # 读取待分割的图像文件 image = cv2.imread("image.jpg", 0) # 指定种子点坐标 seed = (50, 50) # 基于改进区域生长算法进行分割 segmented = region_growing(image, seed) # 将分割结果可视化 segmented = segmented * 255 cv2.imshow("Segmented Image", segmented) cv2.waitKey(0) cv2.destroyAllWindows() ``` 该代码可以读取一张图片并基于改进区域生长算法将多个物体进行分割,并将分割结果可视化显示出来。可以根据实际需求调整相似性判断条件和其他参数。 ### 回答3: 基于改进区域生长算法图像分割方法是一种将图像中相似的像素点划分为一个区域的技术。以下是实现基于改进区域生长算法图像分割方法的Python代码,用于切割一张图片中的多个物体。 ```python import cv2 import numpy as np def region_growing(img, seed): visited = np.zeros_like(img) # 记录已访问的像素点,初始化为0 row, col = img.shape[:2] region = np.zeros_like(img) # 初始化区域 neighbors = [] # 初始化邻域列表 # 定义邻域的8个方向 directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] # 将种子点加入区域 region[seed] = img[seed] visited[seed] = 1 neighbors.append(seed) while len(neighbors) > 0: # 取出一个邻域点 current_point = neighbors.pop(0) # 遍历8个邻域点 for direction in directions: x = current_point[0] + direction[0] y = current_point[1] + direction[1] # 判断邻域点是否超出图像边界 if x >= 0 and y >= 0 and x < row and y < col: # 判断邻域点是否已经被访问过 if visited[x, y] == 0: # 判断邻域点与当前点的相似度是否满足条件 if abs(int(img[x, y]) - int(img[current_point])) < threshold: region[x, y] = img[x, y] visited[x, y] = 1 neighbors.append((x, y)) return region # 读取图像 image = cv2.imread('input.jpg', 0) # 设定种子点,可以手动选择多个种子点或者通过算法自动选择 seeds = [(100, 100), (200, 200)] # 以(100, 100)和(200, 200)为种子点 # 设定相似度阈值 threshold = 10 # 对每个种子点进行区域生长,并进行分割 for seed in seeds: segment = region_growing(image, seed) cv2.imshow('Segmentation', segment) cv2.waitKey(0) cv2.destroyAllWindows() ``` 以上代码根据给定的种子点对图像进行区域生长,得到每个物体的分割结果。在代码中,可以根据需要手动选择多个种子点或通过算法自动选择种子点,并设置相似度阈值来控制分割的精度。每个物体的分割结果将以弹窗显示出来。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值