Last active
April 21, 2025 19:07
-
-
Save woolpeeker/d7e1821e1b5c556b32aafe10b7a1b7e8 to your computer and use it in GitHub Desktop.
Python replacement for cv2.drawMatches(), for which there are no Python bindings in a release version.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# drawMatches numpy version | |
def draw_matches(img1, kp1, img2, kp2, matches, color=None): | |
"""Draws lines between matching keypoints of two images. | |
Keypoints not in a matching pair are not drawn. | |
Places the images side by side in a new image and draws circles | |
around each keypoint, with line segments connecting matching pairs. | |
You can tweak the r, thickness, and figsize values as needed. | |
Args: | |
img1: An openCV image ndarray in a grayscale or color format. | |
kp1: ndarray [n1, 2] | |
img2: An openCV image ndarray of the same format and with the same | |
element type as img1. | |
kp2: ndarray [n2, 2] | |
matches: ndarray [n_match, 2] | |
img1 keypoints and whose queryIdx attribute refers to img2 keypoints. | |
color: The color of the circles and connecting lines drawn on the images. | |
A 3-tuple for color images, a scalar for grayscale images. If None, these | |
values are randomly generated. | |
""" | |
# We're drawing them side by side. Get dimensions accordingly. | |
# Handle both color and grayscale images. | |
if len(img1.shape) == 3: | |
new_shape = (max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], img1.shape[2]) | |
elif len(img1.shape) == 2: | |
new_shape = (max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1]) | |
new_img = np.zeros(new_shape, type(img1.flat[0])) | |
# Place images onto the new image. | |
new_img[0:img1.shape[0],0:img1.shape[1]] = img1 | |
new_img[0:img2.shape[0],img1.shape[1]:img1.shape[1]+img2.shape[1]] = img2 | |
# Draw lines between matches. Make sure to offset kp coords in second image appropriately. | |
r = 15 | |
thickness = 2 | |
if color: | |
c = color | |
for m in matches: | |
# Generate random color for RGB/BGR and grayscale images as needed. | |
if not color: | |
c = np.random.randint(0,256,3) if len(img1.shape) == 3 else np.random.randint(0,256) | |
# So the keypoint locs are stored as a tuple of floats. cv2.line(), like most other things, | |
# wants locs as a tuple of ints. | |
end1 = tuple(np.round(kp1[m[0]]).astype(int)) | |
end2 = tuple(np.round(kp2[m[1]]).astype(int) + np.array([img1.shape[1], 0])) | |
cv2.line(new_img, end1, end2, c, thickness) | |
cv2.circle(new_img, end1, r, c, thickness) | |
cv2.circle(new_img, end2, r, c, thickness) | |
return new_img | |
# drawMatches cv2 version | |
def draw_matches(img1, kp1, img2, kp2, matches, color=None): | |
"""Draws lines between matching keypoints of two images. | |
Keypoints not in a matching pair are not drawn. | |
Places the images side by side in a new image and draws circles | |
around each keypoint, with line segments connecting matching pairs. | |
You can tweak the r, thickness, and figsize values as needed. | |
Args: | |
img1: An openCV image ndarray in a grayscale or color format. | |
kp1: A list of cv2.KeyPoint objects for img1. | |
img2: An openCV image ndarray of the same format and with the same | |
element type as img1. | |
kp2: A list of cv2.KeyPoint objects for img2. | |
matches: A list of DMatch objects whose trainIdx attribute refers to | |
img1 keypoints and whose queryIdx attribute refers to img2 keypoints. | |
color: The color of the circles and connecting lines drawn on the images. | |
A 3-tuple for color images, a scalar for grayscale images. If None, these | |
values are randomly generated. | |
""" | |
# We're drawing them side by side. Get dimensions accordingly. | |
# Handle both color and grayscale images. | |
if len(img1.shape) == 3: | |
new_shape = (max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], img1.shape[2]) | |
elif len(img1.shape) == 2: | |
new_shape = (max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1]) | |
new_img = np.zeros(new_shape, type(img1.flat[0])) | |
# Place images onto the new image. | |
new_img[0:img1.shape[0],0:img1.shape[1]] = img1 | |
new_img[0:img2.shape[0],img1.shape[1]:img1.shape[1]+img2.shape[1]] = img2 | |
# Draw lines between matches. Make sure to offset kp coords in second image appropriately. | |
r = 15 | |
thickness = 2 | |
if color: | |
c = color | |
for m in matches: | |
# Generate random color for RGB/BGR and grayscale images as needed. | |
if not color: | |
c = np.random.randint(0,256,3) if len(img1.shape) == 3 else np.random.randint(0,256) | |
# So the keypoint locs are stored as a tuple of floats. cv2.line(), like most other things, | |
# wants locs as a tuple of ints. | |
end1 = tuple(np.round(kp1[m.trainIdx].pt).astype(int)) | |
end2 = tuple(np.round(kp2[m.queryIdx].pt).astype(int) + np.array([img1.shape[1], 0])) | |
cv2.line(new_img, end1, end2, c, thickness) | |
cv2.circle(new_img, end1, r, c, thickness) | |
cv2.circle(new_img, end2, r, c, thickness) | |
plt.figure(figsize=(15,15)) | |
plt.imshow(new_img) | |
plt.show() |
Author
woolpeeker
commented
Dec 22, 2023
via email
No, you can use it any way you want 😉
oaa3wf ***@***.***> 于2023年12月22日周五 03:25写道:
… ***@***.**** commented on this gist.
------------------------------
@woolpeeker <https://github.com/woolpeeker> , is there a license attached
to this piece of code?
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/woolpeeker/d7e1821e1b5c556b32aafe10b7a1b7e8#gistcomment-4803211>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEWQJOMTXLT3NLK7Q4NQ3VLYKSEI5BFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTAOBYGY4TEMZTU52HE2LHM5SXFJTDOJSWC5DF>
.
You are receiving this email because you were mentioned.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment