Keypoint detection: drawMatches on multiple images / confusion over DRAW_OVER_OUTIMG flag

I’m using opencv-python, and I am trying to visualize results from AKAZE keypoint matching for multiple images. While playing around with drawMatches, several questions arose. Below is the code, and my questions:

import cv2

template_image cv2.imread("img1.tif")
target_image cv2.imread("img2.tif")

akaze = cv2.AKAZE_create()
kp1, des1 = akaze.detectAndCompute(template_image, None)
kp2, des2 = akaze.detectAndCompute(target_image, None)
matcher = cv2.DescriptorMatcher_create(cv2.DescriptorMatcher_BRUTEFORCE_HAMMING)
matches = matcher.knnMatch(des1, des2, 2)

good = []
for m, n in matches:
    if m.distance < 0.8 * n.distance:
        good.append(m)

results = cv2.drawMatches(template_image,kp1,
                          target_image,kp2,
                          good,None,
                          (0,255,0),
                          flags=1)
  1. What is the correct use of the DRAW_OVER_OUTIMG flag (flag “1”)? I tried the following, but I get an error:
    results = cv2.drawMatches(template_image, kp1, target_image, kp2, good, target_image, flags=1)

    error: OpenCV(3.4.9) C:\projects\opencv-python\opencv\modules\features2d\src\draw.cpp:156: error: (-201:Incorrect size of input array) outImg has size less than need to draw img1 and img2 together in function 'cv::_prepareImgAndDrawKeypoints'

  1. drawMatches takes takes only two images, but I would like to visualize keypoint matches between the template and multiple target images - is there any way to do this?

  2. Can I change the image-matrix layout in drawMatches?

Change this

results = cv2.drawMatches(template_image, kp1, target_image, kp2, good, target_image, flags=1)

to:

results = cv2.drawMatches(template_image, kp1, target_image, kp2, good, None, flags=1)

no effect - I get the same error

What if you change flags 0 to 4?
Btw, If you could providing source code. So, everyone can see what wrong.

I added the code in the OP

when I change 0 to 4 I get the rich keypoints output (as per OpenCV: cv::DrawMatchesFlags Struct Reference)

I guess what I am also asking with Q1 is: what I do I get when I use DRAW_OVER_OUTIMG?

I am basically just trying to find better means of visualizing keypoint detection results from one template in multiple target images

You are missing cv2.BFMatche before knnMatch. This is what I have in below

import cv2 
import numpy as np 

img_comp = cv2.imread("image1.jpg")
img_ref  = cv2.imread("image2.jpg")

gray_img_comp = cv2.cvtColor(img_comp, cv2.COLOR_BGR2GRAY)
gray_img_ref  = cv2.cvtColor(img_ref, cv2.COLOR_BGR2GRAY)

akaze = cv2.AKAZE_create() 

kp1, des1 = akaze.detectAndCompute(gray_img_comp, None) 
kp2, des2 = akaze.detectAndCompute(gray_img_ref, None)

bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False)

matches = bf.knnMatch(des1, des2, k=2) 

ratio = 0.5 
good = [] 
for m, n in matches: 
    if m.distance < ratio * n.distance: 
        good.append(m) 

img_result = cv2.drawMatches(img_comp,
                             kp1,
                             img_ref,
                             kp2,
                             good,
                             None,
                             flags=2) 

cv2.namedWindow("Result", cv2.WINDOW_NORMAL)
cv2.imshow('Result', img_result) 
cv2.waitKey(0) 
cv2.destroyAllWindows() 

Btw, You should change flags=2

thanks - but, sorry, I think you misunderstood me: it was working fine with any of the flags before, except for flag 1 ( DRAW_OVER_OUTIMG: Output image matrix will not be created (Mat::create). Matches will be drawn on existing content of output image.)

what does this do?

You need upgrading 3.4.9 to 4.5.1. The 3.4.9 is an obsoleted. OpenCV 4.5.2 in later than June, 2021. I am using 4.5.1. Sorry, mlurgig.

ok, thanks for your help!

is for the case, where you already have a preallocated image, that you want to reuse for drawing. it should have outimg.rows == a.rows == b.rows and outimg.cols = a.cols + b.cols (so they both fit in horizontally next to each other)

you’d also have to pass this image as argument 6 (where you have None)

if you don’t have such an image, pass None as outimg and use the DEFAULT flag, so it will automatically be allocated

and no, you cannot do this with multiple query images at the same time

1 Like

thanks that clarified it!

1 Like

it’s also a very good idea to NEVER use magic numbers. use the named constants that exist.

cv2.DrawMatchesFlags_DRAW_OVER_OUTIMG

these identifiers aren’t easy to find but it’s possible:

>>> [x for x in dir(cv2) if 'DrawMatchesFlags' in x]
['DrawMatchesFlags_DEFAULT', 'DrawMatchesFlags_DRAW_OVER_OUTIMG', 'DrawMatchesFlags_DRAW_RICH_KEYPOINTS', 'DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS']
1 Like

yupp, I did use them initially, but while playing around and making a minimal example for this post I got lazy :slight_smile:

About 2 and 3:

drawMatches is a convenience function: it is easy but not flexible. And because of this often you come out with your own implementation. drawMatches takes only two images, and that’s it.

The function can’t change the layout. If you want a vertical layout, you can rotate both input images, rotate matches, and rotate back the output image. Not nice nor clean.

You can change the layout by generating your own output image and manipulating matches coordinates. Then you are one step away from making your own implementation.

Note: The main purpose of DRAW_OVER_OUTIMG is to let you draw different matches lines with different colors, like green for inliers and red for outliers. With this flag the function already needs input images to get the output coordinates of the second image on the output one.

1 Like