透视变换

2024-05-07  本文已影响0人  leon_tly

透视变换原理及求解推导

透视变换(Perspective Transformation)是将二维的图片投影到一个三维视平面上,然后再转换到二维坐标下,所以也称为投影映射(Projective Mapping)。简单来说就是二维 → 三维 → 二维的一个过程。
矩阵形式表示
\begin{bmatrix} a_{11}&a_{12}&a_{13}\\ a_{21}&a_{22}&a_{23}\\ a_{31}&a_{32}&a_{33}\\ \end{bmatrix} * \begin{bmatrix} x\\ y\\ 1\\ \end{bmatrix} = \begin{bmatrix} X\\ Y\\ Z\\ \end{bmatrix}
其中\begin{bmatrix} a_{11}&a_{12}\\ a_{21}&a_{22}\\ \end{bmatrix} 表示线性变换
其中\begin{bmatrix} a_{31}&a_{32} \end{bmatrix} 表示透视变换
其余表示平移变换
通过除以Z轴将三维坐标转换为二维坐标Z=a_{31}x + a_{32}y + a_{33}
x^{'} = \frac{X}{Z} = \frac{a_{11}x + a_{12}y + a_{13}}{a_{31}x + a_{32}y + a_{33}}
y^{'} = \frac{Y}{Z} = \frac{a_{21}x + a_{22}y + a_{23}}{a_{31}x + a_{32}y + a_{33}}
其中a_{33} = 1
有8个未知数,因此需要原图上的4个点和目标图上的4个点来构成8个方程来求解变换矩阵。
x^{'} = \frac{X}{Z} = \frac{a_{11}x + a_{12}y + a_{13}}{a_{31}x + a_{32}y + 1}
({a_{31}x + a_{32}y + 1}) x^{'} = a_{11}x + a_{12}y + a_{13}
a_{11}x + a_{12}y + a_{13} - {a_{31}xx^{'} - a_{32}yx^{'}} = x^{'}
同理得到
a_{21}x + a_{22}y + a_{23} - {a_{31}xy^{'} - a_{32}yy^{'}} = y^{'}

当有(x_{0}, y_{0}), (x_{1}, y_{1}), (x_{2}, y_{2}), (x_{3}, y_{3})(x^{'}_{0}, y^{'}_{0}), (x^{'}_{1}, y^{'}_{1}), (x^{'}_{2}, y^{'}_{2}), (x^{'}_{3}, y^{'}_{3}) 8个点时可以得到方程

\begin{cases} a_{11}x_{0} + a_{12}y_{0} + a_{13} - {a_{31}x_{0}x_{0}^{'} - a_{32}y_{0}x_{0}^{'}} = x_{0}^{'}\\ a_{21}x_{0} + a_{22}y_{0} + a_{23} - {a_{31}x_{0}y_{0}^{'} - a_{32}y_{0}y_{0}^{'}} = y_{0}^{'}\\ a_{11}x_{1} + a_{12}y_{1} + a_{13} - {a_{31}x_{1}x_{1}^{'} - a_{32}y_{1}x_{1}^{'}} = x_{1}^{'}\\ a_{21}x_{1} + a_{22}y_{1} + a_{23} - {a_{31}x_{1}y_{1}^{'} - a_{32}y_{1}y_{1}^{'}} = y_{1}^{'}\\ a_{11}x_{2} + a_{12}y_{2} + a_{13} - {a_{31}x_{2}x_{2}^{'} - a_{32}y_{2}x_{2}^{'}} = x_{2}^{'}\\ a_{21}x_{2} + a_{22}y_{2} + a_{23} - {a_{31}x_{2}y_{2}^{'} - a_{32}y_{2}y_{2}^{'}} = y_{2}^{'}\\ a_{11}x_{3} + a_{12}y_{3} + a_{13} - {a_{31}x_{3}x_{3}^{'} - a_{32}y_{3}x_{3}^{'}} = x_{3}^{'}\\ a_{21}x_{3} + a_{22}y_{3} + a_{23} - {a_{31}x_{3}y_{3}^{'} - a_{32}y_{3}y_{3}^{'}} = y_{3}^{'}\\ \end{cases}
写成矩阵形式则有
\begin{bmatrix} x_{0} & y_{0} & 1 & 0 & 0 & 0 & -x_{0}x_{0}^{'} & -y_{0}x_{0}^{'}\\ 0 & 0 & 0 & x_{0} & y_{0} & 1 & -x_{0}y_{0}^{'} & -y_{0}y_{0}^{'}\\ x_{1} & y_{1} & 1 & 0 & 0 & 0 & -x_{1}x_{1}^{'} & -y_{1}x_{1}^{'}\\ 0 & 0 & 0 & x_{1} & y_{1} & 1 & -x_{1}y_{1}^{'} & -y_{1}y_{1}^{'}\\ x_{2} & y_{2} & 1 & 0 & 0 & 0 & -x_{2}x_{2}^{'} & -y_{2}x_{2}^{'}\\ 0 & 0 & 0 & x_{2} & y_{2} & 1 & -x_{2}y_{2}^{'} & -y_{2}y_{2}^{'}\\ x_{3} & y_{3} & 1 & 0 & 0 & 0 & -x_{3}x_{3}^{'} & -y_{3}x_{3}^{'}\\ 0 & 0 & 0 & x_{3} & y_{3} & 1 & -x_{3}y_{3}^{'} & -y_{3}y_{3}^{'}\\ \end{bmatrix} * \begin{bmatrix} a_{11} \\ a_{12}\\ a_{13}\\ a_{21}\\ a_{22}\\ a_{23}\\ a_{31}\\ a_{32}\\ \end{bmatrix} = \begin{bmatrix} x_{0}^{'} \\ y_{0}^{'}\\ x_{1}^{'}\\ y_{1}^{'}\\ x_{2}^{'}\\ y_{2}^{'}\\ x_{3}^{'}\\ y_{3}^{'}\\ \end{bmatrix}

高斯消元法求解代码

struct Point{
    float x;
    float y;
};


static std::vector<std::vector<float>> getPerspectiveTransform(const std::vector<Point>& src, const std::vector<Point>& dst) 
{
    std::vector<std::vector<float>> A(8, std::vector<float>(8, 0));
    std::vector<float> b(8, 0);
    
    // 矩阵赋初始值
    for (int i = 0; i < 4; ++i) {
        A[i * 2][0] = src[i].x;
        A[i * 2][1] = src[i].y;
        A[i * 2][2] = 1.0;
        A[i * 2][6] = -src[i].x * dst[i].x;
        A[i * 2][7] = -src[i].y * dst[i].x;

        A[i * 2 + 1][3] = src[i].x;
        A[i * 2 + 1][4] = src[i].y;
        A[i * 2 + 1][5] = 1.0;
        A[i * 2 + 1][6] = -src[i].x * dst[i].y;
        A[i * 2 + 1][7] = -src[i].y * dst[i].y;

        b[i * 2] = dst[i].x;
        b[i * 2 + 1] = dst[i].y;
    }

    std::vector<float> x(8);
    for (int i = 0; i < 8; ++i) {
        x[i] = b[i];
    }

    for (int i = 0; i < 8; ++i) {
        int pivot = i;
        for (int j = i + 1; j < 8; ++j) {
            if (std::abs(A[j][i]) > std::abs(A[pivot][i])) {
                pivot = j;
            }
        }
        // 按照列循环,将该列中值比较大的行向上交换
        std::swap(A[i], A[pivot]);
        std::swap(x[i], x[pivot]);
        // 消除主元
        for (int j = i + 1; j < 8; ++j) {
            float factor = A[j][i] / A[i][i];
            for (int k = i; k < 8; ++k) {
                A[j][k] -= factor * A[i][k];
            }
            x[j] -= factor * x[i];
        }
    }
    // 求解
    std::vector<float> H(9);
    for (int i = 7; i >= 0; --i) {
        float sum = 0;
        for (int j = i + 1; j < 8; ++j) {
            sum += A[i][j] * H[j];
        }
        H[i] = (x[i] - sum) / A[i][i];
    }
    H[8] = 1.0;

    std::vector<std::vector<float>> transform(3, std::vector<float>(3));
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            transform[i][j] = H[i * 3 + j];
        }
    }

    return transform;
}

上一篇下一篇

猜你喜欢

热点阅读