分水岭算法可以用来快速分割图像成为同类区域
原理概述:将图像视为拓扑结构的地图,均质区域就是被陡峭边缘包围的平坦盆地,因而,可以选出较为平坦的区域。
分水岭分割的结果通过cv::watershed函数获取,在这里,我们创建一个WatershedSegmenter类,进行分水岭算法的封装工作。
整体算法概述:
在选取的图像中,我们先进行二值化操作,操作争取将前景物体最大可能性提出。
之后,多次采用腐蚀的方法得到目标的前景图片,这个前景图片就只有0和255两个灰度值;
再多次使用膨胀的方法得到目标的背景图片,在之后使灰度图中高于128灰度的像素变为0,低于的部分为128(采用threshold);
之后,将前景背景相加,得到一张标记图,这张标记图就只有0,128,255三个灰度值。
之后,就可以得到图片了。
啥也不说,上代码。
分水岭算法概述:
1.一张灰度图,我们先将灰度值想象成柱状图,那么我们就能得到一张三维的灰度柱状图分布表。
2.如上所述,二值化后的图片只有0,128,255的几个值。
3.也就是说,腐蚀过后的图片是我们的前景图片,只有0和255这两个值,在三维图上,255的选定区域是最高的山峰,也就是标记区域,代表着目标图像。
膨胀+二值化选取之后的图像拥有128和0两个值,0对应的是目标图像的区域,128对应着无关背景。
4.现在,将二者相加:
我们可以得到中心有着高亮“山峰”的目标图像区域,以及无关的背景(腐蚀图像中255部分和膨胀图像中0相加;膨胀图像中128部分和腐蚀图像中0相加)
5.现在想象一下这张三维灰度直方图,255部分和0连接;0部分和128部分连接;128部分和255部分不相连接
分水岭算法,现在就从255部分开始“注水”,也就是说,255部分用来确定从哪里开始“注水”。当注水到128部分和0部分边界时,注水停止。
这时,前景就全被选出来了。
WatershedSegmenter类
#pragma once #include "stdafx.h" #include <opencv2\opencv.hpp> class WatershedSegmenter { public: WatershedSegmenter(); ~WatershedSegmenter(); private: cv::Mat markers; public: void setMarkets(const cv::Mat &marketImage) { marketImage.convertTo(markers, CV_32S); } cv::Mat process(const cv::Mat &image); cv::Mat getSegmentation(); cv::Mat getWatersheds(); }; #include "stdafx.h" #include "WatershedSegmenter.h" WatershedSegmenter::WatershedSegmenter() { } WatershedSegmenter::~WatershedSegmenter() { } cv::Mat WatershedSegmenter::process(const cv::Mat &image) { cv::watershed(image, markers); return markers; } cv::Mat WatershedSegmenter::getSegmentation() { cv::Mat tmp; markers.convertTo(tmp, CV_8U); return tmp; } cv::Mat WatershedSegmenter::getWatersheds() { cv::Mat tmp; markers.convertTo(tmp, CV_8U, 255, 255); return tmp; }
main函数
#include "stdafx.h" #include <opencv2\opencv.hpp> #include "WatershedSegmenter.h" #include "MorphoFeatures.h" int main() { cv::Mat image = cv::imread("F:\\group.jpg"); cv::Mat binary = cv::imread("F:\\Image\\binary.bmp", 0); cv::Mat fg, bg; cv::Mat markers(binary.size(), CV_8U, cv::Scalar(0)); WatershedSegmenter segmenter; cv::erode(binary, fg, cv::Mat(), cv::Point(-1, -1), 6); cv::dilate(binary, bg, cv::Mat(), cv::Point(-1, -1), 6); cv::threshold(bg, bg, 1, 128, cv::THRESH_BINARY_INV); markers = fg + bg; segmenter.setMarkets(markers); segmenter.process(image); cv::imshow("Foreground Image", fg); cv::imshow("Background Image", bg); cv::imshow("Segmentation", segmenter.getSegmentation()); cv::imshow("Watersheds", segmenter.getWatersheds()); cv::waitKey(0); return 0; }