日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV学习(二十) :分水岭算法:watershed()

發布時間:2024/7/23 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV学习(二十) :分水岭算法:watershed() 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OpenCV學習(二十) :分水嶺算法:watershed()

參考博客:
OpenCV—分水嶺算法
圖像處理——分水嶺算法
OpenCV學習(7) 分水嶺算法(1)
Opencv分水嶺算法——watershed自動圖像分割用法 -牧野-

分水嶺算法是一種圖像區域分割法,在分割的過程中,它會把跟臨近像素間的相似性作為重要的參考依據,從而將在空間位置上相近并且灰度值相近(求梯度)的像素點互相連接起來構成一個封閉的輪廓。分水嶺算法常用的操作步驟:彩色圖像灰度化,然后再求梯度圖,最后在梯度圖的基礎上進行分水嶺算法,求得分段圖像的邊緣線。

1、watershed()函數

void watershed( InputArray image, // 必須是一個8bit 3通道彩色圖像矩陣序列 InputOutputArray markers // markers必須包含了種子點信息:算法會根據markers傳入的輪廓作為種子(也就是所謂的注水點), //對圖像上其他的像素點根據分水嶺算法規則進行判斷,并對每個像素點的區域歸屬進行劃定, //直到處理完圖像上所有像素點。而區域與區域之間的分界處的值被置為“-1”,以做區分。==markers== 在將圖像傳遞給函數之前,您必須粗略地勾勒出索引為正(>0)的圖像標記中的所需區域。 因此,每個區域都表示為一個或多個具有像素值1,2,3的連接組件,以此類推。 可以使用findcontours()drawcontours()從二進制掩碼中檢索這些標記。這些標記是未來圖像區域的“種子”。 標記中的所有其他像素,如果它們與輪廓區域的關系未知,應該由算法定義,則應該設置為0。 在函數輸出中,標記中的每個像素被設置為“seed”組件的值,或者在區域之間的邊界處設置為-1);

2、示例一:手動添加 markers

watershed圖像自動分割的實現步驟:
1)圖像灰度化、濾波、Canny邊緣檢測
2)查找輪廓,并且把輪廓信息按照不同的編號繪制到watershed的第二個入參merkers上,相當于標記注水點。
3)watershed分水嶺運算
4)繪制分割出來的區域,視覺控還可以使用隨機顏色填充,或者跟原始圖像融合以下,以得到更好的顯示效果。

#include <opencv2/opencv.hpp>using namespace cv; using namespace std;#define WINDOW_NAME1 "【程序窗口1】" //為窗口標題定義的宏 #define WINDOW_NAME2 "【分水嶺算法效果圖】" //為窗口標題定義的宏Mat g_maskImage, g_srcImage; Point prevPt(-1, -1);int main() {//輸出一些幫助信息printf( "\n\n\n\t歡迎來到【分水嶺算法】示例程序~\n\n");printf( "\t請先用鼠標在圖片窗口中標記出大致的區域,\n\n\t然后再按鍵【1】或者【SPACE】啟動算法。""\n\n\t按鍵操作說明: \n\n""\t\t鍵盤按鍵【1】或者【SPACE】- 運行的分水嶺分割算法\n""\t\t鍵盤按鍵【2】- 恢復原始圖片\n""\t\t鍵盤按鍵【ESC】- 退出程序\n\n\n");//【1】載入原圖并顯示,初始化掩膜和灰度圖g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.jpg", 1);imshow( WINDOW_NAME1, g_srcImage );Mat srcImage,grayImage;g_srcImage.copyTo(srcImage);cvtColor(g_srcImage, g_maskImage, COLOR_BGR2GRAY); // 彩色圖轉灰度圖cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR); // 轉回三通道圖g_maskImage = Scalar::all(0);//【2】設置鼠標回調函數setMouseCallback( WINDOW_NAME1, on_Mouse, 0 );//【3】輪詢按鍵,進行處理while(1){//獲取鍵值int c = waitKey(0);//若按鍵鍵值為ESC時,退出if( (char)c == 27 )break;//按鍵鍵值為2時,恢復源圖if( (char)c == '2' ){g_maskImage = Scalar::all(0);srcImage.copyTo(g_srcImage);imshow( "image", g_srcImage );}//若檢測到按鍵值為1或者空格,則進行處理if( (char)c == '1' || (char)c == ' ' ){//定義一些參數int i, j, compCount = 0;vector<vector<Point> > contours;vector<Vec4i> hierarchy;//尋找輪廓findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//輪廓為空時的處理if( contours.empty() )continue;//拷貝掩膜Mat maskImage(g_maskImage.size(), CV_32S); //maskImage = Scalar::all(0);//循環繪制出輪廓for( int index = 0; index >= 0; index = hierarchy[index][0], compCount++ )drawContours(maskImage, contours, index, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);//compCount為零時的處理if( compCount == 0 )continue;//生成隨機顏色vector<Vec3b> colorTab;for( 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));}// 計算處理時間并輸出到窗口中double dTime = (double)getTickCount();// 分水嶺算法watershed( srcImage, maskImage ); dTime = (double)getTickCount() - dTime;printf( "\t處理時間 = %gms\n", dTime*1000./getTickFrequency() );//雙層循環,將分水嶺圖像遍歷存入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);else if( index <= 0 || index > compCount )watershedImage.at<Vec3b>(i,j) = Vec3b(0,0,0);elsewatershedImage.at<Vec3b>(i,j) = colorTab[index - 1];}//混合灰度圖和分水嶺效果圖并顯示最終的窗口watershedImage = watershedImage*0.5 + grayImage*0.5;imshow( WINDOW_NAME2, watershedImage );}}waitKey(0);return 0; }

