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