0 引言

在数字图像处理技术中,图像旋转算法是最基本的操作之一。本文实现一种快速的图像旋转算法,并和原始方法以及opencv提供的旋转方案进行速度上的比较。

1 基本原理

图像旋转有两种计算坐标的思路,分别是前向映射和反向映射。其中后向映射在插值运算时较为方便,前向映射时计算插值则需要先知道全局的映射关系。如下图所示,反向映射是从旋转后图像出发,寻找其在原图中的位置,如果该位置不是整数坐标的话,则利用周围像素值进行插值运算。同理前向映射也是类似,但是差别是,前向映射四周的像素灰度值在当前并不是已知的,所以要算完整图的权重后再进行归一化。

![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/381fad556ab6837aa53ec7e30b72ccd0.png)

正向和反向映射的公式见:http://blog.csdn.net/lkj345/article/details/50555870 在网上所有的公式中,这个是比较靠谱的,考虑了笛卡尔坐标系和图像坐标系的差别。

2 优化方法

由上面的原理可以得到朴素的求解方法,以反向映射为例,计算旋转后图像中每个整数坐标在原图中的精确坐标。从上面给出的公式可以知道每个坐标点的计算有若干次浮点乘法和加法。有一个想法是,既然知道旋转角度,知道每行第一个元素的坐标,即可用图形学中直线绘制的算法计算出这一行中剩下点的坐标。例如bresenham直线算法、DDA直线算法等。

这样可以避免多次乘法运算,只计算每行第一个像素的精确坐标,剩下的坐标使用浮点加法计算(DDA)即可。更进一步的,只要知道原图第一行的精确坐标,剩下每一行的第一个坐标也可用此思想计算。这样整个图只有第一个像素点需要使用浮点乘法计算精确坐标,剩下的像素坐标可以通过浮点加法计算出来。
该思想具体内容可参考文献[1]。但是个人觉得其中伪代码的描述不算是bresenham算法,bresenham算法中利用一个递推的表达式完全去掉了浮点运算(包括乘法和加法),倒更像是DDA算法的思想。

3 代码清单

这里给出cpp版本的图像旋转实现

代码1 最近邻插值和双线性插值的实现

/*
% INTERPOLATE 插值方法
% f 原图 sz图像大小
% m 原图y整数坐标
% n 原图x整数坐标
% ex x和亚像素坐标误差
% ey x和亚像素坐标误差
% way 1为最近邻插值,2为双线性插值方法
*/
inline BYTE Interpolate(BYTE f[], int sz[], int m, int n, float ex, float ey, char way){BYTE gray=0;float fr1, fr2, fr3;//1. 误差统一到0到1之间if (ex < 0){ex = 1 + ex;n--;}if (ey < 0){ey = 1 + ey;m--;}if (m <= 0 || n <= 0)return gray;//2. 最邻近差值if (way == 1){if (ex > 0.5)n++;if (ey > 0.5)m++;if (m > sz[0] || n > sz[1])return gray;gray = f[sz[1]*m+n];return gray;}// 3.双线性插值if (((m + 1) > sz[0]) || ((n + 1) > sz[1]))return gray;if (way == 2){fr1 = (1 - ex)*float(f[sz[1] * m + n]) + ex*float(f[sz[1] * m + n + 1]);fr2 = (1 - ex)*float(f[sz[1] * (m + 1) + n]) + ex*float(f[sz[1] * (m+1) + n + 1]);fr3 = (1 - ey)*fr1 + ey*fr2;gray = BYTE(fr3);}return gray;
}

代码2 原始的反向映射算法

