旋轉(zhuǎn)一般是指將圖像圍繞某一指定點旋轉(zhuǎn)一定的角度,圖像旋轉(zhuǎn)后會有一部分圖像轉(zhuǎn)出顯示區(qū)域,可以截圖那部分,也可以改變圖像的尺寸使得圖像顯示完全。
圖像旋轉(zhuǎn)原理
所謂圖像旋轉(zhuǎn)是指圖像以某一點為中心旋轉(zhuǎn)一定的角度,形成一幅新的圖像的過程。這個點通常就是圖像的中心。
由于是按照中心旋轉(zhuǎn),所以有這樣一個屬性:旋轉(zhuǎn)前和旋轉(zhuǎn)后的點離中心的位置不變。
根據(jù)這個屬性,可以得到旋轉(zhuǎn)后的點的坐標(biāo)與原坐標(biāo)的對應(yīng)關(guān)系。
原圖像的坐標(biāo)一般是以左上角為原點的,我們先把坐標(biāo)轉(zhuǎn)換為以圖像中心為原點。假設(shè)原圖像的寬為w,高為h,(x0,y0)為原坐標(biāo)內(nèi)的一點,轉(zhuǎn)換坐標(biāo)后的點為(x1,y1)??梢缘玫剑?/p>
X0’ = x0 -w/2;
y1’ =-y0 + h/2;
在新的坐標(biāo)系下,假設(shè)點(x0,y0)距離原點的距離為r,點與原點之間的連線與x軸的夾角為b,旋轉(zhuǎn)的角度為a,旋轉(zhuǎn)后的點為(x1,y1), 如下圖所示。
那么有以下結(jié)論:
x0=r*cosb;y0=r*sinb
x1 = r*cos(b-a)= r*cosb*cosa+r*sinb*sina=x0*cosa+y0*sina;
y1=r*sin(b-a)=r*sinb*cosa-r*cosb*sina=-x0*sina+y0*cosa;
得到了轉(zhuǎn)換后的坐標(biāo),我們只需要把這些坐標(biāo)再轉(zhuǎn)換為原坐標(biāo)系即可。
x1’ = x1+w/2= x0*cosa+y0*sina+w/2
y1’=-y1+h/2=-(-x0*sina+y0*cosa)+h/2=x0*sina-y0*cosa+h/2
此處的x0/y0是新的坐標(biāo)系中的值,轉(zhuǎn)換為原坐標(biāo)系為:
x1’ = x0*cosa+y0*sina+w/2=(x00-w/2)*consa+(-y00+h/2)*sina+w/2
y1’= x0*sina-y0*cosa+h/2=(x00-w/2)*sina-(-y00+h/2)*cosa+h/2
=(y00-h/2)*cosa+( x00-w/2)*sina+h/2
在OpenCV中,目前并沒有現(xiàn)成的函數(shù)直接用來實現(xiàn)圖像旋轉(zhuǎn),它是用仿射變換函數(shù)cv::warpAffine來實現(xiàn)的,此函數(shù)目前支持4種插值算法,最近鄰、雙線性、雙三次、蘭索斯插值,如果傳進(jìn)去的參數(shù)為基于像素區(qū)域關(guān)系插值算法(INTER_AREA),則按雙線性插值。
通常使用2*3矩陣來表示仿射變換:
其中,T相當(dāng)于變換前的原始圖像,x,y為變換后的圖像坐標(biāo)。
對于cv::getRotationMatrix2D函數(shù)的實現(xiàn)公式為:
其中scale為縮放因子(x、y方向保持一致),angle為旋轉(zhuǎn)角度(弧長),centerx,centery為旋轉(zhuǎn)中心。
1 旋轉(zhuǎn)矩形
這里以圖像圍繞任意點(center_x, center_y)旋轉(zhuǎn)為例,但是圖像的原點在左上角,在計算的時候首先需要將左上角的原點移到圖像中心,并且Y軸需要翻轉(zhuǎn)。
而在旋轉(zhuǎn)的過程一般使用旋轉(zhuǎn)中心為坐標(biāo)原點的笛卡爾坐標(biāo)系,所以圖像旋轉(zhuǎn)的第一步就是坐標(biāo)系的變換。(x’,y’)是笛卡爾坐標(biāo)系的坐標(biāo),(x,y)是圖像坐標(biāo)系的坐標(biāo),經(jīng)過坐標(biāo)系變換后
坐標(biāo)系變換到以旋轉(zhuǎn)中心為原點后,接下來就要對圖像的坐標(biāo)進(jìn)行變換。
逆變換是
由于在旋轉(zhuǎn)的時候是以旋轉(zhuǎn)中心為坐標(biāo)原點的,旋轉(zhuǎn)結(jié)束后還需要將坐標(biāo)原點移到圖像左上角,也就是還要進(jìn)行一次變換。
上邊兩圖,可以清晰的看到,旋轉(zhuǎn)前后圖像的左上角,也就是坐標(biāo)原點發(fā)生了變換。
在求圖像旋轉(zhuǎn)后左上角的坐標(biāo)前,先來看看旋轉(zhuǎn)后圖像的寬和高。從上圖可以看出,旋轉(zhuǎn)后圖像的寬和高與原圖像的四個角旋轉(zhuǎn)后的位置有關(guān)。
我們將這個四個角點記為 transLeftTop, transRightTop, transLeftBottom, transRightBottom
設(shè)top為旋轉(zhuǎn)后最高點的縱坐標(biāo) top = min({ transLeftTop.y, transRightTop.y, transLeftBottom.y, transRightBottom.y });
down為旋轉(zhuǎn)后最低點的縱坐標(biāo) down = max({ transLeftTop.y, transRightTop.y, transLeftBottom.y, transRightBottom.y });
left為旋轉(zhuǎn)后最左邊點的橫坐標(biāo) left = min({ transLeftTop.x, transRightTop.x, transLeftBottom.x, transRightBottom.x });
right為旋轉(zhuǎn)后最右邊點的橫坐標(biāo) right = max({ transLeftTop.x, transRightTop.x, transLeftBottom.x, transRightBottom.x });
旋轉(zhuǎn)后的寬和高為newWidth,newHeight,則可得到下面的關(guān)系:
旋轉(zhuǎn)完成后要將坐標(biāo)系轉(zhuǎn)換為以圖像的左上角為坐標(biāo)原點,可由下面變換關(guān)系得到:
逆變換
綜合以上,也就是說原圖像的像素坐標(biāo)要經(jīng)過三次的坐標(biāo)變換:
將坐標(biāo)原點由圖像的左上角變換到旋轉(zhuǎn)中心
以旋轉(zhuǎn)中心為原點,圖像旋轉(zhuǎn)角度a
旋轉(zhuǎn)結(jié)束后,將坐標(biāo)原點變換到旋轉(zhuǎn)后圖像的左上角
可以得到下面的旋轉(zhuǎn)公式:(x’,y’)旋轉(zhuǎn)后的坐標(biāo),(x,y)原坐標(biāo),(x0,y0)旋轉(zhuǎn)中心,a旋轉(zhuǎn)的角度(順時針)
這種由輸入圖像通過映射得到輸出圖像的坐標(biāo),是向前映射。常用的向后映射是其逆運算
opencv實現(xiàn)圖片旋轉(zhuǎn)功能,非常簡單,幾行代碼即可:
#include《iostream》
#include《time.h》
#include《opencv2/opencv.hpp》
using namespace std;
using namespace cv;
inline double cpu_time(){
return clock()*1000/CLOCKS_PER_SEC;
}
//圖片旋轉(zhuǎn)操作
void imrotate(Mat& img, Mat& newIm, double angle){
int len = max(img.cols, img.rows);
Point2f pt(len/2.,len/2.);
Mat r = getRotationMatrix2D(pt,angle,1.0);
warpAffine(img,newIm,r,Size(len,len));
//Point2f pt(img.cols/2.,img.rows/2.);
//Mat r = getRotationMatrix2D(pt,angle,1.0);
//warpAffine(img,newIm,r,img.size());
}
int main(){
Mat img = imread(“1.jpg”);
//resize(img,img,Size(256,256));
Mat newIm;
double t1,t2;
t1 = cpu_time();
//調(diào)用旋轉(zhuǎn)方法,模仿matlab,取名為imrotate
imrotate(img,newIm,15);
t2 = cpu_time(); c
out《《“Rotate one image cost: ”《《t2-t1《《“ ms”《《endl;
namedWindow(“Rotate”);
imshow(“Rotate”,newIm);
waitKey(10000);
const string save_name = “rotate.jpg”;
imwrite(save_name,newIm);
return 0;
}
評論
查看更多