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,则可得到下面的关系:

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

逆变换

综合以上,也就是说原图像的像素坐标要经过三次的坐标变换:

  1. 将坐标原点由图像的左上角变换到旋转中心
  2. 以旋转中心为原点,图像旋转角度a
  3. 旋转结束后,将坐标原点变换到旋转后图像的左上角

可以得到下面的旋转公式:(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 图像旋转实现的更多相关文章

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

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

  2. opencv 图像旋转

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

  3. OpenCV图像旋转

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

  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. Ubuntu16.04下的主题美化

    1.先下载桌面外观管理工具 sudo apt-get install unity-tweak-tool 2.gnome工具 sudo apt-get install gnome-tweak-tool ...

  2. ORA-01940:无法删除当前已连接的用户

    当时时间比较着急,直接采取了釜底抽薪的办法——拔网线,这招很管用,再次执行drop user就成功了.考虑到以后不一定所有场合都允许你采取这种简单粗暴的办法,就在虚拟机上重现了这个报错,寻求一种较为温 ...

  3. Shell循环输入符合条件为止

    提供用户输入,直到输入d/D/r/R为止. #!/bin/bash ]; do echo -n "(D)ebug or (R)elease?" read select_build_ ...

  4. 899F - Letters Removing

    Codeforces 899F - Letters Removing 思路:考虑一下怎么找到输入的l和r在原来串中的位置,我们想到用前缀和来找,一开始所有位置都为1,删掉后为0,那么前缀和为l的位置就 ...

  5. Codeforces 827C - DNA Evolution

    827C - DNA Evolution 思路: 写4*10*10个树状数组,一个维度是4(ATCG),另一个维度是长度len,另一个维度是pos%len,因为两个pos,如果len和pos%len相 ...

  6. m_Orchestrate learning system---三十、项目中的dist文件一般是做什么的

    m_Orchestrate learning system---三十.项目中的dist文件一般是做什么的 一.总结 一句话总结: Bootstrap switch:dist 目录是放最终的js和css ...

  7. C语言的的free和c++的delete的区别

    首先free对应的是malloc:delete对应的是new:free用来释放malloc出来动态内存,delete用来释放new出来的动态内存空间. 应用的区别为: 1. 数组的时候int *p=( ...

  8. PHP函数总结 (五)

    <?php /** * 回调函数: * 指调用函数时并不是传递一个标准的变量作为参数,而是将另一个函数作为参数传递到调用的函数中 * 使用回调函数可以 将一段自己定义的功能传到函数内部使用 * ...

  9. ssh: connect to host 192.168.11.180 port 22: Connection refused

    错误原因: 1.sshd 未安装:sudo apt-get install openssh-server 2.sshd 未启动:sudo net start sshd 3.防火墙:sudo ufw d ...

  10. 第一个 MVC 应用程序(下半部分)

    2.4 创建一个简单的数据录入应用程序 本章的其余部分将通过一个简单的数据录入应用程序来考查 MVC 的更多基本特性.本小节打算分步进行,目的是演示 MVC 的运用. B1.设计一个数据模型 在 MV ...