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,坐标旋转公式. 这东西,要用 ...
随机推荐
- Netcat使用方法
netcat被誉为网络安全界的‘瑞士军刀',相信没有什么人不认识它吧...... 一个简单而有用的工具,透过使用TCP或UDP协议的网络连接去读写数据.它被设计成一个稳定的后门工具,能够直接由其它 ...
- js事件轮询机制
console.log(1) setTimeout(function(){ console.log(2) },0); console.log(3) 毫无疑问:运行结果是1 3 2 也就是说:setTi ...
- m_Orchestrate learning system---三十二、数据库字段判断为空时容易出现问题,如何从根本上解决这个问题
m_Orchestrate learning system---三十二.数据库字段判断为空时容易出现问题,如何从根本上解决这个问题 一.总结 一句话总结:字段禁止为空,设置默认值0即可 禁止 空 默认 ...
- java中的static关键字 学习总结
使用static关键字修饰的变量和方法为静态变量.静态方法. 非静态方法可以访问静态变量/方法和非静态变量/方法,但静态方法只能访问静态变量/方法. 可以看到在静态方法中调用非静态变量和非静态方法时, ...
- 雷林鹏分享:Ruby 循环
Ruby 循环 Ruby 中的循环用于执行相同的代码块若干次.本章节将详细介绍 Ruby 支持的所有循环语句. Ruby while 语句 语法 while conditional [do] code ...
- [.NET开发] C#面向服务WebService从入门到精通
C#面向服务WebService从入门到精通>包含以下两个部分: 一.<C#远程调用技术WebService修炼手册[基础篇]> 本次分享课您将学习到以下干货知识点: 1).WebS ...
- OTU rank curve(Rank Abundance 曲线)【基本概念】
16S结题报告中都会有这么一张图: 这张图是OTU Rank曲线,该曲线可以展示样品的多样性.而样品的多样性常通过以下两个方面进行解释:物种的丰富程度和均匀程度.Rank曲线中,曲线在横轴上的跨度越长 ...
- SGE:qsub/qstat/qdel/qhost 任务投递和监控
参考: Oracle Grid Engine qsub命令 SGE - qsub使用范例 SGE作业基本用法 qsub是最为稳定的底层任务投递系统,就是把一个脚本投递到集群的计算节点上运行. 注意,只 ...
- 12月6日 看Active Record validation ; 做jdstore ,注意gem bootstrap 版本只支持bootstrap3。
Active Record validation: new_record?()//用于验证刚新建,但没存入database中的数据 ,返回true或false persisted?() //和new_ ...
- php date()
PHP Date() 函数把时间戳格式化为更易读的日期和时间. date(format,timestamp) format:显示时间的格式. timestamp:可选.规定时间戳.默认是当前时间和日 ...