OpenCV實現圖像連通域

圖像的連通域是指圖像中具有相同像素值並且位置相鄰的像素組成的區域,連通域分析是指在圖像中尋找出彼此互相獨立的連通域並將其標記出來。

一般情況下,一個連通域內隻包含一個像素值,因此為瞭防止像素值波動對提取不同連通域的影響,連通域分析常處理的是二值化後的圖像。

4-鄰域和8-鄰域:

常用的圖像鄰域分析法有兩遍掃描法種子填充法。兩遍掃描法會遍歷兩次圖像,第一次遍歷圖像時會給每一個非0像素賦予一個數字標簽,當某個像素的上方和左側鄰域內的像素已經有數字標簽時,取兩者中的最小值作為當前像素的標簽,否則賦予當前像素一個新的數字標簽。第一次遍歷圖像的時候同一個連通域可能會被賦予一個或者多個不同的標簽。

種子填充法源於計算機圖像學,常用於對某些圖形進行填充。該方法首先將所有非0像素放到一個集合中,之後在集合中隨機選出一個像素作為種子像素,根據鄰域關系不斷擴充種子像素所在的連通域,並在集合中刪除掉擴充出的像素,直到種子像素所在的連通域無法擴充,之後再從集合中隨機選取一個像素作為新的種子像素,重復上述過程直到集合中沒有像素。

CV_EXPORTS_AS(connectedComponentsWithAlgorithm) int connectedComponents(InputArray image, OutputArray labels,
                                           int connectivity, int ltype, int ccltype);
  • image:待標記不同連通域的單通道圖像,數據類型必須為CV_8U。
  • labels:標記不同連通域後的輸出圖像,與輸入圖像具有相同的尺寸。
  • connectivity:標記連通域時使用的鄰域種類,4表示4-鄰域,8表示8-鄰域。
  • ltype:輸出圖像的數據類型,目前支持CV_32S和CV_16U兩種數據類型。
  • ccltype:標記連通域時使用的算法類型標志,可以選擇的參數及含義在表中給出

該函數用於計算二值圖像中連通域的個數,並在圖像中將不同的連通域用不同的數字標簽標記出,其中標簽0表示圖像中的背景區域,同時函數具有一個int類型的返回數據,用於表示圖像中連通域的數目。函數的第一個參數是待標記連通域的輸入圖像,函數要求輸入圖像必須是數據類型為CV_8U的單通道灰度圖像,而且最好是經過二值化的二值圖像。函數第二個參數是標記連通域後的輸出圖像,圖像尺寸與第一個參數的輸入圖像尺寸相同,圖像的數據類型與函數的第四個參數相關。函數第三個參數是統計連通域時選擇的鄰域種類,函數支持兩種鄰域,分別用4表示4-鄰域,8表示8-鄰域。函數第四個參數為輸出圖像的數據類型,可以選擇的參數為CV_32S和CV_16U兩種。函數的最後一個參數是標記連通域時使用算法的標志,可以選擇的參數及含義在表給出,目前隻支持Grana(BBDT)和Wu(SAUF)兩種算法。 

OpenCV 還給出瞭簡單的函數形式

int connectedComponents(InputArray image, OutputArray labels,
                                     int connectivity = 8, int ltype = CV_32S);
  • image:待標記不同連通域的圖像單通道,數據類型必須為CV_8U。
  • labels:標記不同連通域後的輸出圖像,與輸入圖像具有相同的尺寸。
  • connectivity:標記連通域時使用的鄰域種類,4表示4-鄰域,8表示8-鄰域,默認參數為8。
  • ltype:輸出圖像的數據類型,目前支持CV_32S和CV_16U兩種數據類型,默認參數為CV_32S。

該函數原型隻有四個參數,前兩個參數分別表示輸入圖像和輸出圖像,第三個參數表示統計連通域時選擇的鄰域種類,分別用4表示4-鄰域,8表示8-鄰域,參數的默認值為8。最後一個參數表示輸出圖像的數據類型,可以選擇的參數為CV_32S和CV_16U兩種,參數的默認值為CV_32S。該函數原型有兩個參數具有默認值,在使用時最少隻需要兩個參數,極大的方便瞭函數的調用。

進一步統計每個連通域的中心位置、矩形區域大小、區域面積等信息

復雜的

CV_EXPORTS_AS(connectedComponentsWithStatsWithAlgorithm) int connectedComponentsWithStats(InputArray image, OutputArray labels,
                              OutputArray stats, OutputArray centroids,
                              int connectivity, int ltype, int ccltype);
  • image:待標記不同連通域的單通道圖像,數據類型必須為CV_8U。
  • labels:標記不同連通域後的輸出圖像,與輸入圖像具有相同的尺寸。
  • stats:含有不同連通域統計信息的矩陣,矩陣的數據類型為CV_32S。矩陣中第i行是標簽為i的連通域的統計特性,存儲的統計信息種類在表6-4中給出。
  • centroids:每個連通域的質心坐標,數據類型為CV_64F。
  • connectivity:標記連通域時使用的鄰域種類,4表示4-鄰域,8表示8-鄰域。
  • ltype:輸出圖像的數據類型,目前支持CV_32S和CV_16U兩種數據類型。
  • ccltype:標記連通域使用的算法類型標志,可以選擇的參數及含義在表中給出。

