3
votes

OpenCV: utiliser Canny et Shi-Tomasi pour détecter les coins arrondis d'une carte à jouer

Je veux faire une rectification planaire, pour convertir de gauche à droite: entrez la description de l'image ici

J'ai le code pour faire la correction, mais j'ai besoin des 4 coins de coin.

J'utilise le code suivant pour les trouver:

import cv2

image = cv2.imread('input.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray, 120, 255, 1)

corners = cv2.goodFeaturesToTrack(canny,4,0.5,50)

for corner in corners:
    x,y = corner.ravel()
    cv2.circle(image,(x,y),5,(36,255,12),-1)

cv2.imshow("result", image)
cv2.waitKey()

Il lit l'image et la transforme en niveaux de gris + astucieux entrez la description de l'image ici

Mais les coins résultants (trouvés par cv2.goodFeaturesToTrack) ne sont pas ceux souhaités: entrez la description de l'image ici

J'ai besoin des coins externes de la carte, un indice pour y parvenir?

Merci

Voici le fichier input.png:

entrez la description de l'image ici


2 commentaires

Pourquoi le détecteur goodFeaturesToTrack devinerait-il que vous voulez les quarts de cercle?


Veuillez ne pas publier de captures d'écran. Publiez des images originales sans bordures, etc. et séparément.


3 Réponses :


1
votes

Canny est un outil de détection des bords, et s'il est correctement réglé, il fait ce qu'il dit sur l'étain.

Une fois que vous obtenez les bords, vous devez définir ce qu'est un coin. Par exemple, est-ce un virage serré dans un bord?

Vous souhaitez utiliser la fonction cv2.goodFeaturesToTrack , qui est censée être un outil de détection de coin , mais encore une fois, qu'est-ce qu'un coin? Il utilise l'algorithme Shi-Tomasi pour trouver les N «meilleurs» coins d'une image, qui n'est qu'un seuil, et une distance minimale entre les points.

En fin de compte, il est garanti de ne presque jamais supporter les quatre coins que vous voulez. Vous devriez essayer ces alternatives et vous en tenir à la meilleure option:

  1. essayez d'obtenir plus de coins et déterminez géométriquement les quatre "les plus éloignés".

  2. combinez votre méthode avec une autre transformation ou correspondance d'objet. Par exemple, si vous recherchez une image rectangulaire, essayez de la faire correspondre à un modèle, calculez la matrice de transformation et résolvez les bords après la transformation.

  3. utilisez une autre méthode de détection des contours ou une combinaison de méthodes.

Notez qu'une carte n'a pas de coins pointus comme un morceau de papier, vous finirez donc par rogner la carte ou l'incliner si vous utilisez un "coin" sur les bords arrondis ou si vous essayez de localiser un bord en dehors du "blanc" réel "de la carte, pour éviter le biais (essayez d'inscrire la carte dans un rectangle aux bords nets) - notez que Canny n'est pas efficace dans ce cas.


0 commentaires

0
votes

Mise à jour: Ajout de la transformation de perspective en quatre points.

J'ai sauté la transformation de perspective car la question est de trouver les bons coins.

Vous pouvez sauter la boucle en obtenant un contour avec maximum area puis en le traitant. Un peu de flou peut l'aider davantage. Appuyez sur le bouton Esc pour obtenir la sortie d'image suivante.

Une autre méthode utile, comment trouver les coins des coins d'une forme dans une image dans opencv?

Images de sortie

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

Code

"""
Task: Detect card corners and fix perspective
"""


import cv2
import numpy as np


img = cv2.imread('resources/KSuVq.png')


gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,255,0)


cv2.imshow('Thresholded original',thresh)
cv2.waitKey(0)



## Get contours
contours,h = cv2.findContours(thresh,cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)


## only draw contour that have big areas
imx = img.shape[0]
imy = img.shape[1]
lp_area = (imx * imy) / 10



#################################################################
# Four point perspective transform
# https://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/
#################################################################

