基于OpenCV和C++ 實現(xiàn)圖片旋轉(zhuǎn)
圖片旋轉(zhuǎn),本質(zhì)上是對旋轉(zhuǎn)后的圖片中每個像素點計算在原圖的位置。然后照搬過來就好。
(多說一句,如果計算出來在原圖中的位置不是整數(shù)而是小數(shù),因為像素點個數(shù)都是整數(shù),就需要小數(shù)到整數(shù)的轉(zhuǎn)換。這個轉(zhuǎn)換過程是有講究的,需要用到插值:最近鄰插值、雙線性插值等等。這里我使用的是最簡單的最近鄰插值,即對小數(shù)四舍五入成整數(shù),C/C++ 實現(xiàn)四舍五入見 這里 )
圖形圖像課上一般會介紹旋轉(zhuǎn)變換矩陣,其中 t 為需要旋轉(zhuǎn)的角度,[x'; y']是變換后坐標(其中分號表示上下關(guān)系):

即表示為:[x'; y'] = [cos(t) sin(t); -sin(t) cos(t)][x; y]
因為我個人興趣愛好(放P就是老師逼的。。。),不允許使用 OpenCV 封裝好的旋轉(zhuǎn)函數(shù)。只能自己實現(xiàn),我開始的想法是:先求變換矩陣逆矩陣,然后將一張全黑圖中每個點一一對應(yīng)插值到原圖中。
結(jié)果發(fā)現(xiàn)轉(zhuǎn)換后圖片全黑了……
后來發(fā)現(xiàn)原點設(shè)置不對。用OpenCV中的 Mat格式存儲(或二維數(shù)組)的圖片, 原點在左上角 。但是想要實現(xiàn)的 旋轉(zhuǎn)原點在圖片中心 。
同時, Mat格式存儲(或二維數(shù)組)的 坐標系中 y軸正方向向下 。這樣人類視覺上的順時針旋轉(zhuǎn),在二維數(shù)組的坐標系中是逆時針旋轉(zhuǎn)。
最重要的一點,也是二維數(shù)組操作中極易忽略的一點: 數(shù)組操作的是 數(shù)組下標 ,不是坐標系(數(shù)組的行數(shù) rows 是矩形的寬 width ,列數(shù) cols 是矩形的長 length ) 。比如坐標系(此時為了更貼近數(shù)組布局,我們 假設(shè) y 軸坐標系是向下的 )中,矩形頂點是:

但是在數(shù)組中,因為是行優(yōu)先,所以四個點的下標取值為:

有沒有發(fā)現(xiàn),兩種坐標是相反的!
總結(jié)下來,我們的圖片旋轉(zhuǎn)需要注意以下幾點:
- 變換后圖片中的每個像素點(i; j),需要平移到相對旋轉(zhuǎn)中心的新坐標,即(i - Mat.rows/2; j - Mat.cols/2)。計算完成之后,需要再次 還原 到相對左上角原點的舊坐標;
- 本來需要 變換后圖片 乘以 原圖 變換矩陣的 逆矩陣 對應(yīng)到原圖中坐標。但是因為y軸方向向下,所以 變換后圖片 乘以 原圖 變換矩陣(無需逆矩陣) 即可對應(yīng)到原圖中坐標(順時針旋轉(zhuǎn)50度,還原操作是逆時針旋轉(zhuǎn)50度);
- 矩陣下標與原圖變換矩陣相乘之前,需要將矩陣 下標兩值互換 。相乘之后,需要 再次互換下標值 還原成矩陣下標。
因此對于一個經(jīng)過旋轉(zhuǎn) t 度之后 數(shù)組下標 為[m‘, n']的像素值,還原成原圖中的 數(shù)組下標 [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;
}
結(jié)果(旋轉(zhuǎn)了20度)為

以上就是基于OpenCV和C++ 實現(xiàn)圖片旋轉(zhuǎn)的詳細內(nèi)容,更多關(guān)于c++ 圖片旋轉(zhuǎn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于VS+QT5應(yīng)用程序換圖標的解決方案
這篇文章主要介紹了VS+QT5應(yīng)用程序換圖標的處理方案,本文給大家提供了兩種解決方案供大家參考,每種方法給大家講解的都非常詳細,需要的朋友可以參考下2021-12-12
C++的template模板中class與typename關(guān)鍵字的區(qū)別分析
這篇文章中我們來談一談C++的template模板中class與typename關(guān)鍵字的區(qū)別分析,同時會講到嵌套從屬名稱時的一些注意點,需要的朋友可以參考下2016-06-06
C++獲取多瀏覽器上網(wǎng)歷史記錄示例代碼(支持獲取IE/Chrome/FireFox)
這篇文章主要介紹了C++獲取多瀏覽器上網(wǎng)歷史記錄示例代碼,支持獲取IE, Chrome,FireFox等瀏覽器2013-11-11
C++基于reactor的服務(wù)器百萬并發(fā)實現(xiàn)與講解
這篇文章主要介紹了C++基于reactor的服務(wù)器百萬并發(fā)實現(xiàn)與講解,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07

