从 C++ 中的斑点检测中提取 OpenCV 轮廓数组
Extract OpenCV Contour Array from Blob Detection in C++
我正在研究 C++ 和 OpenCV 中的斑点检测技术,但我无法以可用格式提取轮廓信息。
目前,我将所有轮廓数据以 cv::Point
格式存储为数组;但是,我希望提取坐标并将它们存储在变量下的数组中,以便我可以根据需要操作这些数据。
我的代码如下所示:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, const char** argv)
{
cv::Mat src = cv::imread("frame-1.jpg");
if (src.empty())
return -1;
cv::Mat gray;
cv::cvtColor(~src, gray, CV_BGR2GRAY);
cv::threshold(gray, gray, 160, 255, cv::THRESH_BINARY);
// Find all contours
std::vector<std::vector<cv::Point> > contours;
cv::findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
// Fill holes in each contour
cv::drawContours(gray, contours, -1, CV_RGB(255, 255, 255), -1);
cout << contours.size();
std::vector<cv::Point> minXY(contours.size()); // Top Left Point of Bounding Box
std::vector<cv::Point> maxXY(contours.size()); // Bottom Right Point of Bounding Box
for (int i = 0; i < contours.size()){
minXY[i] = maxXY[i] = contours[i][0]; // Assumes the contour has at least 1 point
for(int j = 1; j < contours[i].size(); ++j){
if (contours[i][j].x < minXY[i].x){
minXY[i].x = contours[i][j].x;
} else if (contours[i][j].x > maxXY[i].x){
maxXY[i].x = contours[i][j].x;
}
if (contours[i][j].y < minXY[i].y){
minXY[i].y = contours[i][j].y;
} else if (contours[i][j].y > maxXY[i].y){
maxXY[i].y = contours[i][j].y;
}
}
}
namedWindow("MyWindow", CV_WINDOW_AUTOSIZE);
imshow("MyWindow", gray);
waitKey(0);
destroyWindow("MyWindow");
return 0;
}
contours[2]
的结果给出了一个数组,其中包含第三个blob的轮廓坐标;但是,我想将这个数组提取到一个普通变量中,这样我就可以实际使用它了。我假设这意味着某种 cv::Point
转换?
更新:我可能应该澄清一下,我想要最大斑点轮廓的坐标,以便我可以操纵数据,例如找到最小的 x 点、找到平均 x 点、找到斑点的质心等.
目前我已经能够找到所有的轮廓,然后识别出最大的斑点,并识别出它的轮廓坐标。但是,我还没有找到一种方法来搜索这个数组以找到最小的 x 值,或者只对 x 坐标求和的方法。我最终还想找到找到 blob 质心的最有效方法(使用边界框、力矩、简单算术或其他方法。)
** contours[2] 以以下形式吐出一个数组:
[100, 267]
[101, 270]
[102, 271]
我想找到一种方法来搜索左列(即所有 x 值)以找到最小和最大等。我尝试了几种方法,但 none 使我接近。当然有一个简单的解决方案。似乎我所有的问题都源于轮廓数组的形式是 cv::Point.
如果你想要构成第 3 个轮廓的点,你可以执行以下操作:
std::vector<cv::Point> my_contour = contours[2];
或者您可以通过以下方式遍历所有国家:
for (int i = 0; i < contours.size(); ++i){
double avg_x(0), avg_y(0); // average of contour points
for (int j = 0; j < contours[i].size(); ++j){
// Do whatever you need to do with the points in the ith contour
avg_x += contours[i][j].x;
avg_y += contours[i][j].y;
}
avg_x /= contours[i].size();
avg_y /= contours[i].size();
cout << avg_x << " " << avg_y << endl;
}
根据您的评论,您还想计算轮廓的质心。
您可以使用 OpenCV 找到每个斑点的时刻
std::vector<cv::Moment> moments(contours.size());
std::vector<cv::Point> centroids(contours.size());
for (int i = 0; i < contours.size()){
moments[i] = cv::moments(contours[i], false);
centroids[i] = cv::Point(moments[i].m10/moments[i].m00, moments[i].m01/moments[i].m00);
}
第 0 个矩是 blob 的面积,第 1 个矩是质心,第 2 个矩可以用来找到 blob 的方向和偏心率。
维基百科是了解更多图像时刻的良好起点。 https://en.wikipedia.org/wiki/Image_moment
使用 OpenCV,您可以通过首先将轮廓近似为多边形来获取轮廓的边界框和封闭圆。
以下代码来自OpenCV
std::vector<vector<cv::Point> > contours_poly(contours.size());
std::vector<cv::Rect> boundingRect (contours.size());
std::vector<cv::Point> center(contours.size());
std::vector<float> radius(contours.size());
for (int i = 0; i < contours.size(); ++i){
cv::approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 3, true);
boundingRect[i] = cv::boundingRect(cv::Mat(contours_poly[i]));
cv::MinEnclosingCircle((cv::Mat)contours_poly[i], center[i], radius[i]);
}
或者您可以自己做(对于边界框)。
std::vector<cv::Point> minXY(contours.size()); // Top Left Point of Bounding Box
std::vector<cv::Point> maxXY(contours.size()); // Bottom Right Point of Bounding Box
for (int i = 0; i < contours.size()){
minXY[i] = maxXY[i] = contours[i][0]; // Assumes the contour has at least 1 point
for(int j = 1; j < contours[i].size(); ++j){
if (contours[i][j].x < minXY[i].x){
minXY[i].x = contours[i][j].x;
} else if (contours[i][j].x > maxXY[i].x){
maxXY[i].x = contours[i][j].x;
}
if (contours[i][j].y < minXY[i].y){
minXY[i].y = contours[i][j].y;
} else if (contours[i][j].y > maxXY[i].y){
maxXY[i].y = contours[i][j].y;
}
}
}
轮廓[i]:
最左边的点在MinXY[i].x
最右边的点在 MaxXY[i].x
最高点在MinXY[i].y
(注意Y轴反转)
最底部的点在 MaxXY[i].y
编辑:回答修改后的问题。
float min_x = contours[2][0].x;
for (int i = 1; i < countours[2].size(); ++i){
if (contours[2][i].x < min_x){
min_x = contours[2][i].x;
}
}
我正在研究 C++ 和 OpenCV 中的斑点检测技术,但我无法以可用格式提取轮廓信息。
目前,我将所有轮廓数据以 cv::Point
格式存储为数组;但是,我希望提取坐标并将它们存储在变量下的数组中,以便我可以根据需要操作这些数据。
我的代码如下所示:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, const char** argv)
{
cv::Mat src = cv::imread("frame-1.jpg");
if (src.empty())
return -1;
cv::Mat gray;
cv::cvtColor(~src, gray, CV_BGR2GRAY);
cv::threshold(gray, gray, 160, 255, cv::THRESH_BINARY);
// Find all contours
std::vector<std::vector<cv::Point> > contours;
cv::findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
// Fill holes in each contour
cv::drawContours(gray, contours, -1, CV_RGB(255, 255, 255), -1);
cout << contours.size();
std::vector<cv::Point> minXY(contours.size()); // Top Left Point of Bounding Box
std::vector<cv::Point> maxXY(contours.size()); // Bottom Right Point of Bounding Box
for (int i = 0; i < contours.size()){
minXY[i] = maxXY[i] = contours[i][0]; // Assumes the contour has at least 1 point
for(int j = 1; j < contours[i].size(); ++j){
if (contours[i][j].x < minXY[i].x){
minXY[i].x = contours[i][j].x;
} else if (contours[i][j].x > maxXY[i].x){
maxXY[i].x = contours[i][j].x;
}
if (contours[i][j].y < minXY[i].y){
minXY[i].y = contours[i][j].y;
} else if (contours[i][j].y > maxXY[i].y){
maxXY[i].y = contours[i][j].y;
}
}
}
namedWindow("MyWindow", CV_WINDOW_AUTOSIZE);
imshow("MyWindow", gray);
waitKey(0);
destroyWindow("MyWindow");
return 0;
}
contours[2]
的结果给出了一个数组,其中包含第三个blob的轮廓坐标;但是,我想将这个数组提取到一个普通变量中,这样我就可以实际使用它了。我假设这意味着某种 cv::Point
转换?
更新:我可能应该澄清一下,我想要最大斑点轮廓的坐标,以便我可以操纵数据,例如找到最小的 x 点、找到平均 x 点、找到斑点的质心等.
目前我已经能够找到所有的轮廓,然后识别出最大的斑点,并识别出它的轮廓坐标。但是,我还没有找到一种方法来搜索这个数组以找到最小的 x 值,或者只对 x 坐标求和的方法。我最终还想找到找到 blob 质心的最有效方法(使用边界框、力矩、简单算术或其他方法。)
** contours[2] 以以下形式吐出一个数组:
[100, 267]
[101, 270]
[102, 271]
我想找到一种方法来搜索左列(即所有 x 值)以找到最小和最大等。我尝试了几种方法,但 none 使我接近。当然有一个简单的解决方案。似乎我所有的问题都源于轮廓数组的形式是 cv::Point.
如果你想要构成第 3 个轮廓的点,你可以执行以下操作:
std::vector<cv::Point> my_contour = contours[2];
或者您可以通过以下方式遍历所有国家:
for (int i = 0; i < contours.size(); ++i){
double avg_x(0), avg_y(0); // average of contour points
for (int j = 0; j < contours[i].size(); ++j){
// Do whatever you need to do with the points in the ith contour
avg_x += contours[i][j].x;
avg_y += contours[i][j].y;
}
avg_x /= contours[i].size();
avg_y /= contours[i].size();
cout << avg_x << " " << avg_y << endl;
}
根据您的评论,您还想计算轮廓的质心。
您可以使用 OpenCV 找到每个斑点的时刻
std::vector<cv::Moment> moments(contours.size());
std::vector<cv::Point> centroids(contours.size());
for (int i = 0; i < contours.size()){
moments[i] = cv::moments(contours[i], false);
centroids[i] = cv::Point(moments[i].m10/moments[i].m00, moments[i].m01/moments[i].m00);
}
第 0 个矩是 blob 的面积,第 1 个矩是质心,第 2 个矩可以用来找到 blob 的方向和偏心率。
维基百科是了解更多图像时刻的良好起点。 https://en.wikipedia.org/wiki/Image_moment
使用 OpenCV,您可以通过首先将轮廓近似为多边形来获取轮廓的边界框和封闭圆。
以下代码来自OpenCV
std::vector<vector<cv::Point> > contours_poly(contours.size());
std::vector<cv::Rect> boundingRect (contours.size());
std::vector<cv::Point> center(contours.size());
std::vector<float> radius(contours.size());
for (int i = 0; i < contours.size(); ++i){
cv::approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 3, true);
boundingRect[i] = cv::boundingRect(cv::Mat(contours_poly[i]));
cv::MinEnclosingCircle((cv::Mat)contours_poly[i], center[i], radius[i]);
}
或者您可以自己做(对于边界框)。
std::vector<cv::Point> minXY(contours.size()); // Top Left Point of Bounding Box
std::vector<cv::Point> maxXY(contours.size()); // Bottom Right Point of Bounding Box
for (int i = 0; i < contours.size()){
minXY[i] = maxXY[i] = contours[i][0]; // Assumes the contour has at least 1 point
for(int j = 1; j < contours[i].size(); ++j){
if (contours[i][j].x < minXY[i].x){
minXY[i].x = contours[i][j].x;
} else if (contours[i][j].x > maxXY[i].x){
maxXY[i].x = contours[i][j].x;
}
if (contours[i][j].y < minXY[i].y){
minXY[i].y = contours[i][j].y;
} else if (contours[i][j].y > maxXY[i].y){
maxXY[i].y = contours[i][j].y;
}
}
}
轮廓[i]:
最左边的点在MinXY[i].x
最右边的点在 MaxXY[i].x
最高点在MinXY[i].y
(注意Y轴反转)
最底部的点在 MaxXY[i].y
编辑:回答修改后的问题。
float min_x = contours[2][0].x;
for (int i = 1; i < countours[2].size(); ++i){
if (contours[2][i].x < min_x){
min_x = contours[2][i].x;
}
}