基于c++和opencv底层的图像旋转
图像旋转:本质上是对旋转后的图片中的每个像素计算在原图的位置。
在opencv包里有自带的旋转函数,当你知道倾斜角度theta时:
用getRotationMatrix2D可得2X3的旋转变换矩阵 M,在用warpaffine函数可得倾斜后的图像dst。
很方便啊,为什么还要自己实现底层的图像旋转呢?因为有些地方你用这两个函数就会出现问题,比如说:
当原图的size是MXN,且图像是完全填充的(因为如果有留白可能还不能将问题完全反映出来),现在你需要将它90°变换(为了形象说明),可是用前面两个API会产生什么结果呢?如下:


噢?有一部分看不见了…我们来看一下上面两个函数:
C++: Mat getRotationMatrix2D(Point2f center, double angle, double scale)
C++: void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
一开始我以为是这两个参数的设置问题。一般来说,center是设置在源图像中心点进行翻转的,现在有一部分看不见了,是不是中心点的问题呢?我换个中心点可不可以呢?实验证明,分别设置左上、下;右上,下角点为中心点时,都会有部分图像显示不出来。我放弃center,改试dsize,改大一点,还是不行,只是显示和黑色面积分别更大了,显示的比例并没有改变什么。这时候就不必再试了,有这个功夫我还不如自己写个旋转函数呢。为什么会这样呢,因为原图的面积不够大啊,一旋转就出去了啊,所以依然不想自己写函数(不建议,因为写的函数代码很简单啊)的童鞋,用copyMakeborder函数在原图上添加边界再旋转吧。添加完边界后问题又来了:边界添加的小图像还是会旋出去。边界添加的大,图片本身就很小了(就像置身于一片苍茫的黑色的宇宙中,当然是夸张),接下来你要对原来的图片中的像素进行什么处理就更困难了,有点小了。必要时还得去边界,opencv里面好像可没有现成的去边界函数啊。
所以(前言说的太长),我们有必要自己写个简单方便的旋转函数。先给出旋转矩阵及其变换关系(别问我怎么来的,我是拿现成的老师教得,不过知道的朋友可以图解一下)

代码如下:
Mat angleRectify(Mat img,float angle)
{
Mat retMat = Mat::zeros(,, CV_8UC3);
float anglePI =(float) (angle * CV_PI / );
int xSm, ySm;
for(int i = ; i < retMat.rows; i++)
for(int j = ; j < retMat.cols; j++)
{
xSm = (int)((i-retMat.rows/)*cos(anglePI) - (j-retMat.cols/)*sin(anglePI) + 0.5);
ySm = (int)((i-retMat.rows/)*sin(anglePI) + (j-retMat.cols/)*cos(anglePI) + 0.5);
xSm += img.rows / ;
ySm += img.cols / ;
if(xSm >= img.rows || ySm >= img.cols || xSm <= || ySm <= ){
retMat.at<Vec3b>(i, j) = Vec3b(, );
}
else{
retMat.at<Vec3b>(i, j) = img.at<Vec3b>(xSm, ySm);
}
} return retMat;
}
这里有几个问题需要说明:
1 本来需要变换后图片乘以 原图变换矩阵的逆矩阵 对应到原图中坐标。但是因为y轴方向向下,所以变换后图片乘以原图变换矩阵(无需逆矩阵)即可对应到原图中坐标
2 第9、10行需要 -retMat.rows/2和retMat.cols/2的原因在于,图像是以(retMat.cols/2,retMat.rows/2)为坐标原点旋转的,所以变换后图片中的每个像素点(i; j),需要平移到相对旋转中心的新坐标,即(i - Mat.rows/2; j - Mat.cols/2)。
- 第11,12行表示在计算完成之后,需要再次还原到相对左上角原点的旧坐标;
- 矩阵下标与原图变换矩阵相乘之前,需要将矩阵下标两值互换。相乘之后,需要再次互换下标值还原成矩阵下标。这句话是什么意思呢,我们可以看出
,但是实际 xSm = (int)((i-retMat.rows/2)*cos(anglePI) - (j-retMat.cols/2)*sin(anglePI) + 0.5); (i-retMat.rows/2)是我们认为的纵坐标。 - 旋转角度整数代表顺时针,负数代表逆时针(这点在opencv里的很多情况下都使用)
- 这里表示彩色图的旋转,Vec3b是彩色类型,如果是灰度图,将<>里的类型换成uchar并将retMat.at<Vec3b>(i, j) = Vec3b(0, 0); =>retMat.at<uchar>(i, j) = uchar(0);
- 如果是要旋转90°这种已知dst的size的情况下可以指定reMat的size从而不边界,其他类型还是会有边界的(但是比自己copyMakeBorder会小一些),因为你不知道reMat需要多大。
如果是从原图* 变化矩阵算目标图的坐标就会产生一定的黑色点,那是因为再取整时,略过了一些点的坐标,而保留了他的初始化的像素值0



