OpenCV之分水岭算法(watershed)

news2024/11/26 17:29:11

Opencv 中 watershed函数原型:

void watershed( InputArray image, InputOutputArray markers );

 

第一个参数 image,必须是一个8bit 3通道彩色图像矩阵序列,第一个参数没什么要说的。关键是第二个参数 markers,Opencv官方文档的说明如下:

Before passing the image to the function, you have to roughly outline the desired regions in the image markers with positive (>0) indices. So, every region is represented as one or more connected components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary mask using findContours() and drawContours(). The markers are “seeds” of the future image regions. All the other pixels in markers , whose relation to the outlined regions is not known and should be defined by the algorithm, should be set to 0’s. In the function output, each pixel in markers is set to a value of the “seed” components or to -1 at boundaries between the regions.

就不一句一句翻译了,大意说的是在执行分水岭函数watershed之前,必须对第二个参数markers进行处理,它应该包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以通过Opencv中findContours方法实现,这个是执行分水岭之前的要求。

接下来执行分水岭会发生什么呢?算法会根据markers传入的轮廓作为种子(也就是所谓的注水点),对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为“-1”,以做区分。

简单概括一下就是说第二个入参markers必须包含了种子点信息。Opencv官方例程中使用鼠标划线标记,其实就是在定义种子,只不过需要手动操作,而使用findContours可以自动标记种子点。而分水岭方法完成之后并不会直接生成分割后的图像,还需要进一步的显示处理,如此看来,只有两个参数的watershed其实并不简单。

下边通过图示来看一下watershed函数的第二个参数markers在算法执行前后发生了什么变化。对于一个原图:

 

经过灰度化、滤波、Canny边缘检测、findContours轮廓查找、轮廓绘制等步骤后终于得到了符合Opencv要求的merkers,我们把merkers转换成8bit单通道灰度图看看它里边到底是什么内容:

这个是分水岭运算前的merkers:

 这个是findContours检测到的轮廓:

 

看效果,基本上跟图像的轮廓是一样的,也是简单的勾勒出了物体的外形。但如果仔细观察就能发现,图像上不同线条的灰度值是不同的,底部略暗,越往上灰度越高。由于这幅图像边缘比较少,对比不是很明显.

从图像底部往上,线条的灰度值是越来越高的,并且merkers图像底部部分线条的灰度值由于太低,已经观察不到了。相互连接在一起的线条灰度值是一样的,这些线条和不同的灰度值又能说明什么呢?

答案是:每一个线条代表了一个种子,线条的不同灰度值其实代表了对不同注水种子的编号,有多少不同灰度值的线条,就有多少个种子,图像最后分割后就有多少个区域。

再来看一下执行完分水岭方法之后merkers里边的内容发生了什么变化:

 

可以看到,执行完watershed之后,merkers里边被分割出来的区域已经非常明显了,空间上临近并且灰度值上相近的区域被划分为一个区域,灰度值是一样,不同区域间被划分开,这其实就是分水岭对图像的分割效果了。

总的概括一下watershed图像自动分割的实现步骤:

1. 图像灰度化、滤波、Canny边缘检测

2. 查找轮廓,并且把轮廓信息按照不同的编号绘制到watershed的第二个入参merkers上,相当于标记注水点。

3. watershed分水岭运算

4. 绘制分割出来的区域,视觉控还可以使用随机颜色填充,或者跟原始图像融合以下,以得到更好的显示效果。

以下是Opencv分水岭算法watershed实现的完整过程:

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <iostream>

using namespace cv;
using namespace std;

Vec3b RandomColor(int value);  //生成随机颜色函数

