分水岭算法c语言,Opencv分水岭算法学习

article/2025/10/16 5:43:00

分水岭算法可以将图像中的边缘转化成“山脉”,将均匀区域转化为“山谷”,这样有助于分割目标。

分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中的每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响区域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。

分水岭的计算过程是一个迭代标注过程。分水岭计算分成两个步骤:一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高的排序,然后在从低到高实现淹没的过程中,对每一个局部极小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。分水岭变换得到的是输入图像的集水盆图像,集水盆之间的边界点即为分水岭。显然,分水岭表示的是输入图像的极大值点。

简而言之,分水岭算法首先计算灰度图的梯度,这对图像中的“山谷”或没有纹理的“盆地”(亮度值低的点)的形成是很有效的,也对“山头”或图像中有主导线段的“山脉”(山脊对应的边缘)的形成有效。然后开始从用户指定点(或者算法得到点)开始持续“灌注”盆地直到这些区域连成一片。基于这样产生的标记就可以把区域合并到0一起,合并后的区域又通过聚集的方式进行分割,好像图像被“填充”起来一样。

实现分水岭算法–watershed函数

函数watershed实现的分水岭算法是基于标记的分割算法中的一种。在把图像传给函数之前,需要大致勾画标记出图像中的期望进行分割的区域,它们被标记为正指数,所以,每一个区域都会被标记为像素值1、2、3等,表示成为一个或者多个连接组件,这些标记的值可以使用findContours函数和drawContours函数由二进制的掩码检索出来。这些标记就是即将绘制出来的分割区域的“种子”,而没有标记清楚的区域,被置为0,在函数的输出中,每一个标记中的像素被设置为“种子”的值,而区域间的值被设置为-1。

void watershed(inputArray,intputOutputArray markers)

*第一个参数,输入图像,需为8位三通道的彩色图像。

*第二个参数,函数调用后的运算结果存在这里,输入/输入32位单通道图像的标记结果。

#include

#include

#include

using namespace cv;

using namespace std;

//宏定义

#define WINDOW_NAME "image[procedure window]"

//全局变量声明

Mat g_srcImage,g_maskImage;

Point prevPt(-1,-1);

//全局函数声明

static void on_Mouse(int event,int x,int y,int flags,void*);

//主函数

int main()

{

//载入源图像

g_srcImage=imread("/Users/new/Desktop/1.jpg");

if(!g_srcImage.data){printf("读取源图像srcImage错误~!\n");return false;}

//显示源图像

imshow(WINDOW_NAME,g_srcImage);

Mat srcImage,grayImage;

g_srcImage.copyTo(srcImage);

//灰度化

cvtColor(srcImage, g_maskImage, COLOR_BGR2GRAY);

//imshow("image[mask]",g_maskImage);

cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);

//imshow("image[gray]",grayImage);

//掩膜图像初始化为0

g_maskImage=Scalar::all(0);

//设置鼠标回调函数

setMouseCallback(WINDOW_NAME, on_Mouse,0);

//轮询按键处理

while(1)

{

//获取键值

int c=waitKey(0);

//若按键为ESC时,退出

if((char)c == 27)

break;

//若按键为2时,恢复原图

if((char)c=='2')

{

g_maskImage=Scalar::all(0);

srcImage.copyTo(g_srcImage);

imshow("image",g_srcImage);

}

//若按键为1,则进行处理

if((char)c=='1')

{

//定义一些参数

int i,j,compCount=0;

vector>contours;

vector hierarchy;

//寻找轮廓

findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE);

//轮廓为空时的处理

if(contours.empty())

continue;

//复制掩膜

Mat maskImage(g_maskImage.size(),CV_32S);

maskImage=Scalar::all(0);

//循环绘制轮廓

for(int index=0;index>=0;index=hierarchy[index][0],++compCount)

drawContours(maskImage, contours, index, Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX);

//compCount为零时的处理

if(compCount==0)

continue;

//生成随机颜色

vectorcolorTab;

for(int i=0;i

{

int b=theRNG().uniform(0, 255);

int g=theRNG().uniform(0, 255);

int r=theRNG().uniform(0, 255);

colorTab.push_back(Vec3b((uchar)b,(uchar)g,(uchar)r));

}

//计算处理时间并输出到窗口中

double dTime=(double)getTickCount();

//进行分水岭算法

watershed(srcImage, maskImage);

dTime=(double)getTickCount()-dTime;

printf("\t 处理时间=%gms\n",dTime*1000./getTickFrequency());

//双层循环,将分水岭图像遍历存入watershedImage中

Mat watershedImage(maskImage.size(),CV_8UC3);

for(i=0;i

for(j=0;j

{

int index=maskImage.at(i,j);

if(index==-1)

watershedImage.at(i,j)=Vec3b(255,255,255);//图像变白色

else if(index<=0||index>compCount)

watershedImage.at(i,j)=Vec3b(0,0,0);//图像变黑色

else

watershedImage.at(i,j)=colorTab[index-1];

}

//混合灰度图和分水岭效果图并显示最终的窗口

watershedImage=watershedImage*0.5+grayImage*0.5;

imshow("image[watershed]",watershedImage);

}

}

return 0;

}

//回调函数定义

void on_Mouse(int event,int x,int y,int flags,void*)

{

//处理鼠标不在窗口中的情况

if(x<0||x>=g_srcImage.cols||y<0||y>=g_srcImage.rows)

return;

//处理鼠标左键相关消息

if(event==EVENT_LBUTTONUP||!(flags & EVENT_FLAG_LBUTTON))//按下左键

prevPt=Point(-1,-1);

else if(event==EVENT_LBUTTONDOWN)//松开左键

prevPt=Point(x,y);//鼠标所指的位置

//鼠标左键按下并移动,绘制出白色线条

else if(event==EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))

{

Point pt(x,y);

if(prevPt.x<0)//如果指出去了,返回

prevPt=pt;

line(g_maskImage, prevPt, pt, Scalar::all(255),2,8,0);//画白线

line(g_srcImage,prevPt,pt,Scalar::all(255),2,8,0);//画白线

prevPt=pt;

imshow(WINDOW_NAME, g_srcImage);

}

}

