OpenCV连通域分析
连通域分析的原理都大同小异,我就不写了,直接上代码,哪有错的或者不懂得地方,欢迎交流。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
typedef struct connectedComponentAnalysis_struct {
int area; //面积
int perimeter; //周长
float pa_ratio; //周长面积比
Rect boundingRect; //包围连通域的矩形区域
}CCA_struct;
enum connectedComponentMode_enum {
CC_FOUR, CC_EIGHT
};
void connectedComponentAnalysis(Mat &input, int cc_mode, connectedComponentAnalysis_struct *&cca, int &cc_num);
/**
@param input 输入,二值图
@param cc_mode 连通域模式 0为4领域,1为8领域
@param cca 存储连通域信息
@param cc_num 连通域个数
*/
int main()
{
Mat src = imread("sa2.jpg");
imshow("src", src);
Mat src_clone, src_gray, src_bin;
src_clone = src.clone();
cvtColor(src_clone, src_gray, CV_RGB2GRAY);
threshold(src_gray, src_bin, 20, 255, 0);
imshow("src_bin", src_bin);
cout << src_bin << endl << endl;//为了便于理解输出该矩阵
connectedComponentAnalysis_struct *cca;
int cc_num;
connectedComponentAnalysis(src_bin, CC_EIGHT, cca, cc_num);
for (int i = 0; i < cc_num; i++)
{
cout << "第" << (i + 1) << "个连通域" << ": "
<< "面积:" << cca[i].area
<< " 周长:" << cca[i].perimeter
<< " 包围连通域的矩形区域:" << cca[i].boundingRect
<< " 周长面积比:" << cca[i].pa_ratio << endl;
}
waitKey(0);
}
void connectedComponentAnalysis(Mat &input, int cc_mode, connectedComponentAnalysis_struct *&cca, int &cc_num)
{
cc_num = 0;
Mat PointLabel = Mat::zeros(input.size(), CV_8UC1);
compare(input, PointLabel, PointLabel, CMP_EQ);
//记录每个像素点检验状态的标签,0代表未检查,1代表正在检查,2代表已完成检查
PointLabel = (PointLabel / 255) * 2;
cout << PointLabel << endl << endl;//为了便于理解输出该矩阵
copyMakeBorder(PointLabel, PointLabel, 1, 1, 1, 1, BORDER_CONSTANT,2);//扩展边界,防止越界
//定义邻域位置
std::vector<int> NeighborPointOffset;
int step = PointLabel.step; //int NeighborX = extendSeedGroup.at(k).x + 1 + NeighborPointOffset[n];
NeighborPointOffset.push_back(-step); // seed.x-step
NeighborPointOffset.push_back(-1); //四领域 // seed.x-1 seed.x seed.x+1
NeighborPointOffset.push_back(1); // seed.x+step
NeighborPointOffset.push_back(step);
if (cc_mode == 1)
{
NeighborPointOffset.push_back(-step - 1); // seed.x-setp-1 seed.x-step seed.x-step+1
NeighborPointOffset.push_back(-step + 1); //八领域 // seed.x-1 seed.x seed.x+1
NeighborPointOffset.push_back(step - 1); // seed.x+step-1 seed.x+step seed.x+step+1
NeighborPointOffset.push_back(step + 1);
}
vector<Point2i> NeighborCoordinate; //extendSeedGroup.push_back(extendSeedGroup[k] + NeighborCoordinate[n]);
NeighborCoordinate.push_back(cv::Point2i(0, -1)); // seed.x+0,seed.y-1
NeighborCoordinate.push_back(cv::Point2i(-1, 0)); //四领域 // seed.x-1,seed.y+0 seed.x,seed.y seed.x+1,seed.y+0
NeighborCoordinate.push_back(cv::Point2i(1, 0)); // seed.x+0,seed.y+1
NeighborCoordinate.push_back(cv::Point2i(0, 1));
if (cc_mode == 1)
{
NeighborCoordinate.push_back(cv::Point2i(-1, -1));
NeighborCoordinate.push_back(cv::Point2i(1, -1)); // seed.x-1,seed.y-1 seed.x+0,seed.y-1 seed.x+1,seed.y-1
NeighborCoordinate.push_back(cv::Point2i(-1, 1)); //八领域 // seed.x-1,seed.y+0 seed.x,seed.y seed.x+1,seed.y+0
NeighborCoordinate.push_back(cv::Point2i(1, 1)); // seed.x-1,seed.y+1 seed.x+0,seed.y+1 seed.x+1,seed.y+1
}
int Neighbor_Mode = 4 + 4 * cc_mode;
vector<connectedComponentAnalysis_struct>temp_cca;
//开始检测
for (int i = 1; i < PointLabel.rows - 1; ++i)
{
uchar* checkLabel = PointLabel.ptr<uchar>(i);
for (int j = 1; j < PointLabel.cols - 1; ++j)
{
if (checkLabel[j] == 0)//值为0的点就是待检查的点,2是已经检查的点
{
//********开始该点处的检查**********
connectedComponentAnalysis_struct temp_allValue;//用来存储寻找到的连通域的信息
vector<Point> extendSeedGroup;//堆栈,用于存储生长点
extendSeedGroup.push_back(cv::Point2i(j - 1, i - 1));//因为之前图片上下左右边界扩展了1个像素,为了对应原图,横纵坐标都要减1个像素
checkLabel[j] = 1;
int perimeter = 0;
for (int k = 0; k < extendSeedGroup.size(); k++)
{
uchar* checkedLabel = PointLabel.ptr<uchar>(extendSeedGroup[k].y + 1);
for (int n = 0; n < Neighbor_Mode; n++)
{
int NeighborX = extendSeedGroup.at(k).x + 1 + NeighborPointOffset[n];
if (checkedLabel[NeighborX] == 0)//判断领域周围的点是否也为0
{
extendSeedGroup.push_back(extendSeedGroup[k] + NeighborCoordinate[n]);//如果值和种子点同样为0,则存入extendSeedGroup
checkedLabel[NeighborX] = 1;
}
else if (checkedLabel[NeighborX] == 2 && n < 4)
perimeter += 1;
}
}
for (int m = 0; m < extendSeedGroup.size(); m++)
{
PointLabel.at<uchar>(extendSeedGroup[m].y + 1, extendSeedGroup[m].x + 1) += 1;//更新PointLabel的记录
}
//********结束该点处的检查**********
temp_allValue.area = extendSeedGroup.size();
temp_allValue.perimeter = perimeter;
temp_allValue.pa_ratio = 1.0*perimeter*perimeter / temp_allValue.area;
temp_allValue.boundingRect = boundingRect(extendSeedGroup);
temp_cca.push_back(temp_allValue);
cc_num++;
}
}
}
cca = new connectedComponentAnalysis_struct[cc_num];
for (int i = 0; i < cc_num; i++)
{
cca[i] = temp_cca[i];
}
}