def order_points(pts):
    # initialzie a list of coordinates that will be ordered
    # such that the first entry in the list is the top-left,
    # the second entry is the top-right, the third is the
    # bottom-right, and the fourth is the bottom-left
    rect = np.zeros((4, 2), dtype = "float32")
    # the top-left point will have the smallest sum, whereas
    # the bottom-right point will have the largest sum
    s = pts.sum(axis = 1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]
    # now, compute the difference between the points, the
    # top-right point will have the smallest difference,
    # whereas the bottom-left will have the largest difference
    diff = np.diff(pts, axis = 1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    # return the ordered coordinates
    return rect


def four_point_transform(image, pts):
    # obtain a consistent order of the points and unpack them
    # individually
    rect = order_points(pts)
    (tl, tr, br, bl) = rect
    # compute the width of the new image, which will be the
    # maximum distance between bottom-right and bottom-left
    # x-coordiates or the top-right and top-left x-coordinates
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))
    # compute the height of the new image, which will be the
    # maximum distance between the top-right and bottom-right
    # y-coordinates or the top-left and bottom-left y-coordinates
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))
    # now that we have the dimensions of the new image, construct
    # the set of destination points to obtain a "birds eye view",
    # (i.e. top-down view) of the image, again specifying points
    # in the top-left, top-right, bottom-right, and bottom-left
    # order
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype = "float32")
    # compute the perspective transform matrix and then apply it
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    # return the warped image
    return warped


#################################################################


