分水岭算法可以用来快速分割图像成为同类区域
原理概述:将图像视为拓扑结构的地图,均质区域就是被陡峭边缘包围的平坦盆地,因而,可以选出较为平坦的区域。
分水岭分割的结果通过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
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
#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;
}