分水岭分割方法是基于形态学操作

一、分水岭分割方法

1. 分水岭分割方法

        基于浸泡理论的分水岭分割方法

        基于连通图的方法

        基于距离变换的方法

  2. 分水岭算法应用

             ---   分割粘连图像,实现形态学操作与对象计数

             ---   图像分割

   3. 基于距离的分水岭分割流程

opencv python分水岭算法 opencv分水岭分割_图像分割

 二、Opencv中的相关API

1. 距离变换函数cv2.distanceTransform()

      函数功能:计算源图像的每个像素到最近的零像素的距离。函数 cv::distanceTransform 计算从

每个二进制图像像素到最近的零像素的近似或精确距离。 对于零图像像素,距离显然为零。

void cv::distanceTransform 	( 	InputArray  	src,
		OutputArray  	dst,
		int  	distanceType,
		int  	maskSize,
		int  	dstType = CV_32F 
	)

参数解释: 

src     ----8位单通道二值图像(0-255)

 dst     ----计算距离后的输出图像,8位或32位浮点型的单通道图像

distanceType     ----距离变换的类型

maskSize    ----距离变换蒙版的大小, 在 DIST_L1 或 DIST_C 距离类型的情况下,该参数被强制

为 3,因为 3×3 掩膜给出与 5×5 或任何更大孔径相同的结果。

dstType    ----输出图像的类型。 它可以是 CV_8U 或 CV_32F。 类型 CV_8U 只能用于函数的第一个变体和 distanceType == DIST_L1。

 2. 分水岭函数watershed()

void watershed( InputArray image, InputOutputArray markers );

image 是 3 通道彩色(CV_8UC3)图像,

markers 是单通道(CV_32S) 图像。

三、代码演示

1. 对象分离与计数

/*
	对象分离与 计数
*/
#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	Mat src = imread("E:/技能学习/opencv图像分割/coins_001.jpg");
	if (src.empty())
	{
		cout << "could not load image!" << endl;
		return -1;
	}

	//namedWindow("input image", WINDOW_AUTOSIZE);
	//imshow("input image", src);

	Mat gray, binary, shifted;
	pyrMeanShiftFiltering(src, shifted, 21, 51); //边缘保留的滤波方法
	//imshow("shifted image", shifted);

	//变为灰度图
	cvtColor(shifted, gray, COLOR_BGR2GRAY);

	//二值化
	threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	//imshow("binary image", binary);

	//距离变化
	Mat dist;
	distanceTransform(binary, dist, DIST_L2, 3, CV_32F);

	//由于距离变换得到的值比较小,所以对其值进行归一化 便于显示
	//归一化
	normalize(dist, dist, 0, 1, NORM_MINMAX);
	//imshow("distance result", dist);

	//距离变换之后再次进行二值化,目的是为了寻找局部最大
	threshold(dist, dist, 0.4, 1, THRESH_BINARY);
	//imshow("distance binary", dist);

	//生成掩膜(markers)
		//寻找轮廓
	Mat dist_m;
	vector<vector<Point>>contours;

	// finContours只支持CV_8UC1的格式,所以要进行通道转换
	dist.convertTo(dist_m, CV_8U); 
	findContours(dist_m, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//create markers
	Mat markers = Mat::zeros(src.size(), CV_32SC1);
	//绘制轮廓
	for (size_t t = 0; t < contours.size(); t++)
	{
		drawContours(markers, contours, static_cast<int>(t), Scalar::all(static_cast<int>(t) + 1), -1);
	}
	circle(markers, Point(5, 5), 3, Scalar(255), -1);
	imshow("markers", markers * 10000);

	//形态学操作 -- 彩色图像,目的是去掉干扰  让结果更好
	Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	morphologyEx(src, src, MORPH_ERODE, k);
	
	//完成分水岭变换
	watershed(src, markers);

	Mat mark ;
	markers.convertTo(mark, CV_8UC1);

	bitwise_not(mark, mark, Mat()); //非操作
	imshow("watershed result", mark);

	//产生随机颜色
	vector<Vec3b> color;
	for (size_t i = 0; i < contours.size(); i++)
	{
		int r = theRNG().uniform(0, 255);
		int g = theRNG().uniform(0, 255);
		int b = theRNG().uniform(0, 255);

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

	}

	//颜色填充与最终显示
	Mat dst = Mat::zeros(markers.size(), CV_8UC3);
	int index = 0;

	for (int row = 0; row < markers.rows; row++)
	{
		for (int col = 0; col < markers.cols; col++)
		{
			index = <int>(row, col);
			if (index > 0 && index <= contours.size())
			{
				dst.at<Vec3b>(row, col) = color[index - 1];
			}
			else
			{
				dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
			}
		}
	}
	
	imshow("Final Result", dst);
	cout << "number of object:" << contours.size() << endl;

	waitKey(0);
	destroyAllWindows();
	return 0;

}