int main( int argc, char* argv[] )
{
	Mat image=imread(argv[1]);    //载入RGB彩色图像
	imshow("Source Image",image);

	//灰度化,滤波,Canny边缘检测
	Mat imageGray;
	cvtColor(image,imageGray,CV_RGB2GRAY);//灰度转换
	GaussianBlur(imageGray,imageGray,Size(5,5),2);   //高斯滤波
	imshow("Gray Image",imageGray); 
	Canny(imageGray,imageGray,80,150);  
	imshow("Canny Image",imageGray);

	//查找轮廓
	vector<vector<Point>> contours;  
	vector<Vec4i> hierarchy;  
	findContours(imageGray,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());  
	Mat imageContours=Mat::zeros(image.size(),CV_8UC1);  //轮廓	
	Mat marks(image.size(),CV_32S);   //Opencv分水岭第二个矩阵参数
	marks=Scalar::all(0);
	int index = 0;
	int compCount = 0;
	for( ; index >= 0; index = hierarchy[index][0], compCount++ ) 
	{
		//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点
		drawContours(marks, contours, index, Scalar::all(compCount+1), 1, 8, hierarchy);
		drawContours(imageContours,contours,index,Scalar(255),1,8,hierarchy);  
	}

	//我们来看一下传入的矩阵marks里是什么东西
	Mat marksShows;
	convertScaleAbs(marks,marksShows);
	imshow("marksShow",marksShows);
	imshow("轮廓",imageContours);
	watershed(image,marks);

	//我们再来看一下分水岭算法之后的矩阵marks里是什么东西
	Mat afterWatershed;
	convertScaleAbs(marks,afterWatershed);
	imshow("After Watershed",afterWatershed);

	//对每一个区域进行颜色填充
	Mat PerspectiveImage=Mat::zeros(image.size(),CV_8UC3);
	for(int i=0;i<marks.rows;i++)
	{
		for(int j=0;j<marks.cols;j++)
		{
			int index=marks.at<int>(i,j);
			if(marks.at<int>(i,j)==-1)
			{
				PerspectiveImage.at<Vec3b>(i,j)=Vec3b(255,255,255);
			}			 
			else
			{
				PerspectiveImage.at<Vec3b>(i,j) =RandomColor(index);
			}
		}
	}
	imshow("After ColorFill",PerspectiveImage);

	//分割并填充颜色的结果跟原始图像融合
	Mat wshed;
	addWeighted(image,0.4,PerspectiveImage,0.6,0,wshed);
	imshow("AddWeighted Image",wshed);

	waitKey();
}

Vec3b RandomColor(int value)    <span style="line-height: 20.8px; font-family: sans-serif;">//生成随机颜色函数</span>
{
	value=value%255;  //生成0~255的随机数
	RNG rng;
	int aa=rng.uniform(0,value);
	int bb=rng.uniform(0,value);
	int cc=rng.uniform(0,value);
	return Vec3b(aa,bb,cc);
}

 分割效果:

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python函数语法与面向对象回顾(精华)

目录 函数 语法定义 返回值 位置参数 关键字传递 默认参数 函数参数中 / 作用 lambda表达式 递归函数 面向对象 初识对象 继承 构造函数 ​编辑 多态 "私有属性" 动态 类方法和静态方法 函数 语法定义 pyhon的函数定义语法是 def 函数名(参数…

【Python实战】-- 按条件提取所有目录下所有Excel文件指定行数据

系列文章目录 文章目录 系列文章目录前言一、背景二、使用步骤1.源码 总结 前言 一、背景 有多个目录&#xff0c;每个目录下有若干Excel文件&#xff0c;我们要提取每个Excel里面指定的行数据&#xff1a; 目录如下&#xff1a; 注&#xff1a;目录数量、名称不限&#xff0c…

Egg使用jwt拦截jtoken验证

安装 npm install egg-jwt注册插件 在config文件夹子下 plugin,js下 use strict;module.exports {//mysqlmysql: {enable: true,package: egg-mysql},//jwtjwt: {enable: true,package: egg-jwt} };使用中间件 在app文件下创建 middleware 文件夹 在middleware 文件下创建…

Cannot resolve MVC view ‘xxx‘

这是在springboot下通过controller访问templates目录下的静态文件&#xff08;Hello.html)报的错误 原因&#xff1a;缺少thymeleaf依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</ar…

初级篇—第四章聚合函数

文章目录 聚合函数介绍聚合函数介绍COUNT函数AVG和SUM函数MIN和MAX函数 GROUP BY语法基本使用使用多个列分组WITH ROLLUP HAVING基本使用WHERE和HAVING的对比开发中的选择 SELECT的执行过程查询的结构SQL 的执行原理 练习流程函数 聚合函数介绍 聚合函数作用于一组数据&#x…

ELK介绍

一、前言 前面的章节我们介绍通过ES Client将数据同步到ElasticSearch中&#xff0c;但是像日志这种数据没有必要自己写代码同步到ES那样会折腾死&#xff0c;直接采用ELK方案就好&#xff0c;ELK是Elasticsearch、Logstash、Kibana三款开源软件的缩写&#xff0c;ELK主要用于…

【教学类-39】A4红纸-国旗灯笼(庆祝中华人民共和国成立74周年)

作品展示&#xff1a; 背景需求&#xff1a; 从教十余年&#xff0c;我在每年国庆都带领中大班孩子们制作与“国旗相关”国庆庆祝物品——国旗、礼盒 一、国旗&#xff08;吸管、A4红纸、黄纸打印五角星&#xff09; 二、铅画纸手提袋&#xff08;8K铅画纸、A4红纸、黄色打印…

凉鞋的 Godot 笔记 102. 场景与节点的增删改查

在上一篇&#xff0c;我们完成了 Godot 引擎的 Hello World 输出&#xff0c;并且完成了第一个基本循环: 通过这次基本循环的完成&#xff0c;我们获得了一点点的 Godot 使用经验&#xff0c;这非常重要。 有实践经验后再去补充理论 和 先学习理论后去实践相比&#xff0c;前者…

GitHub上有助于开发微信小程序的仓库

2023年9月30日&#xff0c;周六晚上 最近帮同学在GitHub找了一些开发小程序会用到的东西 目录 UI库WePY框架基于WePY框架的Demo微信小程序开发资源汇总 UI库 GitHub - Tencent/weui-wxss: A UI library by WeChat official design team, includes the most useful widgets/m…

LOD1.3快速构建 | 多源数据自动化作业、图元和体块模型快速编辑、智能纹理贴图...

2023年&#xff0c;自然资源部先后发布了《实景三维中国建设总体实施方案&#xff08;2023—2025年&#xff09;》、《实景三维中国建设城市三维模型快速构建技术规定&#xff08;征求意见稿&#xff09;》等文件&#xff0c;明确提出&#xff0c;2024年底完成城市三维模型&…

httpserver 下载服务器demo

实现效果如下&#xff1a; 图片可以直接显示 cpp h 这些可以直接显示 其他的 则是提示是否要下载 单线程 还有bug 代码如下 先放上来 #include "httpserver.h" #include "stdio.h" #include <stdlib.h> #include <arpa/inet.h> #include…

哈希表hash_table

一个人为什么要努力&#xff1f; 我见过最好的答案就是&#xff1a;因为我喜欢的东西都很贵&#xff0c;我想去的地方都很远&#xff0c;我爱的人超完美。文章目录 哈希表的引出unordered系列的关联式容器 底层结构哈希的概念 开放寻址法拉链法&#xff08;哈希桶&#xff09;拉…

【C++进阶】:C++11

C11 一.统一列表的初始化1.{}初始化2.initializer_list 二.声明1.decltype2.nullptr 三.右值引用和移动语义1.左值和右值1.转义语句2.完美转发 四.可变参数模板1.基本概念2.STL里emplace类接口 五.lambda表达式六.新的类功能 一.统一列表的初始化 1.{}初始化 在C98中&#xf…

CSS文本属性和Emmet语法

CSS文本属性 有预定的颜色值 red,green,blue 十六进制 #ff00000,#FF5500 ,#29D794 RGB代码 rgb(255,0,0)或rgb(100%,0%,0%) <head> <style>p {text-align: right;//让字体向右移动text-decoration: normal;}a {text-decoration: none;//去掉连接的下划线color: …

beego---ORM相关操作

Beego框架是go语言开发的web框架。 **那什么是框架呢&#xff1f;**就是别人写好的代码&#xff0c;我们可以直接使用&#xff01;这个代码是专门针对某一个开发方向定制的&#xff0c;例如&#xff1a;我们要做一个网站&#xff0c;利用 beego 框架就能非常快的完成网站的开发…

【随笔记】C++ condition_variable 陷阱

问题说明 通过 std::condition_variable 来实现超时等待&#xff0c;会受到系统时间变化的影响&#xff0c;系统时间倒退修改就会导致延后唤醒&#xff0c;系统时间提前将会导致提前被唤醒&#xff0c;返回结果仍为超时。 这种问题只有在系统时间发生变化的时候才会出现&…

MyBatisPlus(七)等值查询

等值查询 条件查询&#xff1a;使用 Wrapper 对象&#xff0c;传递查询条件。 QueryWrapper&#xff08;不要使用&#xff09; 代码 Testvoid eq() {QueryWrapper<User> wrapper new QueryWrapper<>();wrapper.eq("name", "张三");List<…

装饰器模式详解和实现(设计模式 二)

装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你动态地将对象添加到现有对象中&#xff0c;以提供额外的功能&#xff0c;同时又不影响其他对象。 实现示例 1.定义一个接口或抽象类&#xff0c;表示被装饰对象的公共接口 //抽…

CSS 滚动驱动动画 view-timeline-inset

view-timeline-inset 语法例子&#x1f330; 正 scroll-padding 为正正的 length正的 percentage 负 scroll-padding 为负负的 length负的 percentage 兼容性 view-timeline-inset 在使用 view() 时说过, 元素在滚动容器的可见性推动了 view progress timeline 的进展. 默认…

数据结构—快速排序(续)

引言&#xff1a;在上一篇中我们详细介绍了快速排序和改进&#xff0c;并给出了其中的一种实现方式-挖坑法 但其实快速排序有多种实现方式&#xff0c;这篇文章再来介绍其中的另外两种-左右指针法和前后指针法。有了上一篇挖坑法的启示&#xff0c;下面的两种实现会容易许多。 …