1. 程式人生 > 程式設計 >基於OpenCV和C++ 實現圖片旋轉

基於OpenCV和C++ 實現圖片旋轉

圖片旋轉,本質上是對旋轉後的圖片中每個畫素點計算在原圖的位置。然後照搬過來就好。

(多說一句,如果計算出來在原圖中的位置不是整數而是小數,因為畫素點個數都是整數,就需要小數到整數的轉換。這個轉換過程是有講究的,需要用到插值:最近鄰插值、雙線性插值等等。這裡我使用的是最簡單的最近鄰插值,即對小數四捨五入成整數,C/C++ 實現四捨五入見 這裡 )

圖形影象課上一般會介紹旋轉變換矩陣,其中 t 為需要旋轉的角度,[x'; y']是變換後坐標(其中分號表示上下關係):

基於OpenCV和C++ 實現圖片旋轉

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

因為我個人興趣愛好(放P就是老師逼的。。。),不允許使用 OpenCV 封裝好的旋轉函式。只能自己實現,我開始的想法是:先求變換矩陣逆矩陣,然後將一張全黑圖中每個點一一對應插值到原圖中。

結果發現轉換後圖片全黑了……

後來發現原點設定不對。用OpenCV中的 Mat格式儲存(或二維陣列)的圖片, 原點在左上角 。但是想要實現的 旋轉原點在圖片中心 。

同時, Mat格式儲存(或二維陣列)的 座標系中 y軸正方向向下 。這樣人類視覺上的順時針旋轉,在二維陣列的座標系中是逆時針旋轉。

最重要的一點,也是二維陣列操作中極易忽略的一點: 陣列操作的是 陣列下標 ,不是座標系(陣列的行數 rows 是矩形的寬 width ,列數 cols 是矩形的長 length ) 。比如座標系(此時為了更貼近陣列佈局,我們 假設 y 軸座標系是向下的 )中,矩形頂點是:

基於OpenCV和C++ 實現圖片旋轉

但是在陣列中,因為是行優先,所以四個點的下標取值為:

基於OpenCV和C++ 實現圖片旋轉

有沒有發現,兩種座標是相反的!

總結下來,我們的圖片旋轉需要注意以下幾點:

  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++ 實現圖片旋轉

以上就是基於OpenCV和C++ 實現圖片旋轉的詳細內容,更多關於c++ 圖片旋轉的資料請關注我們其它相關文章!