Connected Domain Analysis Algorithm

Connected domain analysis marking algorithm based on opencv's cv::connectedComponentsWithStats():

1. Function introduction:

In OpenCV3, there are new special functions cv::connectedComponents() and function cv::connectedComponentsWithStats(); to do connected domain analysis. If you need to obtain specific status information of connected domains, use the second one.

Function prototype:

int  cv::connectedComponents (
    cv::InputArray image,                // input 8-bit single-channel (binary)
    cv::OutputArray labels,               // output label map
    int             connectivity = 8,     // 4- or 8-connected components
    int             ltype        = CV_32S // Output label type (CV_32S or CV_16U)
    );
int  cv::connectedComponentsWithStats (
    cv::InputArray image,                // input 8-bit single-channel (binary)
    cv::OutputArray labels,               // output label map
    cv::OutputArray stats,                // Nx5 matrix (CV_32S) of statistics:
                                                          // [x0, y0, width0, height0, area0;
                                                          //  ... ; x(N-1), y(N-1), width(N-1),
                                                           // height(N-1), area(N-1)]
    cv::OutputArray centroids,            // Nx2 CV_64F matrix of centroids:
                                                           // [ cx0, cy0; ... ; cx(N-1), cy(N-1)]
    int             connectivity = 8,     // 4- or 8-connected components
    int             ltype        = CV_32S // Output label type (CV_32S or CV_16U)
);

Parameter analysis:
(1) image generally needs to be a binary grayscale image (can be obtained by threshold segmentation)
(2) labels is an image with grayscale values ​​from 0 to total-1, for example, the grayscale value of the background is 0, the gray value of the first block area is 1, and the gray value of the total-1 block is total-1. The specific length is as follows:
insert image description here

(3) stats is a two-dimensional matrix [total, 5] that stores the state information of the connected domain. The first row stores the background, and each subsequent row represents the information of a connected domain: [x, y, width, height ,area], x, y are the position of the connected domain, width and height are the width and height of the circumscribed rectangle of the connected domain, area is the specific number of pixels of the connected domain, that is, the area of ​​the connected domain. Specifically, it looks like this:
insert image description here
(4) centroids also returns a two-dimensional array [total, 2], which stores the position of the particle of the connected domain [cx0, cy0; ... ; cx(N-1), cy(N-1)] ;
(5) Connectivity defaults to 8 areas, and can be selected as 4 neighborhoods;

2. Practical effect:

After the original image and threshold segmentation:
insert image description here insert image description here
After connected domain analysis:
insert image description here

3. C++ code:

//输入一张二值灰度图像ostuImage,和连通域最小面积area_min ,当背景为白色时,需要key=1,“黑白颠倒”才能进行连通域分析
void connectedAnalysis(Mat ostuImage,int area_min ,bool key){
    
    
	if (key) {
    
    
		bitwise_not(ostuImage, ostuImage);//黑白颠倒
	}
	//imshow("ostuImage_not", ostuImage);

	Mat labels, stats, centroids, img_color;

	int nccomps = connectedComponentsWithStats(ostuImage, labels, stats, centroids);
	cout << "连通区域总数: " << nccomps << endl;

	vector<cv::Vec3b> colors(nccomps + 1);

	colors[0] = Vec3b(0, 0, 0); // 背景为黑色

	for (int i = 1; i < nccomps; i++) {
    
    

		colors[i] = Vec3b(rand() % 256, rand() % 256, rand() % 256);//连通域颜色随机
		if (stats.at<int>(i, cv::CC_STAT_AREA) < area_min) //如果小于最小面积,也认为不是连通域,归类到背景为黑色
			colors[i] = Vec3b(0, 0, 0); 
	}

	img_color = Mat::zeros(ostuImage.size(), CV_8UC3);

	for (int y = 0; y < img_color.rows; y++)

		for (int x = 0; x < img_color.cols; x++)
		{
    
    
			int label = labels.at<int>(y, x);
			CV_Assert(0 <= label && label <= nccomps);
			img_color.at<cv::Vec3b>(y, x) = colors[label];

		}

	imshow("Labeled map", img_color);
}

main() function:

#include <opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <ctime>

using namespace cv;
using namespace std;

int ostu(Mat src);//阈值分割函数,上一篇博客有介绍
Mat cutImage(Mat src, int x, int y);//分块的阈值分割函数,上一篇博客有介绍,这里不使用
void connectedAnalysis(Mat ostuImage,int area_min,bool key=1);//连通域分析

int main()
{
    
    
	Mat src = imread("C:/Users/***/Desktop/blobs.tif");//读取图像
	if (src.empty()) {
    
    
		cout << "error!" << endl;
		return 0;
	}
	imshow("src", src);

	if (src.channels() > 1) {
    
    
		cvtColor(src, src, COLOR_RGB2GRAY);
	}

	int k;//阈值
	k = ostu(src);

	Mat ostuImage;//分割图像
	threshold(src, ostuImage, k, 255, THRESH_BINARY);
	imshow("ostuImage", ostuImage);  //背景为白色 

	connectedAnalysis(ostuImage, 50 ,1);

	waitKey(0);
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_44650358/article/details/116267918