OpenCV圖像旋轉


圖像旋轉是指圖像按照某個位置轉動一定角度的過程,旋轉中圖像仍保持這原始尺寸。圖像旋轉后圖像的水平對稱軸、垂直對稱軸及中心坐標原點都可能會發生變換,因此需要對圖像旋轉中的坐標進行相應轉換。

如下圖:

假設圖像逆時針旋轉\(\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;
 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM