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:

example

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);
                    }
            }
        }
    }
}
31
demandé sur Community 2012-05-10 15:40:55

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.

enter image description here

Hough transformer

puis 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

Espère que ça aide!

41
répondu mevatron 2017-05-23 12:34:25

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:

enter image description here

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

27
répondu Abid Rahman K 2012-05-10 14:27:40

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.

6
répondu karlphillip 2017-05-23 12:34:25
  1. conversion à l'espace de laboratoire
  2. utiliser des moyens pour 2 grappes
  3. détecter suqares un cluster interne il permettra de résoudre beaucoup de chose dans l'espace rvb
-1
répondu user3452134 2014-10-29 08:31:09