Perspective transform
lastmod 2023-10-26created 2023-01-058 edits
OpenCV APIs
A Perspective transform is a transform that preserves only the straightness of lines, not their parallelism. Railroad tracks are parallel to each other, but after a perspective transform their parallelism is not preserved and they appear to meet at a single point. (The reverse transform is also possible.)
You need the Input values of 4 Points and the output Points they move to.
To get the transformation matrix you need the cv2.getPerspectiveTransform function, and you can get the final result image by applying the transformation matrix to the cv2.warpPerspective function.
The example below removes the effect of perspective.
#-*- coding:utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('images/perspective.jpg')
# [x,y] 좌표점을 4x2의 행렬로 작성
# 좌표점은 좌상->좌하->우상->우하
pts1 = np.float32([[504,1003],[243,1525],[1000,1000],[1280,1685]])
# 좌표의 이동점
pts2 = np.float32([[10,10],[10,1000],[1000,10],[1000,1000]])
# pts1의 좌표에 표시. perspective 변환 후 이동 점 확인.
cv2.circle(img, (504,1003), 20, (255,0,0),-1)
cv2.circle(img, (243,1524), 20, (0,255,0),-1)
cv2.circle(img, (1000,1000), 20, (0,0,255),-1)
cv2.circle(img, (1280,1685), 20, (0,0,0),-1)
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (1100,1100))
plt.subplot(121),plt.imshow(img),plt.title('image')
plt.subplot(122),plt.imshow(dst),plt.title('Perspective')
plt.show()
OpenCV_-_Perspective_Transformation.jpg
Sample: recording video after a perspective transform
#-*- coding:utf-8 -*-
import cv2
import sys
from numpy import array, float32
def main(input_file: str, output_file: str, headless=True):
cap = cv2.VideoCapture(input_file)
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
input_size = width, height
print("Input: ", input_file)
print("Input fps: ", fps)
print("Input size: ", input_size)
print("Input frames: ", frames)
resize_size = 1920, 1080
print("Resize size: ", resize_size)
left_top = 600, 37
left_bottom = 479, 906
right_top = 1068, 37
right_bottom = 1212, 906
points = left_top, left_bottom, right_top, right_bottom
pp_from = array(points, dtype=float32)
pp_roi = 0, 0, 733, 877
x1, y1, x2, y2 = pp_roi
pp_width = abs(x2 - x1)
pp_height = abs(y2 - y1)
pp_size = pp_width, pp_height
pp_to = array(((x1, y1), (x1, y2), (x2, y1), (x2, y2)), dtype=float32)
m = cv2.getPerspectiveTransform(pp_from, pp_to, cv2.DECOMP_LU)
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
writer = cv2.VideoWriter(output_file, fourcc, fps, pp_size)
print("Output: ", output_file)
print("Output fps: ", fps)
print("Output size: ", pp_size)
try:
i = 0
while True:
# print(f"{i}/{frames}")
i += 1
ret, image = cap.read()
if not ret:
break
if image.shape[0:2] != resize_size:
image = cv2.resize(image, dsize=resize_size)
image = cv2.warpPerspective(image, m, pp_size)
writer.write(image)
if not headless:
cv2.imshow("Preview", image)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
writer.release()
if not headless:
cv2.destroyAllWindows()
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2], True)
See also
- Affine transformation
- Perspective transform
- OpenCV - Geometric Transformations of Images
- Remapping
- Concave/Convex Lens Distortion
- Radial Distortion
- Pinhole camera model
Favorite site
- 3D projection - Wikipedia # Perspective projection
- Geometric transformations of images — gramman 0.1 documentation
- OpenCV: Geometric Transformations of Images
- (Learned the hard way through errors) Finding a line's position in the original after drawing it on an opencv perspective transform - inverse transform, point transform
- OpenCV - 15. Remapping, Concave/Convex Lens Distortion, Radial Distortion