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

如下图:

假设图像逆时针旋转\(\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. 为什么说Redis是单线程的以及Redis为什么这么块

    一.前言 近乎所有与Java相关的面试都会问到缓存的问题,基础一点的会问到什么是“二八定律”.什么是“热数据和冷数据”,复杂一点的会问到缓存雪崩.缓存穿透.缓存预热.缓存更新.缓存降级等问题,这些看似 ...

  2. HDU - 5033 Building (单调栈+倍增)

    题意:有一排建筑,每座建筑有一定的高度,宽度可以忽略,求在某点的平地上能看到天空的最大角度. 网上的做法基本都是离线的...其实这道题是可以在线做的. 对于向右能看到的最大角度,从右往左倍增维护每个时 ...

  3. BZOJ 2178: 圆的面积并 (辛普森积分)

    code #include <set> #include <cmath> #include <cstdio> #include <cstring> #i ...

  4. docker国内镜像地址

    https://registry.docker-cn.com http://hub-mirror.c.163.com https://docker.mirrors.ustc.edu.cn

  5. keras使用AutoEncoder对mnist数据降维

    import keras import matplotlib.pyplot as plt from keras.datasets import mnist (x_train, _), (x_test, ...

  6. .net core 反编译一小段

    public static class A { private static readonly MethodInfo GetServiceInfo; public static IApplicatio ...

  7. .net上传整个文件夹

    ASP.NET上传文件用FileUpLoad就可以,但是对文件夹的操作却不能用FileUpLoad来实现. 下面这个示例便是使用ASP.NET来实现上传文件夹并对文件夹进行压缩以及解压. ASP.NE ...

  8. NSNull

    集合中是不能放nil值的,因为nil是结尾,但是为了存放表示什么都没有的值,可以使用NSNull,它也是NSObject的一个子类. void null(){ NSNull *nl=[NSNull n ...

  9. Django-cookie-sesson

    一 会话跟踪 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应.例如你给10086打个电话,你就是客户端,而10086服务人员就是服务器 ...

  10. [Linux]kali更新/etc/apt/sources.list

    vim /etc/apt/sources.list #中科大 deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contri ...