基于OpenCV和C++ 实现图片旋转

 更新时间:2020年12月15日 11:54:23   作者:IronYoung  
这篇文章主要介绍了基于OpenCV和C++ 实现图片旋转,帮助大家更好的利用c++处理图片,感兴趣的朋友可以了解下

图片旋转,本质上是对旋转后的图片中每个像素点计算在原图的位置。然后照搬过来就好。

(多说一句,如果计算出来在原图中的位置不是整数而是小数,因为像素点个数都是整数,就需要小数到整数的转换。这个转换过程是有讲究的,需要用到插值:最近邻插值、双线性插值等等。这里我使用的是最简单的最近邻插值,即对小数四舍五入成整数,C/C++ 实现四舍五入见 这里 )

图形图像课上一般会介绍旋转变换矩阵,其中 t 为需要旋转的角度,[x'; y']是变换后坐标(其中分号表示上下关系):

即表示为:[x'; y'] = [cos(t) sin(t); -sin(t) cos(t)][x; y]

因为我个人兴趣爱好(放P就是老师逼的。。。),不允许使用 OpenCV 封装好的旋转函数。只能自己实现,我开始的想法是:先求变换矩阵逆矩阵,然后将一张全黑图中每个点一一对应插值到原图中。

结果发现转换后图片全黑了……

后来发现原点设置不对。用OpenCV中的 Mat格式存储(或二维数组)的图片, 原点在左上角 。但是想要实现的 旋转原点在图片中心 。

同时, Mat格式存储(或二维数组)的 坐标系中 y轴正方向向下 。这样人类视觉上的顺时针旋转,在二维数组的坐标系中是逆时针旋转。

最重要的一点,也是二维数组操作中极易忽略的一点: 数组操作的是 数组下标 ,不是坐标系(数组的行数 rows 是矩形的宽 width ,列数 cols 是矩形的长 length ) 。比如坐标系(此时为了更贴近数组布局,我们 假设 y 轴坐标系是向下的 )中,矩形顶点是:

但是在数组中,因为是行优先,所以四个点的下标取值为:

有没有发现,两种坐标是相反的!

总结下来,我们的图片旋转需要注意以下几点:

  1. 变换后图片中的每个像素点(i; j),需要平移到相对旋转中心的新坐标,即(i - Mat.rows/2; j - Mat.cols/2)。计算完成之后,需要再次 还原 到相对左上角原点的旧坐标;
  2. 本来需要 变换后图片 乘以 原图 变换矩阵的 逆矩阵 对应到原图中坐标。但是因为y轴方向向下,所以 变换后图片 乘以 原图 变换矩阵(无需逆矩阵) 即可对应到原图中坐标(顺时针旋转50度,还原操作是逆时针旋转50度);
  3. 矩阵下标与原图变换矩阵相乘之前,需要将矩阵 下标两值互换 。相乘之后,需要 再次互换下标值 还原成矩阵下标。

因此对于一个经过旋转 t 度之后 数组下标 为[m‘, n']的像素值,还原成原图中的 数组下标 [m; n]计算为:

[cos(t) -sin(t); sin(t) cos(t)] ([m'; n'] - [Mat.rows/2; Mat.cols/2]) = [m; n] -  [Mat.rows/2; Mat.cols/2]

源代码附上:

Mat nearestNeighRotate(cv::Mat img, float angle)
{
	int len = (int)(sqrtf(pow(img.rows, 2) + pow(img.cols, 2)) + 0.5);

	Mat retMat = Mat::zeros(len, len, CV_8UC3);
	float anglePI = angle * CV_PI / 180;
	int xSm, ySm;

	for(int i = 0; i < retMat.rows; i++)
		for(int j = 0; j < retMat.cols; j++)
		{
			xSm = (int)((i-retMat.rows/2)*cos(anglePI) - (j-retMat.cols/2)*sin(anglePI) + 0.5);
			ySm = (int)((i-retMat.rows/2)*sin(anglePI) + (j-retMat.cols/2)*cos(anglePI) + 0.5);
			xSm += img.rows / 2;
			ySm += img.cols / 2;

			if(xSm >= img.rows || ySm >= img.cols || xSm <= 0 || ySm <= 0){
				retMat.at<Vec3b>(i, j) = Vec3b(0, 0);
			}
			else{
				retMat.at<Vec3b>(i, j) = img.at<Vec3b>(xSm, ySm);
			}
		}

	return retMat;
}

好,我们来测试看看:

int main() 
{ 
	Mat img = imread("../HelloWorld.png");
	retImg = nearestNeighRotate(img, -20.f);
	namedWindow("nearNeigh", CV_WINDOW_AUTOSIZE);
	imshow("nearNeigh", retImg);

	waitKey(); 
	cvDestroyAllWindows();
	return 0; 
}

结果(旋转了20度)为

以上就是基于OpenCV和C++ 实现图片旋转的详细内容,更多关于c++ 图片旋转的资料请关注脚本之家其它相关文章!

相关文章

  • C语言的结构体你了解吗

    C语言的结构体你了解吗

    这篇文章主要为大家详细介绍了C语言的结构体,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • C++深拷贝与浅拷贝的区别及应用

    C++深拷贝与浅拷贝的区别及应用

    这篇文章主要给大家介绍了关于C++深拷贝与浅拷贝区别及应用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 怎么在C++二进制文件中注入git信息详解

    怎么在C++二进制文件中注入git信息详解

    这篇文章主要给大家介绍了关于怎么在C++二进制文件中注入git信息的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-06-06
  • 浅析C/C++中sort函数的用法

    浅析C/C++中sort函数的用法

    做项目的时候,排序是一种经常要用到的操作。如果每次都自己写个冒泡之类的O(n^2)排序,不但程序容易超时,而且浪费宝贵的时间,还很有可能写错。STL里面有个sort函数,可以直接对数组排序,复杂度为n*log2(n)。
    2014-09-09
  • VC实现对话框窗口任意分割

    VC实现对话框窗口任意分割

    最近写MFC的程序,想在对话框里实现窗口的任意分割。现在网络资料一大抄,找个东西实在麻烦。总算这个很简单,很快就搞定了,写下来做个笔记。
    2015-06-06
  • C语言实现旅游景点咨询系统

    C语言实现旅游景点咨询系统

    这篇文章主要为大家详细介绍了C语言实现旅游景点咨询系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • C++ stack与queue使用方法详细讲解

    C++ stack与queue使用方法详细讲解

    stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行 元素的插入与提取操作;队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素
    2023-01-01
  • 使用C++的ORM框架QxORM详解

    使用C++的ORM框架QxORM详解

    这篇文章主要介绍了使用C++的ORM框架QxORM的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • 超级详细讲解C++中的多态

    超级详细讲解C++中的多态

    多态是在不同继承关系的类对象,去调同一函数,产生了不同的行为,下面这篇文章主要给大家介绍了关于C++中多态的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • C语言三分钟精通时间复杂度与空间复杂度

    C语言三分钟精通时间复杂度与空间复杂度

    算法复杂度分为时间复杂度和空间复杂度。其作用: 时间复杂度是度量算法执行的时间长短;而空间复杂度是度量算法所需存储空间的大小
    2022-02-02

最新评论