圖像旋轉是指圖像按照某個位置轉動必定角度的過程,旋轉中圖像仍保持這原始尺寸。圖像旋轉後圖像的水平對稱軸、垂直對稱軸及中心座標原點均可能會發生變換,所以須要對圖像旋轉中的座標進行相應轉換。ios
以下圖:測試
假設圖像逆時針旋轉\(\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}\]ui
而旋轉後的圖片的灰度值等於原圖中相應位置的灰度值以下:
\[ f(x', y') = f(x, y) \]
同時咱們要修正原點的位置,由於圖像中的座標原點在圖像的左上角,通過旋轉後圖像的大小會有所變化,原點也須要修正。實現代碼以下:spa
#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; }
測試代碼:3d
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; }