图像算法:分水岭分割程序实现

2025-10-22 15:32:24

1、分水岭算法思想:

分水岭算法是模拟自底向上逐渐淹没地形过程的形象理解;

①此地形中最低区域(种子区域)即盆地,

当水从盆地不断的浸入其中,则该地形由谷底向上逐渐的被淹没;

②当两个集水盆地的水将要汇合时,可在汇合处建立堤坝,直到整个地形都被淹泪侨没,从而就得到了各个堤坝(分水岭)和一个个被堤坝分开的盆地(目标物体)。

【注】:

分水岭算法的优点:

在于它可以得到单一像素宽度的连续边界,能检测出图像中粘连物体的微弱边缘;

图像算法:分水岭分割程序实现

2、OpenCV实现:

OpenCV实涛侨现了一个基于标记图层的分水岭算法;

所谓标记图层,即不用手动选择种子点,直接输入一个包含种子点的图像即可;

格式:

void watershed(InputArray image, InputOutputArray markers)

参数:

image-输入图像

markers-输入的标记图层,标记点是洪水漫冲的入口;

【注】:

要获得较好的分水岭分割效果,一般先获得最佳的种子区域;

那么如何获得标记图层呢?

3、OpenCV分水岭分割程序:

①将输入灰度图转换二值图

②findContours()函数找出图像轮廓

③将轮廓查找结果放入到markers图层中,便于访问;

④调用分水岭分割算法,显示分割结果;

#include <opencv2\opencv.hpp>

#include <opencv2\highgui\highgui.hpp>

#include <opencv2\features2d\features2d.hpp>

#include <opencv2\core\core.hpp>

using namespace std; 

using namespace cv; 

int main()

{

       Mat srcImg = imread("raw.jpg",1);

      

       Mat grayImg;

       cvtColor(srcImg,grayImg,CV_BGR2GRAY);

       Mat binaryImg = Mat::zeros(grayImg.rows,grayImg.cols,CV_8UC1);

       adaptiveThreshold(grayImg,binaryImg,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY_INV,5,10);

       namedWindow("threshold", CV_WINDOW_NORMAL); 

       imshow("threshold",binaryImg); 

       vector< vector<Point> > vContours;

       vector< Vec4i > vHierarchy;

       findContours(binaryImg,vContours,vHierarchy,CV_RETR_LIST,CV_CHAIN_APPROX_NONE);

       Mat markersImg = Mat::zeros(grayImg.rows,grayImg.cols,CV_8UC1);

       for (int idx=0;idx>=0;idx=vHierarchy[idx][0])

       {

              Scalar color(rand()&255,rand()&255,rand()&255);

              drawContours(markersImg,vContours,idx,color,CV_FILLED,8,vHierarchy);

       }

       namedWindow("contours", CV_WINDOW_NORMAL); 

       imshow("contours",markersImg); 

       Mat markers;

       markersImg.convertTo(markers, CV_32S); 

       watershed(srcImg, markers);

 腊蚊屈      Mat segmentImg;

       markers.convertTo(segmentImg,CV_8U);

       namedWindow("segmentation_result", CV_WINDOW_NORMAL); 

       imshow("segmentation_result", segmentImg); 

       waitKey(0);

       return 0;

}

图像算法:分水岭分割程序实现

图像算法:分水岭分割程序实现

图像算法:分水岭分割程序实现

图像算法:分水岭分割程序实现

4、Matlab分水岭程序实现:

clc;clear all;close all

%1.读入彩色图像 并转换成灰度图 显示%

rgb=imread('pears.png');

if ndims(rgb)==3

    I=rgb2gray(rgb);

else

    I=rgb;

end

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(rgb);title('原图');

subplot(1,2,2);imshow(I);title('灰度图');

图像算法:分水岭分割程序实现

5、接上:

%2.进行分割图像%

%采用sobel边缘算子对图像进行水平和垂直滤波

%求取其模值

%soble算子滤波后的图像在边界处会显示比较大的值,没有边界处的值会很小

hy = fspecial('sobel');

hx = hy';

Iy = imfilter(double(I),hy,'replicate');

Ix = imfilter(double(I),hx,'replicate');

gradmag = sqrt(Ix.^2+Iy.^2);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(I,[]);title('灰度图像');