0caf9e2f23efd791848c938e2ff395ae.png

8fa9c03dee77de74410a5addeaefccc9.png

f4c1c6d3c9a596f54bbba96004c1e205.png

Opencv技巧

(1)计算算法运行时间:

//计算处理时间并输出到窗口中

double dTime=(double)getTickCount();

//进行分水岭算法

watershed(srcImage, maskImage);

dTime=(double)getTickCount()-dTime;

printf("\t 处理时间=%gms\n",dTime*1000./getTickFrequency());

(2)改变图像某点像素值:Mat类中的at方法对于获取图像矩阵某点的RGB值或者改变某点的值很方便,

对于单通道的图像:image.at(i, j)

对于RGB通道的图像:image.at(i, j)[0]

image.at(i, j)[1]

image.at(i, j)[2]

(3)Point(-1,-1)解析:由于卷积过程,图像矩阵要进行填充,Point(-1,-1)即代表卷积开始的位置,这决定了不填充时的结果A处于填充后结果B的位置的那个部分,从(-1,-1)开始卷积的结果是A处于B的正中间那块位置。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。


http://chatgpt.dhexx.cn/article/pnmNVIK3.shtml

相关文章

分水岭算法

引言&#xff1a;它是基于拓扑理论的形态学处理方法。将一张图像假想成为一张地貌特征图。 原理理解&#xff1a;灰度图被看作拓扑平面&#xff0c;灰度高看成山峰&#xff0c;灰度低看成山谷。从山谷开始注水&#xff0c;随着水位升高水流会相遇汇合。为了防止汇合&#xff0…

Opencv分水岭算法——watershed自动图像分割用法

分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征。 其他图像分割方法,如阈值,边缘检测等都不会考虑像素在…

目标分割算法之分水岭算法

分水岭算法 1.经典算法原理及实现 传统的目标分割算法主要分为两种 1.基于像素相似性&#xff1a;阈值分割、k-means分割 2.基于像素邻域关系&#xff1a;区域生长、分水岭、基于标记分水岭 分水岭算法原理 如图中展现了凹凸不平的地貌&#xff0c;视觉上明显的位置有盆地及…

分水岭算法的理解和应用

分水岭算法 主要思想 图像的灰度空间很像地球表面的整个地理结构&#xff0c;每个像素的灰度值代表高度。分水岭就是灰度值较大的像素连成的线。二值化阈值可以理解为水平面&#xff0c;比灰度二值化阈值小的像素区域会被淹没。随着水位线的升高&#xff0c;被淹没的区域越来越…

分水岭算法及其实现

&#xff11; - 算法描述 1.1 分水岭算法的原理   分水岭的概念是以三维方式来形象化一幅图像为基础的&#xff1a;两个空间坐标再加上强度。在这种“地形学”解释中&#xff0c;考虑三种类型的点&#xff1a;&#xff08;a&#xff09;局部最小值点&#xff0c;该点对应一个…

传统图像分割——分水岭算法(watershed)

传统图像分割——分水岭算法&#xff08;watershed&#xff09; 文章目录 传统图像分割——分水岭算法&#xff08;watershed&#xff09;前言一、什么是分水岭算法&#xff1f;二、经典的分水岭求解算法1.定义2.算法流程 总结 前言 本篇文章主要梳理分水岭算法的原理&#xf…

图像分割 - 分水岭算法

目录 1. 介绍 2. 分水岭算法的实现 距离变换 连接连通分量 3. 代码 1. 介绍 图像是由x&#xff0c;y表示的&#xff0c;如果将灰度值也考虑进去的话&#xff0c;那么一幅图像需要一个三维的空间去表示。 这样就可以把x&#xff0c;y轴比作大地&#xff0c;将灰度值的z轴…

【OpenCv】图像分割——分水岭算法

