OpenCV C++/Obj-C:高级方块检测
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10533233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
OpenCV C++/Obj-C: Advanced square detection
提问by dom
A while ago I asked a question about square detectionand karlphillipcame up with a decent result.
不久前,我问了一个关于平方检测的问题,karlphillip 得出了一个不错的结果。
Now I want to take this a step further and find squares which edge aren't fully visible. Take a look at this example:
现在我想更进一步,找到边缘不完全可见的方块。看看这个例子:
Any ideas? I'm working with karlphillips code:
有任何想法吗?我正在使用 karlphillips 代码:
void find_squares(Mat& image, vector<vector<Point> >& squares)
{
// blur will enhance edge detection
Mat blurred(image);
medianBlur(image, blurred, 9);
Mat gray0(blurred.size(), CV_8U), gray;
vector<vector<Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
int ch[] = {c, 0};
mixChannels(&blurred, 1, &gray0, 1, ch, 1);
// try several threshold levels
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Canny(gray0, gray, 10, 20, 3); //
// Dilate helps to remove potential holes between edge segments
dilate(gray, gray, Mat(), Point(-1,-1));
}
else
{
gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector<Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
回答by mevatron
You might try using HoughLinesto detect the four sides of the square. Next, locate the four resulting line intersections to detect the corners. The Hough transformis fairly robust to noise and occlusions, so it could be useful here. Also, hereis an interactive demo showing how the Hough transform works (I thought it was cool at least :). Hereis one of my previous answers that detects a laser cross center showing most of the same math (except it just finds a single corner).
您可以尝试使用HoughLines来检测正方形的四个边。接下来,定位四个生成的线交叉点以检测角点。该Hough变换是相当稳健的噪声和遮挡,所以它可能是有用的在这里。此外,这是一个交互式演示,展示了霍夫变换的工作原理(我认为至少它很酷:)。 这是我之前的一个答案,它检测到显示大部分相同数学的激光交叉中心(除了它只找到一个角)。
You will probably have multiple lines on each side, but locating the intersections should help to determine the inliers vs. outliers. Once you've located candidate corners, you can also filter these candidates by area or how "square-like" the polygon is.
每边可能有多条线,但定位交叉点应该有助于确定内点与外点。找到候选角后,您还可以按面积或多边形的“方形”程度过滤这些候选角。
EDIT :All these answers with code and images were making me think my answer was a bit lacking :) So, here is an implementation of how you could do this:
编辑:所有这些带有代码和图像的答案让我觉得我的答案有点缺乏:) 所以,这是一个如何做到这一点的实现:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
Point2f computeIntersect(Vec2f line1, Vec2f line2);
vector<Point2f> lineToPointPair(Vec2f line);
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta);
int main(int argc, char* argv[])
{
Mat occludedSquare = imread("Square.jpg");
resize(occludedSquare, occludedSquare, Size(0, 0), 0.25, 0.25);
Mat occludedSquare8u;
cvtColor(occludedSquare, occludedSquare8u, CV_BGR2GRAY);
Mat thresh;
threshold(occludedSquare8u, thresh, 200.0, 255.0, THRESH_BINARY);
GaussianBlur(thresh, thresh, Size(7, 7), 2.0, 2.0);
Mat edges;
Canny(thresh, edges, 66.0, 133.0, 3);
vector<Vec2f> lines;
HoughLines( edges, lines, 1, CV_PI/180, 50, 0, 0 );
cout << "Detected " << lines.size() << " lines." << endl;
// compute the intersection from the lines detected...
vector<Point2f> intersections;
for( size_t i = 0; i < lines.size(); i++ )
{
for(size_t j = 0; j < lines.size(); j++)
{
Vec2f line1 = lines[i];
Vec2f line2 = lines[j];
if(acceptLinePair(line1, line2, CV_PI / 32))
{
Point2f intersection = computeIntersect(line1, line2);
intersections.push_back(intersection);
}
}
}
if(intersections.size() > 0)
{
vector<Point2f>::iterator i;
for(i = intersections.begin(); i != intersections.end(); ++i)
{
cout << "Intersection is " << i->x << ", " << i->y << endl;
circle(occludedSquare, *i, 1, Scalar(0, 255, 0), 3);
}
}
imshow("intersect", occludedSquare);
waitKey();
return 0;
}
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta)
{
float theta1 = line1[1], theta2 = line2[1];
if(theta1 < minTheta)
{
theta1 += CV_PI; // dealing with 0 and 180 ambiguities...
}
if(theta2 < minTheta)
{
theta2 += CV_PI; // dealing with 0 and 180 ambiguities...
}
return abs(theta1 - theta2) > minTheta;
}
// the long nasty wikipedia line-intersection equation...bleh...
Point2f computeIntersect(Vec2f line1, Vec2f line2)
{
vector<Point2f> p1 = lineToPointPair(line1);
vector<Point2f> p2 = lineToPointPair(line2);
float denom = (p1[0].x - p1[1].x)*(p2[0].y - p2[1].y) - (p1[0].y - p1[1].y)*(p2[0].x - p2[1].x);
Point2f intersect(((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].x - p2[1].x) -
(p1[0].x - p1[1].x)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom,
((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].y - p2[1].y) -
(p1[0].y - p1[1].y)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom);
return intersect;
}
vector<Point2f> lineToPointPair(Vec2f line)
{
vector<Point2f> points;
float r = line[0], t = line[1];
double cos_t = cos(t), sin_t = sin(t);
double x0 = r*cos_t, y0 = r*sin_t;
double alpha = 1000;
points.push_back(Point2f(x0 + alpha*(-sin_t), y0 + alpha*cos_t));
points.push_back(Point2f(x0 - alpha*(-sin_t), y0 - alpha*cos_t));
return points;
}
NOTE :The main reason I resized the image was so I could see it on my screen, and speed-up processing.
注意:我调整图像大小的主要原因是我可以在屏幕上看到它,并加快处理速度。
Canny
精明
This uses Canny edge detection to help greatly reduce the number of lines detected after thresholding.
这使用 Canny 边缘检测来帮助大大减少阈值后检测到的行数。
Hough transform
霍夫变换
Then the Hough transform is used to detect the sides of the square.
然后使用霍夫变换来检测正方形的边。
Intersections
交叉路口
Finally, we compute the intersections of all the line pairs.
最后,我们计算所有线对的交点。
Hope that helps!
希望有帮助!
回答by Abid Rahman K
I tried to use convex hull method
which is pretty simple.
我尝试使用convex hull method
它非常简单。
Here you find convex hull of the contour detected. It removes the convexity defects at the bottom of paper.
在这里您可以找到检测到的轮廓的凸包。去除纸底的凸面缺陷。
Below is the code (in OpenCV-Python):
下面是代码(在 OpenCV-Python 中):
import cv2
import numpy as np
img = cv2.imread('sof.jpg')
img = cv2.resize(img,(500,500))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,255,0)
contours,hier = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt)>5000: # remove small areas like noise etc
hull = cv2.convexHull(cnt) # find the convex hull of contour
hull = cv2.approxPolyDP(hull,0.1*cv2.arcLength(hull,True),True)
if len(hull)==4:
cv2.drawContours(img,[hull],0,(0,255,0),2)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
(Here, i haven't found square in all planes. Do it yourself if you want.)
(在这里,我还没有在所有平面上找到正方形。如果您愿意,请自己做。)
Below is the result i got:
下面是我得到的结果:
I hope this is what you needed.
我希望这是你所需要的。
回答by karlphillip
1st: start experimenting with thresholdtechniques to isolate the whitepaper sheet from the rest of the image. This is a simple way:
第一:开始尝试使用阈值技术将白纸与图像的其余部分隔离开来。这是一个简单的方法:
Mat new_img = imread(argv[1]);
double thres = 200;
double color = 255;
threshold(new_img, new_img, thres, color, CV_THRESH_BINARY);
imwrite("thres.png", new_img);
but there are other alternatives that could provide better result. One is to investigateinRange()
, and another is to detect through colorby converting the image to the HSV color space.
但还有其他替代方案可以提供更好的结果。一种是调查inRange()
,另一种是通过将图像转换为 HSV 颜色空间来检测颜色。
This threadalso provides an interest discussion on the subject.
该线程还提供了有关该主题的兴趣讨论。
2nd: after you execute one of this procedures, you could try to feed the result directly into find_squares()
:
第二:执行此程序之一后,您可以尝试将结果直接输入find_squares()
:
An alternative to find_squares()
is to implement the bounding box technique, which has the potential to provide a more accurate detection of the rectangular area (provided that you have a perfect result of threshold). I've used it hereand here. It's worth noting that OpenCV has it's own bounding box tutorial.
另一种方法find_squares()
是实现边界框技术,它有可能提供更准确的矩形区域检测(前提是您有完美的阈值结果)。我在这里和这里都使用过它。值得注意的是,OpenCV 有它自己的边界框教程。
Another approach besides find_squares()
, as pointed by Abidon his answer, is to use the convexHullmethod. Check OpenCV's C++ tutorial on this methodfor code.
Abid在他的回答中find_squares()
指出,除 之外的另一种方法是使用凸包方法。检查 OpenCV 的C++ 教程关于此方法的代码。
回答by user3452134
- convert to lab space
- use kmeans for 2 clusters
- detect suqares one internal cluster it will solve many thing in the rgb space
- 转换为实验室空间
- 对 2 个集群使用 kmeans
- 检测 suqares 一个内部集群,它将解决 rgb 空间中的许多问题