subplot(1,2,2);imshow(gradmag,[]);title('梯度幅值图像');

   

%可否直接对梯度幅值图像使用分水岭算法?

%直接使用分水岭算法对梯度幅值图像分割 结果往往存在过度分割的现象;

%因此 需要对前景和背景进行标记,以获得更好地分割效果

L=watershed(gradmag);

Lrgb=label2rgb(L);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(gradmag,[]);title('梯度幅值图像');

subplot(1,2,2);imshow(Lrgb,[]);title('梯度幅值做分水岭变换');

图像算法:分水岭分割程序实现

图像算法:分水岭分割程序实现

6、接上:

%3.标记前景目标对象

%标记必须是前景对象内部的连接斑点像素

%开运算可以把结构元素小的突刺滤掉 切断细长搭接而起到分离作用

%闭运算可以把结构元素小的缺口或孔填充上 搭接段的间隔而起到连接作用

se = strel('disk',20);

Io = imopen(I,se);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(I,[]);title('灰度图像');

subplot(1,2,2);imshow(Io,[]);title('开操作');

Ie = imerode(I,se);

Iobr = imreconstruct(Ie,I);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(I,[]);title('灰度图像');

subplot(1,2,2);imshow(Iobr,[]);title('基于开的重建图像');

%闭操作移除较暗的斑点和枝干标记

Ioc = imclose(Io,se);

Ic = imclose(I,se);

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(I,[]);title('灰度图像');

subplot(2,2,2);imshow(Io,[]);title('开操作图像');

subplot(2,2,3);imshow(Ic,[]);title('闭操作图像');

subplot(2,2,4);imshow(Ioc,[]);title('开闭运算');

%采用imdilate imreconstruct 对输入图像求补 对imreconstruct输出图像求补

Iobrd = imdilate(Iobr,se);

Iobrcbr = imreconstruct(imcomplement(Iobrd),imcomplement(Iobr));

Iobrcbr = imcomplement(Iobrcbr);

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(I,[]);title('灰度图像');

subplot(2,2,2);imshow(Ioc,[]);title('开闭操作');

subplot(2,2,3);imshow(Iobr,[]);title('基于开的重建图像');

subplot(2,2,4);imshow(Iobrcbr,[]);title('基于闭的重建图像');

%比较Ioc和Iobrcbr,在移除小污点同时不影响对象全局形状的应用下

%基于重建的开闭操作要比标准的开闭重建更加有效

%故计算Iobrcbr的局部极大值来得到更好地前景标记

fgm = imregionalmax(Iobrcbr);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,3,1);imshow(I,[]);title('灰度图像');

subplot(1,3,2);imshow(Iobrcbr,[]);title('基于重建的开闭操作');

subplot(1,3,3);imshow(fgm,[]);title('局部极大图像');

%为了帮助理解结果 叠加前景标记到原图中

It1 = rgb(:,:,1);

It2 = rgb(:,:,2);

It3 = rgb(:,:,3);

It1(fgm)=255;

It2(fgm)=0;

It3(fgm)=0;

I2 = cat(3,It1,It2,It3);

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(rgb,[]);title('原图像');

subplot(2,2,2);imshow(Iobrcbr,[]);title('基于重建的开闭操作');

subplot(2,2,3);imshow(fgm,[]);title('局部极大图像');

subplot(2,2,4);imshow(I2);title('局部极大叠加到原图像');

%注意 大多闭塞出和阴影对象没有被标记

se2 = strel(ones(5,5));

fgm2 = imclose(fgm,se2);

fgm3 = imerode(fgm2,se2);

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(Iobrcbr,[]);title('基于重建的开闭操作');

subplot(2,2,2);imshow(fgm,[]);title('局部极大图像');

subplot(2,2,3);imshow(fgm2,[]);title('基于局部极大图像的闭操作');

subplot(2,2,4);imshow(fgm3,[]);title('基于闭操作的腐蚀操作');

%这个过程会留下一些偏离的孤立像素,应该移除它们

fgm4 = bwareaopen(fgm3,20);

It1 = rgb(:,:,1);

It2 = rgb(:,:,2);

It3 = rgb(:,:,3);

It1(fgm4)=255;

It2(fgm4)=0;

It3(fgm4)=0;

I3 = cat(3,It1,It2,It3);

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(I2,[]);title('局部极大值叠加到原图像');

subplot(2,2,2);imshow(fgm3,[]);title('闭腐蚀操作');

subplot(2,2,3);imshow(fgm4,[]);title('去除小斑点操作');

subplot(2,2,4);imshow(I3,[]);title('修改局部极大值叠加到原图像');

图像算法:分水岭分割程序实现

图像算法:分水岭分割程序实现

图像算法:分水岭分割程序实现

7、接上:

%4.计算背景标记

%在Iobrcbr中 暗像素属于北京 阈值操作

bw = im2bw(Iobrcbr,graythresh(Iobrcbr));

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(Iobrcbr,[]);title('基于重建的开闭操作');

subplot(1,2,2);imshow(bw,[]);title('阈值分割');

%背景像素在黑色区域 理想情形下 不必要求背景标记太接近于要分割的对象边缘;

%通过计算‘骨架影响范围’来细化背景,或者SKIZ,bw的前景;

%采用bw的距离变换得分水岭变换实现;

%然后寻找结果的分水岭脊线DL==0

%D=bwdist(BW)计算欧几里得距离公式

%BW可以由任意维数,

%D与BW有同样的大小

D=bwdist(bw);

DL=watershed(D);

bgm = DL == 0;

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(Iobrcbr,[]);title('基于重建的开闭操作');

subplot(2,2,2);imshow(bw,[]);title('阈值分割');

subplot(2,2,3);imshow(label2rgb(DL),[]);title('分水岭变换示意图');

subplot(2,2,4);imshow(bgm,[]);title('分水岭变换脊线图');

图像算法:分水岭分割程序实现

8、接上:

%5.计算分水岭分割

%imimposemin用来修改图像 使特定要求位置局部最小

%imimposemin用来修改梯度幅值图像 在前景和背景标记像素局部极小

gradmag2 = imimposemin(gradmag,bgm|fgm4);

figure('units','normalized','position',[0 0 1 1]);

subplot(2,2,1);imshow(bgm,[]);title('分水岭变换脊线图');

subplot(2,2,2);imshow(fgm4,[]);title('前景标记');

subplot(2,2,3);imshow(gradmag,[]);title('梯度幅值图像');

subplot(2,2,4);imshow(gradmag2,[]);title('梯度幅值修改图像');

图像算法:分水岭分割程序实现

9、接上:

%6.基于分水岭图像的分割计算

%查看结果 叠加前景标记、背景标记 分割对象边界

%采用膨胀实现某些要求 比如对象边界更加清晰可见

%对象边界定位于L==0的位置;

L=watershed(gradmag2);

It1 = rgb(:,:,1);

It2 = rgb(:,:,2);

It3 = rgb(:,:,3);

fgm5 = imdilate(L==0,ones(3,3)) | bgm | fgm4;

It1(fgm5)=255;

It2(fgm5)=0;   

It3(fgm5)=0;   

I4=cat(3,It1,It2,It3);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(rgb,[]);title('原图像');

subplot(1,2,2);imshow(I4,[]);title('标记和对象边缘叠加到原图像');

%可视化说明了前景和后景标记如何影响结果

%在几个位置 部分的较暗对象与它们邻近较亮对象相融合,

%这是因为受遮挡的对象没有前景标记

%另一可视化技术 将标记矩阵作为彩色图象显示

%

Lrgb = label2rgb(L,'jet','w','shuffle');

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(rgb,[]);title('原图像');

subplot(1,2,2);imshow(Lrgb);title('彩色分水岭标记矩阵');

%使用透明度来叠加这个伪彩色标记矩阵在原亮度图像上进行显示

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(rgb,[]);title('原图像');

subplot(1,2,2);imshow(rgb,[]);hold on;

himage = imshow(Lrgb);

set(himage,'AlphaData',0.3);

title('标记矩阵叠加到原图像');

图像算法:分水岭分割程序实现

10、完整程序如下:

clc;clear all;close all

%1.读入彩色图像 并转换成灰度图 显示%

rgb=imread('pears.png');

I=rgb2gray(rgb);

%2.进行分割图像%

hy = fspecial('sobel');

hx = hy';

Iy = imfilter(double(I),hy,'replicate');

Ix = imfilter(double(I),hx,'replicate');

