什么是仿射变换?

  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);

最新文章

  1. BootStrap 的modal 中使用typeahead
  2. Java的数组长度无需编译指定,因为它是对象
  3. css 小三角
  4. postgresql 死锁处理
  5. curl命令常见用法汇总 good
  6. EF4.1之基础(实现Code First)
  7. 【小结】有关mysql扩展库和mysqli扩展库的crud操作封装
  8. Android中PopupWindow中有输入框时无法弹出输入法的解决办法
  9. STARTUP.A51详解及如何使能可重入函数
  10. 学号:201521123116 《java程序设计》第六周学习总结
  11. 委托、事件、Observer观察者模式的使用解析一
  12. js通用方法检測浏览器是否已安装指定插件(IE与非IE通用)
  13. 解决ssh登录慢的问题
  14. asp.net 本地服务字段调用(WebSerice)的小问题
  15. Django项目及应用的创建
  16. 转发:已知rsa的模数和指数 生成pem公钥文件
  17. Go-day07
  18. 【js】字符串反转可实现的几种方式
  19. python多进程没有锁队列范例
  20. python记录_day30 多进程

热门文章

  1. android Handler机制之ThreadLocal详解
  2. Android自动打包工具aapt详解
  3. ejabberd编译更新脚本
  4. 集合框架之Map接口
  5. Python图片处理库之PIL
  6. ORA-12519: TNS:no appropriate service handler found 解决
  7. ExtJS学习(三)Grid表格
  8. Linux系统编程-----进程fork()
  9. 使用LRU算法缓存图片,android 3.0
  10. 修改Chrome默认的搜索引擎