OpenCV实现仿射变换

Stella981
• 阅读 689

什么是仿射变换?

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

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

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

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

OpenCV实现仿射变换

  1.  #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);

本文同步分享在 博客“shiter”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
OpenCV实现仿射变换
什么是仿射变换?¶(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.opencv.org.cn%2Fopencvdoc%2F2.3.2%2Fhtml%2Fdoc%2Ftutorials%2Fimgproc%2Fimgtrans%2Fwarp_affine%2Fwarp_af
Wesley13 Wesley13
3年前
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换
1.几何变换的基本概念  图像几何变换又称为图像空间变换,它将一副图像中的坐标位置映射到另一幅图像中的新坐标位置。我们学习几何变换就是确定这种空间映射关系,以及映射过程中的变化参数。图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算:1.原图像任意像素
Wesley13 Wesley13
3年前
D3D12学习笔记3.3——仿射变换
·仿射变换是由一个线性变换与一个平移变换组合而成。对于向量来说平移操作是没有意义的,而平移变换只能应用于点。·齐次坐标表示,是将原先的三元组扩展成四元组,第四个坐标w的取值将根据被描述对象是点还是向量而定。具体如下:w0是向量,w1是点。·说完两个基本概念后,我们来说说仿射变换的具体公式:!a(u)\iota(u)b
Stella981 Stella981
3年前
CoreGraphics 之CGAffineTransform仿射变换(3)
 CoreGraphics的仿射变换可以用于平移、旋转、缩放变换路径或者图形上下文。  (1)平移变换将路径或图形上下文中的形状的当前位置平移到另一个相对位置。举例来说,如果你在(10,20)的位置处画一个点,对它应用(30,40)的平移变换,然后绘制它,这个点将被绘制在(40,60)的位置处。为了创建一个平移变换,使用CGAffineTra
Stella981 Stella981
3年前
OpenCV仿射变换+投射变换+单应性矩阵
OpenCV仿射变换投射变换单应性矩阵(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fwqj1212%2Fp%2F3915859.html)
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这