BYTE* normalRoate(BYTE img[],int w,int h,double theta,int *neww,int *newh){float fsin, fcos,c1,c2,fx,fy,ex,ey;int w1, h1,xx,yy;int sz[2] = {h,w};//1. 计算基本参数fsin = sin(theta);fcos = cos(theta);*newh=h1 = ceilf(abs(h*fcos) + abs(w*fsin));*neww=w1 = ceilf(abs(w*fcos) + abs(h*fsin));auto I1 = new(std::nothrow) BYTE[w1*h1];if (!I1)return NULL;memset(I1, 0, w1*h1);c1 = (w - w1*fcos - h1*fsin) / 2;c2 = (h + w1*fsin - h1*fcos) / 2;//2. 计算反向坐标并计算插值for (int y = 0; y < h1; y++){for (int x = 0; x < w1; x++){//计算后向映射点的精确位置 每个点都使用原始公式计算fx = x*fcos + y*fsin + c1; //四次浮点乘法和四次浮点加法fy = y*fcos - x*fsin + c2;xx = roundf(fx);yy = roundF(fy);ex = fx - float(xx);ey = fy - float(yy);I1[w1*y+x] = Interpolate(img,sz,yy, xx, ex, ey, 2);//双线性插值}}return I1;
}

代码3 基于直线算法的图像旋转

BYTE* DDARoateFast(BYTE img[], int w, int h, double theta, int *neww, int *newh){float fsin, fcos, c1, c2, fx, fy, ex, ey;int w1, h1, xx, yy;int sz[2] = { h, w };//1. 计算旋转参数fsin = sin(theta);fcos = cos(theta);*newh = h1 = ceilf(abs(h*fcos) + abs(w*fsin));*neww = w1 = ceilf(abs(w*fcos) + abs(h*fsin));auto I1 = new(std::nothrow) BYTE[w1*h1];if (!I1)return NULL;memset(I1, 0, w1*h1);c1 = (w - w1*fcos - h1*fsin) / 2; //见文献[1]的公式c2 = (h + w1*fsin - h1*fcos) / 2;fx = c1-fsin;fy = c2-fcos;//2. 计算反向坐标并计算插值for (int y = 0; y < h1; y++){//整个二层循环中计算坐标点时都没有浮点乘法运算//计算第一个后向映射点的精确位置fx = fx + fsin;fy = fy + fcos;// 计算第一个点对应的栅格位置xx = roundf(fx);yy = roundF(fy);ex = fx - float(xx);//误差ey = fy - float(yy);for (int x = 0; x < w1; x++){I1[w1*y + x] = InterpolateFast(img, sz, yy, xx, ex, ey, 2);ex = ex + fcos;ey = ey - fsin;if (ex>0.5){xx++;ex = ex - 1;}if (ey < -0.5){yy--;ey = ey + 1;}}}return I1;
}

代码4 前向映射的opencv实现

void FastRotateImage(Mat &srcImg, Mat &roateImg, float degree){assert((srcImg.cols > 0) && (srcImg.rows > 0));float fsin, fcos, c1, c2, fx, fy, xx, yy;int w1, h1,w,h;w = srcImg.cols;h = srcImg.rows;int sz[2] = { srcImg.rows, srcImg.cols };Mat map1_x, map2_y,m1,m2,m3,sPoint,newMap; //m1 m2 m3 为前文推荐博客中的基本矩阵//1. 计算旋转参数fsin = sin(degree);fcos = cos(degree);h1 = ceilf(abs(h*fcos) + abs(w*fsin));w1 = ceilf(abs(w*fcos) + abs(h*fsin));roateImg.create(h1, w1, CV_8UC1); //srcImg.typemap1_x.create(srcImg.size(), CV_32FC1);map2_y.create(srcImg.size(), CV_32FC1);c1 = (w - w1*fcos - h1*fsin) / 2;c2 = (h + w1*fsin - h1*fcos) / 2;//2. 计算前向的第一个点坐标m1=Mat::eye(3, 3, CV_32FC1);m1.at<float>(2, 0) =-w / 2;m1.at<float>(2, 1) =h / 2;m1.at<float>(1, 1) = -1;m2=Mat::eye(3, 3, CV_32FC1);m2.at<float>(0, 0) = fcos;m2.at<float>(0, 1) = -fsin;m2.at<float>(1, 0) = fsin;m2.at<float>(1, 1) = fcos;m3=Mat::eye(3, 3, CV_32FC1);m3.at<float>(2, 0) =w1 / 2;m3.at<float>(2, 1) =h1 / 2;m3.at<float>(1, 1) = -1;sPoint=Mat::zeros(1,3,CV_32FC1);sPoint.at<float>(0, 2) = 1;Mat snPoint = sPoint*m3*m2*m1;//cout << snPoint << endl;fx =snPoint.at<float>(0,0) -fsin;fy = snPoint.at<float>(0, 1) - fcos;//3. 用直线画法计算剩余其他的映射点坐标for (int y = 0; y < h; y++){//计算第一个前向映射点的精确位置fx = fx + fsin;fy = fy + fcos; xx = fx-fcos;yy = fy+fsin;float *ptrx = map1_x.ptr<float>(y);float *ptry = map2_y.ptr<float>(y);for (int x = 0; x < w; x++){xx = xx + fcos;yy = yy - fsin;*(ptrx++) = xx;*(ptry++) = yy;}}//3.利用opencv的重映射函数完成前向映射的插值运算remap(srcImg, roateImg, map1_x, map2_y, INTER_CUBIC, BORDER_CONSTANT, Scalar(0, 0, 0));
}

代码5 opencv的旋转实现

//逆时针旋转图像degree角度(原尺寸)
void rotateImage(Mat &img, Mat &img_rotate, int degree)
{//旋转中心为图像中心  CvPoint2D32f center;center.x = float(img.cols / 2.0 + 0.5);center.y = float(img.rows / 2.0 + 0.5);//计算二维旋转的仿射变换矩阵  Mat M(2, 3, CV_32FC1);M = getRotationMatrix2D(center, degree, 1);clock_t t1 = clock();//变换图像,并用黑色填充其余值  warpAffine(img, img_rotate, M, img.size(), INTER_CUBIC);clock_t t2 = clock();cout << (t2 - t1)*1.0 / CLOCKS_PER_SEC/10;
}

4 效率对比

在debug模式下,512*512的灰度图效率如下

代码编号 平均时间
5 16ms
4 19ms
3 19ms
2 41ms

在release模式下,代码3和代码4大概是代码2的四倍。同时代码5的速度要更优于代码3。注意到后向映射更便于实现,且对旋转图像放大的操作也更自然。代码5和4中旋转后的图像和原图尺寸一致,若要获取完整的图像需要在原图四周加补充像素。总结,还是opencv的自带的仿射变换做旋转速度快,但是你若受限于条件不能使用opencv库,可以考虑这种快速旋转方法,比朴素方法在各个图像尺度上(512512,10241024,2048*2048)都快四倍。

![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/618c8ddecfd028db0c96b3e1a0e00e49.png) 代码3结果图 ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/9cebeda0b44f92eebfa581bb8b19ed21.png) 代码2结果图

参考文献

[1]:石慎, 张艳宁, 郗润平,等. 基于Bresenham画线算法的图像快速-高精度旋转算法[J]. 计算机辅助设计与图形学学报, 2007, 19(11):1387-1392.

