分水岭算法

分水岭算法

#include<opencv2/opencv.hpp>

/*
* 完成分水岭算法步骤:
1、加载原始图像
2、阈值分割,将图像分割为黑白两个部分
3、对图像进行开运算,即先腐蚀在膨胀
4、对开运算的结果再进行 膨胀,得到大部分是背景的区域
5、通过距离变换 Distance Transform 获取前景区域
6、背景区域sure_bg 和前景区域sure_fg相减,得到即有前景又有背景的重合区域
7、连通区域处理
8、最后使用分水岭算法
*/

cv::Mat waterShed(cv::Mat mat) {

	//转BGR
	cv::Mat img;
	cv::cvtColor(mat, img, CV_BGRA2BGR);
	
	//灰度图
	cv::Mat gray;
	cv::cvtColor(img, gray, CV_BGR2GRAY);

	//高斯滤波
	cv::Mat gaussian;
	cv::GaussianBlur(gray, gaussian, cv::Size(7, 7), 0.5);

	//阈值分割
	cv::Mat thre;

	cv::threshold(gaussian, thre, 125, 255, cv::THRESH_BINARY);	//bianyuan.png
	//cv::threshold(gaussian, thre, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU); //yingbi.jpg

	//开运算
	cv::Mat er;
	cv::Mat kai;
	cv::Mat erode_element = cv::getStructuringElement(
		cv::MORPH_RECT, cv::Size(3, 3));
	cv::Mat dilate_element = cv::getStructuringElement(
		cv::MORPH_RECT, cv::Size(3, 3));
	cv::erode(thre, er, erode_element);
	cv::dilate(er, kai, dilate_element);
	//cv::Mat kai = pengZhang(fuShi(thre));

	//再膨胀,获取背景
	cv::Mat bg;
	cv::dilate(kai, bg, dilate_element);

	//距离变换,获取前景,convertTo()转变图片的类型
	cv::Mat fg;
	cv::Mat th;
	cv::Mat dt;
	double maxValue = 0;

	/*
	cv::distanceTransform(kai, dt, cv::DIST_L2, 5);
	cv::minMaxLoc(dt, NULL, &maxValue);
	cv::threshold(dt, th, 0.1 * maxValue, 255, cv::THRESH_BINARY);
	th.convertTo(fg, 0);
	*/

	//为了求得确定的前景,也就是注水处使用距离的方法转化
	cv::distanceTransform(kai, dt, cv::DIST_L2, cv::DIST_FAIR);
	//归一化所求的距离转换,转化范围是[0, 1]
	cv::normalize(dt, dt, 0, 1.0, cv::NORM_MINMAX);
	cv::minMaxLoc(dt, NULL, &maxValue);
	//再次做二值化,得到确定的前景
	cv::threshold(dt, th, 0.3 * maxValue, 255, cv::THRESH_BINARY);
	th.convertTo(fg, 0);

	//bg与fg相减, 得到既有前景又有背景的重合区域,此区域和轮廓区域的关系未知
	cv::Mat unknow = cv::Mat(bg.size(), bg.type());
	if (bg.type() == fg.type() &&
		bg.rows == fg.rows &&
		bg.cols == fg.cols) {
		cv::addWeighted(bg, 1, fg, -1, 0, unknow);
	}

	/*
	cv::namedWindow("bg", CV_WINDOW_AUTOSIZE);
	cv::namedWindow("fg", CV_WINDOW_AUTOSIZE);
	cv::namedWindow("dt", CV_WINDOW_AUTOSIZE);
	cv::namedWindow("unknow", CV_WINDOW_AUTOSIZE);
	cv::imshow("bg", bg);
	cv::imshow("fg", fg);
	cv::imshow("dt", dt);
	cv::imshow("unknow", unknow);
	*/

	/*
	std::cout << bg.type() << '\t' << fg.type() << std::endl;
	std::cout << bg.cols << '\t' << fg.cols << std::endl;
	std::cout << bg.rows << '\t' << fg.rows << std::endl;
	*/

	//连通区域处理
	cv::Mat market;
	cv::connectedComponents(fg, market, 8);
	//std::cout << market << std::endl;
	//std::cout << unknow << std::endl;
	market = market + 1;
	//std::cout << market.channels() << std::endl;
	
	//不确定的位置标0;这是因为在分水岭算法中,会将标签为0的区域当作不确定区域
	for (int i = 0; i < market.rows; i++) {
		for (int j = 0; j < market.cols; j++) {
			if (unknow.at<uchar>(i, j) == 255) {
				market.at<int>(i, j) = 0;
			}
		}
	}

	/*
	cv::namedWindow("test", CV_WINDOW_AUTOSIZE);
	cv::imshow("test", thre);
	*/

	//std::cout << img.type() << '\t' << market.type() << std::endl;
	//std::cout << img.size() << '\t' << market.size() << std::endl;
	//std::cout << img.channels() << std::endl;
	cv::watershed(img, market);
	//std::cout << market << std::endl;

	//最外圈不计入边缘
	cv::Mat res = cv::Mat(img.size(), img.type());
	for (int i = 1; i < market.rows - 1; i++) {
		for (int j = 1; j < market.cols - 1; j++) {
			if (market.at<int>(i, j) == -1) {
				std::cout << "[" << i << "," << j << "]" << std::endl;
				img.at<cv::Vec3b>(i, j)[0] = 0;
				img.at<cv::Vec3b>(i, j)[1] = 0;
				img.at<cv::Vec3b>(i, j)[2] = 255;
			}
			/*
			if (market.at<int>(i, j) == 2) {;
			res.at<cv::Vec3b>(i, j)[0] = img.at<cv::Vec3b>(i, j)[0];
			res.at<cv::Vec3b>(i, j)[1] = img.at<cv::Vec3b>(i, j)[1];
			res.at<cv::Vec3b>(i, j)[2] = img.at<cv::Vec3b>(i, j)[2];
			}
			*/

		}
	}

	return img;
}

int main() {

	cv::Mat img = cv::imread("D:\\pic\\bianyuan.png", CV_LOAD_IMAGE_UNCHANGED);
	//cv::Mat img = cv::imread("D:\\pic\\yezi.png", CV_LOAD_IMAGE_UNCHANGED);

	/*
	cv::namedWindow("yuantu", CV_WINDOW_AUTOSIZE);
	cv::imshow("yuantu", img);
	*/

	cv::Mat yezi;
	yezi = waterShed(img);

	cv::namedWindow("yezi", CV_WINDOW_AUTOSIZE);
	cv::imshow("yezi", yezi);
	cv::moveWindow("yezi", 100, 100);
	cv::waitKey(0);

	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值