Opencv學習——影象分割之分水嶺演算法
阿新 • • 發佈:2019-01-06
分水嶺演算法是比較經典的影象分割演算法。最近看到一副區域檢測和統計的影象,感覺可以通過分水嶺演算法進行實現,於是順便對opencv的分水嶺演算法進行學習。如圖需要分割的影象:
opencv有自帶的分水嶺分割示例,分割影象為硬幣影象,如圖:
由於示例是python的程式碼,沒有C++的程式碼,所以打算先用C++實現示例中的功能,然後再對本文開頭的影象進行分割。
基本步驟
- 標記背景區域;
- 標記前景和未知區域;
- 合併標記影象;
- 分水嶺演算法進行分割。
標記背景影象
基本操作:灰度化->閾值分割(OTSU)->形態學去噪->形態學膨脹得到背景圖。
Mat src = imread("water_coins.jpg");
Mat frame;
cvtColor(src, frame, CV_BGR2GRAY); //灰度化
Mat marker = frame.clone();
threshold(marker, frame, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU); //閾值分割(OTSU)
//去除噪聲,開運算
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
morphologyEx(frame, frame, MORPH_OPEN, element, Point(-1 , -1), 2);
//膨脹得到背景區域
Mat imgbg;
dilate(frame, imgbg, element, Point(-1, -1), 3);
標記前景影象
標記前景採用了距離變換函式:
C++: void distanceTransform(InputArray src, OutputArray dst, int distanceType, int maskSize, int dstType=CV_32F )
src – 8位單通道二值影象.
dst – 輸出計算好的距離影象,8位或32位浮點型單通道影象 .
distanceType – 距離計算型別,包括曼哈頓距離,歐式距離等 .
maskSize – 距離變換掩膜尺寸 .
dstType – 輸出影象型別. CV_8U or CV_32F. CV_8U只能用於CV_DIST_L1.
具體步驟:
//找前景
Mat imageThin(frame.size(), CV_32FC1); //定義儲存距離變換結果的Mat矩陣
distanceTransform(frame, imageThin, CV_DIST_L2, 5); //距離變換
normalize(imageThin, imageThin, 0, 255, CV_MINMAX); //歸一化利於顯示
threshold(imageThin, imageThin, 200, 255, CV_THRESH_BINARY);
距離變換後效果圖:
前景圖:
標記未知區域
//找未知區域
imageThin.convertTo(imageThin, imgbg.type());
Mat unknown;
subtract(imgbg, imageThin, unknown); //影象相減
合併標記影象
Mat imglabels, imgstats, imgcentroid;
connectedComponentsWithStats(imageThin, imglabels, imgstats, imgcentroid); //連通域標記
imglabels = imglabels + 100; //背景區域畫素為100
imglabels.convertTo(imglabels, CV_8U);
for (int i=0;i<unknown.rows;i++)
{
uchar* ptr = unknown.ptr<uchar>(i);
for (int j=0;j<unknown.cols;j++)
{
if (255==ptr[j])
{
imglabels.at<uchar>(i, j) = 0; //未知區域畫素為0
}
}
}
imglabels.convertTo(imglabels, CV_32S); //影象型別轉換
標記影象:
分水嶺分割
watershed(src, imglabels);
分割後圖像:
可以看到最後分割的效果,在某些地方由於硬幣靠的太近分割效果不理想。後續可以通過圓檢測等方式進行處理。
回到最初的氣泡圖,同樣可以通過上述步驟進行分割,主要是得到標記影象,氣泡圖由於無法確定背景區域,所以在選定前景區域後,其他區域都設定為未知區域。最後分割的效果如圖: