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

   

 

1  图象几何变换

1.1  等距变换

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

   $\quad \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} \cos \theta & -\sin \theta & t_x \\ \sin \theta & \cos \theta & t_y \\ 0&0&1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1\end{bmatrix}  =\begin{bmatrix} R_{2 \times 2} & T_{2 \times 1} \\ 0_{1 \times 2} & 1_{1 \times 1} \end{bmatrix} \begin{bmatrix} x \\ y \\1 \end{bmatrix}$

    其中, $R=\begin{bmatrix} \cos \theta &-\sin\theta \\ \sin \theta & \cos \theta \end{bmatrix}$ 为旋转矩阵, $T=\begin{bmatrix}t_x \\ t_y \end{bmatrix}$ 为平移矩阵

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

    -- 配图

1.2  相似变换

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

   $\quad \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} s \cos \theta & -s\sin \theta & t_x \\ s \sin \theta & s \cos \theta & t_y \\ 0&0&1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1\end{bmatrix} = \begin{bmatrix} sR_{2 \times 2} & T_{2 \times 1} \\ 0_{1 \times 2} & 1_{1 \times 1} \end{bmatrix} \begin{bmatrix} x \\ y \\1 \end{bmatrix}$,其中 $s$ 为缩放系数

   想象无限大光滑平面内的一张图片,在旋转和平移的过程中,其大小也会均匀缩放,则这样的变换就是相似变换

   -- 配图

1.3  仿射变换

1.3.1  定义

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

    矩阵表达式为 $\quad \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a_{11} & a_{12} & t_x \\ a_{21} & a_{22} & t_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} =\begin{bmatrix} A_{2 \times 2} & T_{2 \times 1} \\ 0_{1 \times 2} & 1_{1 \times 1} \end{bmatrix} \begin{bmatrix} x \\ y \\1 \end{bmatrix}$

    其中,当 $A = \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix}$ 是非奇异时,称 $A$ 为仿射矩阵

1.3.2  分解

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

    $\quad A = R(\theta) R(-\phi) D R(\phi)$,其中 $D = \begin{bmatrix} \lambda_1 & 0 \\ 0 & \lambda_2 \end{bmatrix}$是一个对角矩阵

    首先,旋转角度 $\phi$;然后在 $x$ 和 $y$ 方向上 (其中 $x\perp y$) 分别缩放 $\lambda_1$ 和 $\lambda_2$;再旋转角度 $-\phi$,也即回转 $\phi$;最后旋转角度 $\theta$

         

 

2  OpenCV 函数

