OpenCV Perspective Transform giving unexpected result

I am trying to transform from a trapezoid (in the first image) to a rectangle (in the second image), but getting a strange result (in the third image).

enter image description here

My plan was to use a perspective transform, defined by the four corner points of the trapezoid and the four corner points of the rectangle.

In this example, for the trapezoid they are:

ptsTrap = [[  50.          100.        ]
           [  50.          200.        ]
           [ 250.           64.73460388]
           [ 250.          235.26539612]]

and for the rectangle:

ptsRect = [[  50.  100.]
           [  50.  200.]
           [ 250.  100.]
           [ 250.  200.]]

I am getting a perspective transform from these points:

T = cv2.getPerspectiveTransform(ptsTrap, ptsRect)

And then building the image from that:

arrTrapToRect = cv2.warpPerspective(arrTrap, T, arrTrap.shape[:2])

However, as you can see from the image, this isn’t giving the expected transformation.

I can’t seem to work out why even the points that defined the transform are not being projected according to it. Any ideas?

Best answer

Your methodology is correct. The problem arises when you specify the coordinates of your corner points. I don’t know how you calculated them, but you have swapped your X and Y axes. This is reflected in the transformation applied to your final image. I find the corner points to be:

ptsTrap = [[[  99.   51.]]
           [[  64.  251.]]
           [[ 234.  251.]]
           [[ 199.   51.]]]

ptsRect = [[[ 102.   49.]]
           [[ 100.  249.]]
           [[ 200.  250.]]
           [[ 200.   50.]]]

Finding the perspective transform from these points gives the correct result:
Perspective Transform result

For reference, this is the code I used:

import cv2
import numpy as np

def find_corners(image):
    im = cv2.Canny(image, 100, 200)

    cnt = cv2.findContours(im,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0]
    cnt = cv2.approxPolyDP(cnt[0], 5, True)
    return cnt.astype(np.float32)

def main(argv):
    trap = cv2.imread('trap.png', cv2.IMREAD_GRAYSCALE)
    rect = cv2.imread('rect.png', cv2.IMREAD_GRAYSCALE)

    ptsTrap = find_corners(trap)
    ptsRect = find_corners(rect)

    T = cv2.getPerspectiveTransform(ptsTrap, ptsRect)

    warp = cv2.warpPerspective(trap, T, rect.shape[:2])

    cv2.imshow('', warp)
    cv2.imwrite('warp.png', warp)
    cv2.waitKey()
    cv2.destroyAllWindows()