C++ OpenCV';s fitEllipse()有时返回完全错误的椭圆
我的目标是识别图像中的所有形状。 这个想法是:C++ OpenCV';s fitEllipse()有时返回完全错误的椭圆,c++,opencv,image-processing,C++,Opencv,Image Processing,我的目标是识别图像中的所有形状。 这个想法是: 提取等高线 用不同的形状拟合每个轮廓 正确的形状应该是面积最接近的形状 轮廓面积 示例图像: 我使用fitEllipse() 可能正确的椭圆用蓝色填充,边界椭圆用黄色填充。 可能不正确的轮廓用绿色填充,(错误的)边界椭圆为青色 如您所见,第一行中包围三角形的椭圆看起来非常适合最佳拟合。第三行三角形的边界椭圆似乎不是最合适的,但仍然可以作为拒绝错误椭圆的标准 但我不明白为什么剩下的三角形的边界椭圆完全在它们的轮廓之外。 最坏的情况是最后一行的第三
fitEllipse()
可能正确的椭圆用蓝色填充,边界椭圆用黄色填充。
可能不正确的轮廓用绿色填充,(错误的)边界椭圆为青色
如您所见,第一行中包围三角形的椭圆看起来非常适合最佳拟合。第三行三角形的边界椭圆似乎不是最合适的,但仍然可以作为拒绝错误椭圆的标准
但我不明白为什么剩下的三角形的边界椭圆完全在它们的轮廓之外。
最坏的情况是最后一行的第三个三角形:椭圆完全错误,但它的区域恰好靠近轮廓的区域,因此三角形被错误地识别为椭圆
我错过什么了吗?我的代码:
#include <iostream>
#include <opencv/cv.h>
#include <opencv/highgui.h>
using namespace std;
using namespace cv;
void getEllipses(vector<vector<Point> >& contours, vector<RotatedRect>& ellipses) {
ellipses.clear();
Mat img(Size(800,500), CV_8UC3);
for (unsigned i = 0; i<contours.size(); i++) {
if (contours[i].size() >= 5) {
RotatedRect temp = fitEllipse(Mat(contours[i]));
if (area(temp) <= 1.1 * contourArea(contours[i])) {
//cout << area(temp) << " < 1.1* " << contourArea(contours[i]) << endl;
ellipses.push_back(temp);
drawContours(img, contours, i, Scalar(255,0,0), -1, 8);
ellipse(img, temp, Scalar(0,255,255), 2, 8);
imshow("Ellipses", img);
waitKey();
} else {
//cout << "Reject ellipse " << i << endl;
drawContours(img, contours, i, Scalar(0,255,0), -1, 8);
ellipse(img, temp, Scalar(255,255,0), 2, 8);
imshow("Ellipses", img);
waitKey();
}
}
}
}
int main() {
Mat img = imread("image.png", CV_8UC1);
threshold(img, img, 127,255,CV_THRESH_BINARY);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
vector<RotatedRect> ellipses;
getEllipses(contours, ellipses);
return 0;
}
#包括
#包括
#包括
使用名称空间std;
使用名称空间cv;
void getellipes(向量和等高线、向量和椭圆){
省略号。清除();
材料img(尺寸(800500),CV_8UC3);
for(无符号i=0;i=5){
RotatedRect temp=fitEllipse(图[i]);
如果(面积(温度),最好逐像素比较,即轮廓和“拟合”椭圆之间的重叠百分比是多少
另一个更简单的想法是还比较轮廓的质心及其椭圆拟合。如果您在cv::fitEllipse()
方面遇到问题,请讨论一些方法,以最大限度地减少在不进行任何进一步测试而直接绘制cv::RotatedRect
时发生的错误。结果是cv::fitEllipse()
不是完美的,可能会出现问题,如问题中所述
现在,还不完全清楚项目的约束是什么,但是解决这个问题的另一种方法是根据轮廓的面积来分离这些形状
这种方法非常简单,但在这种特殊情况下非常有效:圆的面积在1300-1699之间变化,三角形的面积在1-1299之间变化
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
int main()
{
cv::Mat img = cv::imread("input.png");
if (img.empty())
{
std::cout << "!!! Failed to open image" << std::endl;
return -1;
}
/* Convert to grayscale */
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
/* Convert to binary */
cv::Mat thres;
cv::threshold(gray, thres, 127, 255, cv::THRESH_BINARY);
/* Find contours */
std::vector<std::vector<cv::Point> > contours;
cv::findContours(thres, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
int circles = 0;
int triangles = 0;
for (size_t i = 0; i < contours.size(); i++)
{
// Draw a contour based on the size of its area:
// - Area > 0 and < 1300 means it's a triangle;
// - Area >= 1300 and < 1700 means it's a circle;
double area = cv::contourArea(contours[i]);
if (area > 0 && area < 1300)
{
std::cout << "* Triangle #" << ++triangles << " area: " << area << std::endl;
cv::drawContours(img, contours, i, cv::Scalar(0, 255, 0), -1, 8); // filled (green)
cv::drawContours(img, contours, i, cv::Scalar(0, 0, 255), 2, 8); // outline (red)
}
else if (area >= 1300 && area < 1700)
{
std::cout << "* Circle #" << ++circles << " area: " << area << std::endl;
cv::drawContours(img, contours, i, cv::Scalar(255, 0, 0), -1, 8); // filled (blue)
cv::drawContours(img, contours, i, cv::Scalar(0, 0, 255), 2, 8); // outline (red)
}
else
{
std::cout << "* Ignoring area: " << area << std::endl;
continue;
}
cv::imshow("OBJ", img);
cv::waitKey(0);
}
cv::imwrite("output.png", img);
return 0;
}
#包括“opencv2/highgui/highgui.hpp”
#包括“opencv2/imgproc/imgproc.hpp”
#包括
int main()
{
cv::Mat img=cv::imread(“input.png”);
if(img.empty())
{
标准::cout 0和面积<1300)
{
std::cout将cv::CHAIN\u approw\u SIMPLE
更改为cv::CHAIN\u approw\u NONE
在调用cv::findContours()
时,我得到了更合理的结果
如果轮廓中包含更多的点,我们可以得到更好的椭圆近似值,这是有道理的,但我仍然不确定为什么结果与简单的链近似值相差如此之远。有关差异的解释,请参阅
使用cv::CHAIN_APPROX_SIMPLE
时,三角形的相对水平边几乎完全从轮廓上移除
至于您的最佳拟合分类,正如其他人所指出的,仅使用面积将得到您观察到的结果,因为根本不考虑定位。请记住,fitEllipse
不是边界椭圆的计算,而是假设点位于椭圆上的最小二乘优化
我无法告诉你为什么它在最后一行的3个三角形上失败得如此糟糕,但在上面一行的三角形上“起作用”,但我看到的一件事是,最后一行的所有3个三角形都被拟合到一个角度为0的旋转体上。可能最小二乘拟合在那里失败了
但我不知道openCV实现中是否存在缺陷,或者该算法是否无法处理这些情况。该算法用于:
我的建议是,如果你非常确定这些点真的属于一个椭圆,那么只使用fitEllipse
。如果你有随机数据点,你不会假设从fitLine
得到合理的结果。你可能想看的其他函数有:minarealect
和mineConclosingCircle
如果您使用rotatedlect temp=minarealect(Mat(轮廓[i]);
而不是fitEllipse
您将获得如下图像:
也许你甚至可以使用这两种方法,拒绝所有在两个版本中都失败的椭圆,接受所有在两个版本中都被接受的椭圆,但对那些不同的椭圆进行进一步的调查?你是否尝试过绘制/显示正在计算的轮廓?也许问题在于这一点,而不是fitEllipse。而且,因为你看起来为了有一个二进制输入,你可以考虑使用形态学运算来寻找等值线,即原始腐蚀(原始)。然后每个连接的分量应该是一个轮廓。另一个建议可能是使用一个最大树,这就消除了主(2)线上的阈值的需要。。您需要定义正确的属性过滤器。参考:或重叠区域和质心的组合。所有建议都很好,但它们没有解决主要问题:椭圆拟合怎么会完全错误?为什么OpenCV返回的椭圆显然不是此轮廓的最小椭圆?如果您运行此应用程序,你必须按键盘上的一个键来执行循环的下一次迭代。这允许用户看到被检测和绘制的每个形状。