欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenCV分水岭算法

程序员文章站 2022-05-16 11:21:32
...
#include<iostream>
#include <opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/video/tracking.hpp>
using namespace std;
using namespace cv;



/*
任务:

选择一副彩色图像,对其中前景进行初步分割(可采用任何合适的分割方式,
例如利用阈值或利用颜色信息等等,得到二值图,其中前景用白色表示,背景用黑色表示),
然后利用形态学运算得到标记图(前景、背景、不确定区域各1种标记,不确定区域标记为0),
最后利用分水岭分割算法进行分割,要求显示出原彩色图、初步分割结果(二值图)、标记图、
3种最终分割结果(边缘图、区域图、区域图与原图混合结果)
*/
class WatershedSegmenter {
private:
	cv::Mat markers; // 标记图(既是输入,也是结果)
public:
	// 设置标记图
	void setMarkers(const cv::Mat& markerImage) {
		// 转换成整数型图像
		markerImage.convertTo(markers, CV_32S);
	}
	// 应用分水岭分割
	cv::Mat process(const cv::Mat& image) {
		cv::watershed(image, markers);
		return markers.clone();
	}
	// 以图像的形式返回分割结果
	cv::Mat getSegmentation() {
		cv::Mat tmp;
		// 所有标签值大于255的区域都赋值为255
		// 标签值小于0的赋为0
		markers.convertTo(tmp, CV_8U);

		return tmp;
	}
	// 以图像的形式返回分水岭
	cv::Mat getWatersheds() {
		//cv::Mat tmp;
		// 在转换时将每个像素p转换为255p+255
		//markers.convertTo(tmp, CV_8U, 255, 255);

		return markers > -1;
	}
};


void main()
{
	Mat image;
	image = cv::imread("1.jpg");
	if (image.empty())
	{
		cout << "读取图像失败" << endl;
		exit(EXIT_FAILURE);
	}
	cv::imshow("Origional Image", image);
	cv::Mat binary = cv::imread("bin.bmp", cv::IMREAD_GRAYSCALE);
	if (binary.empty())
	{
		cout << "读取图像失败" << endl;
		exit(EXIT_FAILURE);
	}
	cv::imshow("初步分割结果(二值图)", binary);
	cv::Mat fg;
	// 用默认结构元素腐蚀4次
	cv::erode(binary, fg, cv::Mat(), cv::Point(-1, -1), 4);

	// 标识不含物体的图像像素
	cv::Mat bg;
	// 用默认结构元素膨胀4次
	cv::dilate(binary, bg, cv::Mat(), cv::Point(-1, -1), 4);
	// 将背景像素设为128,阈值1-254区间均可
	cv::threshold(bg, bg, 1, 128, cv::THRESH_BINARY_INV);
	// 通过合并之前两幅图得到标记图像
	// (前景为255,背景为128,待定为0)
	cv::Mat markers = fg + bg;
	cv::imshow("标记图", markers);
	// 创建分水岭分割类的对象
	WatershedSegmenter segmenter;
	// 设置标记图像,然后执行分割过程
	segmenter.setMarkers(markers);
	segmenter.process(image);
	cv::imshow("分割结果(边缘图)", segmenter.getWatersheds());
	// 得到方便观察的分割结果(边缘像素为黑色)
	cv::Mat resultForShow = segmenter.getSegmentation();
	cv::imshow("分割结果(区域图)", resultForShow);
	// 将分割结果与原图融合,以便于观察结果的正确性
	cv::cvtColor(resultForShow, resultForShow, cv::COLOR_GRAY2BGR);
	resultForShow = resultForShow * 0.5 + image * 0.5;
	cv::imshow("分割结果", resultForShow);
	int c = waitKey(0);
	if (c == 27)
	{
		exit;
	}
	destroyAllWindows();
}