鼠標回調函數:

static void on_Mouse( int event, int x, int y, int flags, void* ) {// 1)處理鼠標不在窗口中的情況if( x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows )return;// 2)處理鼠標左鍵相關消息if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) )prevPt = Point(-1,-1);else if( event == CV_EVENT_LBUTTONDOWN )prevPt = Point(x,y);// 3)鼠標左鍵按下并移動,繪制出白色線條else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON) ){Point pt(x, y); // 當前坐標值if( prevPt.x < 0 )prevPt = pt;line( g_maskImage, prevPt, pt, Scalar::all(200), 5, 8, 0 ); // 掩模圖line( g_srcImage, prevPt, pt, Scalar::all(200), 5, 8, 0 ); //prevPt = pt;imshow(WINDOW_NAME1, g_srcImage);} }

結果:

3、示例二:自動添加 markers

int main() {//1、載入原圖并顯示,初始化掩膜和灰度圖Mat image = imread("F:/C++/2. OPENCV 3.1.0/TEST/1.jpg", 1);imshow("Source Image",image);// 2、灰度化,濾波,Canny邊緣檢測Mat imageGray;cvtColor(image,imageGray,CV_RGB2GRAY);//灰度轉換GaussianBlur(imageGray,imageGray,Size(5,5),2); //高斯濾波imshow("Gray Image",imageGray);Canny(imageGray,imageGray,80,150);imshow("Canny Image",imageGray);// 3、查找輪廓vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(imageGray,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());Mat imageContours=Mat::zeros(image.size(),CV_8UC1); //輪廓Mat marks(image.size(),CV_32S); //Opencv分水嶺第二個矩陣參數marks=Scalar::all(0);int index = 0;int compCount = 0;for( ; index >= 0; index = hierarchy[index][0], compCount++ ){// 4、對marks進行標記,對不同區域的輪廓進行編號,相當于設置注水點,有多少輪廓,就有多少注水點drawContours(marks, contours, index, Scalar::all(compCount+1), 1, 8, hierarchy); // 每個輪廓的標記點不一樣drawContours(imageContours,contours,index,Scalar(255),1,8,hierarchy);}//我們來看一下傳入的矩陣marks里是什么東西Mat marksShow;convertScaleAbs(marks,marksShow); // 轉換前的 marker 圖imshow("marksBefore",marksShow);imshow("imageContours 輪廓",imageContours);// 5、分水嶺算法watershed(image,marks);// 6、我們再來看一下分水嶺算法之后的矩陣marks里是什么東西Mat afterWatershed;convertScaleAbs(marks,afterWatershed); // 轉換后的 marker 圖imshow("After Watershed",afterWatershed);// 7、對每一個區域進行顏色填充Mat PerspectiveImage=Mat::zeros(image.size(),CV_8UC3);for(int i=0;i<marks.rows;i++){for(int j=0;j<marks.cols;j++){int index=marks.at<int>(i,j);if(marks.at<int>(i,j)==-1){PerspectiveImage.at<Vec3b>(i,j)=Vec3b(255,255,255);}else{PerspectiveImage.at<Vec3b>(i,j) =RandomColor(index);}}}imshow("After ColorFill",PerspectiveImage);//分割并填充顏色的結果跟原始圖像融合Mat wshed;addWeighted(image,0.4,PerspectiveImage,0.6,0,wshed);imshow("AddWeighted Image",wshed);waitKey(0);return 0; } Vec3b RandomColor(int value) //生成隨機顏色函數 {value=value%255; //生成0~255的隨機數RNG rng;int aa=rng.uniform(0,value);int bb=rng.uniform(0,value);int cc=rng.uniform(0,value);return Vec3b(aa,bb,cc); }




3、基于距離的分水嶺算法

使用分水嶺算法進行圖像分割:

(一)獲取灰度圖像,二值化圖像,進行形態學操作,消除噪點
??(二)在距離變換前加上一步操作:通過對上面形態學去噪點后的圖像,進行膨脹操作,可以得到大部分都是背景的區域(原黑色不是我們需要的部分是背景)
??(三)使用距離變換distanceTransform獲取確定的前景色
相關知識補充(重點)
??(四)在獲取了背景區域和前景區域(其實前景區域是我們的種子,我們將從這里進行灌水,向四周漲水,但是這個需要在markers中表示)后,這兩個區域中有未重合部分(注1)怎么辦?首先確定這些區域(尋找種子)
?? 開始獲取未知區域unknown(柵欄會創建在這一區域),為下一步獲取種子做準備
?? (五)獲取了這些區域,我們可以獲取種子,這是通過connectedComponents實現,獲取masker標簽,確定的前景區域會在其中顯示為以1開始的數據,這就是我們的種子,會從這里開始漫水
??重點:
??(六)根據未知區域unknown在markers中設置柵欄,并將背景區域加入種子區域,一起漫水
??(七)根據種子開始漫水,讓水漫起來找到最后的漫出點(柵欄邊界),越過這個點后各個山谷中水開始合并。注意watershed會將找到的柵欄在markers中設置為-1

總結

以上是生活随笔為你收集整理的OpenCV学习(二十) :分水岭算法:watershed()的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。