分水嶺算法,是一種基於拓撲理論的數學形態學的分割方法,其基本思想是把圖像看作是測地學上的拓撲地貌,圖像中每一點像素的灰度值表示該點的海拔高度,每一個局部極小值及其影響區域稱為集水盆,而集水盆的邊界則形成分水嶺。
一般的分水嶺算法會對微弱邊緣,圖像中的噪聲,物體表面細微的灰度變化造成過度的分割。
以下為分水嶺算法的示例程序。
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; }