【转】【opencv】仿射变换
仿射变换
原理
什么是仿射变换?
一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移).
综上所述, 我们能够用仿射变换来表示:
- 旋转 (线性变换)
- 平移 (向量加)
- 缩放操作 (线性变换)
你现在可以知道, 事实上, 仿射变换代表的是两幅图之间的 关系 .
我们通常使用
矩阵来表示仿射变换.
考虑到我们要使用矩阵
和
对二维向量
做变换, 所以也能表示为下列形式:
or
怎样才能求得一个仿射变换?
好问题. 我们在上文有提到过仿射变换基本表示的就是两幅图片之间的 联系 . 关于这种联系的信息大致可从以下两种场景获得:
- 我们已知
和 T 而且我们知道他们是有联系的. 接下来我们的工作就是求出矩阵
- 我们已知
and
. 要想求得
. 我们只要应用算式
即可. 对于这种联系的信息可以用矩阵
清晰的表达 (即给出明确的2×3矩阵) 或者也可以用两幅图片点之间几何关系来表达.
- 我们已知
让我们形象地说明一下. 因为矩阵
联系着两幅图片, 我们以其表示两图中各三点直接的联系为例. 见下图:
点1, 2 和 3 (在图一中形成一个三角形) 与图二中三个点一一映射, 仍然形成三角形, 但形状已经大大改变. 如果我们能通过这样两组三点求出仿射变换 (你能选择自己喜欢的点), 接下来我们就能把仿射变换应用到图像中所有的点.
例程
- 这个例程是做什么的?
- 加载一幅图片
- 对这幅图片应用仿射变换. 这个变换是从源图像和目标图像的两组三点之间的联系获得的. 这一步我们使用函数 warpAffine 来实现.
- 仿射变换结束后再对图像应用旋转. 这里的旋转绕图像中点
- 等待用户退出程序
- 例程代码在下面给出. 当然你也可以从 这里 下载
- #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;
- }
说明
定义一些需要用到的变量, 比如需要用来储存中间和目标图像的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;
- Point2f srcTri[3];
加载源图像:
- 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 );
你可能想把这些点绘出来以获得对变换的更直观感受. 他们的位置大概就是在上面图例中的点的位置 (原理部分). 你会注意到由三点定义的三角形的大小和方向改变了.
- srcTri[0] = Point2f( 0,0 );
通过这两组点, 我们能够使用OpenCV函数 getAffineTransform 来求出仿射变换:
- warp_mat = getAffineTransform( srcTri, dstTri );
我们获得了用以描述仿射变换的
矩阵 (在这里是 warp_mat)
将刚刚求得的仿射变换应用到源图像
- warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
函数有以下参数:
- src: 输入源图像
- warp_dst: 输出图像
- warp_mat: 仿射变换矩阵
- warp_dst.size(): 输出图像的尺寸
这样我们就获得了变换后的图像! 我们将会把它显示出来. 在此之前, 我们还想要旋转它...
旋转: 想要旋转一幅图像, 你需要两个参数:
- 旋转图像所要围绕的中心
- 旋转的角度. 在OpenCV中正角度是逆时针的
- 可选择: 缩放因子
我们通过下面的代码来定义这些参数:
- Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
- double angle = -50.0;
- double scale = 0.6;
我们利用OpenCV函数 getRotationMatrix2D 来获得旋转矩阵, 这个函数返回一个
矩阵 (这里是 rot_mat)
- 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 );
- namedWindow( source_window, CV_WINDOW_AUTOSIZE );
等待用户退出程序
- waitKey(0);
结果
编译上述例程之后, 我们给出一个图像的路径作为参数. 比如这样一幅图片:
仿射变换后我们获得:
最后, 应用了一个负角度旋转 (记住负角度指的是顺时针) 和基于一个缩放因子的缩放之后, 我们得到:
翻译者
zhlifly@ OpenCV中文网站 <zhlifly@gmail.com>
【转】【opencv】仿射变换的更多相关文章
- 【OpenCV新手教程之十八】OpenCV仿射变换 & SURF特征点描写叙述合辑
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨) ...
- OpenCV——仿射变换
什么是仿射变换? 一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移). 综上所述, 我们能够用仿射变换来表示: 旋转 (线性变换) 平移 (向量加) 缩放操作 ( ...
- OpenCV仿射变换+投射变换+单应性矩阵
本来想用单应性求解小规模运动的物体的位移,但是后来发现即使是很微小的位移也会带来超级大的误差甚至错误求解,看起来这个方法各种行不通,还是要匹配知道深度了以后才能从三维仿射变换来入手了,纠结~ esti ...
- opencv 仿射变换 投射变换, 单应性矩阵
仿射 estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变. getAffineTransform( ...
- opencv 仿射变换
import cv2 as cv import numpy as np img = cv.imread('../images/face.jpg') h, w = img.shape[:2] mat_s ...
- 系列文章 -- OpenCV入门教程
<OpenCV3编程入门>内容简介&勘误&配套源代码下载 [OpenCV入门教程之十八]OpenCV仿射变换 & SURF特征点描述合辑 [OpenCV入门教程之 ...
- 目标检测之harr---角点检测harr 的opencv实现
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/29356187 作者:毛星云(浅墨) ...
- opencv 用户文档 错误更正 仿射变换
今天在看opencv官方给出的仿射变换计算仿射变换矩阵的文档的时候,发现官方文档中有个很明显的错误,再次给大家提个醒. 官方文档连接: http://opencv.willowgarage.com/d ...
- opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移
常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...
随机推荐
- Windows平台下Qt开发环境的搭建
Qt 是采用开源和商用双协议发布的开放源代码的图形开发类库,现在很多图形化的开源软件都使用了Qt. 下载地址:http://qt-project.org/downloads 1. 下载安装包 你可以从 ...
- php 商务网站购物车联动地址
数据表如下: CREATE TABLE IF NOT EXISTS `china` (`region_id` smallint(5) unsigned NOT NULL, `parent_id` s ...
- 新开窗口不被拦截的方法-window.open和表单提交form
$("#btn").click(function() { var w = window.open(); setTimeout(function() { w.location = & ...
- CentOS 6.4 使用YUM 安装MySQL5.5
1.首先需要下载Yum的支持包: http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm 2.下载完成后将Yum库导入到你的 ...
- xcopy总是询问是文件名还是目录名
我需要运行类似xcopy /y a.xml .\pics\b.xml很多次,但xcopy总是问我“文件名还是目录名” 可以这样通过管道来做echo f | xcopy /y a.xml .\pics\ ...
- silverlight5开发的翻牌游戏
扑克牌用了一个自定义控件,代码如下 public class CardButton : Button { public int state;//是否翻转0是未翻转,1是已翻转 private stat ...
- python 遍历字典
dict={"a":"apple","b":"banana","o":"orange&qu ...
- HTML5解决跨域问题
HTML5解决跨域问题 由于浏览器的同源策略,网络连接的跨域访问是不被允许的,XHR对象不能直接与非同源的网站处理数据交互.而同源指的是什么呢?同源的范畴包括:规则(协议),主机号(域名.ip等),端 ...
- 如何使用mysql的grant命令(详解)
grant命令的基本格式 grant 权限 on 数据库对象 to 用户 实例一:在任意ip地址登陆的common_user用户可以对testdb数据库里的数据进行查询操作.插入操作.更新操作.删除操 ...
- ANDROID_MARS学习笔记_S02_012_ANIMATION_利用AnimationListener在动画结束时删除或添加组件
一.代码 1.xml(1)activity_main.xml <?xml version="1.0" encoding="utf-8"?> < ...