描述
给定输入图像,图像为二值图像,计算图像中非零像素的连通区域
要求:统计连通域的数目以及每个连通域的像素数目
代码
#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()
评论(0)
您还未登录,请登录后发表或查看评论