分水岭算法源代码及其分析和注释
- 格式:pdf
- 大小:1.92 MB
- 文档页数:13
分水岭分割算法原理
嘿,朋友!今天咱就来聊聊这神奇的分水岭分割算法原理。
你知道吗,这分水岭分割算法就像是一个超级侦探,能把复杂的图像分割得清清楚楚!比如说,咱看一张有好多物体混合在一起的图片,就像一个乱糟糟的房间。
而分水岭算法呢,就是那个能把房间里的东西一一整理清楚,区分开来的厉害角色!
它是怎么做到的呢?哎呀,就好像它在图像上慢慢行走,根据不同区域的特点来划分边界。
就拿山脉来类比吧,分水岭不就是那些把水流分开的山脊嘛!这算法也是在找那些能把图像区域分开的“山脊线”呢!
想象一下,图像中的不同区域就像是不同的群体,而分水岭算法能精准地把它们区分开,这多牛啊!比如说有张照片,里面有个红苹果和一个绿梨子,那分水岭算法就能清楚地把苹果和梨子各自归为一类,从不会搞混。
咱再想想,如果没有这个算法,那我们要处理图像得多麻烦呀!那简直就像在黑暗中摸索,不知所措。
但有了它,就像是打开了一盏明灯,一下子就把路给照亮了!
这分水岭分割算法原理啊,真的是太神奇、太有用了!它让我们能更轻松地处理图像,让图像变得更清晰、更有条理。
难道你不想更深入地了解它吗?反正我觉得它真的是超级棒的!它就是图像处理领域的一颗耀眼明星啊!。
分水岭算法1. 简介分水岭算法(Watershed algorithm)是一种图像分割算法,可以将图像中的不同区域进行分离和标记。
它基于图像的灰度值和梯度信息,将图像看作一个地形地貌,并从低处向高处逐渐充满水,直到不同区域之间的水汇聚形成分割线。
该算法最初是由Belknap和Hoggan在1979年提出的,后来被广泛应用于计算机视觉领域,特别是在医学图像处理、目标检测和图像分析等方面。
2. 原理2.1 灰度变换在进行分水岭算法之前,需要对原始图像进行灰度变换。
这可以通过将彩色图像转换为灰度图像来实现。
灰度图像中的每个像素点都代表了原始彩色图像中相应位置的亮度值。
2.2 梯度计算接下来,需要计算灰度图像中每个像素点的梯度值。
梯度表示了亮度变化的速率,可以帮助我们找到不同区域之间的边界。
常用的梯度计算方法有Sobel、Prewitt和Scharr等算子。
这些算子对图像进行卷积操作,将每个像素点的梯度计算为其周围像素点的亮度差值。
2.3 标记初始化在进行分水岭算法之前,需要为每个像素点初始化一个标记值。
通常情况下,我们可以将背景区域标记为0,前景区域标记为正整数。
2.4 梯度图像处理接下来,我们将梯度图像中的每个像素点看作一个地形地貌中的一个位置,并将其灌满水。
初始时,所有像素点的水位都是0。
2.5 水汇聚从灰度最小值开始,逐渐增加水位直到灰度最大值。
在每次增加水位时,会发生以下情况: - 当前水位高于某个位置的梯度值时,该位置被认为是不同区域之间的边界。
- 如果两个不同区域之间存在连接路径,则会发生水汇聚现象。
此时需要将这两个区域合并,并更新合并后区域的标记值。
2.6 分割结果当水位达到最大值时,分割过程结束。
此时所有不同区域之间都有了明确的边界,并且每个区域都有了唯一的标记值。
3. 算法优缺点3.1 优点•分水岭算法是一种无监督学习方法,不需要依赖任何先验知识或训练数据。
•可以对图像中的任意区域进行分割,不受形状、大小和数量的限制。
C++中实现OpenCV图像分割与分⽔岭算法分⽔岭算法是⼀种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从⽽将在空间位置上相近并且灰度值相近的像素点互相连接起来构成⼀个封闭的轮廓,封闭性是分⽔岭算法的⼀个重要特征。
API介绍void watershed( InputArray image, InputOutputArray markers );参数说明:image: 必须是⼀个8bit 3通道彩⾊图像矩阵序列markers: 在执⾏分⽔岭函数watershed之前,必须对第⼆个参数markers进⾏处理,它应该包含不同区域的轮廓,每个轮廓有⼀个⾃⼰唯⼀的编号,轮廓的定位可以通过Opencv中findContours⽅法实现,这个是执⾏分⽔岭之前的要求。
算法会根据markers传⼊的轮廓作为种⼦(也就是所谓的注⽔点),对图像上其他的像素点根据分⽔岭算法规则进⾏判断,并对每个像素点的区域归属进⾏划定,直到处理完图像上所有像素点。
⽽区域与区域之间的分界处的值被置为“-1”,以做区分。
我们将⼀个如何使⽤距离变换和分⽔岭分割相互接触的物体的例⼦。
考虑⼀下下⾯的硬币图像,这些硬币相互接触。
即使你去阈值化它,它也会互相碰触。
我们从找到硬币的⼤概估计值开始。
为此,我们可以利⽤⼤津的⼆值化。
#include<iostream>#include<opencv2\opencv.hpp>using namespace std;using namespace cv;int main() {Mat gray, thresh;Mat img = imread("coins.jpg");cvtColor(img, gray, COLOR_BGR2GRAY);threshold(gray, thresh, 0, 255, THRESH_BINARY_INV+CV_THRESH_OTSU);imshow("Otst阈值图像", thresh);waitKey(0);return 0;}阈值后的图像如下所⽰:现在需要去除图像中任何微⼩的⽩⾊噪声。
Step 1: Read in the Color Image and Convert it to Grayscale第一步:读入彩色图像,将其转化成灰度图像clc; clear all; close all;rgb = imread('pears.png');if ndims(rgb) == 3I = rgb2gray(rgb);elseI = rgb;endfigure('units', 'normalized', 'position', [0 0 1 1]);subplot(1, 2, 1); imshow(rgb); title('原图');subplot(1, 2, 2); imshow(I); title('灰度图');Step 2: Use the Gradient Magnitude as the Segmentation Function第2步:将梯度幅值作为分割函数Use the Sobel edge masks, imfilter, and some simple arithmetic to compute the gradient magnitude. The gradient is high at the borders of the objects and low (mostly) inside the objects.使用Sobel边缘算子对图像进行水平和垂直方向的滤波,然后求取模值,sobel算子滤波后的图像在边界处会显示比较大的值,在没有边界处的值会很小。
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('梯度幅值图像')Can you segment the image by using the watershed transform directly on the gradient magnitude?可否直接对梯度幅值图像使用分水岭算法?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('梯度幅值做分水岭变换')No. Without additional preprocessing such as the marker computations below, using the watershed transform directly often results in "oversegmentation."直接使用梯度模值图像进行分水岭算法得到的结果往往会存在过度分割的现象。
程序代码:(代码标记[code]...[/code] )clear,clc%三种方法进行分水岭分割%读入图像filename='Fig1021(a)(small-blobs).tif';f=imread(filename);Info=imfinfo(filename);if Info.BitDepth>8f=rgb2gray(f);endfigure,mesh(double(f));%显示图像,类似集水盆地%方法1:一般分水岭分割,从结果可以看出存在过分割问题b=im2bw(f,graythresh(f));%二值化,注意应保证集水盆地的值较低(为0),否则就要对b取反d=bwdist(b); %求零值到最近非零值的距离,即集水盆地到分水岭的距离l=watershed(-d); %matlab自带分水岭算法,l中的零值即为风水岭w=l==0; %取出边缘g=b&~w; %用w作为mask从二值图像中取值figuresubplot(2,3,1),imshow(f);subplot(2,3,2),imshow(b);subplot(2,3,3),imshow(d);subplot(2,3,4),imshow(l);subplot(2,3,5),imshow(w);subplot(2,3,6),imshow(g);%方法2:使用梯度的两次分水岭分割,从结果可以看出还存在过分割问题(在方法1的基础上改进)h=fspecial('sobel');%获得纵方向的sobel算子fd=double(f);g=sqrt(imfilter(fd,h,'replicate').^2+imfilter(fd,h','replicate').^2);%使用sobel算子进行梯度运算l=watershed(g);%分水岭运算wr=l==0;g2=imclose(imopen(g,ones(3,3)),ones(3,3));%进行开闭运算对图像进行平滑l2=watershed(g2);%再次进行分水岭运算wr2=l2==0;f2=f;f2(wr2)=255;figuresubplot(2,3,1),imshow(f);subplot(2,3,2),imshow(g);subplot(2,3,3),imshow(l);subplot(2,3,4),imshow(g2);subplot(2,3,5),imshow(l2);subplot(2,3,6),imshow(f2);%方法3:使用梯度加掩模的三次分水岭算法(在方法2的基础上改进)h=fspecial('sobel');%获得纵方向的sobel算子fd=double(f);g=sqrt(imfilter(fd,h,'replicate').^2+imfilter(fd,h','replicate').^2);%使用sobel算子进行梯度运算l=watershed(g);%分水岭运算wr=l==0;rm=imregionalmin(g); %计算图像的区域最小值定位,该函数仅仅是用来观察为何分水岭算法产生这么多集水盆地im=imextendedmin(f,2);%上面仅是产生最小值点,而该函数则是得到最小值附近的区域,此处的附近是相差2的区域fim=f;fim(im)=175; %将im在原图上标识出,用以观察lim=watershed(bwdist(im));%再次分水岭计算em=lim==0;g2=imimposemin(g,im|em);%在梯度图上标出im和em,im是集水盆地的中心,em是分水岭l2=watershed(g2); %第三次分水岭计算f2=f;f2(l2==0)=255; %从原图对分水岭进行观察figuresubplot(3,3,1),imshow(f);subplot(3,3,2),imshow(g);subplot(3,3,3),imshow(l);subplot(3,3,4),imshow(im);subplot(3,3,5),imshow(fim);subplot(3,3,6),imshow(lim);subplot(3,3,7),imshow(g2);subplot(3,3,8),imshow(l2)subplot(3,3,9),imshow(f2);。
pythonopencv之分⽔岭算法⽰例本⽂介绍了python opencv之分⽔岭算法⽰例,分享给⼤家,具体如下:⽬标1. 使⽤分⽔岭算法对基于标记的图像进⾏分割2. 使⽤函数cv2.watershed()原理:灰度图像可以被看成拓扑平⾯,灰度值⾼的区域可以看出⼭峰,灰度值低的区域可以看成是⼭⾕。
向每⼀个⼭⾕当中灌不同颜⾊的⽔。
⽔位升⾼,不同⼭⾕的⽔会汇合,为防⽌不同⼭⾕的⽔汇合,⼩在汇合处建⽴起堤坝。
然后继续灌⽔,然后再建⽴堤坝,直到⼭峰都掩模。
构建好的堤坝就是图像的分割。
此⽅法通常会得到过渡分割的结果,因为图像中的噪声以及其他因素。
为了减少此影响,opencv使⽤基于标记的分⽔岭算法,此算法要设置哪些⼭⾕中的汇合点,哪些不是。
这是⼀种交互式的图像分割算法那。
我们要给已知对象打上不同表情。
如果某个区域肯定是前景或对象,就使⽤某个颜⾊或灰度值标签标记它。
如果是背景那么使⽤其他颜⾊进⾏标记,其余不能确定的部分⽤0标记。
然后使⽤分⽔岭算法,每次灌⽔,标签会被更新,当两个不同颜⾊的标签相遇就会构建堤坝,知道所有⼭峰掩模,最后得到的边界对象值是-1。
代码:对挨在⼀起的对象进⾏分割。
使⽤Otsu's ⼆值化后的结果为要出去图像中的⽩噪声。
可以使⽤形态学运算,使⽤闭运算去除对象中的空洞。
靠近对象中⼼的区域是前景,离对象远的区域是背景,不确定的区域是边界。
⾸先提取硬币区域,使⽤腐蚀操作去掉边缘,剩下的就是硬币。
但硬币没有接触时,此⽅法有效,但是由于硬币相互接触,就要使⽤另外⼀种有效的⽅法:距离变换加上合适的阈值。
之后,要寻找不确定是否是硬币的区域。
这⾥需要膨胀操作。
膨胀操作会将对象边界延伸到背景当中。
由于边界区域被去除,现在就能知道哪些区域是前景,哪些是背景。
余下的区域不知道如何区分,那么使⽤分⽔岭算法。
这些区域通常是前景与背景的交界处。
从能否确认是否是背景的区域中减去确定是前景的区域就得到了边界。
分水岭分割算法matlab -回复标题:分水岭分割算法在MATLAB环境中的实现与解析一、引言分水岭分割算法是一种基于图像拓扑特性的图像分割方法,其基本思想源于地理学中的分水岭概念。
在图像处理中,我们可以将图像看作是一个地形图,每个像素的灰度值视为该点的高度,然后通过寻找局部极小值并将其作为汇水盆地,进而扩展到相邻的高地区域,形成分水岭,最终实现图像的分割。
本文将详细阐述如何在MATLAB环境中实现分水岭分割算法。
二、分水岭分割算法的基本原理分水岭分割算法主要包括以下几个步骤:1. 检测局部极小值:在图像中找到灰度值小于其所有邻域像素的点,这些点被视为分水岭的起点,即汇水盆地。
2. 扩展种子点:从每个局部极小值开始,向周围灰度值较高的区域扩张,直到遇到另一个局部极小值或者图像边缘。
3. 分割图像:当所有的局部极小值都被扩展后,图像就被分割成多个互不相连的区域,每个区域对应一个汇水盆地。
三、在MATLAB中实现分水岭分割算法以下是在MATLAB中实现分水岭分割算法的步骤:1. 导入图像:首先,我们需要导入需要进行分割的图像。
可以使用imread 函数来读取图像。
matlabimg = imread('your_image_file.jpg');2. 转换为灰度图像:如果原始图像为彩色图像,需要将其转换为灰度图像。
matlabgray_img = rgb2gray(img);3. 高斯滤波:为了消除图像中的噪声,可以对灰度图像进行高斯滤波。
matlabfiltered_img = imgaussfilt(gray_img, 2);4. 检测局部极小值:使用MATLAB的imregionalmin函数可以检测图像中的局部极小值。
matlabmarkers = imregionalmin(filtered_img);5. 扩展种子点:使用MATLAB的watershed函数进行分水岭分割。