## Get only rectangles given exceeding area
for cnt in contours:
    approx = cv2.approxPolyDP(cnt,0.01 * cv2.arcLength(cnt, True), True)
    ## calculate number of vertices
    #print(len(approx))


    if len(approx) == 4 and cv2.contourArea(cnt) > lp_area:
        print("rectangle")

        tmp_img = img.copy()
        cv2.drawContours(tmp_img, [cnt], 0, (0, 255, 255), 6)
        cv2.imshow('Contour Borders', tmp_img)
        cv2.waitKey(0)


        tmp_img = img.copy()
        cv2.drawContours(tmp_img, [cnt], 0, (255, 0, 255), -1)
        cv2.imshow('Contour Filled', tmp_img)
        cv2.waitKey(0)


        # Make a hull arround the contour and draw it on the original image
        tmp_img = img.copy()
        mask = np.zeros((img.shape[:2]), np.uint8)
        hull = cv2.convexHull(cnt)
        cv2.drawContours(mask, [hull], 0, (255, 255, 255), -1)
        cv2.imshow('Convex Hull Mask', mask)
        cv2.waitKey(0)


        # Draw minimum area rectangle
        tmp_img = img.copy()
        rect = cv2.minAreaRect(cnt)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        cv2.drawContours(tmp_img, [box], 0, (0, 0, 255), 2)
        cv2.imshow('Minimum Area Rectangle', tmp_img)
        cv2.waitKey(0)


        # Draw bounding rectangle
        tmp_img = img.copy()
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(tmp_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.imshow('Bounding Rectangle', tmp_img)
        cv2.waitKey(0)


        # Bounding Rectangle and Minimum Area Rectangle
        tmp_img = img.copy()
        rect = cv2.minAreaRect(cnt)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        cv2.drawContours(tmp_img, [box], 0, (0, 0, 255), 2)
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(tmp_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.imshow('Bounding Rectangle', tmp_img)
        cv2.waitKey(0)


        # determine the most extreme points along the contour
        # https://www.pyimagesearch.com/2016/04/11/finding-extreme-points-in-contours-with-opencv/
        tmp_img = img.copy()
        extLeft = tuple(cnt[cnt[:, :, 0].argmin()][0])
        extRight = tuple(cnt[cnt[:, :, 0].argmax()][0])
        extTop = tuple(cnt[cnt[:, :, 1].argmin()][0])
        extBot = tuple(cnt[cnt[:, :, 1].argmax()][0])
        cv2.drawContours(tmp_img, [cnt], -1, (0, 255, 255), 2)
        cv2.circle(tmp_img, extLeft, 8, (0, 0, 255), -1)
        cv2.circle(tmp_img, extRight, 8, (0, 255, 0), -1)
        cv2.circle(tmp_img, extTop, 8, (255, 0, 0), -1)
        cv2.circle(tmp_img, extBot, 8, (255, 255, 0), -1)


        print("Corner Points: ", extLeft, extRight, extTop, extBot)


        cv2.imshow('img contour drawn', tmp_img)
        cv2.waitKey(0)
        #cv2.destroyAllWindows()



        ## Perspective Transform
        tmp_img = img.copy()
        pts = np.array([extLeft, extRight, extTop, extBot])
        warped = four_point_transform(tmp_img, pts)
        cv2.imshow("Warped", warped)
        cv2.waitKey(0)


cv2.destroyAllWindows()

Références

https://docs.opencv.org/4.5.0/dd/d49/tutorial_py_contour_features.html

https://www.pyimagesearch.com/2016/04/11/finding-extreme-points-in-contours-with-opencv/

https://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/


0 commentaires

0
votes

Voici une façon de trouver les coins dans Python OpenCV. Je note que c'est plus compliqué car les points verts sur l'entrée compliquent le problème et ils ne seraient probablement pas dans l'image d'entrée. On pourrait simplement limiter les points verts en utilisant cv2.inRange () pour trouver les points verts. Mais je suppose que ce n'est pas vraiment ce que vous voulez.

306 167
42 149
114 283
227 69

Contribution:

entrez la description de l'image ici

227 69
41 149
114 284
307 167
228 70

Contours et polygone sur l'image d'entrée:

entrez la description de l'image ici

Contours et polygone sur fond noir:

entrez la description de l'image ici

Sommets du polygone:

import cv2
import numpy as np
import time


# load image
img = cv2.imread("hello.png")

# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)[1]

# get the largest contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
peri = cv2.arcLength(big_contour, True)

# draw contour on input in red
result = img.copy()
result2 = np.zeros_like(img)
cv2.drawContours(result, [big_contour], 0, (0,0,255), 1)
cv2.drawContours(result2, [big_contour], 0, (0,0,255), 1)

# reduce to fewer vertices on polygon
poly = cv2.approxPolyDP(big_contour, 0.1 * peri, False)

# draw polygon on input in green
cv2.polylines(result, [poly], False, (0,255,0), 1)
cv2.polylines(result2, [poly], False, (0,255,0), 1)

# list polygon points
print("Polygon Points:")
for p in poly:
    px = p[0][0]
    py = p[0][1]
    print(px,py)

print('')

# draw white filled polygon on black background
result3 = np.zeros_like(thresh)
cv2.fillPoly(result3,[poly],255)

# get corners
corners = cv2.goodFeaturesToTrack(result3,4,0.01,50,useHarrisDetector=True,k=0.04)

# print corner coords and draw circles
result3 = cv2.merge([result3,result3,result3])
print("Corners:")
for c in corners:
    x,y = c.ravel()
    print(int(x), int(y))
    cv2.circle(result3,(x,y),3,(0,0,255),-1)

# save result
cv2.imwrite("hello_contours.png", result)
cv2.imwrite("hello_polygon.png", result2)
cv2.imwrite("hello_corners.png", result3)

# display it
cv2.imshow("thresh", thresh)
cv2.imshow("result", result)
cv2.imshow("result2", result2)
cv2.imshow("result3", result3)
cv2.waitKey(0)

Notez que les premier et dernier sommets sont à moins d'un pixel l'un de l'autre

Coins sur polygone blanc sur fond noir:

entrez la description de l'image ici

Sommets d'angle:

 - Read the input
 - Convert to gray
 - Threshold
 - Get the largest contour and draw it on the input
 - Reduce the number of vertices in the contour as a polygon and draw the polygon on the input.
 - The polygon has 5 vertices and two are virtually the same. Normally, one would get 4 verices if the green dots were not there. So draw a white filled polygon on a black background.
 - Get the corners from the white polygon on black background and draw on these vertices
 - Save the results


0 commentaires