如何在 OpenCV connectedComponents 函数后分割对象
How to segment objects after OpenCV connectedComponents function
我用C++ OpenCV的connectedComponents
函数得到了一个标注,如图所示:
这是ccLabels
变量的输出,是一个cv::Mat
和原图一样大小的cv::Mat
所以我需要做的是:
- 计算每个数字出现的次数,select
出现超过
N
次,即 "big" 次。
- 分段
"big" 个分量的面积,然后计算 4 和
0 在该区域内。
我的最终目标是计算图像中的孔数,所以我的目标是从 (number of 0's / number of 4's)
中推断出孔数。这可能不是最漂亮的方式,但图像在大小和照明方面非常均匀,所以它会满足我的需要。
但是我是 OpenCV 的新手,我不太清楚如何完成这个任务。
这是我到目前为止所做的:
cv::Mat1b outImg;
cv::threshold(grayImg, outImg, 150, 255, 0); // Thresholded -binary- image
cv::Mat ccLabels;
cv::connectedComponents(outImg, ccLabels); // Each non-zero pixel is labeled with their connectedComponent ID's
// write the labels to file:
std::ofstream myfile;
myfile.open("ccLabels.txt");
cv::Size s = ccLabels.size();
myfile << "Size: " << s.height << " , " << s.width <<"\n";
for (int r1 = 0; r1 < s.height; r1++) {
for (int c1 = 0; c1 < s.height; c1++) {
myfile << ccLabels.at<int>(r1,c1);
}
myfile << "\n";
}
myfile.close();
因为我知道如何在矩阵内部迭代,所以计算数字应该没问题,但首先我必须分开(消除/忽略)"background" 像素,即 0 的 在 个连通分量之外。然后计数应该很容易。
如何分割这些 "big" 组件?也许获得掩码,并且只考虑 mask(x,y) = 1
?
处的像素
感谢您的帮助!
编辑
这是阈值图像:
这就是我在 Canny 边缘检测之后得到的结果:
这是实际图像(阈值):
更简单的方法是 findContours 方法。您找到内部轮廓并计算它们的面积(因为内部轮廓将是孔)并相应地处理此信息。
要解决您的第一个问题,请考虑您在 values.Count 每个出现的数字中有一组值。
int m=0;
for(int n=0;n<256;n++)
{
int c=0;
for(int q=0;q<values.size();q++)
{
if(n==values[q])
{
//int c;
c++;
m++;
}
}
cout<<n<<"= "<< c<<endl;
}
cout<<"Total number of elements "<< m<<endl;
为了解决你的第二个问题,使用 findcontours 找到图像中最大的轮廓,在它周围绘制边界矩形,然后裁剪它。再次使用上面的代码来计算像素值“4”和“0”。您可以在此处找到 link
这是一个简单的程序,可以从您的阈值图像开始查找骰子上的数字
- 寻找外部轮廓
- 对于每个轮廓
- 最终丢弃小斑点
- 绘制填充蒙版
- 使用AND和XOR隔离内部漏洞
- 再次找到轮廓
- 计数轮廓
结果:
Number: 5
Number: 2
图片:
代码:
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main(void)
{
// Grayscale image
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// Minimum area of the contour
double minContourArea = 10;
// Prepare outpot
Mat3b result;
cvtColor(img, result, COLOR_GRAY2BGR);
// Find contours
vector<vector<Point>> contours;
findContours(img.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); ++i)
{
// Check area
if (contourArea(contours[i]) < minContourArea) continue;
// Black mask
Mat1b mask(img.rows, img.cols, uchar(0));
// Draw filled contour
drawContours(mask, contours, i, Scalar(255), CV_FILLED);
mask = (mask & img) ^ mask;
vector<vector<Point>> cntrs;
findContours(mask, cntrs, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
cout << "Number: " << cntrs.size() << endl;
// Just for showing results
drawContours(result, cntrs, -1, Scalar(0,0,255), CV_FILLED);
}
imshow("Result", result);
waitKey();
return 0;
}
我用C++ OpenCV的connectedComponents
函数得到了一个标注,如图所示:
这是ccLabels
变量的输出,是一个cv::Mat
和原图一样大小的cv::Mat
所以我需要做的是:
- 计算每个数字出现的次数,select
出现超过
N
次,即 "big" 次。 - 分段 "big" 个分量的面积,然后计算 4 和 0 在该区域内。
我的最终目标是计算图像中的孔数,所以我的目标是从 (number of 0's / number of 4's)
中推断出孔数。这可能不是最漂亮的方式,但图像在大小和照明方面非常均匀,所以它会满足我的需要。
但是我是 OpenCV 的新手,我不太清楚如何完成这个任务。
这是我到目前为止所做的:
cv::Mat1b outImg;
cv::threshold(grayImg, outImg, 150, 255, 0); // Thresholded -binary- image
cv::Mat ccLabels;
cv::connectedComponents(outImg, ccLabels); // Each non-zero pixel is labeled with their connectedComponent ID's
// write the labels to file:
std::ofstream myfile;
myfile.open("ccLabels.txt");
cv::Size s = ccLabels.size();
myfile << "Size: " << s.height << " , " << s.width <<"\n";
for (int r1 = 0; r1 < s.height; r1++) {
for (int c1 = 0; c1 < s.height; c1++) {
myfile << ccLabels.at<int>(r1,c1);
}
myfile << "\n";
}
myfile.close();
因为我知道如何在矩阵内部迭代,所以计算数字应该没问题,但首先我必须分开(消除/忽略)"background" 像素,即 0 的 在 个连通分量之外。然后计数应该很容易。
如何分割这些 "big" 组件?也许获得掩码,并且只考虑 mask(x,y) = 1
?
感谢您的帮助!
编辑
这是阈值图像:
这就是我在 Canny 边缘检测之后得到的结果:
这是实际图像(阈值):
更简单的方法是 findContours 方法。您找到内部轮廓并计算它们的面积(因为内部轮廓将是孔)并相应地处理此信息。
要解决您的第一个问题,请考虑您在 values.Count 每个出现的数字中有一组值。
int m=0;
for(int n=0;n<256;n++)
{
int c=0;
for(int q=0;q<values.size();q++)
{
if(n==values[q])
{
//int c;
c++;
m++;
}
}
cout<<n<<"= "<< c<<endl;
}
cout<<"Total number of elements "<< m<<endl;
为了解决你的第二个问题,使用 findcontours 找到图像中最大的轮廓,在它周围绘制边界矩形,然后裁剪它。再次使用上面的代码来计算像素值“4”和“0”。您可以在此处找到 link
这是一个简单的程序,可以从您的阈值图像开始查找骰子上的数字
- 寻找外部轮廓
- 对于每个轮廓
- 最终丢弃小斑点
- 绘制填充蒙版
- 使用AND和XOR隔离内部漏洞
- 再次找到轮廓
- 计数轮廓
结果:
Number: 5
Number: 2
图片:
代码:
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main(void)
{
// Grayscale image
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// Minimum area of the contour
double minContourArea = 10;
// Prepare outpot
Mat3b result;
cvtColor(img, result, COLOR_GRAY2BGR);
// Find contours
vector<vector<Point>> contours;
findContours(img.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); ++i)
{
// Check area
if (contourArea(contours[i]) < minContourArea) continue;
// Black mask
Mat1b mask(img.rows, img.cols, uchar(0));
// Draw filled contour
drawContours(mask, contours, i, Scalar(255), CV_FILLED);
mask = (mask & img) ^ mask;
vector<vector<Point>> cntrs;
findContours(mask, cntrs, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
cout << "Number: " << cntrs.size() << endl;
// Just for showing results
drawContours(result, cntrs, -1, Scalar(0,0,255), CV_FILLED);
}
imshow("Result", result);
waitKey();
return 0;
}