话说,平凡之处显真格,这一点也没错!  比如,对旋转图像进行双线性插值,很简单吧?  可,对我,折腾了大半天,也没有达到预期效果!  尤其是三个误区让我抓瞎好久:

1,坐标旋转公式。   这东西,要用的时候查资料,抄过来,从不记清,猛地一下让人写正确,确实不容易,虽然只是正余弦的排列问题。画图推导的方法也是知道,但是,奈何又记不得三角形的和角展开公式。没办法,只好逐一测试验证了,心血经验,45、90,135,180这几个角度最好都验证一下。

2,双插的数据来源。 一开始,思维上习惯地数据来源认定应该是旋转之后的,为此施展多种手段都不能较好克服数据有效性、配对性等异常。搞个带掩模的3*3滤波吧,却使图像变模糊了。  绝境反思,数据来源取自源图数据,该是多好的事呀。 仿射变换 warpAffine() 函数中的仿射矩阵就是默认为逆向。

3,双插的方法。 一直来,都知道X、Y方向要各插值一次,但却不明确它们的相互关系是 串行,而非并行!

以下贴出我后来完善出的旋转部分代码,有路过的高手请帮忙指点优化一下:

//旋转 平移 点坐标,依据旋转矩阵而来
void rotatePoint(const Point2d& src, Point2d& dst, const double angle, const Point2d& offset=Point2f(0,0))
{const double cosAngle =cos(angle);const double sinAngle =sin(angle);dst.x = src.x * cosAngle + src.y * sinAngle + offset.x;dst.y = src.y * cosAngle - src.x * sinAngle + offset.y;}//旋转 平移 点坐标 angle中的x值为 cos(angle)  y为sin(angle)
inline void rotatePoint(const Point2d& src, Point2d& dst, const Point2d& angle, const Point2d& offset=Point2f(0,0))
{//dst.x = src.dot(angle) + offset.x;dst.x = src.x * angle.x + src.y * angle.y + offset.x;dst.y = src.y * angle.x - src.x * angle.y + offset.y;}//双线性插值  a为左上点 b右上 c左下 d右下 权重因子Sx Sy 的取值范围为(0 , 1)由小坐标指向大坐标距离比
template<typename T>
inline double insertDLine(const T a, const T b, const T c, const T d, const double Sx, const double Sy)
{const double Sx1 = 1 -Sx;const double Sy1 = 1 -Sy;return (a *Sx1 *Sy1 + b *Sx *Sy1 + c *Sx1 *Sy + d *Sx *Sy);
}//双线性插值  dst为data图像中的2*2子块 权重因子Sx Sy 的取值范围为(0 , 1)由小坐标指向大坐标距离比
void insertDLine(const Mat& src, Scalar& dst,const double Sx, const double Sy)
{const int channels =src.channels();const uchar *pU = src.ptr(0);const uchar *pD = src.ptr(1);const int depth =min(4, channels);for (int i = 0; i < depth; i++){dst[i] = insertDLine(pU[i], pU[i + channels], pD[i], pD[i + channels], Sx, Sy);}}//将源图像旋转一定的角度
int rotateImage(const Mat& src, Mat& dst, double angle, const bool isDegree)
{const int channels =src.channels();if(channels > 4){//dst=src;  // return -1;}//将角度化为弧度 if(isDegree){angle *=CV_PI/180;}//参数初始化const double cosAngle =cos(angle);const double sinAngle =sin(angle);const int srcRows = src.rows;const int srcCols = src.cols;const int  dstCols =srcRows *abs(sinAngle) + srcCols *abs(cosAngle);const int  dstRows =srcRows *abs(cosAngle) + srcCols *abs(sinAngle);const int srcRowsLess2 = srcRows -2;const int srcColsLess2 = srcCols -2;const Point2d centerA(srcCols/2 +0.5, srcRows/2 +0.5);const Point2d centerB(dstCols/2 +0.5, dstRows/2 +0.5);const Point2d rotateAngle(cosAngle, -sinAngle);  //用于从目标图回旋转到初始图,角度取反Point2d hitPoint;int xL, yL;Rect insertROI(0, 0, 2, 2);Scalar insertVaule;//申请内存空间,并设置为 0dst.create(dstRows, dstCols, CV_8UC(channels));dst.setTo(Scalar(0,0,0,0));for (int i = 0; i < dstRows; i++){uchar *pDst=dst.ptr(i);for (int j = 0; j  < dstCols; j ++){rotatePoint(Point2d(j, i)-centerB, hitPoint, rotateAngle, centerA);xL =floor(hitPoint.x);yL =floor(hitPoint.y);//从目标图中回转至源图像,不在区域内的直接跳过if (xL < 0 || xL > srcColsLess2 || yL < 0 || yL > srcRowsLess2){continue;}insertROI.x =xL;insertROI.y =yL;insertDLine(src(insertROI), insertVaule, hitPoint.x -xL, hitPoint.y -yL);int base =j *channels;for (int z = 0; z < channels; z++){pDst[base +z] = insertVaule[z];}}}return 0;}

为了展示我的手写旋转函数rotateImage() 与仿射变换 warpAffine() 函数的效果比较,有如下代码段:

    double angle =90.0 * CV_PI/180; //将角度化为弧度 30Mat rotateImg;rotateImage(colorImg, rotateImg, angle);const double cosAngle =cos(angle);const double sinAngle =sin(angle);Mat rrM =(Mat_<double>(2,3) << cosAngle, sinAngle, cosAngle *colorImg.rows,  -sinAngle, cosAngle, sinAngle *colorImg.cols);Mat rotatewarpAffine; warpAffine(colorImg, rotatewarpAffine, rrM, colorImg.size()*2);

旋转30度时:

rotateImage30

warpAffine 30

旋转90度时:

rotateImage90.jpg

warpAffine 90

【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起相关推荐

  1. OpenCV学习笔记(二十一)——绘图函数core OpenCV学习笔记(二十二)——粒子滤波跟踪方法 OpenCV学习笔记(二十三)——OpenCV的GUI之凤凰涅槃Qt OpenCV学习笔记(二十

    OpenCV学习笔记(二十一)--绘图函数core 在图像中,我们经常想要在图像中做一些标识记号,这就需要绘图函数.OpenCV虽然没有太优秀的GUI,但在绘图方面还是做得很完整的.这里就介绍一下相关 ...

  2. OpenCV学习笔记(三十六)——Kalman滤波做运动目标跟踪 OpenCV学习笔记(三十七)——实用函数、系统函数、宏core OpenCV学习笔记(三十八)——显示当前FPS OpenC

    OpenCV学习笔记(三十六)--Kalman滤波做运动目标跟踪 kalman滤波大家都很熟悉,其基本思想就是先不考虑输入信号和观测噪声的影响,得到状态变量和输出信号的估计值,再用输出信号的估计误差加 ...

  3. 深度学习笔记:手写一个单隐层的神经网络

    出处:数据科学家养成记 深度学习笔记2:手写一个单隐层的神经网络 笔记1中我们利用 numpy 搭建了神经网络最简单的结构单元:感知机.笔记2将继续学习如何手动搭建神经网络.我们将学习如何利用 num ...

  4. matlab将图片旋转的代码_从零开始的matlab学习笔记——(27)图像旋转与动态图...

    matlab应用--求极限,求导,求积分,解方程,函数绘图,三维图像,拟合函数....更多内容尽在个人专栏:matlab学习 昨天思考了一下,因为笔者的GUI只是刚刚入门,如果直接说的话也说不了多少东 ...

  5. OpenCV学习笔记(三)——图像像素(图像的最大(小)值、均值、标准差、比较运算、逻辑运算、图像二值化)

    目录 1 图像像素统计 1.1 图像像素的最大值和最小值 1.2 计算图像的均值和标准差 2 两图像间的像素操作 2.1 比较运算 2.2 逻辑运算 3 图像二值化 1 图像像素统计 数字图像可以用大 ...

  6. OpenCV学习笔记(十)——图像卷积(cv.filter2D()、矩阵旋转cv.filp())

    目录 1 图像卷积过程 2 cv.filter2D() 3 cv.filp() 卷积运算在信号处理中十分常见,而图像信息可以看成一种信号.例如,图像的每一行可以看出测量亮度变化的信号,而每一列可以看作 ...

  7. opencv计算图像亮度调节_【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整...

    今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...

  8. OpenCv学习笔记(二)--Mat矩阵(图像容器)的创建及CV_8UC1,CV_8UC2等参数详解

    (一)Mat矩阵(图像容器)创建时CV_8UC1,CV_8UC2等参数详解 1--Mat不但是一个非常有用的图像容器类,同时也是一个通用的矩阵类 2--创建一个Mat对象的方法很多,我们现在先看一下M ...

  9. OpenCV学习笔记:创建/分配图像、图像读写保存、图像像素点访问修改

    环境:CentOS7 g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16) $ pkg-config --modversion opencv 2.4.13 以下程序的 ...

最新文章

  1. C# Sato CL4NX打印机发送SBPL指令打印表面信息、RFID芯片数据写入
  2. 【C】——线程引入 pthread_self 和 pthread_equal 原因 ——解决不同平台的问题!
  3. 实现数组类(C++ 拷贝构造函数、拷贝函数)要判断赋值左右对象不相等,坑惨了...
  4. Unity之计时器功能(转)
  5. 交互 点击变色_这个95%的人都没用过的PPT功能,几分钟帮你做出发布会级的交互动画...
  6. [CF1082E] Increasing Frequency
  7. C# DataTable 和List之间相互转换的方法
  8. android 管理activity
  9. [数据结构]A*寻路算法
  10. 9 Node 中的 TensorFlow.js
  11. APP自动化测试系列之3种元素定位工具
  12. 学习了pr后的收获_pr心得体会
  13. angularJs过滤器(货币转换,大小写,字数限制,日期)
  14. 快捷键Ctrl+s快速保存,屏蔽保存网页到本地
  15. 应届毕业生零基础转行做程序员,怎么看?
  16. 【Python】Windows微信清理工具
  17. RC微分电路、积分电路和低通滤波电路LPF
  18. 电脑开机总是自动修复
  19. 数据库原理及应用——熟悉数据库管理工具、数据库和表的创建与管理
  20. uni-app中实现微信小程序/公众号订阅消息推送功能

热门文章

  1. U盘病毒的传播途径和如何安全使用U盘
  2. 绝地求生服务器延迟检测源码,绝地求生不停网络延迟检测怎么办 绝地求生网络问题解决办法...
  3. iPhoneX、iPhoneXS、iPhoneXR、iPhoneXSMax屏幕适配尺寸@media
  4. [PCB]PCB光板生产过程中板子弯曲或翘起原因分析
  5. 解决 NDP40-KB2468871不能安装
  6. 7-1 人民币与美元汇率兑换程序 (10分)
  7. 微信小程序如何更换头像
  8. 前端解决:此图片来自微信公众平台未经允许不可引用
  9. HashMap常见面试题汇总:建议初步了解源码后再细品
  10. 家用洗地机买什么牌子好一点?家用洗地机推荐