2.1  仿射变换的矩阵

    仿射变换有 6 个未知数 ($\phi, \theta, \lambda_1, \lambda_2, t_x, t_y$),需列 6 组方程,而一组对应特征点 $(x,y)$ -> $(x′,y′)$ 可构造 2 个方程,因此,求解 6 个未知数,需要 3 组对应特征点

        

    OpenCV 中 getAffineTransform() 可求解 2x3 矩阵 $\begin{bmatrix} a_{11} & a_{12} & t_{x} \\ a_{21} & a_{22} & t_y \end{bmatrix}$     

 Mat getAffineTransform ( const Point2f    src[], // 源图像的三角形顶点坐标 const Point2f    dst[] // 目标图像的三角形顶点坐标 )   

    其代码实现比较简单,先构建方程组,再利用 solve() 求解 $Ax=b$   

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.2  相似变换的矩阵

    对于相似变换,有 4 个未知数 ($s, \theta, t_x, t_y$),对应 OpenCV 中的 getRotationMatrix2D() 函数    

 Mat getRotationMatrix2D (
      Point2f     center, // 原图像中的旋转中心点  double angle, // 旋转角度(正值代表逆时针旋转)  double scale // 均匀缩放系数  )

    该函数可得到如下矩阵:

    $\begin{bmatrix} \alpha & \beta & (1- \alpha ) \cdot \texttt{center.x} - \beta \cdot \texttt{center.y} \\ - \beta & \alpha & \beta \cdot \texttt{center.x} + (1- \alpha ) \cdot \texttt{center.y} \end{bmatrix}$

    其中 $\alpha=scale \cdot \cos angle$,$\beta=scale \cdot \sin angle$

2.3  仿射变换的图象 

     已知仿射变换的 $A_{2 \times 2}$ 和 $T_{2 \times 1}$ ,将原图像带入 warpAffine() ,便可得到仿射变换后的目标图像        

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  代码示例

    首先利用三角形的顶点,代入 getAffineTransform() 得到仿射变换的矩阵;再用 getRotationMatrix2D() 构造相似变换的矩阵;然后,warpAffine() 求解经过相似变换和仿射变换的图像;最后,显示对比变换后的目标图像

#include "opencv2/core.hpp" #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_mat1 = getAffineTransform(srcTri, dstTri);// 3.2) getRotationMatrix2D Mat warp_mat2 = getRotationMatrix2D(Point2f(0.5*src.cols, 0.5*src.rows), 45, 0.5); // 4) warpAffine image  Mat dst1,dst2;
    warpAffine(src, dst1, warp_mat1, Size(src.cols, src.rows));
    warpAffine(src, dst2, warp_mat2, Size(src.cols, src.rows)); // 5) show image imshow("image", src);
    imshow("warp affine 1", dst1);
    imshow("warp affine 2", dst2); waitKey(0);    
}

      检测结果对比如下:

               

 

参考资料

    《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 

    

 

OpenCV 之 图象几何变换的更多相关文章

  1. C/C++内存对齐详解

    1、什么是内存对齐 还是用一个例子带出这个问题,看下面的小程序,理论上,32位系统下,int占4byte,c......

  2. C++ 控制台弹出文件管理对话框案例

    在控制台程序中打开文件管理对话框,效果图如下所示:在需要弹出对话框的地方插入以下代码://打开文件管理窗口TCHAR......

  3. 虚函数表-C++多态的实现原理解析

    参考:http://c.biancheng.net/view/267.html1、说明我们都知道多态指的是父类的指针......

  4. 关于C++中构造函数的常见疑问

    基本概念我们已经知道在定义一个对象时,该对象会根据你传入的参数来调用类中对应的构造函数。同时,在释放这个对象时,会调......

  5. std::async的使用总结

    C++98标准中并没有线程库的存在,直到C++11中才终于提供了多线程的标准库,提供了管理线程、保护共享数据、线程间......

  6. std::async的使用总结

    C++98标准中并没有线程库的存在,直到C++11中才终于提供了多线程的标准库,提供了管理线程、保护共享数据、线程间......

  7. C++ 入门篇

    C++基础入门 1 C++初识 1.1 第一个C++程序 编写一个C++程序总共分为4个步骤 创建项目 ......

  8. C语言的进制转换及算法实现教程

    1、其他进制转十进制1.1、二进制转十进制转换规程: 从最低位开始,将每个位上的数提取出来,乘以2的(位数-1)次方......

  9. C语言之漫谈指针(下)

    C语言之漫谈指针(下)在上节我们讲到了一些关于指针的基础知识:详见:C语言之漫谈指针(上)本节大纲:零.小tips一......

  10. 关于 C++ 中的强制转换 - 基础篇

    引言假设有基类 A,包含了虚函数 func1,以及有派生类 B,继承于类 A,派生类 B 中实现了函数 func1。......

随机推荐

  1. Python jieba库分词模式实例用法

    在中文分词中,jiebe库是最为常见的,主要的原因还是它独特的支持分词模式如:精确模式、全模式、搜索引擎模式。也对应......

  2. Python抓取文件夹的所有文件,包括子文件夹和子文件夹的文件

    #!/user/bin/python # -*- coding:utf8 -*- import Basic i......

  3. 如何不使用 overflow: hidden 实现 overflow: hidden

    一个很有意思的题目。如何不使用 overflow: hidden 实现 overflow: hidden?CSS 中......

  4. Python骚操作从列表推导和生成器表达式开始

    序列序列是指一组数据,按存放类型分为容器序列与扁平序列,按能否被修改分为不可变序列与可变序列。容器序列与扁平序列容器......

  5. Python学习(7)(模块、pyc文件)

    Python学习(7)一、python的模块二、Pyc 文件一、python的模块模块是python程序架构的一个核......

  6. C# 两个类的实例之间相同属性的值的复制

    在进行实体转换操作的时候通常需要在对两个实体之间两个属性字段相同的类要进行一个互相的转换,我们要把a对象的所有字段的......

  7. css3实现背景渐变

    #grad {background: -webkit-linear-gradient(left,rgba(255,0......

  8. C#扫盲篇(四):.NET Core 的异步编程-只讲干货(async,await,Task)

    关于async,await,task的用法和解释最精简的说明关于async,await,task的用法和解释这里就不......

  9. 利用python为PostgreSQL的表自动添加分区

    PostgreSQL引进“分区”表特性,解放了之前采用“表继承”+“触发器”来实现分区表的繁琐、低效。而添加分区,都......

  10. PHP中PDO关闭连接的方法问题

    在之前我们手写 mysql 的连接操作时,一般都会使用 mysql_close() 来进行关闭数据库连接的操作。不过......