分水岭算法
#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;
}