透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。通用的变换公式为:
u,v是原始图片坐标,对应得到变换后的图片坐标x,y,其中。
变换矩阵可以拆成4部分,表示线性变换,比如scaling,shearing和ratotion。用于平移,产生透视变换。所以可以理解成仿射等是透视变换的特殊形式。经过透视变换之后的图片通常不是平行四边形(除非映射视平面和原来平面平行的情况)。
所以,已知变换对应的几个点就可以求取变换公式。反之,特定的变换公式也能新的变换后的图片。简单的看一个正方形到四边形的变换:
变换的4组对应点可以表示成:
根据变换公式得到:
求解出的变换矩阵就可以将一个正方形变换到四边形。反之,四边形变换到正方形也是一样的。于是,我们通过两次变换:四边形变换到正方形+正方形变换到四边形就可以将任意一个四边形变换到另一个四边形。
原理来自于这篇文章
import cv2
import numpy as np
def order_points(pts):
#顺时针排列点
#topleft,topright,bottomleft,bottomright
rect = np.zeros((4,2),dtype='float32')
s = pts.sum(axis = 1)
rect[0] = pts[np.argmin(s)]
rect[3] = pts[np.argmax(s)]
d = np.diff(pts,axis = 1)
rect[1] = pts[np.argmin(d)]
rect[2] = pts[np.argmax(d)]
return rect
def four_point_transform(image,pts):
rect = order_points(pts)
(tl,tr,bl,br) = rect
widthA = np.sqrt((tl[0] - tr[0])**2 + (tl[1] - tr[1])**2)
widthB = np.sqrt((bl[0] - br[0])**2 + (bl[1] - br[1])**2)
width = int(max(widthA,widthB))
heightA = np.sqrt((tl[0] - bl[0])**2 + (tr[1] - br[1])**2)
heightB = np.sqrt((tl[0] - bl[0])**2 + (tr[1] - br[1])**2)
height = int(max(heightA,heightB))
dst = np.array([[0,0],[width-1,0],[width-1,height-1],[0,height-1]],dtype='float32')
#变换矩阵交给cv2去算啦
M = cv2.getPerspectiveTransform(pts,dst)
wraped = cv2.warpPerspective(image,M,(width,height))
return wraped
img = cv2.imread('card.jpg')
c = np.array([[280,254],[668,443],[532,694],[127,485]],dtype='float32')
img_t = four_point_transform(img,c)
cv2.imshow('origin',img)
cv2.imshow('transformed',img_t)
cv2.waitKey(0)
cv2.destroyAllWindows()