本系列代码托管于:https://github.com/chintsan-code/opencv4-tutorials

本篇使用的项目为:ccl

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, const char** argv) {
	Mat src = imread("../sample/rice.png");
	if (src.empty()) {
		cout << "could not load image..." << endl;
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);
	GaussianBlur(src, src, Size(3, 3), 0);
	Mat gray, binary;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("binary", binary);
	Mat labels = Mat::zeros(binary.size(), CV_32S);
	Mat stats, centroids;
	int num_labels = connectedComponentsWithStats(binary, labels, stats, centroids, 8, CV_32S, CCL_DEFAULT);
	vector<Vec3b> colorTable(num_labels);
	// backgound color
	colorTable[0] = Vec3b(0, 0, 0);
	RNG rng(12345);
	for (int i = 1; i < num_labels; i++) {
		colorTable[i] = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
	}

	Mat result = Mat::zeros(binary.size(), CV_8UC3);
	int w = result.cols;
	int h = result.rows;
	for (int row = 0; row < h; row++) {
		for (int col = 0; col < w; col++) {
			int label = labels.at<int>(row, col);
			result.at<Vec3b>(row, col) = colorTable[label];
		}
	}

	for (int i = 1; i < num_labels; i++) {
		// center
		int cx = centroids.at<double>(i, 0);  // 参数0代表获取中心x坐标
		int cy = centroids.at<double>(i, 1);  // 参数1代表获取中心y坐标
		// rectangle and area
		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);

		// 绘制
		drawMarker(result, Point(cx, cy), Scalar(0, 0, 255), MARKER_TILTED_CROSS,10);
		//circle(result, Point(cx, cy), 3, Scalar(0, 0, 255), 2, 8, 0);
		// 外接矩形
		Rect box(x, y, width, height);
		rectangle(result, box, Scalar(0, 255, 0), 2, 8);
		putText(result, format("%d", area), Point(x, y), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 0), 1);

	}
	putText(result, format("number: %d", num_labels - 1), Point(10, 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 0), 1);
	printf("total labels : %d \n", (num_labels - 1));
	imshow("CCL demo", result);

	waitKey(0);
	destroyAllWindows();
	return 0;
}

四邻域与八邻域

opencv4入门笔记(28):联通组件扫描-萤火

常见算法

(1)基于像素扫描的方法:重复计算较多
opencv4入门笔记(28):联通组件扫描-萤火
opencv4入门笔记(28):联通组件扫描-萤火
(2)基于块扫描方法:减少了重复计算
opencv4入门笔记(28):联通组件扫描-萤火
(3)两步法扫描

connectedComponents:不携带附加信息的联通组件扫描

int connectedComponents(InputArray image, OutputArray labels, int connectivity, int ltype, int ccltype);
  • image:输入图像,8-bit单通道图像
  • labels:目标图像,每个像素都有一个label。背景的label为0
  • connectivity:8或4,代表8-邻域或4-邻域
  • ltype:输出的labels类型。CV_32S或CV_16U
  • ccltype:算法类型
    • CCL_DEFAULT:8-邻域BBTD
    • CCL_WU
    • CCL_GRANA
  • @return:label数量。这里包括了背景,所以应该减去1

connectedComponentsWithStats:携带附加信息的联通组件扫描

int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids, int connectivity, int ltype, int ccltype);
  • image: 输入图像,8-bit单通道图像
  • labels: 目标图像,每个像素都有一个label。背景的label为0
  • stats:每个label的统计信息
    • CC_STAT_LEFT:外界矩形左上角x坐标
    • CC_STAT_TOP:外界矩形左上角y坐标
    • CC_STAT_WIDTH:外界矩形宽
    • CC_STAT_HEIGHT:外界矩形高
    • CC_STAT_AREA:像素面积
  • centroids:每个label的中心坐标
    • centroids.at(label, 0); // 获取中心x坐标
    • centroids.at(label, 1); // 获取中心y坐标
  • connectivity:8或4,代表8-邻域或4-邻域
  • ltype:输出的labels类型。CV_32S或CV_16U
  • ccltype:算法类型
    • CCL_DEFAULT:8-邻域BBTD
    • CCL_WU
    • CCL_GRANA
  • @return:label数量。这里包括了背景,所以应该减去1