OpenCV透视变换
本文是对上一篇的补充。透视变换(Perspective Transformation)是一种常见的几何变换,用于将图像从一种视角转换到另一种视角。它广泛应用于文档校正、车道检测、增强现实(AR)以及图像拼接等场景。本文将带你深入了解透视变换的数学原理、如何利用 OpenCV 实现透视变换,并通过示例代码展示实际应用。
1. 透视变换原理
透视变换基于投影变换理论,其数学表达式如下:
[ x ′ y ′ w ′ ] = [ M 11 M 12 M 13 M 21 M 22 M 23 M 31 M 32 M 33 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ w' \end{bmatrix}=\begin{bmatrix} M_{11} & M_{12} & M_{13} \\ M_{21} & M_{22} & M_{23} \\ M_{31} & M_{32} & M_{33} \end{bmatrix}\begin{bmatrix} x \\ y \\ 1 \end{bmatrix} x′y′w′ = M11M21M31M12M22M32M13M23M33 xy1
通过将x′和y′分别除以w′得到最终映射到图像上的坐标:
x ′ ′ = x ′ w ′ x'' = \frac{x'}{w'} x′′=w′x′
y ′ ′ = y ′ w ′ y'' = \frac{y'}{w'} y′′=w′y′
这里的 3×3 变换矩阵(通常记为M)定义了原图到目标图的映射关系。在 OpenCV 中,我们可以利用两个点集来计算该矩阵。
2. OpenCV 中透视变换的实现
OpenCV 为透视变换提供了两个主要函数:
-
cv::getPerspectiveTransform
根据原始点集和目标点集计算透视变换矩阵。
Mat cv::getPerspectiveTransform(const Point2f src[], const Point2f dst[]);
-
cv::warpPerspective
根据计算得到的透视矩阵对图像进行变换,并输出变换后的图像。
void cv::warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize);
这两个函数组合使用,可以实现从任意四边形到目标矩形(或其他四边形)的透视变换。
3. 示例代码
3.1 非交互式透视变换
在本示例中,我们手动指定原始图像中待变换区域的四个顶点,并将其映射到一个标准矩形。
适用于预先已知四个点坐标的场景,如文档校正。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 读取图像(确保图像路径正确)
Mat img = imread("E:/image/lena.png");
if (img.empty()) {
cerr << "无法加载图像!" << endl;
return -1;
}
int img_height = img.rows;
int img_width = img.cols;
// 定义原始图像的四个角点(按顺时针或逆时针顺序)
vector<Point2f> corners(4);
corners[0] = Point2f(0, 0);
corners[1] = Point2f(img_width - 1, 0);
corners[2] = Point2f(0, img_height - 1);
corners[3] = Point2f(img_width - 1, img_height - 1);
// 定义目标图像中的四个点
vector<Point2f> corners_trans(4);
corners_trans[0] = Point2f(150, 250);
corners_trans[1] = Point2f(771, 0);
corners_trans[2] = Point2f(0, img_height - 1);
corners_trans[3] = Point2f(650, img_height - 1);
// 计算透视变换矩阵
Mat transform = getPerspectiveTransform(corners, corners_trans);
cout << "透视变换矩阵:" << endl << transform << endl;
// 使用 warpPerspective 应用透视变换
Mat img_trans;
warpPerspective(img, img_trans, transform, img.size());
imshow("原始图像", img);
imshow("透视变换后图像", img_trans);
waitKey(0);
return 0;
}
3.2 交互式鼠标选择透视变换点
在交互式示例中,我们通过鼠标点击在图像上选取四个点,动态标记选取点,然后根据用户选择的点进行透视变换。该方法更灵活,适用于实际应用中无法预知准确点坐标的情况。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
Mat image;
vector<Point2f> selectedPoints;
// 鼠标回调函数:用于选取透视变换的四个点
void mouseHandler(int event, int x, int y, int flags, void* userdata) {
if (event == EVENT_LBUTTONDOWN) {
Point2f pt(x, y);
selectedPoints.push_back(pt);
cout << "选取点: (" << x << ", " << y << ")" << endl;
circle(image, pt, 5, Scalar(0, 0, 255), -1);
imshow("选择点", image);
}
}
int main() {
// 读取图像
image = imread("E:/image/lena.png");
if (image.empty()) {
cerr << "无法加载图像!" << endl;
return -1;
}
// 显示图像并绑定鼠标事件
imshow("选择点", image);
setMouseCallback("选择点", mouseHandler);
cout << "请在图像上点击四个角点,然后按任意键进行透视变换" << endl;
waitKey(0);
if (selectedPoints.size() != 4) {
cerr << "必须选择4个点!" << endl;
return -1;
}
// 定义目标图像上的四个点,将转换为 500x400 的矩形
vector<Point2f> dstPoints;
dstPoints.push_back(Point2f(0, 0));
dstPoints.push_back(Point2f(500, 0));
dstPoints.push_back(Point2f(0, 400));
dstPoints.push_back(Point2f(500, 400));
// 计算透视变换矩阵
Mat perspectiveMatrix = getPerspectiveTransform(selectedPoints, dstPoints);
// 进行透视变换
Mat warpedImage;
warpPerspective(image, warpedImage, perspectiveMatrix, Size(500, 400));
// 显示透视变换后的图像
imshow("透视变换结果", warpedImage);
waitKey(0);
return 0;
}
在这个示例中:
- 用户点击图像选取四个点后,通过鼠标回调函数将选取点绘制到图像上。
- 当选取完毕后按任意键后,程序计算透视变换矩阵,并将选取的区域映射到预设目标矩形上,实现文档或目标区域的校正。
4. 透视变换的应用场景介绍
透视变换技术在实际项目中有着广泛应用,主要包括:
- 文档校正:对手机拍摄的书页、名片、票据等图像进行校正,使其恢复为标准矩形,便于后续 OCR(光学字符识别)处理。
- 车道检测:在自动驾驶系统中,将摄像头拍摄的道路图像透视变换为鸟瞰图,有助于车道线检测和车辆定位。
- 增强现实(AR):通过透视变换将虚拟图像投影到真实物体表面,实现虚实结合的效果。
- 图像拼接:在全景图制作过程中,对多个重叠图像进行透视变换,便于图像之间的无缝拼接。