優(yōu)OpenCV3 圖像旋轉(zhuǎn)算法實現(xiàn)_第1頁
優(yōu)OpenCV3 圖像旋轉(zhuǎn)算法實現(xiàn)_第2頁
優(yōu)OpenCV3 圖像旋轉(zhuǎn)算法實現(xiàn)_第3頁
優(yōu)OpenCV3 圖像旋轉(zhuǎn)算法實現(xiàn)_第4頁
優(yōu)OpenCV3 圖像旋轉(zhuǎn)算法實現(xiàn)_第5頁
已閱讀5頁,還剩23頁未讀 繼續(xù)免費閱讀

付費下載

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

OpenCV3圖像旋轉(zhuǎn)算法實現(xiàn)圖像旋轉(zhuǎn)是非常常見的圖像變換,通常應(yīng)用于圖像矯正,在OpenCV可以使用密集仿射變換函數(shù)cv::warpAffine()實現(xiàn)圖像旋轉(zhuǎn)。為了理解圖像旋轉(zhuǎn)的原理,本文實現(xiàn)了一個圖像旋轉(zhuǎn)算法。圖像旋轉(zhuǎn)是指將圖像繞某個中心點旋轉(zhuǎn)一定角度后,得到一幅新的圖像。圖像旋轉(zhuǎn)的示意圖如圖1所示。其中,四邊形ABCD表示需要旋轉(zhuǎn)的圖像區(qū)域,它經(jīng)過旋轉(zhuǎn)角度后得到的圖像區(qū)域為四邊形A'B'C'D'。點p(x,y)為圖像內(nèi)任意一點,它經(jīng)過旋轉(zhuǎn)角度后對應(yīng)的點為p'(x',y')。圖1圖像旋轉(zhuǎn)示意圖圖像是如何進行旋轉(zhuǎn)的?通常這個過程有三個步驟。第一步,把圖像內(nèi)的坐標點繞旋轉(zhuǎn)中心點旋轉(zhuǎn)到對應(yīng)的坐標上。由于圖像是通過二維數(shù)組進行保存的,所以圖像的坐標點一定要落在坐標系的第一象限內(nèi),并且要保證它們是整數(shù)坐標點。通常情況下,進行旋轉(zhuǎn)后得到的坐標點不是整數(shù)點也不一定在第一象限內(nèi),因此需要對旋轉(zhuǎn)后得到的點進行平移和取整,使得它們都是落在第一象限內(nèi)的整數(shù)點。圖像內(nèi)任意一點p(x,y)繞某個旋轉(zhuǎn)點(X,Y)逆時針旋轉(zhuǎn)角度后得到點p'(x',y')的計算公式如下:點旋轉(zhuǎn)的C++實現(xiàn)代碼如下。(OpenCV3.45+VS2019)//將點point2繞點point1逆時針旋轉(zhuǎn)angle度后得到新的點newPointvoidrotatePoint(cv::Point&point1,cv::Point&point2,cv::Point&newPoint,doubleangle){ intdx,dy; doubledx1,dy1; dy1=-((double)point2.x-point1.x)*sin(angle)+((double)point2.y-point1.y)*cos(angle); dx1=((double)point2.x-point1.x)*cos(angle)+((double)point2.y-point1.y)*sin(angle); if(dx1-(int)dx1>0.5)//做一個四舍五入取整 dx=(int)dx1+1; else { if(dx1-(int)dx1<-0.5) dx=(int)dx1-1; else dx=(int)(dx1); } if(dy1-(int)dy1>0.5)//做一個四舍五入取整 dy=(int)dy1+1; else { if(dy1-(int)dy1<-0.5) dy=(int)dy1-1; else dy=(int)(dy1); } newPoint.x=point1.x+dx; newPoint.y=point1.y+dy;}用來平移坐標點的代碼如下。voidtranslationPoint(cv::Point&point,intx,inty)//平移運算{ point.x=point.x+x; point.y=point.y+y;}注:平移量x與y的大小,可以根據(jù)旋轉(zhuǎn)后圖像的四個頂點A'、B'、C'、D'獲得。第二步,把圖像對應(yīng)的坐標像素大小賦給旋轉(zhuǎn)后的坐標。即,圖像內(nèi)任意點p(x,y)對應(yīng)的像素值為I(x,y),那它旋轉(zhuǎn)后得到的點p'(x',y')的像素值I(x',y')=I(x,y)。如下圖2所示。當我們需要旋轉(zhuǎn)的圖像區(qū)域在圖片內(nèi)時(這區(qū)域也可以是整張圖片),如何確定旋轉(zhuǎn)區(qū)域ABCD是很重要的,只有這樣才能判斷整張圖片內(nèi)的哪些點是四邊形ABCD區(qū)域內(nèi)的。圖2圖片中要旋轉(zhuǎn)的區(qū)域我們以圖片的左上頂點為原點建立如圖2所示的坐標系,其中四邊形ABCD的四個頂點是已知的,分別為A(x0,y0)、B(x1,y1)、C(x2,y2)、D(x3,y3)。這時根據(jù)兩點式可得到四條邊的直線方程如下:根據(jù)線性規(guī)劃的知識,可以通過直線方程來表示四邊形ABCD的區(qū)域。注:因為四邊形ABCD內(nèi)的任意點p在直線AB上方,所以直線AB方程大于等于0;點p在直線BC左側(cè),所以直線BC方程小于等于0。同理可得,直線CD方程小于等于0、直線AD方程大于等于0。實現(xiàn)代碼塊如下:std::vector<cv::Point>newPoints; cv::PointnewP; for(inti=0;i<4;++i) { if(points[i]!=point)//判斷輸入的4個頂點是否與旋轉(zhuǎn)點point相同 { rotatePoint(point,points[i],newP,angle);//頂點points[i]與旋轉(zhuǎn)點point不同,則進行旋轉(zhuǎn)計算 newPoints.push_back(newP); } else { newPoints.push_back(points[i]); } } //獲取經(jīng)旋轉(zhuǎn)后,新圖像的大小,其中w表示圖像寬長,h表示圖像高長。 intw=0,h=0; intsuw[4]={newPoints[1].x-newPoints[0].x,newPoints[1].x-newPoints[3].x, newPoints[2].x-newPoints[0].x,newPoints[2].x-newPoints[3].x}; intsuh[4]={newPoints[2].y-newPoints[0].y,newPoints[2].y-newPoints[1].y, newPoints[3].y-newPoints[0].y,newPoints[3].y-newPoints[1].y}; w=absMax4(suw); h=absMax4(suh); //獲取需要旋轉(zhuǎn)的四邊形區(qū)域的外接矩形表示區(qū)域范圍(x_min,y_min)、(x_max,y_max) inty_max,y_min,x_max,x_min; intpoints_x[4]={points[0].x,points[1].x,points[2].x,points[3].x}; intpoints_y[4]={points[0].y,points[1].y,points[2].y,points[3].y}; y_max=Max4(points_y); y_min=Min4(points_y); x_max=Max4(points_x); x_min=Min4(points_x); //計算向x軸的平移量dx,向y軸的平移量dy intdx,dy; inta[4]={newPoints[0].x,newPoints[1].x,newPoints[2].x,newPoints[3].x}; intb[4]={newPoints[0].y,newPoints[1].y,newPoints[2].y,newPoints[3].y}; dx=Min4(a); dy=Min4(b); //初始化輸出矩陣 if(inputMat.type()==CV_8UC1) cv::Mat(h,w,CV_8UC1,cv::Scalar::all(255)).copyTo(outputMat); if(inputMat.type()==CV_8UC3) cv::Mat(h,w,CV_8UC3,cv::Scalar(255,255,255)).copyTo(outputMat);//實現(xiàn)I(x',y')=I(x,y) doublez1,z2,z3,z4; for(inti=y_min;i<y_max;++i) { for(intj=x_min;j<x_max;++j) { //四邊形頂點A為points[0],頂點B為points[1],頂點C為points[2],頂點D為points[3]. z1=i-(double)points[0].y- (j-(double)points[0].x)*((double)points[0].y-points[1].y)/((double)points[0].x-points[1].x); z2=j-(double)points[1].x- (i-(double)points[1].y)*((double)points[1].x-points[2].x)/((double)points[1].y-points[2].y); z3=i-(double)points[2].y- (j-(double)points[2].x)*((double)points[2].y-points[3].y)/((double)points[2].x-points[3].x); z4=j-(double)points[0].x- (i-(double)points[0].y)*((double)points[0].x-points[3].x)/((double)points[0].y-points[3].y); if(z1>=0&&z2<=0&&z3<=0&&z4>=0) { cv::Pointpoint0(j,i); rotatePoint(point,point0,point0,angle);//將點point0繞點point旋轉(zhuǎn)angle度得到新的點point0 translationPoint(point0,-dx,-dy);//平移 if(point0.x>=0&&point0.x<w&&point0.y>=0&&point0.y<h) { if(inputMat.type()==CV_8UC1) { uchar*str=inputMat.ptr<uchar>(i); outputMat.at<uchar>(point0.y,point0.x)=str[j]; } if(inputMat.type()==CV_8UC3) { cv::Vec3b*str=inputMat.ptr<cv::Vec3b>(i); outputMat.at<cv::Vec3b>(point0.y,point0.x)=str[j]; } } } } }第三步,對旋轉(zhuǎn)后的圖像進行插值。由于在第一步中對旋轉(zhuǎn)后的點進行了取整,這難免會使得新圖像存在間隙,所以需要對這些間隙進行填充。在OpenCV中常用的插值方法有以下5種:圖3常見的插值方法在本文中采用的插值方法與最近鄰插值類似,即把最近四個方向(上下左右)的平均值作為插值。//灰度圖(CV_8UC1)的插值代碼for(inti=1;i<outputMat.rows-1;++i) { for(intj=1;j<outputMat.cols-1;++j) { if(outputMat.at<uchar>(i,j)==255) { intsum=0; uchar*str1=outputMat.ptr<uchar>(i-1); sum=str1[j-1]+str1[j]+str1[j+1]; uchar*str2=outputMat.ptr<uchar>(i); sum=sum+str2[j-1]+str2[j+1]; uchar*str3=outputMat.ptr<uchar>(i+1); sum=sum+str3[j-1]+str3[j]+str3[j+1]; sum=sum/8; outputMat.at<uchar>(i,j)=(uchar)sum; } } }///彩色圖(CV_8UC3)的插值代碼for(inti=1;i<outputMat.rows-1;++i) { for(intj=1;j<outputMat.cols-1;++j) { if(outputMat.at<cv::Vec3b>(i,j)==cv::Vec3b(255,255,255)) { intsum[3]={0,0,0}; ucharr,g,b; for(intk=0;k<3;k++) { cv::Vec3b*str1=outputMat.ptr<cv::Vec3b>(i-1); sum[k]=str1[j-1][k]+str1[j][k]+str1[j+1][k]; cv::Vec3b*str2=outputMat.ptr<cv::Vec3b>(i); sum[k]=sum[k]+str2[j-1][k]+str2[j+1][k]; cv::Vec3b*str3=outputMat.ptr<cv::Vec3b>(i+1); sum[k]=sum[k]+str3[j-1][k]+str3[j][k]+str3[j+1][k]; sum[k]=sum[k]/8; } r=(uchar)sum[0]; g=(uchar)sum[1]; b=(uchar)sum[2]; outputMat.at<cv::Vec3b>(i,j)=cv::Vec3b(r,g,b); } } }整個算法的完整代碼如下:#include<iostream>#include<opencv2/opencv.hpp>//計算點point2繞點point1逆時針旋轉(zhuǎn)angle度后得到新的點newPointvoidrotatePoint(cv::Point&point1,cv::Point&point2,cv::Point&newPoint,doubleangle){ intdx,dy; doubledx1,dy1; dy1=-((double)point2.x-point1.x)*sin(angle)+((double)point2.y-point1.y)*cos(angle); dx1=((double)point2.x-point1.x)*cos(angle)+((double)point2.y-point1.y)*sin(angle); if(dx1-(int)dx1>0.5)//做一個四舍五入 dx=(int)dx1+1; else { if(dx1-(int)dx1<-0.5) dx=(int)dx1-1; else dx=(int)(dx1); } if(dy1-(int)dy1>0.5)//做一個四舍五入 dy=(int)dy1+1; else { if(dy1-(int)dy1<-0.5) dy=(int)dy1-1; else dy=(int)(dy1); } newPoint.x=point1.x+dx; newPoint.y=point1.y+dy;}voidtranslationPoint(cv::Point&point,intx,inty)//平移運算{ point.x=point.x+x; point.y=point.y+y;}intMax4(inta[4])//獲取四個數(shù)中的最大值{ intmax=a[0]; for(inti=1;i<4;i++) { if(max<a[i]) max=a[i]; } returnmax;}intMin4(inta[4])//獲取四個數(shù)中的最小值{ intmin=a[0]; for(inti=1;i<4;i++) { if(min>a[i]) min=a[i]; } returnmin;}intabsMax4(inta[4]){ intmax=0,m; for(inti=0;i<4;i++) { if(a[i]<0) m=-a[i]; elsem=a[i]; if(max<m) max=m; } returnmax;}voidrotateImage(cv::MatinputMat,cv::Mat&outputMat,std::vector<cv::Point>points,cv::Pointpoint,doubleangle){ std::vector<cv::Point>newPoints; cv::PointnewP; for(inti=0;i<4;++i) { if(points[i]!=point)//判斷輸入的4個頂點是否與旋轉(zhuǎn)點point相同 { rotatePoint(point,points[i],newP,angle);//頂點points[i]與旋轉(zhuǎn)點point不同,則進行旋轉(zhuǎn)計算 newPoints.push_back(newP); } else { newPoints.push_back(points[i]); } } //獲取經(jīng)旋轉(zhuǎn)后,新圖像的大小,其中w表示圖像寬長,h表示圖像高長。 intw=0,h=0; intsuw[4]={newPoints[1].x-newPoints[0].x,newPoints[1].x-newPoints[3].x, newPoints[2].x-newPoints[0].x,newPoints[2].x-newPoints[3].x}; intsuh[4]={newPoints[2].y-newPoints[0].y,newPoints[2].y-newPoints[1].y, newPoints[3].y-newPoints[0].y,newPoints[3].y-newPoints[1].y}; w=absMax4(suw); h=absMax4(suh); //獲取需要旋轉(zhuǎn)的四邊形區(qū)域的外接矩形表示區(qū)域范圍(x_min,y_min)、(x_max,y_max) inty_max,y_min,x_max,x_min; intpoints_x[4]={points[0].x,points[1].x,points[2].x,points[3].x}; intpoints_y[4]={points[0].y,points[1].y,points[2].y,points[3].y}; y_max=Max4(points_y); y_min=Min4(points_y); x_max=Max4(points_x); x_min=Min4(points_x); //計算向x軸的平移量dx,向y軸的平移量dy intdx,dy; inta[4]={newPoints[0].x,newPoints[1].x,newPoints[2].x,newPoints[3].x}; intb[4]={newPoints[0].y,newPoints[1].y,newPoints[2].y,newPoints[3].y}; dx=Min4(a); dy=Min4(b); //初始化輸出矩陣 if(inputMat.type()==CV_8UC1) cv::Mat(h,w,CV_8UC1,cv::Scalar::all(255)).copyTo(outputMat); if(inputMat.type()==CV_8UC3) cv::Mat(h,w,CV_8UC3,cv::Scalar(255,255,255)).copyTo(outputMat);//實現(xiàn)I(x',y')=I(x,y) doublez1,z2,z3,z4; for(inti=y_min;i<y_max;++i) { for(intj=x_min;j<x_max;++j) { //四邊形頂點A為points[0],頂點B為points[1],頂點C為points[2],頂點D為points[3].//直線AB z1=i-(double)points[0].y- (j-(double)points[0].x)*((double)points[0].y-points[1].y)/((double)points[0].x-points[1].x); //直線BC z2=j-(double)points[1].x- (i-(double)points[1].y)*((double)points[1].x-points[2].x)/((double)points[1].y-points[2].y); //直線CD z3=i-(double)points[2].y- (j-(double)points[2].x)*((double)points[2].y-points[3].y)/((double)points[2].x-points[3].x); //直線AD z4=j-(double)points[0].x- (i-(double)points[0].y)*((double)points[0].x-points[3].x)/((double)points[0].y-points[3].y); if(z1>=0&&z2<=0&&z3<=0&&z4>=0) { cv::Pointpoint0(j,i); rotatePoint(point,point0,point0,angle);//將點point0繞點point旋轉(zhuǎn)angle度得到新的點point0 translationPoint(point0,-dx,-dy); if(point0.x>=0&&point0.x<w&&point0.y>=0&&point0.y<h) { if(inputMat.type()==CV_8UC1) { uchar*str=inputMat.ptr<uchar>(i); outputMat.at<uchar>(point0.y,point0.x)=str[j]; } if(inputMat.type()==CV_8UC3) { cv::Vec3b*str=inputMat.ptr<cv::Vec3b>(i); outputMat.at<cv::Vec3b>(point0.y,point0.x)=str[j]; } } } } } if(inputMat.type()==CV_8UC1) {//插值 for(inti=1;i<outputMat.rows-1;++i) { for(intj=1;j<outputMat.cols-1;++j) { if(outputMat.at<uchar>(i,j)==255) { intsum=0; uchar*str1=outputMat.ptr<uchar>(i-1); sum=str1[j-1]+str1[j]+str1[j+1]; uchar*str2=outputMat.ptr<uchar>(i); sum=sum+str2[j-1]+str2[j+1]; uchar*str3=outputMat.ptr<uchar>(i+1); sum=sum+str3[j-1]+str3[j]+str3[j+1]; sum=sum/8; outputMat.at<uchar>(i,j)=(uchar)sum; } } } } if(inputMat.type()==CV_8UC3) {//插值 for(inti=1;i<outputMat.rows-1;++i) { for(intj=1;j<outputMat.cols-1;++j) { if(outputMat.at<cv::Vec3b>(i,j)==cv::Vec3b(255,255,255)) { intsum[3]={0,0,0}; ucharr,g,b; for(intk=0;k<3;k++) { cv::Vec3b*str1=outputMat.ptr<cv::Vec3b>(i-1); sum[k]=str1[j-1][k]+str1[j][k]+str1[j+1][k]; cv::Vec3b*str2=outputMat.ptr<cv::Vec3b>(i); sum[k]=sum[k]+str2[j-1][k]+str2[j+1][k]; cv::Vec3b*str3=outputMat.ptr<cv::Vec3b>(i+1); sum[k]=sum[k]+str3[j-1][k]+str3[j][k]+str3[j+1][k]; sum[k]=sum[k]/8; } r=(uchar)sum[0]; g=(uchar)sum[1]; b=(uchar)sum[2]; outputMat.at<cv::V

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論