上一节讲到,知道了图像的
C∗∞
,就可以恢复图像的度量结构,事实上,对
C∗∞
进行SVD分解,就能得到图像的各种性质,分解如下:
C∗∞′=HpHAHsC∗∞(HpHAHs)T=(HpHA)(HsC∗∞HTp)(HTAHTp)=[KKTvTKKTKKTvvTKKTv]
其中 k2×2 为相似变换矩阵相关的系数,二维向量v为射影变换相关的系数
在恢复了仿射性质基础上恢复度量结构
使用角度信息
在前面的文章我们讲了如何恢复图像的仿射性质。在此基础上(即已经确定了图像的无穷远直线),确定图像的虚圆点只需要两个约束。一种简单的方法是寻找两对正交的直线,如图所示。
假设知道图像中一对正交的直线l,m,由前一篇可以知道,
l′TC∗∞′m′=0
。又因为我们已经恢复了图像的仿射性质,故v=0,于是
(l′1 l′2 l′3)[KKT0T00](m′1 m′2 m′3)T=0
其中 S=KKT 为 2×2 的对称正定阵,显然S的自由度为2(不考虑全局尺度).进一步简化上式有 (l′1,l′2)S(m′1,m′2)T=0 ,即
(l′1m′1,l′1m′2+l′2m′1,l′2m′2)s=0
使用两对正交直线可以解出S,从而使用 cholesky分解得到K。
使用线段比例信息
同样的我们也可以寻找两对相等的线段作为约束,如下图。
直接消除图像的射影变换的变形
从射影变换后的图像开始恢复图像的度量结构做法如下:
假设图像中一对正交直线为l,m。经过与上面类似的过程可以得到
(l1m1,(l1m2+l2m1)/2,l2m2,(l1m3+l3m1)/2,(l2m3+l3m2)/2,l3m3)c=0
其中
c=(a,b,c,d,e,f)T
为二次曲线
C∗∞
的参数向量化形式。需要5对这样的正交直线才能解出
C∗∞
。
附录:简单实现以及结果
笔者写程序的时候并没有使用上述方法,而是利用了更多的先验知识,通过找出射影变换后图像与射影变换前图像的4对对应点求出H,最终得到的结果如下:
实现的代码如下(先选取投影后的四个点,再确定恢复后图像对应4个点的位置,按任意键确认)
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
vector<Point2d> pts;
Mat temp;
void onMouse( int event, int x, int y, int flags, void* ustc)
{
static int cnt = 0;
if( event == EVENT_LBUTTONDOWN )
{
Point2d pt(x,y);
pts.push_back(pt);
if(++cnt>4)
circle(temp,pt,2,{0,255,0},FILLED);
else
circle(temp,pt,2,{255,0,0},FILLED);
imshow( "img", temp );
}
}
int main(int arg,char** argv) {
auto img = imread("../img.png");
temp = img.clone();
imshow("img",temp);
setMouseCallback("img",onMouse);
waitKey(0);
vector<Point2d> before(pts.begin(),pts.begin()+4);
vector<Point2d> after(pts.begin()+4,pts.begin()+8);
auto H = findHomography(before,after);
Mat result;
warpPerspective(img,result,H,{600,400});
imshow("result",result);
waitKey(0);
return 0;
}