《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(五)分水岭算法(watershed algorithm)
8.5 分水嶺算法(watershed algorithm)
1.基于拓?fù)淅碚摰臄?shù)學(xué)形態(tài)學(xué)的分割方法。
2.基本思想:把圖像看作測(cè)地學(xué)上的拓?fù)涞孛?#xff0c;圖像中每一點(diǎn)像素的灰度值表示該點(diǎn)的海拔高度,每一個(gè)局部極小值及其影響區(qū)域稱為集水盆,分水嶺變換得到的是輸入圖像的集水盆圖像,集水盆邊界形成分水嶺(輸入圖像的極大值點(diǎn))。
3.計(jì)算過(guò)程:一個(gè)迭代標(biāo)注過(guò)程,包括排序過(guò)程和淹沒(méi)過(guò)程,先對(duì)每個(gè)像素的灰度級(jí)進(jìn)行從低到高的排序,然后在從低到高實(shí)現(xiàn)淹沒(méi)的過(guò)程中,對(duì)每一個(gè)局部最小值在h階高度的影響域采用先進(jìn)先出(FIFO)結(jié)構(gòu)進(jìn)行判斷及標(biāo)注。
8.5.1 實(shí)現(xiàn)分水嶺算法:watershed()函數(shù)
1.基本操作:
??基于標(biāo)記的分割算法,在把圖像傳給函數(shù)之前,需要大致標(biāo)記出圖像中期望進(jìn)行分割的區(qū)域,每個(gè)區(qū)域被標(biāo)記為像素值1、2、3等,表示成為一個(gè)或多個(gè)連接組件,標(biāo)記的值可使用findContours()函數(shù)和drawContours()函數(shù)由二進(jìn)制的掩碼檢索出來(lái),函數(shù)輸出中,每一個(gè)標(biāo)記中的像素被設(shè)置為標(biāo)記的值,區(qū)域間的值被設(shè)置為-1。
2.函數(shù)原型:
void watershed(InputArray image,InputOutputArray markers)
3.參數(shù)說(shuō)明:
??(1)輸入圖像,8位三通道彩色圖像
??(2)函數(shù)調(diào)用后運(yùn)算結(jié)果,輸入/輸出32位單通道圖像的標(biāo)記結(jié)果
8.5.2 綜合示例
/*程序說(shuō)明:鼠標(biāo)大致標(biāo)記出圖像中期望進(jìn)行分割的區(qū)域鍵盤按鍵【1】啟動(dòng)分水嶺算法按鍵【2】恢復(fù)原始圖重新標(biāo)記
*/
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//定義輔助宏
#define WINDOW_NAME1 "【原始圖窗口】"
#define WINDOW_NAME2 "【恢復(fù)原始圖窗口】"
#define WINDOW_NAME3 "【分水嶺變換窗口】"
//全局變量
Mat g_srcImage, g_maskImage;
Point prevPt(-1, -1);
//全局函數(shù)
static void on_Mouse(int event, int x, int y, int flags, void*);
static void ShowHelpText();int main()
{//【0】顯示幫助信息ShowHelpText();//【1】載入原圖并顯示g_srcImage = imread("night.jpg");if (!g_srcImage.data){ printf("載入原圖像失敗~!\n");return false;}imshow(WINDOW_NAME1, g_srcImage);//【2】初始化掩模和灰度圖Mat srcImage, grayImage;g_srcImage.copyTo(srcImage);cvtColor(srcImage, g_maskImage, COLOR_BGR2GRAY);cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);g_maskImage = Scalar::all(0);//【3】設(shè)置鼠標(biāo)回調(diào)函數(shù)setMouseCallback(WINDOW_NAME1, on_Mouse, 0);//【4】輪詢按鍵,進(jìn)行處理while (1){//獲取鍵值int c = waitKey(0);//按鍵值為ESC時(shí),退出程序if ((char)c == 27)break;//按鍵為2時(shí),恢復(fù)原圖,使g_maskImage和g_srcImage可重新標(biāo)記if ((char)c == '2'){g_maskImage = Scalar::all(0);srcImage.copyTo(g_srcImage);imshow(WINDOW_NAME2, g_srcImage);}//按鍵值為1時(shí),進(jìn)行處理if ((char)c == '1'){//定義一些參數(shù)int i, j;int compCount = 0; //記錄輪廓數(shù)vector<vector<Point>>contours;vector<Vec4i> hierarchy;//尋找輪廓findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE);//輪廓為空時(shí)的處理if (contours.empty())continue;//復(fù)制掩膜Mat maskImage(g_maskImage.size(), CV_32S);maskImage = Scalar::all(0);//循環(huán)繪制輪廓for (int index = 0; index >= 0; index = hierarchy[index][0], compCount++){drawContours(maskImage, contours, index, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);}//為每一個(gè)輪廓生成一個(gè)隨機(jī)顏色vector<Vec3b>colorTab;for (int i = 0; i < compCount; i++){int b = theRNG().uniform(0, 255);int g = theRNG().uniform(0, 255);int r = theRNG().uniform(0, 255);colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));}//進(jìn)行分水嶺算法并計(jì)算處理時(shí)間輸出到窗口中double dTime = (double)getTickCount();watershed(srcImage, maskImage);dTime = (double)getTickCount() - dTime;printf("\t 處理時(shí)間 = %gms\n", dTime*1000. / getTickFrequency());//雙層循環(huán),將分水嶺圖像遍歷存入watershedImage中Mat watershedImage(maskImage.size(), CV_8UC3);for (i = 0; i < maskImage.rows; i++)for (j = 0; j < maskImage.cols; j++){int index = maskImage.at<int>(i, j);if (index == -1)watershedImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);//maskImage中區(qū)域間值像素為-1,將對(duì)應(yīng)watershed圖像像素設(shè)置為白色(255,255,255)else if (index <= 0 || index > compCount)watershedImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0); //maskImage中非標(biāo)記區(qū)域,將對(duì)應(yīng)watershed圖像像素設(shè)置為黑色(0,0,0)elsewatershedImage.at<Vec3b>(i, j) = colorTab[index - 1]; //maskImage中標(biāo)記區(qū)域,將對(duì)應(yīng)watershed圖像像素設(shè)置為之前隨機(jī)出的顏色colorTab}//混合灰度圖和分水嶺效果圖并顯示最終的窗口watershedImage = watershedImage * 0.5 + grayImage * 0.5;imshow(WINDOW_NAME3, watershedImage);}}return 0;
}//鼠標(biāo)消息回調(diào)函數(shù)
void on_Mouse(int event, int x, int y, int flags, void*)
{//處理鼠標(biāo)不在窗口中的情況if (x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows)return;//處理鼠標(biāo)左鍵相關(guān)消息if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON)) //松開左鍵prevPt = Point(-1, -1);else if (event == EVENT_LBUTTONDOWN)//按下左鍵prevPt = Point(x, y); //記錄鼠標(biāo)按下時(shí)的位置,作為白色線條的起始點(diǎn)//鼠標(biāo)左鍵呈按下?tīng)顟B(tài)并移動(dòng),繪制出白色線條else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)){Point pt(x, y);if (prevPt.x < 0) //如果白線起始點(diǎn)范圍溢出,則白線起始點(diǎn)為當(dāng)前點(diǎn)ptprevPt = pt;line(g_maskImage, prevPt, pt, Scalar::all(255), 2, 8, 0);//畫白線line(g_srcImage, prevPt, pt, Scalar::all(255), 2, 8, 0); //畫白線prevPt = pt;imshow(WINDOW_NAME1, g_srcImage);}
}
static void ShowHelpText()
{printf("\n\n\t歡迎來(lái)到【分水嶺算法】示例程序~\n");printf("\n\t請(qǐng)先用鼠標(biāo)在圖片窗口中標(biāo)記出大致的區(qū)域\n");printf("\n\t然后再按鍵【1】啟動(dòng)分水嶺算法\n");printf("\n\t按鍵操作說(shuō)明:\n");printf("\t\t\t鍵盤按鍵【1】--運(yùn)行分水嶺分割算法\n");printf("\t\t\t鍵盤按鍵【2】--恢復(fù)原始圖\n");printf("\t\t\t鍵盤按鍵【ESC】--退出程序\n");
}
運(yùn)行效果:
總結(jié)
以上是生活随笔為你收集整理的《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(五)分水岭算法(watershed algorithm)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 求越字开头的成语接龙!
- 下一篇: 《OpenCV3编程入门》学习笔记8 图