OpenCV 图像旋转实现

1 旋转矩形
首先建议阅读图像旋转算法原理-旋转矩阵,这篇博客可以让你很好地理解图像中的每一个点是如何进行旋转操作的。其中涉及到了图像原点与笛卡尔坐标原点之间的相互转换以及点旋转的一些公式推导。 这里以图像围绕任意点(center_x, center_y)旋转为例,但是图像的原点在左上角,在计算的时候首先需要将左上角的原点移到图像中心,并且Y轴需要翻转。
而在旋转的过程一般使用旋转中心为坐标原点的笛卡尔坐标系,所以图像旋转的第一步就是坐标系的变换。(x’,y’)是笛卡尔坐标系的坐标,(x,y)是图像坐标系的坐标,经过坐标系变换后

坐标系变换到以旋转中心为原点后,接下来就要对图像的坐标进行变换。

逆变换是

由于在旋转的时候是以旋转中心为坐标原点的,旋转结束后还需要将坐标原点移到图像左上角,也就是还要进行一次变换。


上边两图,可以清晰的看到,旋转前后图像的左上角,也就是坐标原点发生了变换。
在求图像旋转后左上角的坐标前,先来看看旋转后图像的宽和高。从上图可以看出,旋转后图像的宽和高与原图像的四个角旋转后的位置有关。
我们将这个四个角点记为 transLeftTop, transRightTop, transLeftBottom, transRightBottom
设top为旋转后最高点的纵坐标 top = min({ transLeftTop.y, transRightTop.y, transLeftBottom.y, transRightBottom.y });
down为旋转后最低点的纵坐标 down = max({ transLeftTop.y, transRightTop.y, transLeftBottom.y, transRightBottom.y });
left为旋转后最左边点的横坐标 left = min({ transLeftTop.x, transRightTop.x, transLeftBottom.x, transRightBottom.x });
right为旋转后最右边点的横坐标 right = max({ transLeftTop.x, transRightTop.x, transLeftBottom.x, transRightBottom.x });
旋转后的宽和高为newWidth,newHeight,则可得到下面的关系:

旋转完成后要将坐标系转换为以图像的左上角为坐标原点,可由下面变换关系得到:

逆变换

综合以上,也就是说原图像的像素坐标要经过三次的坐标变换:
- 将坐标原点由图像的左上角变换到旋转中心
- 以旋转中心为原点,图像旋转角度a
- 旋转结束后,将坐标原点变换到旋转后图像的左上角
可以得到下面的旋转公式:(x’,y’)旋转后的坐标,(x,y)原坐标,(x0,y0)旋转中心,a旋转的角度(顺时针)

这种由输入图像通过映射得到输出图像的坐标,是向前映射。常用的向后映射是其逆运算

