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. lua_VC6环境

    1. 下载得到 lua-5.1.4.tar.gz,解压得到 文件夹"lua-5.1.4" 2. 视频[02:00] 将 lua-5.1.4/etc/luavs.bat 复制到 lu ...

  2. Python mysql-SQL概要

    2017-09-05 20:10:58 一.SQL语句及其种类 SQL使用关键字,表名,列名等组合成一条语句来描述操作的内容.关键字是指那些含义或者使用方法是先已经定义好的英语单词.根据RDBMS赋予 ...

  3. js中文乱码

    js中文乱码 我的页面是uft-8,处理中文还是乱码, 所在在处理页面增加了 request.setCharsetEncoding("UFT-8"); ////////////// ...

  4. English Words Type

    经常见到的: v = 动词,兼指及物动词和不及物动词,verb的缩写 n = 名词,noun的缩写 adj = 形容词, adjective的缩写 adv.表示副词, adverb的缩写 prep.表 ...

  5. 20170523xlVBA多条件分类求和一例

    Public Sub NextSeven_CodeFrame() Application.ScreenUpdating = False Application.DisplayAlerts = Fals ...

  6. week 1

    day1 订正 学习AC自动机 day2 mobius反演 对偶图 codeforces day3 ZR模拟赛 订正 day4 复习AC自动机 题库 https://www.cnblogs.com/c ...

  7. PHP函数笔记

    一.函数(Function) 1.什么是函数:封装的,可以重复使用的                       完成特定功能的代码段. 2.分类  (1)系统函数  (2)自定义函数 3.自定义函数 ...

  8. 10046 event 知多少

    10046 event 知多少 2017年5月10日 10:08 1.在当前session级打开trace 适用于SQL语句可以在新的session创建后再运行. 在session级收集10046 t ...

  9. ubuntu下没有Language Support

    sudo apt-get installlanguage-selector-gnome

  10. zk请求和响应对

    zk的请求和响应是通过id对应上的: 请求头(RequestHeader)和响应头(ReplyHeader)共用一个xid,它的本质是ClientCnxn类中的一个计数器. 1. 首先看客户端: Pa ...