web-dev-qa-db-fra.com

Comment trouver des coins sur une image en utilisant OpenCv

J'essaie de trouver les coins sur une image, je n'ai pas besoin des contours, seulement les 4 coins. Je vais changer la perspective en utilisant 4 coins.

J'utilise Opencv, mais j'ai besoin de connaître les étapes pour trouver les coins et la fonction que j'utiliserai.

Mes images seront comme ceci: (sans points rouges, je peindrai les points après) enter image description here

ÉDITÉ:

Après les étapes suggérées, j'ai écrit le code: (Remarque: je n'utilise pas OpenCv pur, j'utilise javaCV, mais la logique est la même).

// Load two images and allocate other structures (I´m using other image)
    IplImage colored = cvLoadImage(
            "res/scanteste.jpg",
            CV_LOAD_IMAGE_UNCHANGED);

enter image description here

    IplImage gray = cvCreateImage(cvGetSize(colored), IPL_DEPTH_8U, 1);
    IplImage smooth = cvCreateImage(cvGetSize(colored), IPL_DEPTH_8U, 1);

    //Step 1 - Convert from RGB to grayscale (cvCvtColor)
    cvCvtColor(colored, gray, CV_RGB2GRAY);

enter image description here

    //2 Smooth (cvSmooth)
    cvSmooth( gray, smooth, CV_BLUR, 9, 9, 2, 2); 

enter image description here

    //3 - cvThreshold  - What values?
    cvThreshold(gray,gray, 155, 255, CV_THRESH_BINARY);

enter image description here

    //4 - Detect edges (cvCanny) -What values?
    int N = 7;
    int aperature_size = N;
    double lowThresh = 20;
    double highThresh = 40;     
    cvCanny( gray, gray, lowThresh*N*N, highThresh*N*N, aperature_size );   

enter image description here

    //5 - Find contours (cvFindContours)
    int total = 0;
    CvSeq contour2 = new CvSeq(null);
    CvMemStorage storage2 = cvCreateMemStorage(0);
    CvMemStorage storageHull = cvCreateMemStorage(0);
    total = cvFindContours(gray, storage2, contour2, Loader.sizeof(CvContour.class), CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
    if(total > 1){
          while (contour2 != null && !contour2.isNull()) {
              if (contour2.elem_size() > 0) {
                //6 - Approximate contours with linear features (cvApproxPoly)
                  CvSeq points = cvApproxPoly(contour2,Loader.sizeof(CvContour.class), storage2, CV_POLY_APPROX_DP,cvContourPerimeter(contour2)*0.005, 0);
                  cvDrawContours(gray, points,CvScalar.BLUE, CvScalar.BLUE, -1, 1, CV_AA);

              }
              contour2 = contour2.h_next();
          }

    } 

enter image description here

Donc, je veux trouver les cornes, mais je ne sais pas comment utiliser les fonctions de coins comme cvCornerHarris et autres.

39
Ricardo

Tout d'abord, consultez /samples/c/squares.c dans votre distribution OpenCV. Cet exemple fournit un détecteur carré, et cela devrait être un bon début sur la façon de détecter les caractéristiques de type coin. Ensuite, jetez un œil aux fonctions orientées fonctionnalités d'OpenCV comme cvCornerHarris () et cvGoodFeaturesToTrack ().

Les méthodes ci-dessus peuvent renvoyer beaucoup des fonctionnalités de type coin - la plupart ne seront pas les "vrais coins" que vous recherchez. Dans ma demande, j'ai dû détecter des carrés qui avaient été tournés ou inclinés (en raison de la perspective). Mon pipeline de détection était composé de:

  1. Conversion de RVB en niveaux de gris (cvCvtColor)
  2. Lisse (cvSmooth)
  3. Seuil (cvThreshold)
  4. Détecter les bords (cvCanny)
  5. Rechercher des contours (cvFindContours)
  6. Contours approximatifs avec des caractéristiques linéaires (cvApproxPoly)
  7. Trouvez des "rectangles" qui étaient des structures qui: avaient des contours polygonalisés possédant 4 points, avaient une surface suffisante, avaient des bords adjacents d'environ 90 degrés, avaient une distance entre les sommets "opposés" était de taille suffisante, etc.

L'étape 7 était nécessaire car une image légèrement bruyante peut produire de nombreuses structures qui semblent rectangulaires après la polygonalisation. Dans ma candidature, j'ai également dû faire face à des structures de type carré apparaissant à l'intérieur ou chevauchant le carré souhaité. J'ai trouvé que la propriété de surface et le centre de gravité du contour étaient utiles pour discerner le rectangle approprié.

34
Throwback1986

À première vue, pour un œil humain, il y a 4 coins. Mais en vision par ordinateur, un coin est considéré comme un point qui a un grand changement de gradient d'intensité à travers son voisinage. Le voisinage peut être un voisinage de 4 pixels ou un voisinage de 8 pixels.

enter image description here

Dans l'équation fournie pour trouver le gradient d'intensité, il a été considéré pour le voisinage à 4 pixels VOIR DOCUMENTATION .

Voici mon approche pour l'image en question. J'ai le code dans python aussi:

path = r'C:\Users\selwyn77\Desktop\Stack\corner'
filename = 'env.jpg'

img = cv2.imread(os.path.join(path, filename))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)    #--- convert to grayscale 