最后附上代码
void imageRotation(Mat& srcImage, Mat& dstImage, Mat_<double>& shape, float& angle)
{
const double cosAngle = cos(angle);
const double sinAngle = sin(angle);
// 计算标注中心
double center_x = ;
double center_y = ;
for (int i = ; i < shape.rows; i++){
center_x += shape(i, );
center_y += shape(i, );
}
center_x /= shape.rows;
center_y /= shape.rows; //原图像四个角的坐标变为以旋转中心的坐标系
Point2d leftTop(-center_x, center_y); //(0,0)
Point2d rightTop(srcImage.cols - center_x, center_y); // (width,0)
Point2d leftBottom(-center_x, -srcImage.rows + center_y); //(0,height)
Point2d rightBottom(srcImage.cols - center_x, -srcImage.rows + center_y); //(width,height) //以center为中心旋转后四个角的坐标
Point2d transLeftTop, transRightTop, transLeftBottom, transRightBottom;
transLeftTop = rotationPoint(leftTop, cosAngle, sinAngle);
transRightTop = rotationPoint(rightTop, cosAngle, sinAngle);
transLeftBottom = rotationPoint(leftBottom, cosAngle, sinAngle);
transRightBottom = rotationPoint(rightBottom, cosAngle, sinAngle); //计算旋转后图像的width,height
double left = min({ transLeftTop.x, transRightTop.x, transLeftBottom.x, transRightBottom.x });
double right = max({ transLeftTop.x, transRightTop.x, transLeftBottom.x, transRightBottom.x });
double top = min({ transLeftTop.y, transRightTop.y, transLeftBottom.y, transRightBottom.y });
double down = max({ transLeftTop.y, transRightTop.y, transLeftBottom.y, transRightBottom.y }); int width = static_cast<int>(abs(left - right) + 0.5);
int height = static_cast<int>(abs(top - down) + 0.5); // 分配内存空间
dstImage.create(height, width, srcImage.type()); const double dx = -abs(left) * cosAngle - abs(down) * sinAngle + center_x;
const double dy = abs(left) * sinAngle - abs(down) * cosAngle + center_y; int x, y;
for (int i = ; i < height; i++) // y
{
for (int j = ; j < width; j++) // x
{
//坐标变换
x = float(j)*cosAngle + float(i)*sinAngle + dx;
y = float(-j)*sinAngle + float(i)*cosAngle + dy; if ((x<) || (x >= srcImage.cols) || (y<) || (y >= srcImage.rows))
{
if (srcImage.channels() == )
{
dstImage.at<cv::Vec3b>(i, j) = cv::Vec3b(, , );
}
else if (srcImage.channels() == )
{
dstImage.at<uchar>(i, j) = ;
}
}
else
{
if (srcImage.channels() == )
{
dstImage.at<cv::Vec3b>(i, j) = srcImage.at<cv::Vec3b>(y, x);
}
else if (srcImage.channels() == )
{
dstImage.at<uchar>(i, j) = srcImage.at<uchar>(y, x);
}
}
}
}
} Point2d rotationPoint(Point2d srcPoint, const double cosAngle, const double sinAngle)
{
Point2d dstPoint;
dstPoint.x = srcPoint.x * cosAngle + srcPoint.y * sinAngle;
dstPoint.y = -srcPoint.x * sinAngle + srcPoint.y * cosAngle;
return dstPoint;
}
OpenCV 图像旋转实现的更多相关文章
- OpenCV:OpenCV图像旋转的代码
OpenCV图像旋转的代码 cv::transpose( bfM, bfM ) 前提:使用两个矩阵Mat型进行下标操作是不行的,耗费的时间太长了.直接使用两个指针对拷贝才是王道.不知道和OpenCV比 ...
- opencv 图像旋转
理论 http://www.cnblogs.com/wangguchangqing/p/4045150.html 翻开任意一本图像处理的书,都会讲到图像的几何变换,这里面包括:仿射变换(affine ...
- OpenCV图像旋转
图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持这原始尺寸.图像旋转后图像的水平对称轴.垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换. 如下图: 假 ...
- OpenCL + OpenCV 图像旋转
▶ 使用 OpenCV 从文件读取彩色的 png 图像,旋转一定角度以后写回文件 ● 代码,核函数 // rotate.cl //__constant sampler_t sampler = CLK_ ...
- opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移
常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...
- [opencv] 图像几何变换:旋转,缩放,斜切
几何变换 几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动. 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定 ...
- 基于c++和opencv底层的图像旋转
图像旋转:本质上是对旋转后的图片中的每个像素计算在原图的位置. 在opencv包里有自带的旋转函数,当你知道倾斜角度theta时: 用getRotationMatrix2D可得2X3的旋转变换矩阵 M ...
- OpenCV计算机视觉学习(11)——图像空间几何变换(图像缩放,图像旋转,图像翻转,图像平移,仿射变换,镜像变换)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 图像 ...
- 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起
话说,平凡之处显真格,这一点也没错! 比如,对旋转图像进行双线性插值,很简单吧? 可,对我,折腾了大半天,也没有达到预期效果! 尤其是三个误区让我抓瞎好久: 1,坐标旋转公式. 这东西,要用 ...
随机推荐
- MongoDB(课时30 $group)
3.7.5.聚合框架(核心) MapReduce功能强大,但是它的复杂度和功能一样强大,那么我们需要MapReduce的功能,使用聚合框架中的聚合函数:aggregate(). 3.7.5.1.gro ...
- pycharm Django
上面的两张图片,是Django项目出错的图片,记得以前也出现过这个情况,当时好像是关闭了一些端口程序,后来就可以了,但是忘记了,那个链接也找不到了,所以现在很困惑,再找找. 电脑上现在程序安装的太多, ...
- 最大容积 Container With Most Water
2018-07-31 17:28:42 问题描述: 问题求解: 很容易想到的是Brute Force,也就是枚举所有可能的pairs,这种解法的时间复杂度为O(n ^ 2),由于本题的数据规模较大,会 ...
- 一个表单里,如果有<button>标签存在,它会自动提交表单
可以用button代替input type=”submit”吗? 在ie下,<button>标记恐怕还存在几个不大不小的问题. 在一个表单里,如果有一个以上"submit&quo ...
- [.NET开发] C# 合并、拆分PDF文档
在整理文件时,将多个同类型文档合并是实现文档归类的有效方法,也便于文档管理或者文档传输.当然,也可以对一些比较大的文件进行拆分来获取自己想要的部分文档.可以任意地对文档进行合并.拆分无疑为我们了提供极 ...
- [Java学习] Java super关键字
super 关键字与 this 类似,this 用来表示当前类的实例,super 用来表示父类. super 可以用在子类中,通过点号(.)来获取父类的成员变量和方法.super 也可以用在子类的子类 ...
- Spring Data Rest如何暴露ID字段
package com.example.demo.config; import com.example.demo.model.Comp; import com.example.demo.model.P ...
- OC BLOCK和协议
一.BOLCK (一)简介 BLOCK是什么?苹果推荐的类型,效率高,在运行中保存代码.用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行. block实际上是: 指向结构体的指针 BOLC ...
- RabbitMQ脑裂问题解决方案调查
现象: RabbitMQ GUI上显示 Network partition detectedMnesia reports that this RabbitMQ cluster has experien ...
- AOJ2025 Eight Princes
我们查看更一般的情况,设人数为m 则n < m * 2无解 然后n为奇数的情况: 我们把一个人和一个空格打包,于是剩下m个"人"和n - m个空格,随便排列这些"人 ...