文章参考:http://blog.csdn.net/ironyoung/article/details/41117039?utm_source=tuicool
基于c++和opencv底层的图像旋转的更多相关文章
- 【图像处理】基于OpenCV底层实现的图片旋转
image processing 系列 [图像处理]直方图匹配 [图像处理]高斯滤波.中值滤波.均值滤波 图片旋转,本质上是对旋转后的图片中每一个像素点计算在原图的位置.然后照搬过来就好. (多说一句 ...
- OpenCV计算机视觉学习(11)——图像空间几何变换(图像缩放,图像旋转,图像翻转,图像平移,仿射变换,镜像变换)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 图像 ...
- opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移
常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...
- 基于qml创建最简单的图像处理程序(3)-使用opencv&qml进行图像处理
<基于qml创建最简单的图像处理程序>系列课程及配套代码基于qml创建最简单的图像处理程序(1)-基于qml创建界面http://www.cnblogs.com/jsxyhelu/p/83 ...
- OpenCV:OpenCV图像旋转的代码
OpenCV图像旋转的代码 cv::transpose( bfM, bfM ) 前提:使用两个矩阵Mat型进行下标操作是不行的,耗费的时间太长了.直接使用两个指针对拷贝才是王道.不知道和OpenCV比 ...
- opencv 图像旋转
理论 http://www.cnblogs.com/wangguchangqing/p/4045150.html 翻开任意一本图像处理的书,都会讲到图像的几何变换,这里面包括:仿射变换(affine ...
- OpenCV图像旋转
图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持这原始尺寸.图像旋转后图像的水平对称轴.垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换. 如下图: 假 ...
- 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起
话说,平凡之处显真格,这一点也没错! 比如,对旋转图像进行双线性插值,很简单吧? 可,对我,折腾了大半天,也没有达到预期效果! 尤其是三个误区让我抓瞎好久: 1,坐标旋转公式. 这东西,要用 ...
- OpenCV 图像旋转实现
1 旋转矩形 首先建议阅读图像旋转算法原理-旋转矩阵,这篇博客可以让你很好地理解图像中的每一个点是如何进行旋转操作的.其中涉及到了图像原点与笛卡尔坐标原点之间的相互转换以及点旋转的一些公式推导. 这里 ...
随机推荐
- ecshop跨站漏洞详情及修补网站漏洞
ecshop目前最新版本为4.0,是国内开源的一套商城系统,很多外贸公司,以及电商平台都在使用,正因为使用的人数较多,很多攻击者都在挖掘该网站的漏洞,就在最近ecshop被爆出高危漏洞,该漏洞利用跨站 ...
- 立个Flag (20180617-20181231)
入行7年,今年年初正式接触Java,前面6年一直在做C++相关的工作,去年年中跳槽,语言从C++转向了C#,半年之后又转向了Java. 虽说语言有相似性,但每种语言都有自己独有的知识体系,想要游刃有余 ...
- hive 打印日志
hive -hiveconf hive.root.logger=INFO,console -e 'select 1' hive 打印log ,有时hive 在配置时默认不会将mapper reduce ...
- JetBrains Makes its Products Free for Students(JetBrains 对学生免费了)
只要你有大学有些 后缀是 .edu的 如:@buaa.edu.cn,用你的邮箱注册,就可以免费试用 JetBrains了 下面是详细注册步骤: Hello everyone, If you’re o ...
- 成都Uber优步司机奖励政策(3月11日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- CodeForces 547D Mike and Fish 思维
题意: 二维平面上给出\(n\)个点,然后对每个点进行染色:红色和蓝色,要求位于同一行或同一列的点中,红色点和蓝色点的个数相差不超过1 分析: 正解是求欧拉路径,在这篇博客中看到一个巧妙的思路: 对于 ...
- 水灾 1000MS 64MB (广搜)
水灾(sliker.cpp/c/pas) 1000MS 64MB 大雨应经下了几天雨,却还是没有停的样子.土豪CCY刚从外地赚完1e元回来,知道不久除了自己别墅,其他的地方都将会被洪水淹没. CCY ...
- 数据爬取后台(PHP+Python)联合作战
一. 项目声明 本项目从前端,到后台,以及分布式数据抓取,乃我一个人所写,因此项目并不太完善!在语义分析以及数据处理上并不能尽如意.但是极大的减轻了编辑的工作量! 二. 项目所用技术 本项目中前端采用 ...
- 鸡啄米:C++编程之十四学习之构造函数和析构函数
1. 本人学习鸡啄米课程的笔记记录,用来记录学习的历程和进度 2. 构造函数 我们在声明一个变量时,如果对它进行了初始化,那么在为此变量分配内存空间时还会向内存单元中写入变量的初始化.声明对象有相似的 ...
- python切片技巧
写一个程序,打印数字1到100,3的倍数打印“Fizz”来替换这个数,5的倍数打印“Buzz”,对于既是3的倍数又是5的倍数的数字打印“FizzBuzz” for x in range(101): p ...