透视变换原理及求解推导
透视变换(Perspective Transformation)是将二维的图片投影到一个三维视平面上,然后再转换到二维坐标下,所以也称为投影映射(Projective Mapping)。简单来说就是二维 → 三维 → 二维的一个过程。
矩阵形式表示
其中 表示线性变换
其中 表示透视变换
其余表示平移变换
通过除以轴将三维坐标转换为二维坐标
其中
有8个未知数,因此需要原图上的4个点和目标图上的4个点来构成8个方程来求解变换矩阵。
同理得到
当有, , , 和 , , , 8个点时可以得到方程
写成矩阵形式则有
高斯消元法求解代码
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;
}
网友评论