文章目录 1 原理2 算法改进3 API4 实例 1 原理 分水岭分割方法&#xff0c;是一种基于拓扑理论的数学形态学的分割方法&#xff0c;其基本思想是把图像看作是测地学上的拓扑地貌&#xff0c;图像中每一点像素的灰度值表示该点的海拔高度&#xff0c;每一个局部极小值及其影响区…

MFC图像处理CImage类常用操作

原文作者&#xff1a;aircraft 原文地址&#xff1a;https://www.cnblogs.com/DOMLX/p/9598974.html MFC图像处理CImage类常用操作 CImage类头文件为#include<atlimage.h> CImage类读取图片CImage.Load("src.bmp"); CImage类保存图片CImage.Save("dst…

使用CImage进行图像处理

MFC和ATL共享的新类CImage为图像处理提供了许多相应的处理方法 CImage类 我们知道&#xff0c;Visual C的CBitmap类和静态图片控件的功能是比较弱的&#xff0c;它只能显示出在资源中的图标、位图、光标以及图元文件的内容&#xff0c;而不像VB中的Image控件可 以显示出绝大多数…

用CImage类来显示PNG、JPG等图片

系统环境&#xff1a;Windows 7 软件环境&#xff1a;Visual Studio 2008 SP1 本次目的&#xff1a;实现VC单文档、对话框程序显示图片效果 CImage 是VC.NET中定义的一种MFC/ATL共享类&#xff0c;也是ATL的一种工具类&#xff0c;它提供增强型的&#xff08;DDB和DIB&#xff…

CImage类(外部图像文件(BMP、GIF、JPEG等)

CImage类 我们知道&#xff0c;Visual C的CBitmap类和静态图片控件的功能是比较弱的&#xff0c;它只能显示出在资源中的图标、位图、光标以及图元文件的内容&#xff0c;而不像VB中的Image控件可 以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。因此&#xff0c;想要在对…

CImage的一般使用方法和技巧

Visual C的CBitmap类的功能是比较弱的,它只能显示出在资源中的图标、位图、光标以及图元文件的内容&#xff0c;而不像VB中的Image控件可以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。如果想要在对话框或其他窗口中显示外部图像文件则只能借助于第三方提供的控件或代码,…

图像处理(C++ CImage class)学习笔记

基础篇 A. 图像三原色及灰度值 A1. 彩色图像的三原色 图像三原色 — R&#xff1a;红色red — G&#xff1a;绿色green — B&#xff1a;蓝色blue三原色的取值范围&#xff1a;0&#xff08;无&#xff09;~255&#xff08;满&#xff09; — 红色&#xff1a;R255 G0 B0 —…

Cimage

本系列文章由zhmxy555编写&#xff0c;转载请注明出处。 http://blog.csdn.net/zhmxy555/article/details/7422922 作者&#xff1a;毛星云 邮箱&#xff1a; happylifemxyqq.com 欢迎邮件交流编程心得 我们知道&#xff0c;Visual C中的CBitmap类的功能简直太弱小了&am…

【无标题】c++ MFC图像处理CImage类常用操作代码

原文作者&#xff1a;aircraft 原文地址&#xff1a;https://www.cnblogs.com/DOMLX/p/9598974.html 我看了一下发现关于c下的CImage图像处理类 的图像处理相关的介绍真的是比较少&#xff0c;因为我要做大二的数据结构的课程设计&#xff0c;要用纯c语言去实现&#xff08;老…

C++,CImage类的建立方法(可以打开图像和保存)

建立CImage类&#xff08;以vs2015为例&#xff09; 一&#xff0c; 新建一个MFC项目&#xff1a;名字为 image3 二&#xff0c; 单个文档&#xff0c;MFC标准&#xff0c;然后完成。 三&#xff0c;打开应用程序的 stdafx.h 文件添加 CImage 类的包含文件&#xff1a; #incl…

VB.net 进程通信中FindWindow、FindWindowEX、SendMessage函数的理解

目录 一、代码背景 二、主要工具 三、函数解析 1、FindWindow&#xff1a; 2、 FindWindowEx&#xff1a; 3、SendMessage&#xff1a; 四、具体代码示例&#xff1a; 1、第一部分功能&#xff1a; A、接收端&#xff1a; B、发送端 C、运行测试 2.第二部分功能&…

C#-FindWindow的用法

C# FindWindow用法 函数功能&#xff1a;该函数获得一个顶层窗口的句柄&#xff0c;该窗口的类名和窗口名与给定的字符串相匹配。 这个函数不查找子窗口。在查找时不区分大小写。 函数型&#xff1a;HWND FindWindow&#xff08;LPCTSTR IpClassName&#xff0c;LPCTSTR IpWi…

vb.net中FindWindow方法的使用

问题描述 遇到的问题是&#xff0c;需要判断MsgBox是否已经弹出&#xff0c;如果已经弹出就不要重复弹出了。 解决方案&#xff1a; 利用FindWindow方法判断MsgBox是否已经出现 MsgBox的本质就是一个窗体&#xff0c;有标题和内容&#xff0c;可以使用FindWindow这个API函数去…