什么是仿射变换?

  1. 一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移).

  2. 综上所述, 我们能够用仿射变换来表示:

    1. 旋转 (线性变换)
    2. 平移 (向量加)
    3. 缩放操作 (线性变换)

    你现在可以知道, 事实上, 仿射变换代表的是两幅图之间的 关系 .

    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include <iostream>
    #include <stdio.h> using namespace cv;
    using namespace std; /// 全局变量
    char* source_window = "Source image";
    char* warp_window = "Warp";
    char* warp_rotate_window = "Warp + Rotate"; /** @function main */
    int main( int argc, char** argv )
    {
    Point2f srcTri[3];
    Point2f dstTri[3]; Mat rot_mat( 2, 3, CV_32FC1 );
    Mat warp_mat( 2, 3, CV_32FC1 );
    Mat src, warp_dst, warp_rotate_dst; /// 加载源图像
    src = imread( argv[1], 1 ); /// 设置目标图像的大小和类型与源图像一致
    warp_dst = Mat::zeros( src.rows, src.cols, src.type() ); /// 设置源图像和目标图像上的三组点以计算仿射变换
    srcTri[0] = Point2f( 0,0 );
    srcTri[1] = Point2f( src.cols - 1, 0 );
    srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
    dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
    dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 ); /// 求得仿射变换
    warp_mat = getAffineTransform( srcTri, dstTri ); /// 对源图像应用上面求得的仿射变换
    warpAffine( src, warp_dst, warp_mat, warp_dst.size() ); /** 对图像扭曲后再旋转 */ /// 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
    Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
    double angle = -50.0;
    double scale = 0.6; /// 通过上面的旋转细节信息求得旋转矩阵
    rot_mat = getRotationMatrix2D( center, angle, scale ); /// 旋转已扭曲图像
    warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() ); /// 显示结果
    namedWindow( source_window, CV_WINDOW_AUTOSIZE );
    imshow( source_window, src ); namedWindow( warp_window, CV_WINDOW_AUTOSIZE );
    imshow( warp_window, warp_dst ); namedWindow( warp_rotate_window, CV_WINDOW_AUTOSIZE );
    imshow( warp_rotate_window, warp_rotate_dst ); /// 等待用户按任意按键退出程序
    waitKey(0); return 0;
    }

    说明

    1. 定义一些需要用到的变量, 比如需要用来储存中间和目标图像的Mat和两个需要用来定义仿射变换的二维点数组.

      Point2f srcTri[3];
      Point2f dstTri[3]; Mat rot_mat( 2, 3, CV_32FC1 );
      Mat warp_mat( 2, 3, CV_32FC1 );
      Mat src, warp_dst, warp_rotate_dst;
    2. 加载源图像:

      src = imread( argv[1], 1 );
      
    3. 以与源图像同样的类型和大小来对目标图像初始化:

      warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
      
    4. 仿射变换: 正如上文所说, 我们需要源图像和目标图像上分别一一映射的三个点来定义仿射变换:

      srcTri[0] = Point2f( 0,0 );
      srcTri[1] = Point2f( src.cols - 1, 0 );
      srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
      dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
      dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );

      你可能想把这些点绘出来以获得对变换的更直观感受. 他们的位置大概就是在上面图例中的点的位置 (原理部分). 你会注意到由三点定义的三角形的大小和方向改变了.

    5. 通过这两组点, 我们能够使用OpenCV函数 getAffineTransform 来求出仿射变换:

      warp_mat = getAffineTransform( srcTri, dstTri );
      

      我们获得了用以描述仿射变换的 2X3 矩阵 (在这里是 warp_mat)

    6. 将刚刚求得的仿射变换应用到源图像

      warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
      

      函数有以下参数:

      • src: 输入源图像
      • warp_dst: 输出图像
      • warp_mat: 仿射变换矩阵
      • warp_dst.size(): 输出图像的尺寸

      这样我们就获得了变换后的图像! 我们将会把它显示出来. 在此之前, 我们还想要旋转它...

    7. 旋转: 想要旋转一幅图像, 你需要两个参数:

      1. 旋转图像所要围绕的中心
      2. 旋转的角度. 在OpenCV中正角度是逆时针的
      3. 可选择: 缩放因子

      我们通过下面的代码来定义这些参数:

      Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
      double angle = -50.0;
      double scale = 0.6;
    8. 我们利用OpenCV函数 getRotationMatrix2D 来获得旋转矩阵,
      这个函数返回一个 2X3  矩阵 (这里是 rot_mat)

      rot_mat = getRotationMatrix2D( center, angle, scale );
      
    9. 现在把旋转应用到仿射变换的输出.

      warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
      
    10. 最后我们把仿射变换和旋转的结果绘制在窗体中,源图像也绘制出来以作参照:

      namedWindow( source_window, CV_WINDOW_AUTOSIZE );
      imshow( source_window, src ); namedWindow( warp_window, CV_WINDOW_AUTOSIZE );
      imshow( warp_window, warp_dst ); namedWindow( warp_rotate_window, CV_WINDOW_AUTOSIZE );
      imshow( warp_rotate_window, warp_rotate_dst );
    11. 等待用户退出程序

      waitKey(0);

