透视变换:
这个问题主要就是求出右边那个3X3的矩阵,只要求出来了,问题就解决了,我认为有两种方法,一种是代进去求通解,已知8个顶点解8个未知数,就是笔算计算复杂了点;第二种是最小二乘法,这里我采用了后者,当然这里偷懒调用了矩阵库来进行矩阵运算,但还有个问题要解决,那就是怎么把上面的矩阵形式写成Ax=b形式,其中x是[a b c d e f g h]向量,是我需要求的,至于这里i为什么不用是因为i就是等于1,而b是[x1 y1 x2 y2 x3 y3 x4 y4]向量,对应的是原图中我们需要做变换的A4纸的顶点坐标。重点就是这个A要怎么求出来。
(以下是关于求这个矩阵的paper)
如图,A对应的就是这个形式。既然需要的东西都有了,那么就可以用最小二乘法求出这8个参数了。
然后再利用
求出对应的原图坐标(u, v),把它的RGB值赋给新图中的(x, y),对所有的xy遍历一遍即可完成变换。
(也可以解8个方程求出通解来完成这个变换)
测试结果:
这是我这个方法的实现代码:
#include "stdafx.h"
#include <iostream>
#include <Eigen/Dense>
#include <CImg.h>
#include <canny.hpp>
using namespace cimg_library;
using namespace std;
using namespace Eigen;
using namespace Eigen::internal;
using namespace Eigen::Architecture;
int main()
{
string str;
cin >> str;
CImg<float> img(str.c_str());
MatrixXd A(8, 8);
MatrixXd b(8, 1);
float x1, x2, x3, x4, y1, y2, y3, y4;
float u1, u2, u3, u4, v1, v2, v3, v4;
//经过hough变换得到的四个顶点坐标
cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3 >> x4 >> y4;
//预设的A4纸尺寸和顶点坐标
u1 = 0, v1 = 0, u2 = 2100, v2 = 0, u3 = 0, v3 = 2970, u4 = 2100, v4 = 2970;
//写成矩阵A形式
A << u1, v1, 1, 0, 0, 0, -u1*x1, -v1*x1,
0, 0, 0, u1, v1, 1, -u1*y1, -v1*y1,
u2, v2, 1, 0, 0, 0, -u2*x2, -v2*x2,
0, 0, 0, u2, v2, 1, -u2*y2, -v2*y2,
u3, v3, 1, 0, 0, 0, -u3*x3, -v3*x3,
0, 0, 0, u3, v3, 1, -u3*y3, -v3*y3,
u4, v4, 1, 0, 0, 0, -u4*x4, -v4*x4,
0, 0, 0, u4, v4, 1, -u4*y4, -v4*y4;
// b为原图顶点坐标向量
b << x1, y1, x2, y2, x3, y3, x4, y4;
// A的转置
MatrixXd A_t = A.transpose();
//利用Ax=b计算x 其中这里x为h h只是8*1的向量,因为a33默认为1,后面补上
//计算h 用的最小二乘法 h = (A的转置*A)^-1 *A的转置*b
MatrixXd h = (A_t*A).inverse() * A_t * b;
//cout << h.rows() << " " << h.cols() << endl;
float H[9]; // 用来存储变换矩阵的9个值
for (int i = 0; i < 8; i++) {
if (h(i, 0) > -0.000001 && h(i, 0) < 0.000001) // 一些精度问题
H[i] = 0;
else
H[i] = h(i, 0);
}
H[8] = 1; // 不能忘记第九个参数
CImg<float> img_(2100, 2970, 1, 3); //预设结果图
cimg_forXY(img_, x, y) {
//根据公式计算透视变换 这里采用后向,可以避免一些映射问题
float srcx = H[0] * x + H[1] * y + H[2];
float srcy = H[3] * x + H[4] * y + H[5];
float srcw = H[6] * x + H[7] * y + H[8];
int u = srcx / srcw;
int v = srcy / srcw;
img_(x, y, 0) = img(u, v, 0);
img_(x, y, 1) = img(u, v, 1);
img_(x, y, 2) = img(u, v, 2);
}
//img_.display("result");
img_.save("res.bmp");
system("pause");
return 0;
}
图像变形:
主要用了特征线来完成,算法如下:
简单来说就是根据算出X’和P’Q’的关系u和v,根据u和v和PQ算出X的位置。
但要做到多个特征线变换才有“意义”,需要算出每个特征线对应的X位置,并且保存一个对应的权重用于最后确定X具体的位置,如下:
这里a,b,p是参数,参考别的测试结果认为a = 1, b = 2, p = 0效果比较好,可以根据自己测试改。
我的测试结果如下
其中一帧:
动图:
代码核心部分: