opencv 图像各方向旋转

56 篇文章 10 订阅
25 篇文章 6 订阅

1. 简介

计算机图形学中的应用非常广泛的变换是一种称为仿射变换的特殊变换,在仿射变换中的基本变换包括平移、旋转、缩放、剪切这几种。本文以及接下来的几篇文章重点介绍一下关于旋转的变换,包括二维旋转变换、三维旋转变换以及它的一些表达方式(旋转矩阵、四元数、欧拉角等)。

2. 绕原点二维旋转

首先要明确旋转在二维中是绕着某一个点进行旋转,三维中是绕着某一个轴进行旋转。二维旋转中最简单的场景是绕着坐标原点进行的旋转,如下图所示:

2DRotation

如图所示点v 绕 原点旋转θθ 角,得到点v’,假设 v点的坐标是(x, y) ,那么可以推导得到 v’点的坐标(x’, y’)(设原点到v的距离是r,原点到v点的向量与x轴的夹角是ϕϕ )

尽管图示中仅仅表示的是旋转一个锐角θθ的情形,但是我们推导中使用的是三角函数的基本定义来计算坐标的,因此当旋转的角度是任意角度(例如大于180度,导致v’点进入到第四象限)结论仍然是成立的。

旋转和平移  代码1

Rx 可以通过 getRotationMatrix2D 得到

Point center(face_img.cols/2, face_img.rows/2);
	//cv::Mat rot_mat = cv::getRotationMatrix2D(center, -1 * arctan, 1.0);

	cv::Mat Rx(2, 3, CV_32FC1);

	double theta_r = roll * 3.1415926 / 180; /** 3.1415926 / 180*/
	float cos_theta = cos(theta_r);
	float sin_theta = sin(theta_r);

	Rx.at<float>(0, 0) = cos_theta;
	Rx.at<float>(0, 1) = -sin_theta;
	Rx.at<float>(0, 2) = (1-cos_theta)*center.x + center.y * sin_theta;

	Rx.at<float>(1, 0) = sin_theta;
	Rx.at<float>(1, 1) = cos_theta;
	Rx.at<float>(1, 2) = (1-cos_theta) * center.y - center.x* sin_theta;

	//std::cout << rot_mat << std::endl;

	cv::Mat rotated_ROI;
	cv::warpAffine(face_img, rotated_ROI, Rx, face_img.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	

3. 绕任意点的二维旋转

绕原点的旋转是二维旋转最基本的情况,当我们需要进行绕任意点旋转时,我们可以把这种情况转换到绕原点的旋转,思路如下: 
1. 首先将旋转点移动到原点处 
2. 执行如2所描述的绕原点的旋转 
3. 再将旋转点移回到原来的位置

2DArbitraryRotate

旋转和平移  代码2

	
	Point center(face_img.cols/2, face_img.rows/2);
	//cv::Mat rot_mat = cv::getRotationMatrix2D(center, -1 * arctan, 1.0);

	cv::Mat Rx(3, 3, CV_32FC1);

	double theta_r = roll * 3.1415926 / 180; /** 3.1415926 / 180*/
	float cos_theta = cos(theta_r);
	float sin_theta = sin(theta_r);
	Rx.at<float>(0, 0) = cos_theta;
	Rx.at<float>(0, 1) = -sin_theta;
	Rx.at<float>(0, 2) = (1-cos_theta)*center.x + center.y * sin_theta;

	Rx.at<float>(1, 0) = sin_theta;
	Rx.at<float>(1, 1) = cos_theta;
	Rx.at<float>(1, 2) = (1-cos_theta) * center.y - center.x* sin_theta;

	Rx.at<float>(2, 0) = 0;
	Rx.at<float>(2, 1) = 0;
	Rx.at<float>(2, 2) = 1;
	//std::cout << rot_mat << std::endl;

	cv::Mat rotated_ROI;
	//cv::warpAffine(face_img, rotated_ROI, rot_mat, face_img.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	warpPerspective(face_img, rotated_ROI, Rx, cv::Size(face_img.cols, face_img.rows));
	
	
	
	cv::imshow("roll face", rotated_ROI);

 

 

 

 

绕X 轴 Y轴 Z轴旋转的结果

void warp_perspect_3_angle(cv::Mat face, float roll, float yaw, float pitch) {

	cv::Mat face_img = face.clone();
	int imgHeight = face_img.rows;
	int imgWidth = face_img.cols;

	float alpha, beta, gamma;
	alpha = pitch * 3.1415926 / 180;
	beta = yaw* 3.1415926 / 180;
	gamma = roll * 3.1415926 / 180;
	Mat Rot = Mat::eye(3, 3, CV_32FC1);

	Rot.at<float>(0, 0) = cos(beta) * cos(gamma);
	Rot.at<float>(0, 1) = cos(beta) * sin(gamma);
	Rot.at<float>(0, 2) = -sin(beta);
	Rot.at<float>(1, 0) = sin(alpha) * sin(beta) * cos(gamma) - cos(alpha) * sin(gamma);
	Rot.at<float>(1, 1) = sin(alpha) * sin(beta) * sin(gamma) + cos(alpha) * cos(gamma);
	Rot.at<float>(1, 2) = sin(alpha) * cos(beta);
	Rot.at<float>(2, 0) = cos(alpha) * sin(beta) * cos(gamma) + sin(alpha) * sin(gamma);
	Rot.at<float>(2, 1) = cos(alpha) * sin(beta) * sin(gamma) - sin(alpha) * cos(gamma);
	Rot.at<float>(2, 2) = cos(alpha) * cos(beta);

	Mat invRot;
	invert(Rot, invRot, DECOMP_SVD);

	float fx = imgWidth/2;
	float fy = imgHeight/2;
	float cx = imgWidth / 2;
	float cy = imgHeight / 2;

	Mat point3D = Mat::zeros(3, 1, CV_32FC1);
	Mat oldPoint3D = Mat::zeros(3, 1, CV_32FC1);

	Mat dstImg = face_img.clone();
	dstImg.setTo(0);

	uchar* pImgData = (uchar*)face_img.data;
	uchar* pDstData = (uchar*)dstImg.data;
	for (int j = 0; j < imgHeight; j++)
	{
		for (int i = 0; i < imgWidth; i++)
		{
			float X = (i - cx) / fx;
			float Y = (j - cy) / fy;
			float Z = 1;

			point3D.at<float>(0, 0) = X;
			point3D.at<float>(1, 0) = Y;
			point3D.at<float>(2, 0) = Z;
			//求旋转前坐标点
			oldPoint3D = invRot*point3D;
			float oldX = oldPoint3D.at<float>(0, 0);
			float oldY = oldPoint3D.at<float>(1, 0);
			float oldZ = oldPoint3D.at<float>(2, 0);
			//重投影到二维平面
			if (oldZ > 1e-3)
			{
				float u = ((fx*oldX + cx*oldZ) / oldZ);
				float v = ((fy*oldY + cy*oldZ) / oldZ);

				int u0 = floor(u);
				int v0 = floor(v);
				int u1 = u0 + 1;
				int v1 = v0 + 1;

				if (u0 >= 0 && v0 >= 0 && u1 < imgWidth && v1 < imgHeight)
				{
					float dx = u - u0;
					float dy = v - v0;
					float weight1 = (1 - dx)*(1 - dy);
					float weight2 = dx*(1 - dy);
					float weight3 = (1 - dx)*dy;
					float weight4 = dx*dy;

					pDstData[j*imgWidth * 3 + i * 3 + 0] = weight1*pImgData[v0*imgWidth * 3 + u0 * 3 + 0] +
						weight2*pImgData[v0*imgWidth * 3 + u1 * 3 + 0] +
						weight3*pImgData[v1*imgWidth * 3 + u0 * 3 + 0] +
						weight4*pImgData[v1*imgWidth * 3 + u1 * 3 + 0];

					pDstData[j*imgWidth * 3 + i * 3 + 1] = weight1*pImgData[v0*imgWidth * 3 + u0 * 3 + 1] +
						weight2*pImgData[v0*imgWidth * 3 + u1 * 3 + 1] +
						weight3*pImgData[v1*imgWidth * 3 + u0 * 3 + 1] +
						weight4*pImgData[v1*imgWidth * 3 + u1 * 3 + 1];

					pDstData[j*imgWidth * 3 + i * 3 + 2] = weight1*pImgData[v0*imgWidth * 3 + u0 * 3 + 2] +
						weight2*pImgData[v0*imgWidth * 3 + u1 * 3 + 2] +
						weight3*pImgData[v1*imgWidth * 3 + u0 * 3 + 2] +
						weight4*pImgData[v1*imgWidth * 3 + u1 * 3 + 2];
				}
			}
		}
	}
	imshow("show", dstImg);

}

 

备注: 改部分旋转 主要是应用的 2D 图像旋转, 如果被旋转的内容在同一平面内,才能打开比较好的效果,例如一本书,如果知道四个点 进行 透视变换,效果会比较好,但是如果利用人脸 ,则效果不太好,因为人脸没有在同一个平面上面。

参考:

https://blog.csdn.net/csxiaoshui/article/details/65446125

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NineDays66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值