描述

给定输入图像,图像为二值图像,计算图像中非零像素的连通区域
要求:统计连通域的数目以及每个连通域的像素数目

代码

#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <vector>

// 快排从大到小
void quickSort(int low_id, int high_id, std::vector<double>& arr)
{
    if(low_id >= high_id)
        return;
    int i, j;
    double base, temp;
    i = low_id;
    j = high_id;
    base = arr[low_id];
    while (i < j)
    {
        while (arr[j] <= base && i < j)
            j--;
        while (arr[i] >= base && i < j)
            i++;
        if(i < j)
        {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    arr[low_id] = arr[i];
    arr[i] = base;
    quickSort(low_id, i - 1, arr);
    quickSort(i + 1, high_id, arr);
}

void calc_connected_areas(const char* in_img_name, int &cnt, float area[], float rect_areas[])
{
    // 读取图像
    cv::Mat img_proj2 = cv::imread(in_img_name, cv::IMREAD_GRAYSCALE);

    // 二值化图像
    cv::threshold(img_proj2, img_proj2, 80, 255, cv::THRESH_BINARY);

    cv::Mat labels;
    // 输入图像必须是二值化后的图像
    // 函数返回值为连通区域的总数N,范围为[0,N-1],其中0代表背景
    int nums = cv::connectedComponents(img_proj2, labels, 8, CV_16U);
    cnt = nums -1; // 减去背景区域

    std::vector< std::vector<cv::Point> > contours;
    cv::findContours(img_proj2, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    std::vector<double> area_forSort;
    std::vector<double> rect_forSort;

    for(size_t i = 0; i < contours.size(); i++) {
        // 当前连通域的面积
        double sub_area = cv::contourArea(contours[i]);
        area[i] = sub_area;
        area_forSort.push_back(sub_area);

        // 连通域的最小外接矩形
        cv::RotatedRect rect = cv::minAreaRect(contours[i]);

        // 外接矩形的四个顶点
        cv::Mat points;
        cv::boxPoints(rect, points);    
        // std::cout<<points.at<float>(0, 0)<<std::endl;   

        // 画出来
        cv::line(img_proj2, points.at<cv::Point2f>(0), points.at<cv::Point2f>(1), cv::Scalar(255), 1);
        cv::line(img_proj2, points.at<cv::Point2f>(1), points.at<cv::Point2f>(2), cv::Scalar(255), 1);
        cv::line(img_proj2, points.at<cv::Point2f>(2), points.at<cv::Point2f>(3), cv::Scalar(255), 1);
        cv::line(img_proj2, points.at<cv::Point2f>(3), points.at<cv::Point2f>(0), cv::Scalar(255), 1);

        // 求外接矩形的面积
        float length = sqrt(pow(points.at<cv::Point2f>(0).x - points.at<cv::Point2f>(1).x, 2) 
            + pow(points.at<cv::Point2f>(0).y - points.at<cv::Point2f>(1).y, 2) );
        float width = sqrt(pow(points.at<cv::Point2f>(0).x - points.at<cv::Point2f>(3).x, 2) 
            + pow(points.at<cv::Point2f>(0).y - points.at<cv::Point2f>(3).y, 2) );
        float a = length*width;       
        rect_forSort.push_back(a);

        // 画一个重心,确保我们找的是对的
        cv::Mat tmp(contours[i]); 
        cv::Moments moment = moments(tmp, false); 
        if (moment.m00 != 0)
        { 
            int x = cvRound(moment.m10 / moment.m00);
            int y = cvRound(moment.m01 / moment.m00);
            cv::circle(img_proj2, cv::Point(x, y), 5, cv::Scalar(0)); 
        }
    }

    // 快排一下连通域面积
    quickSort(0, cnt-1, area_forSort);
    for(size_t i = 0; i < contours.size(); i++) {
        area[i] = area_forSort[i];
    }

    // 快排一下外接矩形的面积
    quickSort(0, cnt-1, rect_forSort);
    for(size_t i = 0; i < contours.size(); i++) {
        rect_areas[i] = rect_forSort[i];   
    }

    cv::namedWindow( "Display window", cv::WINDOW_AUTOSIZE ); // Create a window for display.
    cv::imshow( "Display window", img_proj2 );       
    cv::waitKey(0);    
}

#define MAX_AREA_CNT 1024

int main(int argc, char **argv)
{
    float area[MAX_AREA_CNT], rect_areas[MAX_AREA_CNT];
    int cnt;   
    // 调用函数,得到三个信息:连通域个数、排好序的连通域面积、排好序的外接矩形面积 
    calc_connected_areas("../example1.jpg", cnt, area, rect_areas); 
    for (int i = 0; i < cnt; i++)
    {
        printf("[%03d]area = %d , rect_area = %f\n", 
        i, int(area[i]), rect_areas[i]);
    }

    return 0;
}

项目结果


项目输出

[000]area = 17644 , rect_area = 17701.998047
[001]area = 8798 , rect_area = 11266.000000
[002]area = 7922 , rect_area = 7980.000000
[003]area = 7844 , rect_area = 7844.000000
[004]area = 4054 , rect_area = 6745.909180
[005]area = 3475 , rect_area = 5343.877441
[006]area = 3086 , rect_area = 4146.565918

再来一张示例

关键函数

threshold()
connectedComponents()
findContours()
contourArea()
minAreaRect()
moments()
boxPoints()