二维平面中,图像的几何变换有等距、相似、仿射、投影等,如下所示:
1 图像几何变换
1.1 等距变换
等距变换 (Isometric Transformation),是一种二维的刚体变换,可理解为旋转和平移的组合
[x′y′1]=[cosθ−sinθtxsinθcosθty001][xy1]=[R2×2T2×101×211×1][xy1]
其中, R=[cosθ−sinθsinθcosθ] 为旋转矩阵, T=[txty] 为平移矩阵
想象一个无限大的平面上,放一张极薄的图片,让它只能在平面内做旋转和平移运动,则这样的运动就是等距变换
1.2 相似变换
相似变换 (Similarity Transformation),是一个等距变换和各向均匀缩放的组合
[x′y′1]=[scosθ−ssinθtxssinθscosθty001][xy1]=[sR2×2T2×101×211×1][xy1],其中 s 为缩放系数
想象平面内的一张图片,在旋转和平移的过程中,其大小也会均匀缩放(各个方向),则这样的变换就是相似变换
1.3 仿射变换
1.3.1 定义
仿射变换(Affine Transformation),是一个非奇异线性变变换 (矩阵乘法) 和 平移变换 (向量加法) 的组合
矩阵表达式为 [x′y′1]=[a11a12txa21a22ty001][xy1]=[A2×2T2×101×211×1][xy1]
其中,当 A=[a11a12a21a22] 是非奇异时,称 A 为仿射矩阵
1.3.2 分解
仿射矩阵 A 可分解为:旋转和各向 (正交) 非均匀缩放
A=R(θ)R(−ϕ)DR(ϕ),其中 D=[λ100λ2]是一个对角矩阵
首先,旋转角度 ϕ;然后在 x 和 y 方向上 (其中 x⊥y) 分别缩放 λ1 和 λ2;再旋转角度 −ϕ,也即回转 ϕ;最后旋转角度 θ
本质上,平面中的仿射变换,就是奇异值分解的过程:A=UDVT
想象无限大光滑平面内的一张图片,在旋转和平移的过程中,其大小在正交方向上非均匀缩放,则这样的变换就是仿射变换
1.3.3 不变量
仿射变换的过程中,有三个重要的不变量,分别是:平行线,平行线段长度比,面积比
2 OpenCV 函数
2.1 矩阵 - 相似变换
对于相似变换,有 4 个未知数 (s,θ,tx,ty),对应 OpenCV 中的 getRotationMatrix2D() 函数
1 2 3 4 5 | Mat getRotationMatrix2D ( Point2f center, // 原图像中的旋转中心点 double angle, // 旋转角度(正值代表逆时针旋转) double scale // 均匀缩放系数 ) |
该函数可得到如下矩阵:
[αβ(1−α)⋅center.x−β⋅center.y−βαβ⋅center.x+(1−α)⋅center.y]
其中, α=scale⋅cosangle
β=scale⋅sinangle
2.2 矩阵 - 仿射变换
仿射变换有 6 个未知数 (ϕ,θ,λ1,λ2,tx,ty),需列 6 组方程,而一组对应特征点 (x,y) -> (x′,y′) 可构造 2 个方程,因此,求解 6 个未知数,需要 3 组对应特征点
OpenCV 中 getAffineTransform() 可求解 2x3 矩阵 [a11a12txa21a22ty]
1 2 3 4 | Mat getAffineTransform ( const Point2f src[], // 原图像的三角顶点坐标 const Point2f dst[] // 目标图像的三角顶点坐标 ) |
其代码实现比较简单,先构建方程组,再利用 solve() 求解 Ax=b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Mat getAffineTransform( const Point2f src[], const Point2f dst[]) { Mat M(2, 3, CV_64F), X(6, 1, CV_64F, M.ptr()); double a[6 * 6], b[6]; Mat A(6, 6, CV_64F, a), B(6, 1, CV_64F, b); for ( int i = 0; i < 3; i++) { int j = i * 12; int k = i * 12 + 6; a[j] = a[k + 3] = src[i].x; a[j + 1] = a[k + 4] = src[i].y; a[j + 2] = a[k + 5] = 1; a[j + 3] = a[j + 4] = a[j + 5] = 0; a[k] = a[k + 1] = a[k + 2] = 0; b[i * 2] = dst[i].x; b[i * 2 + 1] = dst[i].y; } solve(A, B, X); return M; } |
2.3 变换后图象
已知仿射变换矩阵 M ,利用 warpAffine() 函数,可得变换后的图像:dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23)
1 2 3 4 5 6 7 8 9 | void warpAffine( InputArray src, // 输入图象 OutputArray dst, // 输出图像(大小为 dsize,类型同 src) InputArray M, // 2x3 矩阵 Size dsize, // 输出图像的大小 int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar() ) |
3 代码示例
首先构造3组三角顶点坐标,代入 getAffineTransform() 得到仿射变换的矩阵;再用 getRotationMatrix2D() 构造相似变换的矩阵;
然后,warpAffine() 求解经过相似变换和仿射变换的图像;最后,显示对比变换后的目标图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" using namespace cv; int main() { // 1) read image Mat src = imread( "horse.jpg" ); // 2) triangle vertices Point2f srcTri[3]; srcTri[0] = Point2f(0.f, 0.f); srcTri[1] = Point2f(src.cols - 1.f, 0.f); srcTri[2] = Point2f(0.f, src.rows - 1.f); Point2f dstTri[3]; dstTri[0] = Point2f(0.f, src.rows * 0.33f); dstTri[1] = Point2f(src.cols * 0.85f, src.rows * 0.25f); dstTri[2] = Point2f(src.cols * 0.15f, src.rows * 0.7f); // 3-1) getAffineTransform Mat warp_m1 = getAffineTransform(srcTri, dstTri); // 3-2) getRotationMatrix2D Mat warp_m2 = getRotationMatrix2D(Point2f(0.5*src.cols, 0.5*src.rows), 45, 0.5); // 4) warpAffine image Mat dst1,dst2; warpAffine(src, dst1, warp_m1, Size(src.cols, src.rows)); warpAffine(src, dst2, warp_m2, Size(src.cols, src.rows)); // 5) show image imshow( "image" , src); imshow( "warp affine 1" , dst1); imshow( "warp affine 2" , dst2); waitKey(); } |
结果对比如下:
参考
《Computer Vision: Algorithms and Applications》 Chapter 2 Image Formation
《Multiple View Geometry in Computer Vision》 2.4 A hierarchy of transformations
OpenCV Tutorials / Image Processing (imgproc module) / Affine Transformations
OpenCV-Python Tutorials / Image Processing in OpenCV / Geometric Transformations of Images
原文链接: http://www.cnblogs.com/xinxue/
专注于机器视觉、OpenCV、C++ 编程
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异