您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“C++中如何實(shí)現(xiàn)OpenCV圖像分割與分水嶺算法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“C++中如何實(shí)現(xiàn)OpenCV圖像分割與分水嶺算法”吧!
分水嶺算法是一種圖像區(qū)域分割法,在分割的過程中,它會把跟臨近像素間的相似性作為重要的參考依據(jù),從而將在空間位置上相近并且灰度值相近的像素點(diǎn)互相連接起來構(gòu)成一個封閉的輪廓,封閉性是分水嶺算法的一個重要特征。
API介紹
void watershed( InputArray image, InputOutputArray markers );
參數(shù)說明:
image: 必須是一個8bit 3通道彩色圖像矩陣序列
markers: 在執(zhí)行分水嶺函數(shù)watershed之前,必須對第二個參數(shù)markers進(jìn)行處理,它應(yīng)該包含不同區(qū)域的輪廓,每個輪廓有一個自己唯一的編號,輪廓的定位可以通過Opencv中findContours方法實(shí)現(xiàn),這個是執(zhí)行分水嶺之前的要求。算法會根據(jù)markers傳入的輪廓作為種子(也就是所謂的注水點(diǎn)),對圖像上其他的像素點(diǎn)根據(jù)分水嶺算法規(guī)則進(jìn)行判斷,并對每個像素點(diǎn)的區(qū)域歸屬進(jìn)行劃定,直到處理完圖像上所有像素點(diǎn)。而區(qū)域與區(qū)域之間的分界處的值被置為“-1”,以做區(qū)分。
我們將一個如何使用距離變換和分水嶺分割相互接觸的物體的例子。
考慮一下下面的硬幣圖像,這些硬幣相互接觸。即使你去閾值化它,它也會互相碰觸。
我們從找到硬幣的大概估計(jì)值開始。為此,我們可以利用大津的二值化。
#include<iostream> #include<opencv2\opencv.hpp> using namespace std; using namespace cv; int main() { Mat gray, thresh; Mat img = imread("coins.jpg"); cvtColor(img, gray, COLOR_BGR2GRAY); threshold(gray, thresh, 0, 255, THRESH_BINARY_INV+CV_THRESH_OTSU); imshow("Otst閾值圖像", thresh); waitKey(0); return 0; }
閾值后的圖像如下所示:
現(xiàn)在需要去除圖像中任何微小的白色噪聲。為此,我們可以使用形態(tài)開操作。為了去除物體上的任何小洞,我們可以使用形態(tài)閉操作。所以,現(xiàn)在我們可以確定的是,靠近物體中心的區(qū)域是前景,遠(yuǎn)離物體的區(qū)域是背景。只有我們不確定的區(qū)域是硬幣的邊界區(qū)域。
所以我們需要提取我們確定是硬幣的區(qū)域。侵蝕去除邊界像素。所以不管剩下多少,我們都能確定是硬幣。如果物體不互相接觸,那就可以了。但是由于它們彼此接觸,另一個好的選擇是找到距離變換并應(yīng)用適當(dāng)?shù)拈撝?。接下來我們需要找到我們確信不是硬幣的區(qū)域。為此,我們擴(kuò)展了結(jié)果。膨脹將物體邊界增加到背景。通過這種方式,我們可以確保結(jié)果中的任何背景區(qū)域都是真正的背景,因?yàn)檫吔鐓^(qū)域。
剩下的區(qū)域是我們不知道的,無論是硬幣還是背景。分水嶺算法應(yīng)該能找到它。這些區(qū)域通常圍繞著硬幣的邊界,也就是前景和背景相遇的地方(甚至是兩個不同的硬幣相遇的地方)。我們稱之為邊界。用sure_fg 面積減去sure_bg面積可得。
Mat opening; Mat sure_bg; Mat sure_fg; Mat unknow; Mat dist_transform; double maxValue; // noise removal Mat kernel = Mat::ones(3, 3, CV_8U); morphologyEx(thresh, opening, MORPH_OPEN, kernel); // sure background area dilate(opening, sure_bg, kernel, Point(-1, -1), 3); // Finding sure foreground area distanceTransform(opening, dist_transform, DIST_L2, 5); minMaxLoc(dist_transform, 0, &maxValue, 0, 0); threshold(dist_transform, sure_fg, 0.7*maxValue, 255, 0); // Finding unknown region sure_fg.convertTo(sure_fg, CV_8U); subtract(sure_bg, sure_fg, unknow);
看到結(jié)果。在閾值圖像中,我們得到了一些區(qū)域的硬幣,我們確定這些硬幣是獨(dú)立的。(在某些情況下,你可能只對前景分割感興趣,而對相互接觸的對象的分割不感興趣。在這種情況下,你不需要使用距離變換,只要侵蝕就足夠了。侵蝕只是提取前景區(qū)域的另一種方法,僅此而已。)
現(xiàn)在我們可以確定哪些是硬幣區(qū)域,哪些是背景等等。因此我們創(chuàng)建了marker(它是一個與原始圖像大小相同的數(shù)組,但是使用int32數(shù)據(jù)類型),并在其中標(biāo)記區(qū)域。我們確定的區(qū)域(無論是前景還是背景)被標(biāo)記為任何正整數(shù),但是不同的整數(shù),而我們不確定的區(qū)域則被保留為0。為此,我們使用了connectedComponents()。它用0標(biāo)記圖像的背景,然后用從1開始的整數(shù)標(biāo)記其他對象。
但是我們知道,如果將background標(biāo)記為0,watershed將認(rèn)為它是未知區(qū)域。所以我們要用不同的整數(shù)來標(biāo)記它。相反,我們將標(biāo)記未知區(qū)域,由unknown定義,為0。
// Marker labelling Mat markers; connectedComponents(sure_fg, markers); // Add one to all labels so that sure background is not 0, but 1 markers = markers + 1; // Now, mark the region of unknown with zero markers.setTo(0, unknow);
現(xiàn)在我們的標(biāo)記圖像準(zhǔn)備好了。到了最后一步,應(yīng)用分水嶺。然后修改標(biāo)記圖像。邊界區(qū)域?qū)?biāo)記為-1。
Mat marker; Mat mask; watershed(img, markers); compare(markers, -1, mask, CMP_EQ); img.setTo(Scalar(0, 0, 255), mask);
參見下面的結(jié)果。對于一些硬幣,它們接觸的區(qū)域被正確分割,而對于另一些硬幣,它們沒有被分割。
到此,相信大家對“C++中如何實(shí)現(xiàn)OpenCV圖像分割與分水嶺算法”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。