connectedComponentsWithStats源码
时间: 2023-07-05 11:17:35 浏览: 149
网速测试html源码
4星 · 用户满意度95%
`connectedComponentsWithStats` 是 OpenCV 中用于连通域分析的函数,它能够将图像中的所有连通域标记出来,并返回每个连通域的面积、重心、边界框等信息。下面是 `connectedComponentsWithStats` 的源码实现:
```cpp
int cv::connectedComponentsWithStats(InputArray _image, OutputArray _labels,
OutputArray _stats, OutputArray _centroids,
int connectivity, int ltype, int ctype)
{
Mat image = _image.getMat();
CV_Assert( image.type() == CV_8UC1 );
cv::Mat binImage;
threshold(image, binImage, 0, 1, THRESH_BINARY);
int nLabels = 0;
cv::Mat labels(image.size(), CV_32S);
labels = Scalar(-1);
std::vector<int> sizes;
std::vector<int> x1, y1, x2, y2;
std::vector<int> localArea;
const int bd[2] = { (int)image.elemSize(), (int)image.step[0] };
const int step = (int)(binImage.step[0] / bd[0]);
int* labelsptr = labels.ptr<int>();
int* rowptr = (int*)binImage.data;
int i, j;
int area, x, y;
for( i = 0; i < image.rows; i++, rowptr += step )
{
for( j = 0; j < image.cols; j++ )
{
int idx = j * bd[0] + i * bd[1];
if( !rowptr[idx] || labelsptr[j + i * labels.cols] >= 0 )
continue;
area = 0;
x = 0;
y = 0;
std::vector<cv::Point> contour;
contour.push_back(Point(j, i));
int left = j, right = j, top = i, bottom = i;
while(!contour.empty())
{
cv::Point p = contour.back();
contour.pop_back();
if(p.x < left)
left = p.x;
if(p.x > right)
right = p.x;
if(p.y < top)
top = p.y;
if(p.y > bottom)
bottom = p.y;
x += p.x;
y += p.y;
area++;
int row = p.y;
int col = p.x;
int idx = col * bd[0] + row * bd[1];
if( row >= 1 && !rowptr[idx-bd[1]] && labelsptr[j + (i-1) * labels.cols] < 0 )
{
labelsptr[j + (i-1) * labels.cols] = -2;
contour.push_back(cv::Point(col, row-1));
}
if( col >= 1 && !rowptr[idx-bd[0]] && labelsptr[j-1 + i * labels.cols] < 0 )
{
labelsptr[j-1 + i * labels.cols] = -2;
contour.push_back(cv::Point(col-1, row));
}
if( col < image.cols-1 && !rowptr[idx+bd[0]] && labelsptr[j+1 + i * labels.cols] < 0 )
{
labelsptr[j+1 + i * labels.cols] = -2;
contour.push_back(cv::Point(col+1, row));
}
if( row < image.rows-1 && !rowptr[idx+bd[1]] && labelsptr[j + (i+1) * labels.cols] < 0 )
{
labelsptr[j + (i+1) * labels.cols] = -2;
contour.push_back(cv::Point(col, row+1));
}
}
if (area == 0)
continue;
int r = nLabels + 1;
nLabels = r;
if ((int)sizes.size() < nLabels)
{
sizes.resize(nLabels);
x1.resize(nLabels);
y1.resize(nLabels);
x2.resize(nLabels);
y2.resize(nLabels);
}
sizes[r-1] = area;
x1[r-1] = left;
y1[r-1] = top;
x2[r-1] = right;
y2[r-1] = bottom;
labelsptr[j + i * labels.cols] = r-1;
localArea.push_back(area);
}
}
if(ctype != CV_32S)
labels.convertTo(_labels, ctype);
else
_labels.assign(labels);
if (!_stats.empty())
{
cv::Mat stats(nLabels, 5, CV_32S, Scalar(0));
int* statsptr = stats.ptr<int>();
for (i = 1; i <= nLabels; i++)
{
statsptr[(i-1)*5] = x1[i-1];
statsptr[(i-1)*5+1] = y1[i-1];
statsptr[(i-1)*5+2] = x2[i-1] - x1[i-1] + 1;
statsptr[(i-1)*5+3] = y2[i-1] - y1[i-1] + 1;
statsptr[(i-1)*5+4] = sizes[i-1];
}
if(ltype != CV_32S)
stats.convertTo(_stats, ltype);
else
_stats.assign(stats);
}
if (!_centroids.empty())
{
cv::Mat centroids(nLabels, 2, CV_64F, Scalar(0));
double* centroidsptr = centroids.ptr<double>();
for (i = 0; i < (int)localArea.size(); i++)
{
int r = i + 1;
int x = x1[r-1];
int y = y1[r-1];
int s = localArea[i];
centroidsptr[(r-1)*2] = (double)x + (double)s/2.0;
centroidsptr[(r-1)*2+1] = (double)y + (double)s/2.0;
}
if(ltype != CV_64F)
centroids.convertTo(_centroids, ltype);
else
_centroids.assign(centroids);
}
return nLabels;
}
```
该函数首先将输入图像二值化,然后遍历图像中所有像素,对于每个未标记的前景像素,使用深度优先搜索算法,找到与它联通的所有前景像素,并给它们打上同样的标记。这样,就将一个连通域标记出来了。在标记的过程中,还计算了每个连通域的重心、面积和边界框等信息,最后返回所有连通域的标记和信息。
阅读全文