透视变换是将图像从一个视平面投影到另外一个视平面的过程,所以透视变换也被称为投影映射(Projection Mapping)。我们知道在图像的仿射变换中需要变换矩阵是一个2x3的两维平面变换矩阵,而透视变换本质上空间立体三维变换,根据其次坐标方差,要把三维坐标投影到另外一个视平面,就需要一个完全不同的变换矩阵M,所以这个是透视变换跟OpenCV中几何仿射变换最大的不同。
常用的应用场景如将放在桌面的文档用OCR得到其文字,需要进行透视变换把这张纸“放正”
参考博客:https://blog.csdn.net/zhangjunp3/article/details/80036310
u,v是原始图片左边,对应得到变换后的图片坐标x,y,其中。
变换矩阵产生透视,
所以可以理解成仿射等是透视变换的特殊形式。经过透视变换之后的图片通常不是平行四边形(除非映射视平面和原来平面平行的情况)。
重写之前的变换公式可以得到:
所以,已知变换对应的几个点就可以求取变换公式。反之,特定的变换公式也能新的变换后的图片。简单的看一个正方形到四边形的变换:
根据变换公式得到:
定义几个辅助变量:
都为0时变换平面与原来是平行的,可以得到:
不为0时,得到
求解出的变换矩阵就可以将一个正方形变换到四边形。反之,四边形变换到正方形也是一样的。于是,我们通过两次变换:四边形变换到正方形+正方形变换到四边形就可以将任意一个四边形变换到另一个四边形。
原图,要把发票放正:
代码:
1 import cv2 2 import matplotlib.pyplot as plt 3 import numpy as np 4 5 if __name__ == '__main__': 6 img = cv2.imread('../pics/10.png', 0) 7 img = cv2.GaussianBlur(img, (5, 5), 0) 8 9 edge = cv2.Canny(img, 40, 180) 10 contours, hierarchy = cv2.findContours(edge, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) 11 12 tmp = np.zeros(img.shape, np.uint8) 13 draw_cnt = [] 14 arcs = [] 15 for c in contours: 16 arc = cv2.arcLength(c, False) 17 arcs.append(arc) 18 19 draw_cnt.append(contours[arcs.index(max(arcs))]) 20 cv2.drawContours(tmp, draw_cnt, -1, (250, 255, 255), 2) 21 22 approx = cv2.approxPolyDP(draw_cnt[0], 10, True) # 近似多边形,10为精度,调这个参数使边数为4 23 24 approx_point1 = approx.reshape(4, 2).astype(np.float32) 25 26 plane = np.array([[0, 0], [0, 600], [400, 600], [400, 0]], dtype="float32") # 投影到400*600的面上 27 28 M = cv2.getPerspectiveTransform(approx_point1, plane) 29 out_img = cv2.warpPerspective(img, M, (400, 600)) 30 dst = cv2.perspectiveTransform(plane.reshape(1, 4, 2), M) 31 32 transformed = cv2.resize(out_img, (400, 600)) 33 34 cv2.imshow('edge', transformed) 35 36 cv2.waitKey()
效果: