随笔 - 54  文章 - 0  评论 - 45  阅读 - 45万 

  二维平面中,图像的几何变换有等距、相似、仿射、投影等,如下所示:

    

 

1  图像几何变换

1.1  等距变换

    等距变换 (Isometric Transformation),是一种二维的刚体变换,可理解为旋转和平移的组合

   [xy1]=[cosθsinθtxsinθcosθty001][xy1]=[R2×2T2×101×211×1][xy1]

    其中, R=[cosθsinθsinθcosθ] 为旋转矩阵, T=[txty] 为平移矩阵

    想象一个无限大的平面上,放一张极薄的图片,让它只能在平面内做旋转和平移运动,则这样的运动就是等距变换

    

1.2  相似变换

  相似变换 (Similarity Transformation),是一个等距变换和各向均匀缩放的组合

   [xy1]=[scosθssinθtxssinθscosθty001][xy1]=[sR2×2T2×101×211×1][xy1],其中 s 为缩放系数

   想象平面内的一张图片,在旋转和平移的过程中,其大小也会均匀缩放(各个方向),则这样的变换就是相似变换

     

1.3  仿射变换

1.3.1  定义

    仿射变换(Affine Transformation),是一个非奇异线性变变换 (矩阵乘法) 和 平移变换 (向量加法) 的组合

    矩阵表达式为 [xy1]=[a11a12txa21a22ty001][xy1]=[A2×2T2×101×211×1][xy1]

    其中,当 A=[a11a12a21a22] 是非奇异时,称 A 为仿射矩阵

1.3.2  分解

    仿射矩阵 A 可分解为:旋转和各向 (正交) 非均匀缩放

    A=R(θ)R(ϕ)DR(ϕ),其中 D=[λ100λ2]是一个对角矩阵

    首先,旋转角度 ϕ;然后在 xy 方向上 (其中 xy) 分别缩放 λ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]

    其中, α=scalecosangle

                β=scalesinangle

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 

    

 

posted on   飞鸢逐浪  阅读(1751)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示