opencvimage-processingmatchingsimilaritypattern-recognition

Why cv.matchShape is NOT invariant to translation as it claims?


I have two contours to match (think of them as any arbitrary 2D closed curves). opencv claims to have matchShapes function that is invariant under translation, rotation and scale. But it seems to me that this is not the case, when I add shift (10, 5) to one of the curves, the function returns a different result, let alone if I did something whackier. Why is that?

matchShape

Reproducible example:

t = np.arange(0, np.pi, 0.001)
x, y = np.cos(t), np.sin(t)
xy = np.stack([x, y], -1)
print(cv.matchShapes(xy, xy, 1, 0))
print(cv.matchShapes(xy, xy + (2, 10), 1, 0))

Solution

  • The objects you send to cv.matchShapes() need to be contour objects which are different to a straight up 2D numpy array. The following code converts your curves to a plot,

    enter image description here

    then to an image & the contours of the 2 curves are found.

    enter image description here

    Finally cv.matchShapes() is run.

    The output: 0 for the self match & 6.637412841570267e-12 for the match with the translated curve, a pretty accurate match under translation.

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    
    fig  = plt.figure()
    ax   = fig.add_subplot(111)
    t    = np.arange(0, np.pi, 0.001)
    x, y = np.cos(t), np.sin(t)
    ax.plot(x, y)
    
    x_new = x + 2
    y_new = y + 10
    ax.plot(x_new, y_new, 'b')
    
    [s.set_visible(False) for s in ax.spines.values()]
    [t.set_visible(False) for t in ax.get_xticklines()]
    [t.set_visible(False) for t in ax.get_yticklines()]
    ax.axis('off')
    
    plt.savefig('xy.jpg')
    
    xy_img          = cv.imread('xy.jpg',  cv.IMREAD_COLOR)
    xy_cpy          = cv.cvtColor(xy_img,   cv.COLOR_BGR2GRAY)
    (threshold, bw) = cv.threshold(xy_cpy, 127, 255, cv.THRESH_BINARY)
    contours, hier  = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
    contours        = contours[0:-1] # remove box surounding whole image
    
    print(cv.matchShapes(contours[0], contours[0], method=cv.CONTOURS_MATCH_I1, parameter=0))
    print(cv.matchShapes(contours[0], contours[1], method=cv.CONTOURS_MATCH_I1, parameter=0))
    
    cv.namedWindow("xy")
    cv.drawContours(xy_img, contours, -1, (0, 255, 0), 3)
    cv.imshow("xy", xy_img)
    
    cv.waitKey()