OpenCV C++ / Obj - C: détection carrée Avancée
il y a peu de temps, j'ai demandé une question à propos de la détection carrée et karlphillip a donné un résultat décent.
maintenant je veux aller un peu plus loin et trouver des carrés dont le bord n'est pas entièrement visible. Regardez cet exemple:
des idées? Je travaille avec karlphillips code:
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);
}
}
}
}
}
4 réponses
vous pourriez essayer d'utiliser HoughLines pour détecter les quatre côtés du carré. Ensuite, Localisez les quatre intersections de ligne résultantes pour détecter les coins. Le Hough transform est assez robuste au bruit et aux occlusions, il pourrait donc être utile ici. Aussi, ici est une démo interactive montrant comment la Hough transform fonctionne (j'ai pensé que c'était cool au moins :). ici est une de mes réponses précédentes qui détecte un centre de croix laser montrant la plupart de la même maths (sauf qu'il trouve juste un coin simple).
vous aurez probablement plusieurs lignes de chaque côté, mais la localisation des intersections devrait aider à déterminer les valeurs inliers par rapport aux valeurs aberrantes. Une fois que vous avez localisé les coins des candidats, vous pouvez également filtrer ces candidats par zone ou par forme carrée du polygone.
EDIT: toutes ces réponses avec le code et les images ont été me faire penser que ma réponse était un peu manquant :) donc, voici une mise en œuvre de la façon dont vous pourriez faire cela:
#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: la principale raison pour laquelle j'ai redimensionné l'image était pour que je puisse la voir sur mon écran, et accélérer le traitement.
Savant
cela utilise la détection de bord pour aider à réduire considérablement le nombre de lignes détectées après le battage.
Hough transformer
puis la Transformée de Hough est utilisée pour détecter les côtés du carré.
Intersections
enfin, nous calculons les intersections de toutes les paires de lignes.
Espère que ça aide!
j'ai essayé d'utiliser convex hull method
ce qui est assez simple.
ici vous trouvez coque convexe du contour détecté. Il élimine les défauts convexes au fond du papier.
ci-dessous est le code (en 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()
(ici, je n'ai pas trouvé de place dans tous les avions. Le faire vous-même si vous le souhaitez.)
ci-dessous est le résultat que j'ai obtenu:
j'espère que c'est ce dont vous aviez besoin.
1er : commencer à expérimenter avec seuil techniques pour isoler la blanc feuille de papier du reste de l'image. C'est un moyen simple:
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);
mais il existe d'autres solutions qui pourraient donner de meilleurs résultats. L'un est de enquêter inRange()
, et une autre est de détecter par la couleur en convertissant l'image à L'espace de couleur HSV.
Ce fil fournit également un intérêt de la discussion sur le sujet.
2nd : après avoir exécuté l'une de ces procédures, vous pouvez essayer d'alimenter le résultat directement dans find_squares()
:
une alternative à find_squares()
est de mettre en œuvre la technique de la boîte limite , qui a le potentiel de fournir une détection plus précise de la zone rectangulaire (à condition que vous avez un résultat parfait de seuil). Je l'ai utilisé ici et ici . Il est intéressant de noter Qu'OpenCV a son propre bounding box tutorial .
une autre approche que find_squares()
, comme indiqué par Abid sur sa réponse, est d'utiliser la convexHull méthode. Consultez le tutoriel D'OpenCV C++ sur cette méthode pour le code.
- conversion à l'espace de laboratoire
- utiliser des moyens pour 2 grappes
- détecter suqares un cluster interne il permettra de résoudre beaucoup de chose dans l'espace rvb