C'est un bon choix de toujours brouiller l'image pour supprimer le moins de changements de dégradés possibles et conserver les plus intenses. J'ai choisi de choisir le filtre bilatéral qui contrairement au filtre gaussien ne brouille pas tous les pixels du voisinage. Il brouille plutôt les pixels qui ont une intensité de pixel similaire à celle du pixel central. En bref, il préserve les bords/coins des changements de gradient élevés mais brouille les régions qui ont des changements de gradient minimaux.

bi = cv2.bilateralFilter(gray, 5, 75, 75)
cv2.imshow('bi',bi)

enter image description here

Pour un humain, ce n'est pas tellement une différence par rapport à l'image d'origine. Mais ça compte. Trouver maintenant les coins possibles:

dst = cv2.cornerHarris(bi, 2, 3, 0.04)

dst renvoie un tableau (la même forme 2D de l'image) avec des valeurs propres obtenues à partir de l'équation finale mentionnée ICI .

Maintenant, un seuil doit être appliqué pour sélectionner ces coins au-delà d'une certaine valeur. J'utiliserai celui de la documentation:

#--- create a black image to see where those corners occur ---
mask = np.zeros_like(gray)

#--- applying a threshold and turning those pixels above the threshold to white ---           
mask[dst>0.01*dst.max()] = 255
cv2.imshow('mask', mask)

enter image description here

Les pixels blancs sont des régions de coins possibles. Vous pouvez trouver de nombreux coins voisins.

Pour dessiner les coins sélectionnés sur l'image:

img[dst > 0.01 * dst.max()] = [0, 0, 255]   #--- [0, 0, 255] --> Red ---
cv2.imshow('dst', img)

enter image description here

(Les pixels de couleur rouge sont les coins, pas si visibles)

Afin d'obtenir un tableau de tous les pixels avec des coins:

coordinates = np.argwhere(mask)

MISE À JOUR

La variable coor est un tableau de tableaux. Conversion en liste de listes

coor_list = [l.tolist() for l in list(coor)]

Conversion de ce qui précède en liste de tuples

coor_tuples = [Tuple(l) for l in coor_list]

J'ai un moyen simple et plutôt naïf de trouver les 4 coins. J'ai simplement calculé la distance de chaque coin à chaque autre coin. J'ai conservé ces coins dont la distance dépassait un certain seuil.

Voici le code:

thresh = 50

def distance(pt1, pt2):
    (x1, y1), (x2, y2) = pt1, pt2
    dist = math.sqrt( (x2 - x1)**2 + (y2 - y1)**2 )
    return dist

coor_tuples_copy = coor_tuples

i = 1    
for pt1 in coor_tuples:

    print(' I :', i)
    for pt2 in coor_tuples[i::1]:
        print(pt1, pt2)
        print('Distance :', distance(pt1, pt2))
        if(distance(pt1, pt2) < thresh):
            coor_tuples_copy.remove(pt2)      
    i+=1

Avant d'exécuter l'extrait de code ci-dessus, coor_tuples Avait tous les points d'angle: [(4, 42), (4, 43), (5, 43), (5, 44), (6, 44), (7, 219), (133, 36), (133, 37), (133, 38), (134, 37), (135, 224), (135, 225), (136, 225), (136, 226), (137, 225), (137, 226), (137, 227), (138, 226)]

Après avoir exécuté l'extrait de code, je suis resté avec 4 coins:

[(4, 42), (7, 219), (133, 36), (135, 224)]

MISE À JOUR 2

Il ne vous reste plus qu'à marquer ces 4 points sur une copie de l'image d'origine.

img2 = img.copy()
for pt in coor_tuples:
    cv2.circle(img2, Tuple(reversed(pt)), 3, (0, 0, 255), -1)
cv2.imshow('Image with 4 corners', img2) 

enter image description here

13
Jeru Luke
  1. rechercher les contours avec l'option RETR_EXTERNAL. (gris -> filtre gaussien -> Bord cany -> trouver le contour)
  2. trouver le contour le plus grand -> ce sera le bord du rectangle
  3. trouver des coins avec peu de calcul

    Mat m;//image file
    findContours(m, contours_, hierachy_, RETR_EXTERNAL);
    auto it = max_element(contours_.begin(), contours_.end(),
        [](const vector<Point> &a, const vector<Point> &b) {
            return a.size() < b.size(); });
    Point2f xy[4] = {{9000,9000}, {0, 1000}, {1000, 0}, {0,0}};
    for(auto &[x, y] : *it) {
        if(x + y < xy[0].x + xy[0].y) xy[0] = {x, y};
        if(x - y > xy[1].x - xy[1].y) xy[1] = {x, y};
        if(y - x > xy[2].y - xy[2].x) xy[2] = {x, y};
        if(x + y > xy[3].x + xy[3].y) xy[3] = {x, y};
     }
    

xy [4] sera les quatre coins. J'ai pu extraire quatre coins de cette façon.

0
Zeta