web-dev-qa-db-fra.com

module 'objet n'a pas d'attribut' drawMatches 'opencv python

Je fais juste un exemple de détection de fonctionnalité dans OpenCV. Cet exemple est illustré ci-dessous. Cela me donne l'erreur suivante

le module 'objet n'a pas d'attribut' drawMatches '

J'ai vérifié les documents OpenCV et je ne sais pas pourquoi j'obtiens cette erreur. Quelqu'un sait-il pourquoi?

import numpy as np
import cv2
import matplotlib.pyplot as plt

img1 = cv2.imread('box.png',0)          # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage

# Initiate SIFT detector
orb = cv2.ORB()

# find the keypoints and descriptors with SIFT
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

# create BFMatcher object
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# Match descriptors.
matches = bf.match(des1,des2)

# Draw first 10 matches.
img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:10], flags=2)

plt.imshow(img3),plt.show()

Erreur:

Traceback (most recent call last):
File "match.py", line 22, in <module>
img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:10], flags=2)
AttributeError: 'module' object has no attribute 'drawMatches'
45
Javed

La fonction drawMatches ne fait pas partie de l'interface Python.
Comme vous pouvez le voir dans docs , il n'est défini que pour C++ en ce moment.

Extrait des documents:

 C++: void drawMatches(const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<DMatch>& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<char>& matchesMask=vector<char>(), int flags=DrawMatchesFlags::DEFAULT )
 C++: void drawMatches(const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<vector<DMatch>>& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<vector<char>>& matchesMask=vector<vector<char> >(), int flags=DrawMatchesFlags::DEFAULT )

Si la fonction avait une interface Python, vous trouveriez quelque chose comme ceci:

 Python: cv2.drawMatches(img1, keypoints1, [...]) 

[~ # ~] modifier [~ # ~]

Il y avait en fait un commit qui a introduit cette fonction il y a 5 mois. Cependant, ce n'est pas (encore) dans la documentation officielle.
Assurez-vous que vous utilisez la dernière version d'OpenCV (2.4.7). Par souci d'exhaustivité, l'interface des fonctions pour OpenCV 3.0.0 ressemblera à this :

cv2.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2[, outImg[, matchColor[, singlePointColor[, matchesMask[, flags]]]]]) → outImg
18
Mailerdaimon

Je suis également en retard à la fête, mais j'ai installé OpenCV 2.4.9 pour Mac OS X, et la fonction drawMatches n'existe pas dans ma distribution. J'ai également essayé la deuxième approche avec find_obj et ça n'a pas marché pour moi non plus. Avec cela, j'ai décidé d'en écrire ma propre implémentation qui imite drawMatches au mieux de mes capacités et c'est ce que j'ai produit.

J'ai fourni mes propres images où l'une est celle d'un caméraman et l'autre est la même image mais tournée de 55 degrés dans le sens antihoraire.

Les bases de ce que j'ai écrit sont que j'alloue une image RVB de sortie où le nombre de lignes est le maximum des deux images pour permettre de placer les deux images dans l'image de sortie et les colonnes sont simplement la somme des deux colonnes ensemble . Sachez que je suppose que les deux images sont en niveaux de gris.

Je place chaque image dans leurs emplacements correspondants, puis je parcours une boucle de tous les points clés correspondants. J'extrais quels points clés correspondaient entre les deux images, puis j'extrayais leur (x,y) coordonnées. Je dessine des cercles à chacun des emplacements détectés, puis je trace une ligne reliant ces cercles entre eux.

Gardez à l'esprit que le point clé détecté dans la deuxième image est par rapport à son propre système de coordonnées. Si vous souhaitez placer cela dans l'image de sortie finale, vous devez décaler les coordonnées de la colonne par le nombre de colonnes de la première image afin que la coordonnée de la colonne soit par rapport au système de coordonnées de l'image de sortie.

Sans plus tarder:

import numpy as np
import cv2

def drawMatches(img1, kp1, img2, kp2, matches):
    """
    My own implementation of cv2.drawMatches as OpenCV 2.4.9
    does not have this function available but it's supported in
    OpenCV 3.0.0

    This function takes in two images with their associated 
    keypoints, as well as a list of DMatch data structure (matches) 
    that contains which keypoints matched in which images.

    An image will be produced where a montage is shown with
    the first image followed by the second image beside it.

    Keypoints are delineated with circles, while lines are connected
    between matching keypoints.

    img1,img2 - Grayscale images
    kp1,kp2 - Detected list of keypoints through any of the OpenCV keypoint 
              detection algorithms
    matches - A list of matches of corresponding keypoints through any
              OpenCV keypoint matching algorithm
    """

    # Create a new output image that concatenates the two images together
    # (a.k.a) a montage
    rows1 = img1.shape[0]
    cols1 = img1.shape[1]
    rows2 = img2.shape[0]
    cols2 = img2.shape[1]

    # Create the output image
    # The rows of the output are the largest between the two images
    # and the columns are simply the sum of the two together
    # The intent is to make this a colour image, so make this 3 channels
    out = np.zeros((max([rows1,rows2]),cols1+cols2,3), dtype='uint8')

    # Place the first image to the left
    out[:rows1,:cols1] = np.dstack([img1, img1, img1])

    # Place the next image to the right of it
    out[:rows2,cols1:] = np.dstack([img2, img2, img2])

    # For each pair of points we have between both images
    # draw circles, then connect a line between them
    for mat in matches:

        # Get the matching keypoints for each of the images
        img1_idx = mat.queryIdx
        img2_idx = mat.trainIdx

        # x - columns
        # y - rows
        (x1,y1) = kp1[img1_idx].pt
        (x2,y2) = kp2[img2_idx].pt

        # Draw a small circle at both co-ordinates
        # radius 4
        # colour blue
        # thickness = 1
        cv2.circle(out, (int(x1),int(y1)), 4, (255, 0, 0), 1)   
        cv2.circle(out, (int(x2)+cols1,int(y2)), 4, (255, 0, 0), 1)

        # Draw a line in between the two points
        # thickness = 1
        # colour blue
        cv2.line(out, (int(x1),int(y1)), (int(x2)+cols1,int(y2)), (255,0,0), 1)


    # Show the image
    cv2.imshow('Matched Features', out)
    cv2.waitKey(0)
    cv2.destroyWindow('Matched Features')

    # Also return the image if you'd like a copy
    return out

