opencv3實現分水嶺演算法-watershed函式
阿新 • • 發佈:2019-01-03
#include<iostream> #include<opencv2/opencv.hpp> using namespace std; using namespace cv; bool g_bDrawing = false; Point g_CurrPoint, g_OrgPoint; int g_nThick = 5, g_nBlue = 255, g_nGreen = 255, g_nRed = 0; int g_nImageOneValue = 49; Mat srcImage; Mat grayImage; Mat maskImage; /*注意:不能在毀掉函式中寫入未初始化的矩陣類,所以需要用時,需要寫一個標誌位,然後再在while(1)迴圈內使用*/ void onMouse(int event, int x, int y, int flag, void *param) { Mat &img = *(cv::Mat*)param; switch (event) { //移動滑鼠的時候 case CV_EVENT_MOUSEMOVE: { g_OrgPoint = g_CurrPoint; g_CurrPoint = Point(x, y); if (g_bDrawing == 1) { line(srcImage, g_CurrPoint, g_OrgPoint, Scalar(g_nBlue, g_nGreen, g_nRed), g_nThick); imshow("【滑鼠事件視窗】", srcImage); //在掩碼圖上進行顯示 line(maskImage, g_CurrPoint, g_OrgPoint, Scalar(g_nBlue, g_nGreen, g_nRed), g_nThick); imshow("【掩碼影象】", maskImage); } } break; //點選滑鼠左鍵時 case CV_EVENT_LBUTTONDOWN: { g_bDrawing = true; g_OrgPoint = Point(x, y); g_CurrPoint = g_OrgPoint; } break; //鬆開滑鼠左鍵時 case CV_EVENT_LBUTTONUP: { g_bDrawing = false; } break; } } int main() { Mat tempImage; RNG &rng = theRNG(); srcImage = imread("2.jpg"); //用一個變數來儲存原影象 Mat g_srcImage; srcImage.copyTo(g_srcImage); //為掩碼圖 分配空間 //因為後面需要在掩碼影象中尋找輪廓,所以需要一幅單通道的影象 maskImage.create(srcImage.size(), CV_8UC1); maskImage = Scalar::all(0); //需要得到一個 三個通道的灰度圖, 以便之後在該灰度圖中畫上彩色,便於看清效果 //首先將原圖灰度化,再將灰度化的圖轉換到 bgr空間中 cvtColor(srcImage, tempImage, CV_BGR2GRAY); cvtColor(tempImage, grayImage, CV_GRAY2BGR); namedWindow("【滑鼠事件視窗】"); setMouseCallback("【滑鼠事件視窗】", onMouse, 0); namedWindow("【滾動條視窗】", 0); createTrackbar("thick", "【滾動條視窗】", &g_nThick, 100, 0); createTrackbar("Blue", "【滾動條視窗】", &g_nBlue, 255, 0); createTrackbar("Green", "【滾動條視窗】", &g_nGreen, 255, 0); createTrackbar("Red", "【滾動條視窗】", &g_nRed, 255, 0); createTrackbar("Value", "【滾動條視窗】", &g_nImageOneValue, 100, 0); char key; while (1) { imshow("【滑鼠事件視窗】", srcImage); key = waitKey(); if (key == 27) break; //如果檢測到 鍵值是1 則恢復原圖 if (key == '1') { g_srcImage.copyTo(srcImage); maskImage = Scalar::all(0); imshow("【滑鼠事件視窗】", srcImage); } //如果檢測到空格 則開始執行分水嶺演算法 if (key == ' ') { //分水嶺演算法的步驟: /*1、首先在掩碼圖中找到輪廓, 目的是讓區域有索引值 2、用索引值來替代輪廓所包圍的所有畫素,使在一個輪廓中的畫素,具有相同的畫素值 3、利用分水嶺演算法函式將 區域內的畫素值用索引值來代替 4、遍歷得到的影象,使具有相同畫素值的元素得到同一種顏色,方便顯示(這一步 是使之前不易顯示的影象得到顯示)*/ //首先,在掩碼影象中尋找輪廓 vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(maskImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); //利用分水嶺演算法函式 //因為該函式的輸出的影象和輸入的影象是一個型別的,所以需要藉助中間變數,同時也要保證中間變數具有索引值 //這就可以通過在中間變變數中繪製輪廓來實現對輪廓索引值的標註 Mat midImage(maskImage.size(), CV_32S, Scalar(0)); for (int i = 0; i < (int)contours.size(); i++) { //讓每個畫素都得到標記 drawContours(midImage, contours, i, Scalar(i + 1), -1/*填充*/, 8, hierarchy); } imshow("【繪製輪廓的圖】", midImage); //分水嶺演算法的實現 //函式中的midImage已經為影象的每個畫素儲存了索引值,就利用這些索引值對原影象進行分水嶺操作 //以標記作為基準,利用分水嶺演算法進行擴充套件 watershed(g_srcImage, midImage); //顯示分水嶺演算法後的影象 imshow("【分水嶺演算法結果圖】", midImage); //遍歷得到的掩碼影象,用顏色值來替代相同的索引值 //在這之前需要先定義一個隨機顏色 vector<Vec3b> rngColor((int)contours.size()); rngColor.clear(); for (int i = 0; i < (int)contours.size(); i++) { uchar blue = rng.uniform(0, 255); uchar green = rng.uniform(0, 255); uchar red = rng.uniform(0, 255); rngColor.push_back(Vec3b(blue, green, red)); } //開始繪製 Mat waterImage(srcImage.size(), CV_8UC3); for (int i = 0; i < midImage.rows; i++) { for (int j = 0; j < midImage.cols; j++) { int g_nValue = midImage.at<int>(i, j); if (g_nValue == -1) waterImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255); else if (g_nValue == 0) waterImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0); else waterImage.at<Vec3b>(i, j) = rngColor[g_nValue - 1]; } } waterImage = waterImage * ((1 + g_nImageOneValue) / 100.0) + grayImage * (1 - ((1 + g_nImageOneValue) / 100.0)); imshow("【分水嶺影象】", waterImage); } } return 0; }