引言
上篇博文寫了關於基於圖像分割的產品計數問題(主要還是求解邊緣問題)。opencv——機器視覺檢測和計數 - 唯有自己強大 - 博客園 (cnblogs.com)
本篇博文就來說一說對於沒有粘連的區域分析。用opencv實現halcon中的connection算子(即斷開不同的連通域)並獲取區域相關信息。
一,連通組件標記算法介紹
連接組件標記算法(connected component labeling algorithm)是圖像分析中最常用的算法之一,算法的實質是掃描一幅圖像的每個像素,對於像素值相同的分為相同的組(group),最終得到圖像中所有的像素連通組件。掃描的方式可以是從上到下,從左到右,對於一幅有N個像素的圖像來說,最大連通組件個數為N/2。掃描是基於每個像素單位,對於二值圖像而言,連通組件集合可以是V={1|白色}或者V={0|黑色}, 取決於前景色與背景色的不同。對於灰度圖像來說,連圖組件像素集合可能是一系列在0 ~ 255之間k的灰度值。
應用:
連通域分析一般對區域分割后的處理。在需要將前景目標提取出來以便后續進行處理的應用場景中都能夠用到連通區域分析方法,通常連通區域分析處理的對象是一張二值化后的圖像。
二,OpenCV中連通組件標記API
OpenCV中支持連通組件掃描的API有兩個,一個是帶統計信息一個不帶統計信息。
不帶統計信息的API及其解釋如下:
int connectedComponents( InputArray image, // 輸入二值圖像,黑色背景 OutputArray labels, // 輸出的標記圖像,背景index=0 int connectivity = 8, // 連通域,默認是8連通 int ltype = CV_32S // 輸出的labels類型,默認是CV_32S )
該函數對圖像的每個區域分析,然后將背景標記為0,其他的區域用從1開始的正整數依次標記。最后將標記結果返回給labels。
帶統計信息的API及其解釋如下:
int connectedComponentsWithStats( InputArray image, // 輸入二值圖像,黑色背景 OutputArray labels, // 輸出的標記圖像,背景index=0 OutputArray stats, // 統計信息,包括每個組件的位置、寬、高與面積 OutputArray centroids, // 每個組件的中心位置坐標cx, cy int connectivity, // 尋找連通組件算法的連通域,默認是8連通 int ltype, // 輸出的labels的Mat類型CV_32S int ccltype // 連通組件算法 )
其中stats包括以下枚舉類型數據信息:
區域的外接矩形左上角點像素點坐標的X位置:CC_STAT_LEFT
區域的外接矩形左上角點像素點坐標的Y位置:CC_STAT_TOP
區域外接矩形的寬度:CC_STAT_WIDTH
區域外接矩形的高度:CC_STAT_HEIGHT
區域的面積(像素單位)CC_STAT_AREA
該函數對圖像的每個區域分析,除了對區域和背景添加標記外(結果返回給labels),還可以獲取每個區域的統計信息:區域的數目,外接矩形大小,面積,中心位置。(結果返回給stats)
三,OpenCV實現
- 🙂不帶統計信息的API實現
int main(int argc, char** argv) { RNG rng(12345); Mat src, src_binary, dst; src = imread("D:/opencv練習圖片/維生素片機器視覺檢測和計數.png"); imshow("原圖片", src); Mat kernel = getStructuringElement(MORPH_RECT, Size(18, 18), Point(-1, -1)); morphologyEx(src, dst, MORPH_OPEN, kernel); imwrite("D:/111.png", dst); imshow("形態學", dst); cvtColor(dst, dst, COLOR_RGB2GRAY); threshold(dst, src_binary, 100, 255, THRESH_OTSU); imshow("二值化", src_binary); Mat labels = Mat::zeros(src.size(), CV_32S); //連通域分析 int num_labels = connectedComponents(src_binary, labels, 8, CV_32S); vector<Vec3b> colors(num_labels); //背景顏色(黑色) colors[0] = Vec3b(0, 0, 0); // 區域顏色(隨機) for (int i = 1; i < num_labels; i++) { colors[i] = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256)); } //顯示 Mat dst1 = Mat::zeros(src.size(), src.type()); int w = src.cols; int h = src.rows; for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { int label = labels.at<int>(row, col); if (label == 0) continue; dst1.at<Vec3b>(row, col) = colors[label]; } } imshow("不帶統計的連通域", dst1); waitKey(0); return 0; }


代碼分析:
原圖通過形態學已經將每個葯片完全分割,再通過connectedComponents使其斷開不同的連通域。
在返回的labels內,將標記為0的像素點置黑(即背景為黑),將標記大於1的像素點隨機上色(即斷開的連通域隨機上色)。
- 😃帶統計信息的API實現
int main(int argc, char** argv) { RNG rng(12345); Mat src, src_binary, dst; src = imread("D:/opencv練習圖片/維生素片機器視覺檢測和計數.png"); imshow("原圖片", src); Mat kernel = getStructuringElement(MORPH_RECT, Size(18, 18), Point(-1, -1)); morphologyEx(src, dst, MORPH_OPEN, kernel); imwrite("D:/111.png", dst); imshow("形態學", dst); cvtColor(dst, dst, COLOR_RGB2GRAY); threshold(dst, src_binary, 100, 255, THRESH_OTSU); imshow("二值化", src_binary); Mat labels = Mat::zeros(src.size(), CV_32S); //連通域分析 Mat stats, centroids;//統計信息存放 int num_labels = connectedComponentsWithStats(src_binary, labels, stats, centroids, 8, 4); vector<Vec3b> colors(num_labels); //背景顏色(黑色) colors[0] = Vec3b(0, 0, 0); // 區域顏色(隨機) for (int i = 1; i < num_labels; i++) { colors[i] = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256)); } //顯示 Mat dst1 = Mat::zeros(src.size(), src.type()); int w = src.cols; int h = src.rows; for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { int label = labels.at<int>(row, col); if (label == 0) continue; dst1.at<Vec3b>(row, col) = colors[label]; } } for (int i = 1; i < num_labels; i++) { Vec2d pt = centroids.at<Vec2d>(i, 0); int x = stats.at<int>(i, CC_STAT_LEFT); int y = stats.at<int>(i, CC_STAT_TOP); int width = stats.at<int>(i, CC_STAT_WIDTH); int height = stats.at<int>(i, CC_STAT_HEIGHT); int area = stats.at<int>(i, CC_STAT_AREA); printf("area : %d, center point(%.2f, %.2f)\n", area, pt[0], pt[1]);//面積信息 circle(dst1, Point(pt[0], pt[1]), 2, Scalar(0, 0, 255), -1, 8, 0);//中心點坐標 rectangle(dst1, Rect(x, y, width, height), Scalar(255, 0, 255), 1, 8, 0);//外接矩形 } imshow("帶統計的連通域", dst1); waitKey(0); return 0; }



參考博客:(8條消息) OpenCV實現圖像連通組件標記與分析_關注微信公眾號【OpenCV學堂】-CSDN博客_opencv 連通域標記
