如何在Python中实现OpenCV的PerspectiveTransform

时间:2018-12-20 02:19:57

标签: python numpy opencv

我正在将2d点从源矩形映射到目标矩形。我希望能够在不需要OpenCV的情况下执行此操作。我已经实现了var querystring = 'lang[]=EN&lang[]=FR&type[]=1&type[]=2'; let params = new URLSearchParams(decodeURI(querystring)); let entries = params.entries(); let result = {} for(let entry of entries) { // each 'entry' is a [key, value] var key = entry[0]; var val = entry[1]; if(key in result){ result[key].push(val); }else{ result[key] = [val]; } } console.log(result);,但是在寻找getPerspectiveTransform所需的数学源时遇到了麻烦。我发现的所有内容都是关于使用perspectiveTransform或如何实现cv2.perspectiveTransform的。

到目前为止,我已经有了它并且可以正常工作:

cv2.getPerspectiveTransform

结果:

import numpy as np
import cv2

def getPerspectiveTransform(sourcePoints, destinationPoints):
    """
    Calculates the 3x3 matrix to transform the four source points to the four destination points

    Comment copied from OpenCV:
    /* Calculates coefficients of perspective transformation
    * which maps soruce (xi,yi) to destination (ui,vi), (i=1,2,3,4):
    *
    *      c00*xi + c01*yi + c02
    * ui = ---------------------
    *      c20*xi + c21*yi + c22
    *
    *      c10*xi + c11*yi + c12
    * vi = ---------------------
    *      c20*xi + c21*yi + c22
    *
    * Coefficients are calculated by solving linear system:
    *             a                         x    b
    * / x0 y0  1  0  0  0 -x0*u0 -y0*u0 \ /c00\ /u0\
    * | x1 y1  1  0  0  0 -x1*u1 -y1*u1 | |c01| |u1|
    * | x2 y2  1  0  0  0 -x2*u2 -y2*u2 | |c02| |u2|
    * | x3 y3  1  0  0  0 -x3*u3 -y3*u3 |.|c10|=|u3|,
    * |  0  0  0 x0 y0  1 -x0*v0 -y0*v0 | |c11| |v0|
    * |  0  0  0 x1 y1  1 -x1*v1 -y1*v1 | |c12| |v1|
    * |  0  0  0 x2 y2  1 -x2*v2 -y2*v2 | |c20| |v2|
    * \  0  0  0 x3 y3  1 -x3*v3 -y3*v3 / \c21/ \v3/
    *
    * where:
    *   cij - matrix coefficients, c22 = 1
    */

    """
    if sourcePoints.shape != (4,2) or destinationPoints.shape != (4,2):
        raise ValueError("There must be four source points and four destination points")

    a = np.zeros((8, 8))
    b = np.zeros((8))
    for i in range(4):
        a[i][0] = a[i+4][3] = sourcePoints[i][0]
        a[i][1] = a[i+4][4] = sourcePoints[i][1]
        a[i][2] = a[i+4][5] = 1
        a[i][3] = a[i][4] = a[i][5] = 0
        a[i+4][0] = a[i+4][1] = a[i+4][2] = 0
        a[i][6] = -sourcePoints[i][0]*destinationPoints[i][0]
        a[i][7] = -sourcePoints[i][1]*destinationPoints[i][0]
        a[i+4][6] = -sourcePoints[i][0]*destinationPoints[i][1]
        a[i+4][7] = -sourcePoints[i][1]*destinationPoints[i][1]
        b[i] = destinationPoints[i][0]
        b[i+4] = destinationPoints[i][1]

    x = np.linalg.solve(a, b)
    x.resize((9,), refcheck=False)
    x[8] = 1 # Set c22 to 1 as indicated in comment above
    return x.reshape((3,3))

if __name__ == "__main__":
    # Create a transform to change table coordinates in inches to projector coordinates
    sourceCorners = np.array([[0.0, 0.0],[120.0,0.0],[120.0,63.0],[0.0,63.0]])
    destinationCorners = np.array([[4095.0,0],[3071,4095],[1024,4095],[0,0]])
    perspectiveTransform = getPerspectiveTransform(sourceCorners, destinationCorners)

    points = np.array([0,0,120,63,120,0,0,63,120,63,0,0,120,0], dtype=float).reshape(-1,1,2)
    perspectivePoints = cv2.perspectiveTransform(points, perspectiveTransform)

    print(perspectivePoints)

图形:

Source

Destination

[[[4095. 0.]] [[1024. 4095.]] [[3071. 4095.]] [[ 0. 0.]] [[1024. 4095.]] [[4095. 0.]] [[3071. 4095.]]] 的OpenCV C源代码具有隐含的变量名称,没有注释,并且非常难以理解。

有人可以为我介绍如何实施perspectiveTransform的好消息吗?

1 个答案:

答案 0 :(得分:2)

基本上,透视变换是

[                  ]   [source_x]   [target_x * w]  
[perspectivesMatrix] x [source_y] = [target_y * w]
[                  ]   [    1   ]   [      w     ]

其中perspectiveMatrix是以下形式的3x3矩阵

[c00 c01 c02]
[c10 c11 c12]
[c20 c21 c22]

由于您已经拥有perspectiveMatrix,所以我们要做的就是复制以前的公式。

def perspectiveTransform(perspectiveMatrix, sourcePoints):
    '''
    perspectiveMatrix as above
    sourcePoints has shape (n,2)
    '''

    # first we extend source points by a column of 1
    # augment has shape (n,1)
    augment = np.ones((sourcePoints.shape[0],1))
    # projective_corners is a 3xn matrix with last row all 1
    # note that we transpose the concatenation
    projective_corners = np.concatenate( (sourceCorners, augment), axis=1).T

    # projective_points has shape 3xn
    projective_points = perspectiveMatrix.dot(projective_corners)

    # obtain the target_points by dividing the projective_points 
    # by its last row (where it is non-zero)
    # target_points has shape (3,n).
    target_points = np.true_divide(projective_points, projective_points[-1])

    # so we want return points in row form
    return target_points[:2].T


if __name__=='__main__':
    # Create a transform to change table coordinates in inches to projector coordinates
    sourceCorners = np.array([[0.0, 0.0],[120.0,0.0],[120.0,63.0],[0.0,63.0]],dtype=np.float32)
    destinationCorners = np.array([[4095.0,0],[3071,4095],[1024,4095],[0,0]],dtype=np.float32)
    perspectiveMatrix = getPerspectiveTransform(sourceCorners, destinationCorners)

    # test points
    points = np.array([0,0,120,63,120,0,0,63,120,63,0,0,120,0], dtype=float)

    # perspectiveTransform by cv2
    cv_perspectivePoints = cv2.perspectiveTransform(points.reshape(-1,1,2), perspectiveMatrix)

    # our implementation of perspectiveTransform
    perspectivePoints = perspectiveTransform(perspectiveMatrix, points)

    # should yields something close to 0.0
    print(cv_perspectivePoints.reshape(-1,2) - perspectivePoints)