gradmag = sqrt(Ix.^2+Iy.^2);

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(I,[]);title('灰度图像');

subplot(1,2,2);imshow(gradmag,[]);title('梯度幅值图像');

%3.标记前景目标对象

%有多种方法可以获得前景标记 但标记必须是前景对象内部的连接斑点像素

%开运算可以把结构元素小的突刺滤掉 切断细长搭接而起到分离作用

%闭运算可以把结构元素小的缺口或孔填充上 搭接段的间隔而起到连接作用

se = strel('disk',20);

Io = imopen(I,se);

Ie = imerode(I,se);

Iobr = imreconstruct(Ie,I);

Ioc = imclose(Io,se);

Ic = imclose(I,se);

%采用imdilate imreconstruct 对输入图像求补 对imreconstruct输出图像求补

Iobrd = imdilate(Iobr,se);

Iobrcbr = imreconstruct(imcomplement(Iobrd),imcomplement(Iobr));

Iobrcbr = imcomplement(Iobrcbr);

%比较Ioc和Iobrcbr,在移除小污点同时不影响对象全局形状的应用下

%基于重建的开闭操作要比标准的开闭重建更加有效

%计算Iobrcbr的局部极大值来得到更好地前景标记

fgm = imregionalmax(Iobrcbr);

%为了帮助理解结果 叠加前景标记到原图中

It1 = rgb(:,:,1);It2 = rgb(:,:,2);It3 = rgb(:,:,3);

It1(fgm)=255;It2(fgm)=0;It3(fgm)=0;

I2 = cat(3,It1,It2,It3);

%注意 大多闭塞出和阴影对象没有被标记 即在结果中将不会得到合理的分割

%清理标记斑点的边缘 然后收缩它们

%闭操作和腐蚀操作完成

se2 = strel(ones(5,5));

fgm2 = imclose(fgm,se2);

fgm3 = imerode(fgm2,se2);

%这个过程会留下一些偏离的孤立像素,应该移除它们

%采用bwareaopen移除少于特定像素个数的斑点

fgm4 = bwareaopen(fgm3,20);

It1 = rgb(:,:,1);

It2 = rgb(:,:,2);

It3 = rgb(:,:,3);

It1(fgm4)=255;

It2(fgm4)=0;

It3(fgm4)=0;

I3 = cat(3,It1,It2,It3);

%4.计算背景标记

%在Iobrcbr中 暗像素属于北京 阈值操作

bw = im2bw(Iobrcbr,graythresh(Iobrcbr));

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(Iobrcbr,[]);title('基于重建的开闭操作');

subplot(1,2,2);imshow(bw,[]);title('阈值分割');

%背景像素在黑色区域 理想情形下 不必要求背景标记太接近于要分割的对象边缘;

%然后寻找结果的分水岭脊线DL==0

D=bwdist(bw);

DL=watershed(D);

bgm = DL == 0;

%5.计算分水岭分割

%imimposemin用来修改图像 使特定要求位置局部最小

gradmag2 = imimposemin(gradmag,bgm|fgm4);

%6.基于分水岭图像的分割计算

%对象边界定位于L==0的位置;

L=watershed(gradmag2);

It1 = rgb(:,:,1);

It2 = rgb(:,:,2);

It3 = rgb(:,:,3);

fgm5 = imdilate(L==0,ones(3,3)) | bgm | fgm4;

It1(fgm5)=255;

It2(fgm5)=0;   

It3(fgm5)=0;   

I4=cat(3,It1,It2,It3);

%另一可视化技术 将标记矩阵作为彩色图象显示

Lrgb = label2rgb(L,'jet','w','shuffle');

%使用透明度来叠加这个伪彩色标记矩阵在原亮度图像上进行显示

figure('units','normalized','position',[0 0 1 1]);

subplot(1,2,1);imshow(rgb,[]);title('原图像');

subplot(1,2,2);imshow(rgb,[]);hold on;

himage = imshow(Lrgb);

set(himage,'AlphaData',0.3);

title('标记矩阵叠加到原图像');

声明:本网站引用、摘录或转载内容仅供网站访问者交流或参考,不代表本站立场,如存在版权或非法内容,请联系站长删除,联系邮箱:site.kefu@qq.com。
猜你喜欢