图像处理
前言
单应性矩阵是投影几何中一个术语,本质上它是一个数学概念,但是在OpenCV中却是有几个函数与透视变换相关的函数,都用到了单应性矩阵的概念与知识。跟很多人一样,刚开始学习图像处理对单应性矩阵不是很了解,通过项目实践慢慢知道了一些这方面的知识和自己对它的理解,就跟大家分享一下。
单应性矩阵概念
这里说的单应性矩阵主要是指平面单应性矩阵,在三轴坐标中XYZ,Z=1这个有点类似于三维的齐次坐标。单应性矩阵主要用来解决两个问题,
一是表述真实世界中一个平面与对应它图像的透视变换
二是从通过透视变换实现图像从一种视图变换到另外一种视图
首先看一下在三维空间中任意两个平面
上图的中零点分别表示两个平面中任意两个点,(a1、a2)与(b1、b2)是这两点对应的两个方向上的线性向量。对于这两个平面直接的关系我们就可以通过这些点从而进一步确立两个平面直接的关系,而两个平面之间的关系用单应性矩阵来描述如下:
H表示单应性矩阵,p和q表示三维齐次坐标向量,定义了八个自由度。
这种关系被称为平面单应性。这个当中有一些数学知识推导,感兴趣的大家可以自己去看,我们最重要的是明白这个概念怎么来的。其次知道它的应用场景,下面我们就从应用层面和代码层面来说说单应性矩阵的应用。
- 用来解决拍照时候图像扭曲问题。这个在上一篇文章透视 变换中讲过,但是 当时没有说这个是单应性矩阵的应用。
- 此外还两个计算机图形学的应用场景分布是纹理渲染与计算平面阴影。
- 用来实现图像拼接时候解决对齐问题
应用案例
街拍的时候路两边有很多广告牌,如果在视频实时帧中获取到对应的广告牌位置,就可以获取广告牌的四个角坐标,然后通过准备好的内容,将广告牌内容替换,得到想要的虚拟广告牌效果,而这个过程中最重要的一步,可以通过计算单应性矩阵实现内容替换,演示效果如下:
时代广场的街拍
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
//g++ t1.cpp -o t1 `pkg-config --cflags --libs opencv4`
int main(int argc, char** argv) {
// load images
Mat src = imread("bk.png");
if (!src.data) {
printf("could not load image...\n");
return -1;
}
// show images
namedWindow("input image", cv::WINDOW_AUTOSIZE);
imshow("input image", src);
Mat replaceImg = imread("fk.png");
imshow("adv content", replaceImg);
// 定义两个平面上四个角坐标
vector<Point> src_corners(4);
vector<Point> dst_corners(4);
// 原图像平面四点坐标
src_corners[0] = Point(0, 0);
src_corners[1] = Point(replaceImg.cols, 0);
src_corners[2] = Point(0, replaceImg.rows);
src_corners[3] = Point(replaceImg.cols, replaceImg.rows);
// 目标平面四个角坐标
dst_corners[0] = Point(70, 131);
dst_corners[1] = Point(168,216);
dst_corners[2] = Point(21, 199);
dst_corners[3] = Point(148, 267);
// 计算单应性矩阵与透视变换
Mat h = findHomography(src_corners, dst_corners);
Mat output_img;
warpPerspective(replaceImg, output_img, h, src.size());
// create mask
Mat m1 = Mat::zeros(replaceImg.size(), CV_8UC1);
m1 = Scalar(255);
Mat mask_output;
warpPerspective(m1, mask_output, h, src.size());
imshow("Mask Result", mask_output);
// use mask
Mat result1;
add(output_img, output_img, result1, mask_output);
Mat result2;
bitwise_not(mask_output, mask_output);
add(src, result1, result2, mask_output);
// put them together
Mat result;
add(result1, result2, result);
imshow("Final Result", result);
imwrite("result.png", result);
waitKey(0);
return 0;
}