I am trying to transform a virtual football/soccer pitch to match a real-world image. I have seen a few solutions and they all seem to be doing the same general thing. Match non collinear points from the virtual pitch to the real-world pitch, input them into cv2.findHomography and then apply transform to the 4 corners of the virtual pitch using cv2.perspectiveTransform. The issue I am having is that my output matrix seems to be very extreme and producing extreme result when translating one of the corners.
Here is a screenshot of how my calibration looks. This produces the following points for src & dst
[
[ [ 63.83039855957031, 1734.711669921875 ] ],
[ [ 611.9346923828125, 1734.711669921875 ] ],
[ [ 611.9346923828125, 443.288330078125 ] ],
[ [ 63.83039855957031, 443.288330078125 ] ]
]
[
[ [ 24.28842544555664, 292.6755065917969 ] ],
[ [ 517.95068359375, 383.7571105957031 ] ],
[ [ 1154.307373046875, 149.98101806640625 ] ],
[ [ 768.7286376953125, 111.72675323486328 ] ]
]
And my 4 corners of my virtual pitch
[ [ [ 0, 0 ] ], [ [ 3488, 0 ] ], [ [ 3488, 2178 ] ], [ [ 0, 2178 ] ] ]
Below is my code. I do pass my pass my values in via arguments formatted in json as I am using Electron not sure if that could be effecting something but the values logged above are logged after this.
src = np.array(json.loads(sys.argv[1]), dtype=np.float32).reshape(-1,1,2)
dst = np.array(json.loads(sys.argv[2]), dtype=np.float32).reshape(-1,1,2)
corners = np.array(json.loads(sys.argv[3]), dtype=np.float32).reshape(-1,1,2)
H, mask = cv2.findHomography(src, dst, cv2.RANSAC, 5)
transformed_corners = cv2.perspectiveTransform(corners, H)
src_plt = np.array(src).reshape(-1, 2)
dst_plt = np.array(dst).reshape(-1, 2)
transformed_corners_plt = np.array(transformed_corners).reshape(-1, 2)
# Plot the points
plt.figure()
# Plot src points
plt.scatter(src_plt[:, 0], src_plt[:, 1], c='r', label='src')
for i, point in enumerate(src_plt):
plt.text(point[0], point[1], f'src{i}', fontsize=9, ha='right')
# Plot dst points
plt.scatter(dst_plt[:, 0], dst_plt[:, 1], c='g', label='dst')
for i, point in enumerate(dst_plt):
plt.text(point[0], point[1], f'dst{i}', fontsize=9, ha='right')
# Plot transformed corners
plt.scatter(transformed_corners_plt[:, 0], transformed_corners_plt[:, 1], c='b', label='transformed_corners')
for i, point in enumerate(transformed_corners_plt):
plt.text(point[0], point[1], f'trans{i}', fontsize=9, ha='right')
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.title('src, dst, and transformed_corners')
plt.grid(True)
plt.gca().invert_yaxis()
plt.axis('equal')
plt.show()
As you can see in the above code I plot the points in a graph for debugging.
Matrix
[
[ 0.4490198640828247, -0.5169323112130393, 882.2926013219644 ],
[ 0.03917534322111329, 0.05600069643261656, 71.76807378200303 ],
[ -0.00015152854916043845, -0.00023326614497623852, 1 ]
]
Translated Corners
[
[ [ 882.2926025390625, 71.76807403564453 ] ],
[ [ 5193.29345703125, 442.0480041503906 ] ],
[ [ -36151.05859375, -9030.44921875 ] ],
[ [ -495.1474609375, 393.81854248046875 ] ]
]
These 2 corners looks somewhat correct
Any help would be massively appreciated!