图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持这原始尺寸。图像旋转后图像的水平对称轴、垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换。

如下图:

假设图像逆时针旋转\(\theta\),则根据坐标转换可得旋转转换为:

\[ \begin{cases}
x' = r\cos(\alpha - \theta)\\
y' = r\sin(\alpha - \theta)\tag{1}
\end{cases}\]

\[r = \sqrt{x^2 + y^2},
\sin\alpha = \frac{y}{\sqrt{x^2 + y^2}},
\cos\alpha = \frac{x}{\sqrt{x^2 + y^2}}\]

带入(1)可得:

\[ \begin{cases}
x' = x\cos\theta + y\sin\theta\\
y' = -x\sin\theta + y\cos\theta
\end{cases}\]

即如下:

\[ \begin{bmatrix}
x'&y'& 1
\end{bmatrix} \
=
\begin{bmatrix}
x &y &1
\end{bmatrix} \
\begin{bmatrix}
\cos\theta & -\sin\theta & 0 \\
\sin\theta & \cos\theta &0 \\
0 & 0 & 1\tag{2}
\end{bmatrix}\]

而旋转后的图片的灰度值等于原图中相应位置的灰度值如下:

\[f(x', y') = f(x, y)
\]

同时我们要修正原点的位置,因为图像中的坐标原点在图像的左上角,经过旋转后图像的大小会有所变化,原点也需要修正。实现代码如下:

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <string>
#include <cmath> using namespace cv; Mat imgRotate(Mat matSrc, float angle, bool direction)
{
float theta = angle * CV_PI / 180.0;
int nRowsSrc = matSrc.rows;
int nColsSrc = matSrc.cols;
// 如果是顺时针旋转
if (!direction)
theta = 2 * CV_PI - theta;
// 全部以逆时针旋转来计算
// 逆时针旋转矩阵
float matRotate[3][3]{
{std::cos(theta), -std::sin(theta), 0},
{std::sin(theta), std::cos(theta), 0 },
{0, 0, 1}
};
float pt[3][2]{
{ 0, nRowsSrc },
{nColsSrc, nRowsSrc},
{nColsSrc, 0}
};
for (int i = 0; i < 3; i++)
{
float x = pt[i][0] * matRotate[0][0] + pt[i][1] * matRotate[1][0];
float y = pt[i][0] * matRotate[0][1] + pt[i][1] * matRotate[1][1];
pt[i][0] = x;
pt[i][1] = y;
}
// 计算出旋转后图像的极值点和尺寸
float fMin_x = min(min(min(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);
float fMin_y = min(min(min(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);
float fMax_x = max(max(max(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);
float fMax_y = max(max(max(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);
int nRows = cvRound(fMax_y - fMin_y + 0.5) + 1;
int nCols = cvRound(fMax_x - fMin_x + 0.5) + 1;
int nMin_x = cvRound(fMin_x + 0.5);
int nMin_y = cvRound(fMin_y + 0.5);
// 拷贝输出图像
Mat matRet(nRows, nCols, matSrc.type(), Scalar(0));
for (int j = 0; j < nRows; j++)
{
for (int i = 0; i < nCols; i++)
{
// 计算出输出图像在原图像中的对应点的坐标,然后复制该坐标的灰度值
// 因为是逆时针转换,所以这里映射到原图像的时候可以看成是,输出图像
// 到顺时针旋转到原图像的,而顺时针旋转矩阵刚好是逆时针旋转矩阵的转置
// 同时还要考虑到要把旋转后的图像的左上角移动到坐标原点。
int x = (i + nMin_x) * matRotate[0][0] + (j + nMin_y) * matRotate[0][1];
int y = (i + nMin_x) * matRotate[1][0] + (j + nMin_y) * matRotate[1][1];
if (x >= 0 && x < nColsSrc && y >= 0 && y < nRowsSrc)
{
matRet.at<Vec3b>(j, i) = matSrc.at<Vec3b>(y, x);
}
}
}
return matRet;
}

测试代码:

 int main()
{
std::string strPath = "D:\\MyDocuments\\My Pictures\\OpenCV\\";
Mat matSrc = imread(strPath + "panda.jpg");
if (matSrc.empty())
return 1;
float angle = 30;
Mat matRet = imgRotate(matSrc, angle, true);
imshow("src", matSrc);
imshow("rotate", matRet);
// 保存图像
imwrite(strPath + "rotate_panda.jpg", matRet); waitKey();
return 0;
}

OpenCV图像旋转的更多相关文章

  1. OpenCV:OpenCV图像旋转的代码

    OpenCV图像旋转的代码 cv::transpose( bfM, bfM ) 前提:使用两个矩阵Mat型进行下标操作是不行的,耗费的时间太长了.直接使用两个指针对拷贝才是王道.不知道和OpenCV比 ...

  2. opencv 图像旋转

    理论 http://www.cnblogs.com/wangguchangqing/p/4045150.html 翻开任意一本图像处理的书,都会讲到图像的几何变换,这里面包括:仿射变换(affine ...

  3. OpenCV 图像旋转实现

    1 旋转矩形 首先建议阅读图像旋转算法原理-旋转矩阵,这篇博客可以让你很好地理解图像中的每一个点是如何进行旋转操作的.其中涉及到了图像原点与笛卡尔坐标原点之间的相互转换以及点旋转的一些公式推导. 这里 ...

  4. OpenCL + OpenCV 图像旋转

    ▶ 使用 OpenCV 从文件读取彩色的 png 图像,旋转一定角度以后写回文件 ● 代码,核函数 // rotate.cl //__constant sampler_t sampler = CLK_ ...

  5. opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移

    常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...

  6. [opencv] 图像几何变换:旋转,缩放,斜切

    几何变换 几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动. 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定 ...

  7. 基于c++和opencv底层的图像旋转

    图像旋转:本质上是对旋转后的图片中的每个像素计算在原图的位置. 在opencv包里有自带的旋转函数,当你知道倾斜角度theta时: 用getRotationMatrix2D可得2X3的旋转变换矩阵 M ...

  8. OpenCV计算机视觉学习(11)——图像空间几何变换(图像缩放,图像旋转,图像翻转,图像平移,仿射变换,镜像变换)

    如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 图像 ...

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

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

随机推荐

  1. 题解 [BZOJ4144] Petrol

    题目描述 ​ 有一张 n 个点 m 条边的无向图,其中有 s 个点上有加油站.有 Q 次询问(a,b,c), 问能否开一辆油箱容积为 c 的车从 a 走到 b.(a,b均为加油站) 输入格式 ​ 第一 ...

  2. MFC的静态链接、动态链接

    项目属性页面可以查看更改编译方式,推荐使用静态编译.也可以在创建MFC项目时选择静态编译. 标准Windows库,使用的是系统API,Win32是面向API的编程平台.Win32项目使用的是此编译方式 ...

  3. Python常用模块之hashlib模块

    1.hashilib模块的功能 python的hashlib提供了常见的摘要算法,如MD5, SHA1等等. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换成一 ...

  4. pydub音频处理库的使用

    pydub音频处理库的使用 在使用pydub这个模块之前应确保你的libav或者FFmpeg Mac上安装libav或FFmpeg brew install libav --with-libvorbi ...

  5. isPrototypeOf,instanceof, hasOwnProperty,in的作用与区别

    isPrototypeOf 作用:检测一个对象是否是另一个对象的原型.或者说一个对象是否被包含在另一个对象的原型链中 function Fn(name){ this.name=name; } var ...

  6. Error creating bean with name 'persistenceExceptionTranslationPostProcessor' defined in class path resource [org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfiguration

    dubbo 包和SpringBoot 冲突,注释就可以正常启动

  7. MySort(选做)

    一.题目要求 注意:研究sort的其他功能,要能改的动代码,需要答辩 模拟实现Linux下Sort -t : -k 2的功能. 要有伪代码,产品代码,测试代码(注意测试用例的设计) 参考 Sort的实 ...

  8. antd源码分析之——标签页(tabs 3.Tabs的滚动效果)

    由于ant Tabs组件结构较复杂,共分三部分叙述,本文为目录中第三部分(高亮) 目录 一.组件结构 antd代码结构 rc-ant代码结构 1.组件树状结构 2.Context使用说明 3.rc-t ...

  9. jdbc结合commons-dbutils-1.6.jar做素材数据处理的例子

    前两个 insertTitle,insertOpening 是刚开始做的针对单一素材数据解析及写入的方法,后面一个 insertMaterial 是后期封装的一个可处理各种素材导入的方法,main方法 ...

  10. [MyBatis]最简MyBatis工程

    下载地址:https://files.cnblogs.com/files/xiandedanteng/fillMillionDatum01_191005.rar --END-- 2019年10月5日1 ...