目录
[1.1. 核心原理](#1.1. 核心原理)
[1.3 仿射的一些应用](#1.3 仿射的一些应用)
[1.4 一个仿射应用实例代码](#1.4 一个仿射应用实例代码)
[2.1 核心原理](#2.1 核心原理)
[2.2 计算透视矩阵](#2.2 计算透视矩阵)
[2.3 应用透视变换](#2.3 应用透视变换)
[2.4 应用场景](#2.4 应用场景)
一、仿射变换
1.1. 核心原理
仿射变换是二维平面内的线性变换 + 平移 ,本质是:二维坐标 → 二维坐标 的线性映射,保持平直性 和平行性。
- 平直性:变换后直线依然是直线,不会弯曲。
- 平行性:变换后平行线依然平行,点的顺序不变。
- 包含的基础操作:平移、旋转、缩放、错切,以及它们的组合。



1.2.仿射变化核心函数
仿射变化函数
cpp
void warpAffine(
InputArray src, // 输入图像
OutputArray dst, // 输出图像
InputArray M, // 2×3 仿射变换矩阵
Size dsize, // 输出图像尺寸
int flags = INTER_LINEAR, // 插值方法
int borderMode = BORDER_CONSTANT, // 边界填充方式
const Scalar& borderValue = Scalar() // 边界填充值 默认0黑色不影响图片
);
图像旋转的仿射矩阵getRotationMatrix2D()
专门用于生成 绕某点旋转 + 等比例缩放的仿射矩阵(2×3),是最常用的图像旋转工具。
Mat cv::getRotationMatrix2D(
Point2f center, // 旋转中心坐标 (x, y)
double angle, // 旋转角度(单位:度),正值=逆时针
double scale // 整体缩放因子(不缩放填1)
);
核心参数含义
center:图像旋转的中心点(比如图像中心Point2f(cols/2, rows/2)),绕该点旋转不会让图像偏移。angle:角度单位是度 ,正值为逆时针旋转 (比如90是逆时针转 90°,-90是顺时针转 90°)。scale:两个轴的统一缩放因子(scale=0.5表示缩小一半,scale=2表示放大一倍)。
三点对应法计算仿射变换矩阵getAffineTransform()
-
计算矩阵
M:需要3 组不共线的对应点(原坐标 → 目标坐标),仿射变换有 6 个未知参数(2×3 矩阵),每组对应点提供 2 个方程,3 组点刚好解出 6 个参数。。Mat getAffineTransform(
const Point2f src[], // 原图像3个点
const Point2f dst[] // 目标图像3个对应点
);
1.3 仿射的一些应用
- 图像校正:将倾斜的文档 / 证件转正。
- 数据增强:深度学习训练时,对图像做旋转 / 平移 / 缩放,扩充样本。
- 图像拼接:对齐多张图像的视角。
- UI 交互:图片拖动、旋转、拉伸等操作。
1.4 一个仿射应用实例代码
cpp
void testFangShe()
{
Mat ColorMat = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_COLOR);
//图像旋转
Mat rotaion0, img_wrap0;
double angle = 30;
Point2f center(ColorMat.rows/2,ColorMat.cols/2);
rotaion0 = getRotationMatrix2D(center,angle,1);
warpAffine(ColorMat, img_wrap0, rotaion0, Size(2*ColorMat.cols, 100+ColorMat.rows));
namedWindow("逆时针旋转30度", WindowFlags::WINDOW_NORMAL);
imshow("逆时针旋转30度",img_wrap0);
//三点法确定仿射变化矩阵
// 1. 定义3组对应点(原坐标 → 目标坐标)
Point2f srcPoints[3] = {
Point2f(0, 0), // 左上角
Point2f(ColorMat.cols - 1, 0), // 右上角
Point2f(0, ColorMat.rows - 1) // 左下角
};
Point2f dstPoints[3] = {
Point2f(50, 50), // 目标左上角
Point2f(ColorMat.cols - 1 + 50, 50), // 目标右上角
Point2f(50, ColorMat.rows - 1 + 50) // 目标左下角
};
Mat rotation1, img_warp1;
rotation1 = getAffineTransform(srcPoints, dstPoints);
warpAffine(ColorMat, img_warp1, rotation1, Size(ColorMat.cols+60, ColorMat.rows + 60));
namedWindow("三点法变换", WindowFlags::WINDOW_NORMAL);
imshow("三点法变换", img_warp1);
waitKey();
}

二、透视变化
2.1 核心原理
透视变换是三维空间到二维平面的投影变换






透视变换本质是将三维空间投影到二维平面 的单应性变换(Homography),也叫四点变换(因为需要 4 组对应点求解矩阵)。

2.2 计算透视矩阵
getPerspectiveTransform()
Mat cv::getPerspectiveTransform(
const Point2f src[], // 原图4个点(任意3点不共线)
const Point2f dst[], // 目标图4个对应点
int solveMethod = DECOMP_LU // 矩阵求解方法(默认LU分解)
);
- 参数说明 :
-
src[]/dst[]:各 4 个点,必须一一对应,且任意 3 点不共线(否则矩阵不可逆)。 -
solveMethod:求解线性方程组的方法,常用选项:标志 含义 适用场景 DECOMP_LULU 分解(默认) 速度快,精度高 DECOMP_SVD奇异值分解 数值更稳定,适合点有噪声的情况 DECOMP_EIG特征值分解 特征值问题场景
-
2.3 应用透视变换
warpPerspective()
void cv::warpPerspective(
InputArray src, // 输入图像
OutputArray dst, // 输出图像
InputArray M, // 3×3 透视变换矩阵
Size dsize, // 输出图像尺寸
int flags = INTER_LINEAR, // 插值方法(同resize)
int borderMode = BORDER_CONSTANT, // 边界填充方式
const Scalar& borderValue = Scalar() // 边界填充值
);
- 关键参数 :
M:必须是3×3的单应性矩阵。flags:插值方法,推荐INTER_LINEAR(默认)或INTER_CUBIC(高清场景)。borderMode:变换后超出图像区域的填充方式,默认黑色填充。
2.4 应用场景
- 文档 / 证件矫正:将倾斜拍摄的文档、身份证、发票转正为俯视视角(最常用场景)。
- AR 增强现实:将虚拟物体(如贴纸、模型)贴到真实平面上。
- 全景拼接:多张相邻图像通过单应性矩阵对齐,拼接成全景图。
- 视角转换:将斜视图、鸟瞰图转换为正视图(如自动驾驶的鸟瞰图)。
- 图像校正:纠正相机拍摄时的透视畸变(如广角镜头的边缘变形)。
2.5一个透视变化应用的实例代码
以下是通过透视变化矫正拍摄不正的二维码。
cpp
void testPesPective()
{
Mat ColorMat = imread(R"(D:\Study\OpenCvStudy\QRCode.png)", ImreadModes::IMREAD_COLOR);
Point2f src_points[4],dst_points[4];
src_points[0] = Point2f(58.0, 236.0);
src_points[1] = Point2f(333.0, 0.0);
src_points[2] = Point2f(264.0, 574.0);
src_points[3] = Point2f(458.0, 322.0);
dst_points[0] = Point2f(29.0,78.0);
dst_points[1] = Point2f(473.0, 78.0);
dst_points[2] = Point2f(29.0, 531.0);
dst_points[3] = Point2f(473.0, 531.0);
Mat T_Mat = getPerspectiveTransform(src_points, dst_points);
Mat TransFormQRcode;
warpPerspective(ColorMat, TransFormQRcode, T_Mat, Size(509, 575));
imshow("未矫正二维码", ColorMat);
imshow("透视矫正二维码",TransFormQRcode);
waitKey();
}
