web-dev-qa-db-fra.com

OpenCV C ++ / Obj-C: Détection de carré avancée

Il y a quelque temps, j'ai demandé ne question sur la détection des carrés et karlphillip est arrivé avec un résultat décent.

Maintenant, je veux aller un peu plus loin et trouver des carrés dont Edge n'est pas entièrement visible. Jetez un œil à cet exemple:

example

Des idées? Je travaille avec le 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);
                    }
            }
        }
    }
}
33
dom

Vous pouvez essayer d'utiliser HoughLines pour détecter les quatre côtés du carré. Ensuite, localisez les quatre intersections de lignes résultantes pour détecter les coins. La transformée de Hough est assez robuste au bruit et aux occlusions, elle pourrait donc être utile ici. En outre, ici est une démo interactive montrant comment fonctionne la transformation Hough (je pensais que c'était cool au moins :). Ici est l'une de mes réponses précédentes qui détecte un centre de croix laser montrant la plupart des mêmes mathématiques (sauf qu'il ne trouve qu'un seul coin).

Vous aurez probablement plusieurs lignes de chaque côté, mais la localisation des intersections devrait aider à déterminer les valeurs aberrantes par rapport aux valeurs aberrantes. Une fois que vous avez localisé les coins candidats, vous pouvez également filtrer ces candidats par zone ou par "carré" du polygone.

EDIT: Toutes ces réponses avec du code et des images me faisaient penser que ma réponse manquait un peu :) Alors, voici une implémentation de la façon dont vous pourriez faire cette:

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

REMARQUE: 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.

Prudent

Il utilise la détection Canny Edge pour réduire considérablement le nombre de lignes détectées après le seuillage.

enter image description here

Transformation de Hough

Ensuite, la transformée de Hough est utilisée pour détecter les côtés du carré. enter image description here

Intersections

Enfin, nous calculons les intersections de toutes les paires de lignes. enter image description here

J'espère que cela pourra aider!

43
mevatron

J'ai essayé d'utiliser convex hull method ce qui est assez simple.

Ici vous trouvez la coque convexe du contour détecté. Il supprime les défauts de convexité au bas du papier.

Ci-dessous 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 carré dans tous les plans. Faites-le vous-même si vous le souhaitez.)

Voici le résultat que j'ai obtenu:

enter image description here

J'espère que c'est ce dont vous aviez besoin.

28
Abid Rahman K

1er : commencez à expérimenter avec seuil pour isoler les 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 alternatives qui pourraient fournir de meilleurs résultats. L'une consiste à étudierinRange() , et une autre à détecter à travers la couleur en convertissant l'image en espace colorimétrique HSV.

Ce fil fournit également une discussion d'intérêt sur le sujet.

2ème : après avoir exécuté l'une de ces procédures, vous pouvez essayer de saisir le résultat directement dans find_squares():

Une alternative à find_squares() est d'implémenter la technique du cadre de délimitation , qui a le potentiel de fournir une détection plus précise de la zone rectangulaire (à condition que vous ayez un résultat parfait de seuil) . Je l'ai utilisé ici et ici . Il convient de noter qu'OpenCV a son propre tutoriel de la boîte englobante .

Une autre approche que find_squares(), comme indiqué par Abid dans sa réponse, consiste à utiliser la méthode convexHull . Vérifiez OpenCV tutoriel C++ sur cette méthode pour le code.

6
karlphillip