OpenCV C ++ / Obj-C:高级平方检测

前一阵子我问了一个关于方形检测的问题 , 卡尔菲利普得出了一个相当好的结果。

现在我想更进一步,find那些边缘不完全可见的正方形。 看看这个例子:

例

有任何想法吗? 我正在使用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); } } } } } 

您可以尝试使用HoughLines来检测广场的四面。 接下来,find四个结果线交叉点来检测拐angular。 Hough变换对噪声和遮挡是相当强壮的,所以在这里它可能是有用的。 此外, 这里是一个交互式演示,展示了Hough变换是如何工作的(至less我觉得它很酷:)。 这是我以前的答案之一,检测激光十字中心显示大部分相同的math(除了它只发现一个angular落)。

你可能在每一边都有多条线,但是find交点应该有助于确定内点对exception值。 一旦你find候选angular落,你也可以按面积过滤这些候选人,或者多边形如何“方形”。

编辑:所有这些与代码和图像的答案让我觉得我的答案是有点缺乏:)所以,这里是一个如何执行此操作的实现:

 #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; } 

注:我调整图像的主要原因是我可以在屏幕上看到它,并加速处理。

谨慎的

这使用Canny边缘检测来帮助极大地减less阈值之后检测到的行数。

在这里输入图像描述

霍夫变换

然后使用霍夫变换来检测正方形的边。 在这里输入图像描述

交叉口

最后,我们计算所有线对的交点。 在这里输入图像描述

希望有所帮助!

我试图使用非常简单的convex hull method

在这里,您可以find检测到轮廓的凸包。 它消除了纸张底部的凸起缺陷。

下面是代码(在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() 

(在这里,我没有在所有的飞机上发现广场,如果你想的话,自己动手吧。)

以下是我得到的结果:

在这里输入图像描述

我希望这是你所需要的。

第一 :开始尝试阈值技术,从图像的其余部分隔离白色纸张。 这是一个简单的方法:

 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); 

但还有其他的select可以提供更好的结果。 一个是调查 inRange() ,另一个是通过将图像转换为HSV颜色空间来检测颜色。

这个主题还提供了一个关于这个问题的兴趣讨论。

第二 :在你执行这个过程之后,你可以直接把结果提供给find_squares()

find_squares()一个替代方法是实现边界框技术 ,它有可能提供更准确的矩形区域检测(假设你有一个完美的阈值结果)。 我已经在这里和这里使用它。 值得注意的是,OpenCV有它自己的包围盒教程 。

除了find_squares()之外, Abid在他的答案中指出的另一种方法是使用convexHull方法。 查看OpenCV的C ++教程,了解这个代码的方法 。

  1. 转换为实验室空间
  2. 使用kmeans 2个集群
  3. 检测到一个内部簇将会解决rgb空间中的许多事情