标记符控制的分水岭算法原理及matlab实现

2024-06-12 14:48

本文主要是介绍标记符控制的分水岭算法原理及matlab实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

--------------------------------------------------------------------------------------------------------------------

附录A  教程【3】给出的matlab源码,附详细注释

function [  ] = MarkerControlled_Watershed_tutorial(  )
%标记符控制的分水岭算法教程
%本程序按照官方文档的步骤,展示了如何使用分水岭算法,并测试对于含手势图片的分割结果是否理想
%程序修改自matlab官方文档 Marker-Controlled Watershed Segmentation
%网址: http://cn.mathworks.com/help/images/examples/marker-controlled-watershed-segmentation.html?s_tid=gn_loc_drop%%
%官方文档简介%这个例子展示了如何使用分水岭分割算法来分割一副图像中的物体
%分水岭变换经常应用到这类问题中。
%分水岭变换通过将灰度图像看成拓扑表面(亮的像素看做较高的点,暗像素看做低点),以此在一幅图中寻找“汇水盆地”和“分水岭脊线”%如果你可以识别或者说标记mark前景物体与背景的位置(提供一些先验信息),使用分水岭变换的分割将会表现的很好。
%当然,人为提供这些标记“mark”,算法就算有效,很大程度上也失去了意义,于是就有了下面可以自行标记前景背景的分水岭算法。%Marker-Controlled Watershed Segmentation,即标记符控制的分水岭分割算法,主要遵循以下几个基本步骤:
%1.计算一个分割函数(最基本的分水岭分割)。你尝试去分割的图像需要满足:黑暗区域对应着待分割的目标物体
%2.计算前景标记。得到的前景标记是每个物体内部的联通像素斑块。
%3.计算背景标记。背景标记由那些不属于任何物体的的像素点组成。
%4.修改分割函数,使得它只能在前景标记和背景标记的位置处达到最小值。
%5.计算修改后分割函数的分水岭变换close all;%%
%步骤一:读取彩色图像并转化为灰度图
HandImage = imread('testImgae/pear.jpg');
I = rgb2gray(HandImage);
imshow(I)%%
%******************步骤二:使用梯度幅值作为分割函数**************************
%使用Sobel边缘检测算子,imfilter函数,和一些简单的四则运算来计算梯度幅值。
%梯度值总是在物体的边缘处高,而总是在物体内部低。
hy = fspecial('sobel');%获取sobel算子模板,计算纵向梯度
hx = hy';%横向模板
Iy = imfilter(double(I), hy, 'replicate');%计算纵向梯度
Ix = imfilter(double(I), hx, 'replicate');
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)')
%从结果可以看到,显然不能
%如果没有额外的预处理,比如添加标记符(如下所述),直接使用分水岭变换通常导致严重的过分割%取消下面3行代码的注释可以观察label2rgb函数的作用
% testA=[1 1 2 2 2 2 ;1 2 3 3 3 4;0 1 1 3 4 4];
% testArgb = label2rgb(testA);
% figure, imshow(testArgb), title('测试label2rgb函数的作用,使用简单的标记函数testA')%%
%******************步骤三:标记前景物体************************************
%我们可以应用多种方法来获得前景标记,这些标记必须是前景对象内部的连通像素斑块。
%这个例子中,将使用形态学技术“基于重建的开操作”和“基于重建的闭操作”来清理图像。
%这些操作将会在每个对象内部创建平坦的极大值小斑块,这些斑块可以使用imregionalmax函数来定位。%我们的目标是%开运算和闭运算:先腐蚀后膨胀称为开;先膨胀后腐蚀称为闭(所谓腐蚀还是膨胀是针对白色而言来理解的)
%开和闭这两种运算可以除去比结构元素小的特定图像细节,同时保证不产生全局几何失真。
%开运算可以把比结构元素小的突刺滤掉,切断细长搭接而起到分离作用;闭运算可以把比结构元素小的缺口或孔填充上,搭接短的间隔而起到连接作用。%但是%开操作的第一步腐蚀能去除白色小物体,随后的膨胀趋向于恢复保留下来的物体的形状,这种恢复是不精确的,其精确度取决于形状和结构体之间的相似性。
%基于重建进行的开操作能够准确的恢复腐蚀之后的物体形状。
%所以基于重建的开操作与单纯的开操作功能类似,但更好地保留了原物体的形状。%基于重建的开操作:I先腐蚀成Ie,后进行形态学重建。%开操作是腐蚀后膨胀,基于重建的开操作是腐蚀后进行形态学重建。
%下面通过示例比较这两种方式,直观展示“基于重建的开操作”如何优于“开操作”
%首先,用imopen做开操作。
se = strel('disk',20);%disk指定构建一个圆形的结构体,第二个参数指定结构体的半径
Io = imopen(I, se);%开操作
% figure
%imshow(Io), title('Opening (Io)')%接下来,进行基于重建的开操作。使用imerode和imreconstruct函数实现
Ie = imerode(I, se);%先腐蚀‘erosion’
Iobr = imreconstruct(Ie, I);%再重建
% figure
%imshow(Iobr), title('Opening-by-reconstruction (Iobr)')%对开操作后的结果进行闭操作,可以移除较暗的斑点和枝干标记。
%同样的,我们来对比常规的形态学闭操作和基于重建的闭操作的区别。%首先,使用imclose对开操作的结果进行常规闭操作:
Ioc = imclose(Io, se);
% figure
%imshow(Ioc), title('Opening-closing 开操作+闭操作 (Ioc)')%然后我们来对开操作的结果进行基于重建的闭操作
%即首先使用imdilate函数进行膨胀,然后使用imreconstruct进行重建。
%这里要解释一下,重建操作是通过膨胀操作和交运算定义的,即 Hk+1=(Hk 膨胀 结构体B)∩G。
%重建定义的具体解释可以参考《数字图像处理的MATLAB实现(第2版)》P358页
%重建操作是对标记图像,在模板图像的限制下,进行膨胀。本质上是一种*膨胀*算法
%而这里需要的是模拟闭操作对膨胀后的图像进行*腐蚀*,怎么办呢?
%可以通过对模板图像和标记图像同时求补,然后进行重建操作,对补的膨胀相当于对原图的腐蚀。
%当然,重建的结果也要求补,才能得到实际的结果。
%IM2 = imcomplement(IM)计算图像IM的补集。IM可以是二值图像,或者RGB图像。IM2与IM有着相同的数据类型和大小。
Iobrd = imdilate(Iobr, se);%在基于重建的开操作的结果基础上,进行腐蚀
Iobrcbr = imreconstruct(imcomplement(Iobrd), imcomplement(Iobr));%重建,标记图像为腐蚀后图像取补,模板为腐蚀前原图取补。
Iobrcbr = imcomplement(Iobrcbr);%重建结果再取补,得到实际基于重建的闭操作的结果。
figure
imshow(Iobrcbr), title('Opening-closing by reconstruction 基于重建的开+闭操作 (Iobrcbr)')
%如你所见,通过比较Iobrcbr和loc可以看到,在移除小污点同时不影响对象全局形状的应用下,基于开闭操作的重建要比标准的开闭操作更加有效。%计算Iobrcbr的区域极大值来得到好的前景标记。
%得到的前景标记图fgm是二值图,白色对应前景区域
fgm = imregionalmax(Iobrcbr);
figure
imshow(fgm), title('Regional maxima of opening-closing by reconstruction (fgm)')%为了方便解释结果,将前景标记图叠加到原始图像上
I2 = I;
I2(fgm) = 255;%将fgm中的前景区域(像素值为1)标记到原图上(置白色)
figure
imshow(I2), title('Regional maxima superimposed on original image (I2)')%注意到一些大部分重合或被阴影遮挡的物体没有被标记出来。这意味着这些物体最终可能不会被正确的分割出来。
%并且,有些物体中前景标记正确的到达了物体的边缘。这意味着你应该清除掉标记斑块的边缘,向内收缩一点。
%你可以通过先闭操作,再腐蚀做到这点。
se2 = strel(ones(5,5));
fgm2 = imclose(fgm, se2);
fgm3 = imerode(fgm2, se2);%这个操作会导致遗留下一些离群的孤立点,这些是需要被移除的。
%你可以通过bwareaopen做到这点,函数将移除那些包含像素点个数少于指定值的区域。
fgm4 = bwareaopen(fgm3, 20);
I3 = I;
I3(fgm4) = 255;
figure
imshow(I3)
title('Modified regional maxima superimposed on original image (fgm4)')%%
%*********************第四步:计算背景标记**********************************
%本例中设计出的标记背景算法的前提假设是:图像中相对亮的是物体,相对暗的区域是背景。
%如果不满足这条假设,标记结果可能不甚理想%现在你需要标记背景。在去除噪点后的图像Iobrcbr中,暗像素属于背景,所以你可以先进行一下阈值操作。
%bw = imbinarize(Iobrcbr);
bw=im2bw(Iobrcbr,graythresh(Iobrcbr));%我使用上一行代码报错了,故换了一种二值化方法
figure
imshow(bw), title('Thresholded opening-closing by reconstruction (bw)')%背景像素现在是黑的了,但是理想情况下我们不希望背景标记太接近我们想要分割的物体的边界。
%我们要使背景变瘦,通过计算“骨架影响范围”来“细化”背景,或者SKIZ,bw的前景。这句没翻通,原文如下
%We'll "thin" the background by computing the "skeleton by influence zones", or SKIZ, of the foreground of bw. 
%这个可以通过对bw的距离变换进行分水岭变换来实现,然后寻找结果的分水岭脊线(DL==0)。
D = bwdist(bw);
%D = bwdist(BW)计算二值图像BW的欧几里得矩阵。对BW的每一个像素,距离变换指定像素和最近的BW非零像素的距离。
%bwdist默认使用欧几里得距离公式。BW可以由任意维数,D与BW有同样的大小。%由于bw中目标物体是白色的1.所以D中对应的目标物体处均是0,随着进入背景越深,对应像素值越大。
%这时正好符合我们使用分水岭算法的假设(想分出的目标物体数值较低)
%于是得到的背景标记是 物体与背景间的一个圈,能够包住目标物体
DL = watershed(D);
bgm = DL == 0;%分水岭变换结果L中,同一区域用同一数字表示,区域间分界线同一由0标识
figure
imshow(bgm), title('Watershed ridge lines (bgm)')%%
%******************第五步:计算分割函数(修改后)的分水岭变换*****************
% 函数imimposemin可以被用来修改一副图片,使得其只在指定的位置处取得局部最小值
% 这里你可以使用imimposemin来修改梯度幅值图像,使得局部最小值只出现在前景标记和背景标记处。
% 从结果来看imimposemin会将指定区域置为-Inf,从而成为极小值
% 且图像变得相当“平整”,一块块的相同数值的区域
gradmag2 = imimposemin(gradmag, bgm | fgm4);%这段测试imimposemin都做了什么
% gradmag2;
% temp=gradmag2-gradmag;
% 
% maxValue=max(max(temp));
% minValue=min(min(temp));
% temp=temp/maxValue;
% figure
% imshow(temp)% maxValue=max(max(gradmag));
% temp_gradmag=gradmag/maxValue;
% figure
% imshow(temp_gradmag)
% 
% maxValue=max(max(gradmag2));
% temp_gradmag2=gradmag2/maxValue;
% figure
% imshow(temp_gradmag2)%Finally we are ready to compute the watershed-based segmentation.
L = watershed(gradmag2);%%
%******************第六步:可视化结果************************************
I4 = I;
I4(imdilate(L == 0, ones(3, 3)) | bgm | fgm4) = 255;
figure
imshow(I4)
title('Markers and object boundaries superimposed on original image (I4)')Lrgb = label2rgb(L, 'jet', 'w', 'shuffle');
figure
imshow(Lrgb)
title('Colored watershed label matrix (Lrgb)')figure
imshow(I)
hold on
himage = imshow(Lrgb);
himage.AlphaData = 0.3;
title('Lrgb superimposed transparently on original image')
end