OpenCV实现仿射变换的更多相关文章

  1. 【转】【opencv】仿射变换

    仿射变换 目标 在这个教程中你将学习到如何: 使用OpenCV函数 warpAffine 来实现一些简单的重映射. 使用OpenCV函数 getRotationMatrix2D 来获得一个  旋转矩阵 ...

  2. opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移

    常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...

  3. OpenCV图像变换(仿射变换与透视变换)

    仿射变换(affine transform)与透视变换(perspective transform)在图像还原.图像局部变化处理方面有重要意义.通常,在2D平面中,仿射变换的应用较多,而在3D平面中, ...

  4. 【OpenCV新手教程之十八】OpenCV仿射变换 &amp; SURF特征点描写叙述合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨)  ...

  5. 计算机视觉2D几何基元及其变换介绍和OpenCV WarpPerspective源码分析

    2D图像几何基元 一般的,表示一个2d几何基元只用两个维度(比如x,y)就可以表示了,但是在计算机视觉研究中,为了统一对2d几何基元的操作(后面讲到的仿射,透射变换),一般会以增广矢量的方式表示几何基 ...

  6. EasyPR--开发详解(5)颜色定位与偏斜扭转

    本篇文章介绍EasyPR里新的定位功能:颜色定位与偏斜扭正.希望这篇文档可以帮助开发者与使用者更好的理解EasyPR的设计思想. 让我们先看一下示例图片,这幅图片中的车牌通过颜色的定位法进行定位并从偏 ...

  7. EasyPR--开发详解

    我正在做一个开源的中文车牌识别系统,Git地址为:https://github.com/liuruoze/EasyPR. 我给它取的名字为EasyPR,也就是Easy to do Plate Reco ...

  8. opencv 用户文档 错误更正 仿射变换

    今天在看opencv官方给出的仿射变换计算仿射变换矩阵的文档的时候,发现官方文档中有个很明显的错误,再次给大家提个醒. 官方文档连接: http://opencv.willowgarage.com/d ...

  9. OpenCV探索之路(八):重映射与仿射变换

    重映射 重映射就是把一幅图像中某个位置的像素放置到另一个图片中指定位置的过程. 用一个数学公式来表示就是: 其中的 f 就是映射方式,也就说,像素点在另一个图像中的位置是由 f 来计算的. 在Open ...

随机推荐

  1. 记住经典的斐波拉契递归和阶乘递归转换为while规律

    记住经典的斐波拉契递归和阶乘递归转换为while规律.它为实现更复杂转换提供了启发性思路. # 斐波拉契--树形递归 def fab(n): if n<3: return n return fa ...

  2. Java中synchronized的使用实例

    一.使用场景 在负责后台开发的时候,很多时候都是提供接口给前端开发人员去调用,会遇到这样的场景: 需要提供一个领奖接口,每个用户名只能领取一次,我们可以将成功领取的用户在数据库用个标记保存起来.如果这 ...

  3. Android事件总线EventBus详解

    顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity.Fragment.Service等组件之间的交互,很大程度上降低了它们之间的耦合,使我们的代码 ...

  4. shell 参数列表的获取&shell使用的一些总结

    最近在修改公司的一些cron,自己也是第一次接触和学习shell.对于一些零散但是常用的知识点,做一点点的总结. 拿出一个方法说说吧,方法如下:(信息量挺大的,请耐心看下面的说明) trans_cou ...

  5. app如何更换用户头像信息呢?不妨这样做

    对于现在的手机应用而言,要想获得更多的人的使用,就需要给用户更多的自由功能才行,这也是基于用户体验开发软件的核心思想,一切以用户为中心,想用户之所想,做用户之所需.今天我就来谈一谈刚学到的一个关于设置 ...

  6. android开发之this.finish()的使用

    在一个Activity用完之后应该将之finish掉,但是,之前在学校里自己摸索着开发时并没有太注意这个问题,因为activity无论是否finish掉对功能的影响貌似都不是那么明显(这是读书时候的观 ...

  7. Dynamics CRM 2013 Homepage Ribbon 按钮引用多个Javascript资源

    在CRM的开发中ribbon的开发是比较重要的一环,很多客制化的功能都需要动用ribbon区,CRM2013中的名字已经改叫command bar了,但从老版本过来的人都还是习惯叫他ribbon. R ...

  8. Android初级教程获取手机位置信息GPS与动态获取最佳方式

    简单介绍一下gps定位的操作. 主要是靠locationmanger这个api完成的一些操作:通过获取这个实例,然后调用它的requestLocationUpdates方法进行注册.传入的参数分别有以 ...

  9. SQL Server性能优化与管理的艺术 附件下载地址

    首先感谢读者们对鄙人的支持,购买了<SQL Server性能优化与管理的艺术>,由于之前出版社的一些疏忽,附件没有上传成功,再次本人深表歉意. 请需要下载附件的读者从下面链接下载,谢谢: ...

  10. 类装载器DexClassLoader (android内核剖析)

    在java环境中,有个概念叫做"类装载器",其作用是动态装载Class文件.标准的java SDK中有一个ClassLoader类,借助它可以装载 想要的Class文件,每个Cla ...