Search code examples
pythonopencv

Why does Keypoint_convert fail if applied twice?


I am trying to do operations on point descriptors on an image, and while doing so it's useful to get a numpy representation of the 2D coordinates of my keypoints, applying some linear transformation to it and convert it back to a list of cv2.KeyPoint objects.

I seem to fail to do so, and my problem seems to boil down to the fact that in the code

import cv2
import imutils

img = imutils.url_to_image('https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png') 
detector = cv2.AKAZE_create()
gray_image = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
kpts, desc = detector.detectAndCompute(gray_image, None)
coords = cv2.KeyPoint_convert(kpts)

# here I would to stuff with coords   
# and then finally 
transformed_kpts = cv2.KeyPoint_convert(coords)

the last line fails to execute.

Simplifying things even further, doing something as simple as

cv2.KeyPoint_convert(cv2.KeyPoint_convert(kpts))

crashes with

OpenCV(4.1.2) /io/opencv/modules/core/src/copy.cpp:254: error: (-215:Assertion failed) channels() == CV_MAT_CN(dtype) in function 'copyTo'

Is this a bug or am I doing something fundamentally wrong?

From the docs it looks to me that the applying the function twice on a list of keypoints should run fine and give me back the original list.

My cv2.__version__ is 4.1.2.

EDIT: This doesn't reproduce with a more recent version of python-opencv like 4.8.0. Upgrading to a more recent version results makes the code behave as I expected.


Solution

  • The output of cv2.KeyPoint_convert(kpts) -> pts2f is a (n,2) ndarray
    The input of cv2.KeyPoint_convert(pts2f) -> kpts should be a (n,1,2) ndarray

    Therefore, to run your snippet, you need to reshape the first output as in

    kpts, desc = detector.detectAndCompute(gray_image, None)
    coords = cv2.KeyPoint_convert(kpts)
    transformed_kpts = cv2.KeyPoint_convert(coords.reshape(len(kpts), 1, 2))
    

    You can verify that kpts and transformed_kpts are the same with

    all([transformed_kpts[i].pt == kpts[i].pt for i in range(len(kpts))])