--------------------------------------------------------------------------------------------------------------------

附录B  提炼后的源码,去掉多余演示步骤,只可视化关键步骤

function [  ] = MarkerControlled_Watershed(  )
%标记符控制的分水岭算法
%与教程函数不同,本函数没有多余的步骤和注释,直接实现
%展示的结果图更少更精炼close all;%%
%步骤一:读取彩色图像并转化为灰度图
HandImage = imread('testImgae/hand1.jpg');
I = rgb2gray(HandImage);%如果原图中,目标物体是较暗的,即与假设相反,这里可以取反
% Fan=ones(size(I,1),size(I,2))*255;
% Fan=uint8(Fan);
% Fan=Fan-I;
% I=Fan;imshow(I), title('原图I')
%%
%******************步骤二:基于重建的开闭操作************************************se = strel('disk',20);%disk指定构建一个圆形的结构体,第二个参数指定结构体的半径%接下来,进行基于重建的开操作。使用imerode和imreconstruct函数实现
Ie = imerode(I, se);%先腐蚀‘erosion’
Iobr = imreconstruct(Ie, I);%再重建Iobrd = imdilate(Iobr, se);%在基于重建的开操作的结果基础上,进行腐蚀
Iobrcbr = imreconstruct(imcomplement(Iobrd), imcomplement(Iobr));%重建,标记图像为腐蚀后图像取补,模板为腐蚀前原图取补。
Iobrcbr = imcomplement(Iobrcbr);%重建结果再取补,得到实际基于重建的闭操作的结果。
figure
imshow(Iobrcbr), title('基于重建的开+闭操作 (Iobrcbr)')%%
%******************步骤三:标记前景物体************************************
%计算Iobrcbr的区域极大值来得到好的前景标记。
%得到的前景标记图fgm是二值图,白色对应前景区域
fgm = imregionalmax(Iobrcbr);%为了方便解释结果,将前景标记图叠加到原始图像上
I2 = I;
I2(fgm) = 255;%将fgm中的前景区域(像素值为1)标记到原图上(置白色)%注意到一些大部分重合或被阴影遮挡的物体没有被标记出来。这意味着这些物体最终可能不会被正确的分割出来。
%并且,有些物体中前景标记正确的到达了物体的边缘。这意味着你应该清除掉标记斑块的边缘,向内收缩一点。
%你可以通过先闭操作,再腐蚀做到这点。
se2 = strel(ones(5,5));
fgm2 = imclose(fgm, se2);
fgm3 = imerode(fgm2, se2);%这个操作会导致遗留下一些离群的孤立点,这些是需要被移除的。
%你可以通过bwareaopen做到这点,函数将移除那些包含像素点个数少于指定值的区域。
fgm4 = bwareaopen(fgm3, 20);
I3 = I;
I3(fgm4) = 255;%%
%*********************第四步:计算背景标记**********************************
%本例中设计出的标记背景算法的前提假设是:图像中相对亮的是物体,相对暗的区域是背景。
%如果不满足这条假设,标记结果可能不甚理想%现在你需要标记背景。在去除噪点后的图像Iobrcbr中,暗像素属于背景,所以你可以先进行一下阈值操作。
%bw = imbinarize(Iobrcbr);
bw=im2bw(Iobrcbr,graythresh(Iobrcbr));%我使用上一行代码报错了,故换了一种二值化方法%背景像素现在是黑的了,但是理想情况下我们不希望背景标记太接近我们想要分割的物体的边界。D = bwdist(bw);
%D = bwdist(BW)计算二值图像BW的欧几里得矩阵。对BW的每一个像素,距离变换指定像素和最近的BW非零像素的距离。
%bwdist默认使用欧几里得距离公式。BW可以由任意维数,D与BW有同样的大小。%由于bw中目标物体是白色的1.所以D中对应的目标物体处均是0,随着进入背景越深,对应像素值越大。
%这时正好符合我们使用分水岭算法的假设(想分出的目标物体数值较低)
%于是得到的背景标记是 物体与背景间的一个圈,能够包住目标物体
DL = watershed(D);
bgm = DL == 0;%分水岭变换结果L中,同一区域用同一数字表示,区域间分界线同一由0标识%%
%******************第五步:计算分割函数(修改后)的分水岭变换*****************
% 函数imimposemin可以被用来修改一副图片,使得其只在指定的位置处取得局部最小值
% 这里你可以使用imimposemin来修改梯度幅值图像,使得局部最小值只出现在前景标记和背景标记处。
% 从结果来看imimposemin会将指定区域置为-Inf,从而成为极小值
% 且图像变得相当“平整”,一块块的相同数值的区域%使用Sobel边缘检测算子,imfilter函数,和一些简单的四则运算来计算梯度幅值。
%梯度值总是在物体的边缘处高,而总是在物体内部低。
hy = fspecial('sobel');%获取sobel算子模板,计算纵向梯度
hx = hy';%横向模板
Iy = imfilter(double(I), hy, 'replicate');%计算纵向梯度
Ix = imfilter(double(I), hx, 'replicate');
gradmag = sqrt(Ix.^2 + Iy.^2);%计算梯度幅值gradmag2 = imimposemin(gradmag, bgm | fgm4);%Finally we are ready to compute the watershed-based segmentation.
L = watershed(gradmag2);%%
%******************第六步:可视化结果************************************
I4 = I;
I4(imdilate(L == 0, ones(3, 3)) | bgm | fgm4) = 255;
figure
imshow(I4)
title('在原图上绘制前景、背景标记,以及分割边界')Lrgb = label2rgb(L, 'jet', 'w', 'shuffle');
figure
imshow(Lrgb)
title('Colored watershed label matrix (Lrgb)')figure
imshow(I)
hold on
himage = imshow(Lrgb);
himage.AlphaData = 0.3;
title('Lrgb superimposed transparently on original image')end


这篇关于标记符控制的分水岭算法原理及matlab实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1054524

相关文章

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

基于Python实现简易视频剪辑工具

《基于Python实现简易视频剪辑工具》这篇文章主要为大家详细介绍了如何用Python打造一个功能完备的简易视频剪辑工具,包括视频文件导入与格式转换,基础剪辑操作,音频处理等功能,感兴趣的小伙伴可以了... 目录一、技术选型与环境搭建二、核心功能模块实现1. 视频基础操作2. 音频处理3. 特效与转场三、高

Python实现中文文本处理与分析程序的示例详解

《Python实现中文文本处理与分析程序的示例详解》在当今信息爆炸的时代,文本数据的处理与分析成为了数据科学领域的重要课题,本文将使用Python开发一款基于Python的中文文本处理与分析程序,希望... 目录一、程序概述二、主要功能解析2.1 文件操作2.2 基础分析2.3 高级分析2.4 可视化2.5

Java实现预览与打印功能详解

《Java实现预览与打印功能详解》在Java中,打印功能主要依赖java.awt.print包,该包提供了与打印相关的一些关键类,比如PrinterJob和PageFormat,它们构成... 目录Java 打印系统概述打印预览与设置使用 PageFormat 和 PrinterJob 类设置页面格式与纸张

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

Python实现终端清屏的几种方式详解

《Python实现终端清屏的几种方式详解》在使用Python进行终端交互式编程时,我们经常需要清空当前终端屏幕的内容,本文为大家整理了几种常见的实现方法,有需要的小伙伴可以参考下... 目录方法一:使用 `os` 模块调用系统命令方法二:使用 `subprocess` 模块执行命令方法三:打印多个换行符模拟