2. 图像分割

/*
	图像分割
*/
#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat watershedCluster(Mat &image, int &numComp);

void createDisplaySegments(Mat &markers, int numSegments, Mat &image);

int main(int argc, char** argv)
{
	Mat src = imread("E:/技能学习/opencv图像分割/test.jpg");
	if (src.empty())
	{
		cout << "could not load image!" << endl;
		return -1;
	}

	namedWindow("input image", WINDOW_AUTOSIZE);
	imshow("input image", src);

	int numSegments;

	Mat markers = watershedCluster(src, numSegments);
	createDisplaySegments(markers, numSegments, src);


	waitKey(0);
	destroyAllWindows();
	return 0;

}

Mat watershedCluster(Mat &image,int &numComp)
{
	//二值化
	Mat gray, binary;
	cvtColor(image, gray, CV_BGR2GRAY);
	threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	//形态学与距离变换
	Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	morphologyEx(binary, binary, MORPH_OPEN, k, Point(-1, -1));
	Mat dist;
	distanceTransform(binary, dist, DIST_L2, 3, CV_32F);
	normalize(dist, dist, 0.0, 1.0, NORM_MINMAX);

	//开始生成标记
	threshold(dist, dist, 0.1, 1.0, THRESH_BINARY);
	normalize(dist, dist, 0, 255, NORM_MINMAX);
	dist.convertTo(dist, CV_8UC1);  // finContours只支持CV_8UC1的格式,所以要进行通道转换

	//标记开始
	vector<vector<Point>>contours;
	vector<Vec4i>hireachy;
	findContours(dist, contours, hireachy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
			//cv::RETR_EXTERNAL:只检测最外围轮廓;
			// cv::RETR_LIST:检测所有的轮廓,但是不建立等级关系;
			 //cv::RETR_CCOMP:检测所有的轮廓,但所有轮廓只建立两种等级关系,外围为顶层
			// cv::RETR_TREE:检测所有的轮廓,所有轮廓建立一个等级树结
	if (contours.empty())
	{
		return Mat();
	}

	Mat markers(dist.size(), CV_32S);
	for (int i = 0; i < contours.size(); i++)
	{
		drawContours(markers, contours, i, Scalar(i + 1), -1, 8, hireachy, INT_MAX);
		//INT_MAX表示 将在图上绘制的轮廓层次的最大深度,为0表示只绘制“第0层”,以此类推
	}

	circle(markers, Point(5, 5), 3, Scalar(255), -1);

	//分水岭变换
	watershed(image, markers);
	numComp = contours.size();

	return  markers;
}

void createDisplaySegments(Mat &markers, int numSegments, Mat &image)
{
	//产生随机颜色
	vector<Vec3b> color;
	for (size_t i = 0; i < numSegments; i++)
	{
		int r = theRNG().uniform(0, 255);
		int g = theRNG().uniform(0, 255);
		int b = theRNG().uniform(0, 255);
		
		color.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
		
	}
		
	//颜色填充与最终显示
	Mat dst = Mat::zeros(markers.size(), CV_8UC3);
	int index = 0;
		
	for (int row = 0; row < markers.rows; row++)
	{
		for (int col = 0; col < markers.cols; col++)
		{
			index = <int>(row, col);
			if (index > 0 && index <= numSegments)
			{
				dst.at<Vec3b>(row, col) = color[index - 1];
			}
			else
			{
				dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
			}
		}
	}
			
	imshow("Final Result", dst);
	return;
}