快速图像旋转算法的c++实现相关推荐

  1. 【Android RTMP】NV21 图像旋转处理 ( 图像旋转算法 | 后置摄像头顺时针旋转 90 度 | 前置摄像头顺时针旋转 90 度 )

    文章目录 安卓直播推流专栏博客总结 一. 后置摄像头顺时针旋转 90 度 二. 前置摄像头顺时针旋转 90 度 三. NV21 格式图像旋转代码 安卓直播推流专栏博客总结 Android RTMP 直 ...

  2. 基于DSP的图像旋转算法数据调度策略

    基于DSP的图像旋转算法数据调度策略 [日期:2008-8-14] 来源:电子技术应用  作者:李筱琳,冯 燕,何亦征 [字体:大 中 小] <script src="http://w ...

  3. 学习OpenCV3——图像旋转算法实现

    图像旋转是非常常见的图像变换,通常应用于图像矫正,在OpenCV可以使用密集仿射变换函数cv::warpAffine()实现图像旋转.为了理解图像旋转的原理,本文实现了一个图像旋转算法. 图像旋转是指 ...

  4. 【Android RTMP】NV21 图像旋转处理 ( 快速搭建 RTMP 服务器 Shell 脚本 | 创建 RTMP 服务器镜像 | 浏览器观看直播 | 前置 / 后置摄像头图像旋转效果展示 )

    文章目录 安卓直播推流专栏博客总结 一. 编写快速搭建 RTMP 服务器 Shell 脚本 二. RTMP 快速搭建方法 三.创建阿里云 RTMP 服务器镜像 四.浏览器查看直播内容 五.前置 / 后 ...

  5. 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转

    (2009.03.09  可以到这里下载旋转算法的完整的可以编译的项目源代码:  http://blog.csdn.net/housisong/archive/2009/03/09/3970925.a ...

  6. 【Android RTMP】NV21 图像旋转处理 ( 问题描述 | 图像顺时针旋转 90 度方案 | YUV 图像旋转细节 | 手机屏幕旋转方向 )

    文章目录 安卓直播推流专栏博客总结 一. NV21 图像格式与 Camera图像传感器方向问题 二. NV21 图像格式视频旋转 1. 图像旋转问题及解决方案 ( 顺时针旋转 90 度 ) 2. NV ...

  7. c++实现图像旋转任意角度

    图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转                                 HouSisong@GMail.com   200 ...

  8. 【嵌入式C编程】keil图像旋转仿真

    老爷们求求点个赞,我完全是自己想的,没有参考祖传代码. 一.题目描述与分析 题目:导入一幅128X128的8bit灰度图像,请在ARM 处理器上编程,使图像顺时针旋转45度,并导出图像: 分析:主要可 ...

  9. 二维图像旋转的坐标公式推导

    二维图像旋转后的坐标公式推导: 综上,图像顺时针和逆时针旋转的矩阵分别为: 对于此处红色字体的理解部分,有误解,图像没有翻转. 这个坐标转换只是让图像每个位置在某一坐标系的表达,转换到另一个坐标系上的 ...

最新文章

  1. 基于深度学习OpenCV与python进行字符识别
  2. ROS系统 通过服务编程实现两个数相加并返回
  3. 机器人运动规划调研(pending)
  4. R语言学习笔记 (入门知识)
  5. C++ stringstream输入方式
  6. PHP学习总结(13)——PHP入门篇之常量
  7. 上传Text文档并转换为PDF(解决乱码)
  8. MTCNN——基于级联模型的人脸关键点检测网络
  9. jqgrid学习(二)
  10. js 实现2的n次方计算函数_x的10的n次方解决js浮点数计算
  11. Trustzone的一些理解
  12. Spring的全局(统一)异常处理
  13. 「雕爷学编程」Arduino动手做(37)——MQ-3酒精传感器
  14. mx150 宏碁swift3_大众化的轻薄本-宏碁蜂鸟Swift 3评测报告
  15. mysql占用内存过高_mysql数据库占用内存过高解决办法
  16. DECODE函数常用语法
  17. 【机器学习】先搞懂这八大基础概念,再谈机器学习入门!
  18. 如何使用Google Apps脚本构建YouTube Uploader Bot
  19. uniapp 微信小程序如何解决发版后,旧版本缓存问题
  20. Froala富文本编辑器

热门文章

  1. C语言篇 - (1)初识C语言 - (1)什么是C语言
  2. java xmap_转:使用XMAP完成JavaBean和XML之间转换
  3. ArcGIS JavaScript for API(003):geojsonlayer
  4. mysql索引执行顺序_mysql索引及sql执行顺序
  5. 【软考高项】新一代信息技术及应用之云计算
  6. 建设工程项目全寿命周期管理是指_工程项目全寿命周期管理.ppt
  7. Error creating bean with name xxxxxxx
  8. 修改Android app图标(Android Studio)
  9. 人工智能的发展历程,AI ,路在何方(文章分享)
  10. 帅到没朋友 (20 分)