Pour illustrer que cela fonctionne, voici les deux images que j'ai utilisées:

Cameraman Image

Rotated Cameraman Image

J'ai utilisé le détecteur ORB d'OpenCV pour détecter les points clés et j'ai utilisé la distance de Hamming normalisée comme mesure de distance pour la similitude car il s'agit d'un descripteur binaire. En tant que tel:

import numpy as np
import cv2

img1 = cv2.imread('cameraman.png', 0) # Original image - ensure grayscale
img2 = cv2.imread('cameraman_rot55.png', 0) # Rotated image - ensure grayscale

# Create ORB detector with 1000 keypoints with a scaling pyramid factor
# of 1.2
orb = cv2.ORB(1000, 1.2)

# Detect keypoints of original image
(kp1,des1) = orb.detectAndCompute(img1, None)

# Detect keypoints of rotated image
(kp2,des2) = orb.detectAndCompute(img2, None)

# Create matcher
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# Do matching
matches = bf.match(des1,des2)

# Sort the matches based on distance.  Least distance
# is better
matches = sorted(matches, key=lambda val: val.distance)

# Show only the top 10 matches - also save a copy for use later
out = drawMatches(img1, kp1, img2, kp2, matches[:10])

Voici l'image que j'obtiens:

Matched Features


À utiliser avec knnMatch de cv2.BFMatcher

Je voudrais noter que le code ci-dessus ne fonctionne que si vous supposez que les correspondances apparaissent dans une liste 1D. Cependant, si vous décidez d'utiliser la méthode knnMatch de cv2.BFMatcher par exemple, ce qui est retourné est une liste de listes. Plus précisément, étant donné les descripteurs dans img1 appelé des1 et les descripteurs dans img2 appelé des2, chaque élément de la liste renvoyé par knnMatch est une autre liste de k correspondances de des2 qui sont les plus proches de chaque descripteur dans des1. Par conséquent, le premier élément de la sortie de knnMatch est une liste de correspondances de k de des2 qui étaient les plus proches du premier descripteur trouvé dans des1. Le deuxième élément de la sortie de knnMatch est une liste de correspondances de k de des2 qui étaient les plus proches du deuxième descripteur trouvé dans des1 etc.

Pour tirer le meilleur parti de knnMatch, vous devez limiter la quantité totale de voisins à faire correspondre à k=2. La raison en est que vous voulez utiliser au moins deux points correspondants pour vérifier la qualité du match et si la qualité est assez bonne, vous voudrez les utiliser pour dessiner vos matchs et les afficher à l'écran. Vous pouvez utiliser un test de rapport très simple (le crédit revient à David Lowe ) pour vous assurer que la distance entre le premier point apparié et des2 au descripteur dans des1 est éloigné par rapport au deuxième point correspondant de des2. Par conséquent, pour transformer ce qui est renvoyé de knnMatch en ce qui est requis avec le code que j'ai écrit ci-dessus, parcourez les correspondances, utilisez le test de rapport ci-dessus et vérifiez s'il passe. Si c'est le cas, ajoutez le premier point clé correspondant à une nouvelle liste.

En supposant que vous avez créé toutes les variables comme vous l'avez fait avant de déclarer l'instance BFMatcher, vous devez maintenant procéder ainsi pour adapter la méthode knnMatch à l'utilisation de drawMatches:

# Create matcher
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# Perform KNN matching
matches = bf.knnMatch(des1, des2, k=2)

# Apply ratio test
good = []
for m,n in matches:
    if m.distance < 0.75*n.distance:
       # Add first matched keypoint to list
       # if ratio test passes
       good.append(m)

# Or do a list comprehension
#good = [m for (m,n) in matches if m.distance < 0.75*n.distance]

# Now perform drawMatches
out = drawMatches(img1, kp1, img2, kp2, good)

Je veux attribuer les modifications ci-dessus à l'utilisateur @ ryanmeasel et la réponse que ces modifications ont été trouvées est dans son message: OpenCV Python: Pas de fonction drawMatchesknn .

78
rayryeng

Je sais que cette question a une réponse acceptée qui est correcte, mais si vous utilisez OpenCV 2.4.8 et non 3.0 (-dev), une solution de contournement pourrait être d'utiliser certaines fonctions des exemples inclus trouvés dans opencv\sources\samples\python2\find_obj

import cv2
from find_obj import filter_matches,explore_match

img1 = cv2.imread('../c/box.png',0)          # queryImage
img2 = cv2.imread('../c/box_in_scene.png',0) # trainImage

# Initiate SIFT detector
orb = cv2.ORB()

# find the keypoints and descriptors with SIFT
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

# create BFMatcher object
bf = cv2.BFMatcher(cv2.NORM_HAMMING)#, crossCheck=True)

matches = bf.knnMatch(des1, trainDescriptors = des2, k = 2)
p1, p2, kp_pairs = filter_matches(kp1, kp2, matches)
explore_match('find_obj', img1,img2,kp_pairs)#cv2 shows image

cv2.waitKey()
cv2.destroyAllWindows()

Voici l'image de sortie:

enter image description here

16
PhilWilliammee