該函數能夠在圖像中不同連通域標記標簽的同時統計每個連通域的中心位置、矩形區域大小、區域面積等信息。函數的前兩個參數含義與connectedComponents()函數的前兩個參數含義一致,都是輸入圖像和輸出圖像。函數的第三個參數為每個連通域統計信息矩陣,如果圖像中有N個連通域,那麼該參數輸出的矩陣尺寸為N×5,矩陣中每一行分別保存每個連通域的統計特性,詳細的統計特性在表中給出,如果想讀取包含第i個連通域的邊界框的水平長度,可以通過stats.at(i, CC_STAT_WIDTH)或者stats.at(i, 0)進行讀取。函數的第四個參數為每個連通域質心的坐標,如果圖像中有N個連通域,那麼該參數輸出的矩陣尺寸為N×2,矩陣中每一行分別保存每個連通域質心的x坐標和y坐標,可以通過centroids.at(i, 0)和 centroids.at(i, 1) 分別讀取第i個連通域質心的x坐標和y坐標。函數第五個參數是統計連通域時選擇的鄰域種類,函數支持兩種鄰域,分別用4表示4-鄰域,8表示8-鄰域。函數第六個參數為輸出圖像的數據類型,可以選擇的參數為CV_32S和CV_16U兩種。函數的最後一個參數是標記連通域使用的算法,可以選擇的參數在上表給出,目前隻支持Grana(BBDT)和Wu(SAUF)兩種算法。 

簡單的

int connectedComponentsWithStats(InputArray image, OutputArray labels,
                                              OutputArray stats, OutputArray centroids,
                                              int connectivity = 8, int ltype = CV_32S);

  • image:待標記不同連通域的單通道圖像,數據類型必須為CV_8U。
  • labels:標記不同連通域後的輸出圖像,與輸入圖像具有相同的尺寸。
  • stats:不同連通域的統計信息矩陣,矩陣的數據類型為CV_32S。矩陣中第i行是標簽為i的連通域的統計特性,存儲的統計信息種類在表6-4中給出。
  • centroids:每個連通域的質心坐標,數據類型為CV_64F。
  • connectivity:標記連通域時使用的鄰域種類,4表示4-鄰域,8表示8-鄰域,默認參數值為8。
  • ltype:輸出圖像的數據類型,目前隻支持CV_32S和CV_16U這兩種數據類型,默認參數值為CV_32S。

該函數原型隻有六個參數,前兩個參數分別表示輸入圖像和輸出圖像,第三個參數表示每個連通域的統計信息,第四個參數表示每個連通域的質心位置。後兩個參數分別表示統計連通域時選擇的鄰域種類,分別用4表示4-鄰域,8表示8-鄰域,參數的默認值為8。最後一個參數表示輸出圖像的數據類型,可以選擇的參數為CV_32S和CV_16U兩種,參數的默認值為CV_32S。該函數原型有兩個參數具有默認值,在使用時最少隻需要四個參數,極大的方便瞭函數的調用。

簡單示例

//
// Created by smallflyfly on 2021/6/16.
//
 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
 
#include <iostream>
 
using namespace std;
using namespace cv;
 
int main() {
 
    Mat im = imread("rice.jfif");
    Mat gray;
    cvtColor(im, gray, CV_BGR2GRAY);
//    resize(im, im, Size(0, 0), 0.5, 0.5);
 
    imshow("im", im);
 
    Mat im1;
    threshold(gray, im1, 125, 255, THRESH_BINARY);
 
    imshow("im1", im1);
//    waitKey(0);
 
    RNG rng(10010);
    Mat out;
    int num = connectedComponents(im1, out, 8, CV_16U);
    vector<Vec3b> colors;
    for (int i=0; i<num; i++) {
        // 使用均勻分佈的隨機確定顏色
        Vec3b vec = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
        colors.push_back(vec);
    }
    Mat result = Mat::zeros(im.size(), im.type());
    for (int i = 0; i < im.rows; ++i) {
        for (int j = 0; j < im.cols; ++j) {
            int label = out.at<uint16_t>(i, j);
            if (label == 0) {
                continue;
            }
            result.at<Vec3b>(i, j) = colors[label];
        }
    }
 
    imshow("result", result);
 
    Mat labels, stats, centroids;
    int count = connectedComponentsWithStats(im1, labels, stats, centroids, 8);
    cout << count << endl;
 
    for (int i = 1; i < count; ++i) {
        int x = centroids.at<double>(i, 0);
        int y = centroids.at<double>(i, 1);
        cout << x << " " << y << endl;
        circle(im, Point(x, y), 2, Scalar(0, 0, 255), -1);
        int xmin = stats.at<int>(i, CC_STAT_LEFT);
        int ymin = stats.at<int>(i, CC_STAT_TOP);
        int w = stats.at<int>(i, CC_STAT_WIDTH);
        int h = stats.at<int>(i, CC_STAT_HEIGHT);
 
        Rect rect(xmin, ymin, w, h);
        rectangle(im, rect, Scalar(0, 255, 255), 2);
        putText(im, to_string(i), Point(x+5, y), FONT_HERSHEY_SCRIPT_SIMPLEX, 1, Scalar(0, 0, 255), 2);
    }
 
    imshow("im", im);
 
    waitKey(0);
    destroyAllWindows();
 
    return 0;
 
}

簡單雙色圖效果比較明顯  百度圖片搜的 

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: