【小白】Open-CV 学习笔记 - 8.5 分水岭算法——watershed自动图像分割用法

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

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

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

  3. watershed分水岭运算

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

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

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

在这里插入图片描述
这个是findContours检测到的轮廓:

在这里插入图片描述

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

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

再来看一下执行完分水岭方法之后merkers里边的内容发生了什么变化:
在这里插入图片描述
可以看到,执行完watershed之后,merkers里边被分割出来的区域已经非常明显了,空间上临近并且灰度值上相近的区域被划分为一个区域,灰度值是一样,不同区域间被划分开,这其实就是分水岭对图像的分割效果了。

#include "opencv2/imgprocc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
 
#include <iostream>
 
using namespace cv;
using namespace std;
 
Vec3b RandomColor(int vaclue);  //生成随机颜色函数
 
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);
}

第一幅图像分割效果:
在这里插入图片描述
按比例跟原始图像融合:
在这里插入图片描述


作者:-牧野-
来源:CSDN
原文:https://blog.csdn.net/dcrmg/article/details/52498440
版权声明:本文为博主原创文章,转载请附上博文链接!

发布了34 篇原创文章 · 获赞 8 · 访问量 1875

猜你喜欢

转载自blog.csdn.net/weixin_43583163/article/details/97779216