分水岭算法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。
一般的分水岭算法会对微弱边缘,图像中的噪声,物体表面细微的灰度变化造成过度的分割。
以下为分水岭算法的示例程序。
watershedSegmenter.h
#if !defined WATERSHS #define WATERSHS #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> class WatershedSegmenter { private: Mat markers; public: //设置标记图 void setMarkers(const Mat& markerImage) { //watershed()的输入参数必须为一个32位有符号的标记,所以要先进行转换 markerImage.convertTo(markers,CV_32S); } //执行watershed() Mat process(const Mat &image) { // Apply watershed watershed(image,markers); return markers; } // 以图像形式返回结果 Mat getSegmentation() { Mat tmp; // 从32S到8U(0-255)会进行饱和运算,所以像素高于255的一律复制为255 markers.convertTo(tmp,CV_8U); return tmp; } // 以图像形式返回分水岭 Mat getWatersheds() { Mat tmp; //在设置标记图像,即执行setMarkers()后,边缘的像素会被赋值为-1,其他的用正整数表示 //下面的这个转换可以让边缘像素变为-1*255+255=0,即黑色,其余的溢出,赋值为255,即白色。 markers.convertTo(tmp,CV_8U,255,255); return tmp; } }; #endif
main.cpp
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/calib3d/calib3d.hpp> #include "watershedSegmenter.h" using namespace std; using namespace cv; int main() { //设置视频读入,括号里面的数字是摄像头的选择,一般自带的是0 VideoCapture capture(0); if (!capture.isOpened()) { cout << "can not open the video" << endl; return -1; } Mat frame; Mat binImage; bool stop = false; while (!stop) { //读入视频帧,转换颜色空间,并分割通道 capture >> frame; cvtColor(frame, binImage, CV_BGR2GRAY); threshold(binImage, binImage, 120, 255, THRESH_BINARY); //膨胀图像 dilate(binImage, binImage, Mat()); /*分水岭算法*/ //************************************************************* Mat fg; //腐蚀图像6次 erode(binImage, fg, Mat(), Point(-1, -1), 6); // Identify image pixels without objects Mat bg; //膨胀图像6次 dilate(binImage, bg, Mat(), Point(-1, -1), 6); imshow("bg", bg); //进行固定阈值操作 threshold(bg, bg, 1, 128, THRESH_BINARY_INV); // Show markers image Mat markers(binImage.size(), CV_8U, Scalar(0)); markers = fg + bg; imshow("markers image", markers); WatershedSegmenter segmenter; segmenter.setMarkers(markers); segmenter.process(frame); imshow("segmentation", segmenter.getSegmentation()); imshow("Watersheds", segmenter.getWatersheds()); } waitKey(0); return 0; }