getPerspectiveTransform fails at 1 input point

Hey I am trying to compute a perspective transformation using openCV.
My code is as follows:

original_pts = np.array([[745, 1233], [1505, 1581], [1502, 1212], [855, 1229]], dtype=np.float32)
desired_pts = np.array([[46., 50.], [949., 490.], [949., 50.], [175., 50.]], dtype=np.float32)
M = cv2.getPerspectiveTransform(original_pts, desired_pts)
cv2.perspectiveTransform(np.float32(original_pts[None, :, :]), M)[0]

The output is:

array([[  46.,   50.],
       [   0.,    0.],
       [ 949.,   50.],
       [ 175.,   50.]], dtype=float32)

For some reason the second point is just very wrong

Trying to debug I implemented the linear system myself:

x = original_pts[:, 0]
y = original_pts[:, 1]
xt = desired_pts[:, 0]
yt = desired_pts[:, 1]
A = []
for i in range(4):
    equation = [x[i], y[i], 1, 0, 0, 0, 0, 0]
    equation += [0] * i
    equation += [-xt[i]]
    equation += [0] * (3 - i)
    A.append(equation)

    equation = [0, 0, 0, x[i], y[i], 1, 0, 0]
    equation += [0] * i
    equation += [-yt[i]]
    equation += [0] * (3 - i)
    A.append(equation)

    equation = [0, 0, 0, 0, 0, 0, x[i], y[i]]
    equation += [0] * i
    equation +=  [-1]
    equation += [0] * (3 - i)
    A.append(equation)

A = np.array(A)

b = np.array([0]*8 + [-1]*4)
res = np.linalg.solve(A, b)
my_M = np.array([[res[0], res[1], res[2]], [res[3], res[4], res[5]], [res[6], res[7], 1]])
cv2.perspectiveTransform(np.float32(original_pts[None, :, :]), my_M)[0]

Which yields a better approximation:

array([[  47.653404,   51.79718 ],
       [ 975.5118  ,  503.6889  ],
       [ 949.      ,   50.      ],
       [ 175.0361  ,   50.036106]], dtype=float32)

Does anyone have any idea, whether I’m using cv2.getPerspectiveTransfrom wrongly or does it simply not work for some problems? I thought I’d basically reimplemented a non-optimized version of the function

undeclared crosspost:

the points:

M looks broken.

the problem here is that some of these points are (quasi-)colinear, meaning you don’t actually have enough equations to solve for eight unknowns. you tried to define a rectangle like this. that is no longer a proper perspective transform.

give estimateAffine2D or estimateAffinePartial2D a try

>>> (M1, inliers) = cv2.estimateAffine2D(original_pts[:,None,:], desired_pts[:,None,:]); M1
array([[    1.1938 ,    -0.00919,  -833.13482],
       [    0.03246,     1.19188, -1443.22657]])
>>> cv.transform(original_pts[:,None,:], M1) - desired_pts[:,None,:]
array([[[-1.0849 ,  0.55139]],

       [[ 0.00336, -0.00171]],

       [[-0.18756,  0.09534]],

       [[ 1.26947, -0.64526]]], dtype=float32)
>>> (M2, inliers) = cv2.estimateAffinePartial2D(original_pts[:,None,:], desired_pts[:,None,:]); M2
array([[    1.19316,    -0.03249,  -803.94236],
       [    0.03249,     1.19316, -1444.82473]])
>>> cv.transform(original_pts[:,None,:], M2) - desired_pts[:,None,:]
array([[[-1.09546,  0.55322]],

       [[-8.59692,  0.46753]],

       [[-0.18726,  0.09229]],

       [[ 1.28259, -0.64551]]], dtype=float32)

Thank you that’s very helpful ! However, what am I supposed to do if I don’t know a-priori if my points are quasi-colinear? Do I always have to check whether the perspective transform reproduces the given points before applying it to the larger point set? I won’t always have a affine transformation so switching to that is not an option