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

下載本文檔

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

文檔簡(jiǎn)介

OpenCV3圖像旋轉(zhuǎn)算法實(shí)現(xiàn)圖像旋轉(zhuǎn)是非常常見(jiàn)的圖像變換,通常應(yīng)用于圖像矯正,在OpenCV可以使用密集仿射變換函數(shù)cv::warpAffine()實(shí)現(xiàn)圖像旋轉(zhuǎn)。為了理解圖像旋轉(zhuǎn)的原理,本文實(shí)現(xiàn)了一個(gè)圖像旋轉(zhuǎn)算法。圖像旋轉(zhuǎn)是指將圖像繞某個(gè)中心點(diǎn)旋轉(zhuǎn)一定角度后,得到一幅新的圖像。圖像旋轉(zhuǎn)的示意圖如圖1所示。其中,四邊形ABCD表示需要旋轉(zhuǎn)的圖像區(qū)域,它經(jīng)過(guò)旋轉(zhuǎn)角度后得到的圖像區(qū)域?yàn)樗倪呅蜛'B'C'D'。點(diǎn)p(x,y)為圖像內(nèi)任意一點(diǎn),它經(jīng)過(guò)旋轉(zhuǎn)角度后對(duì)應(yīng)的點(diǎn)為p'(x',y')。圖1圖像旋轉(zhuǎn)示意圖圖像是如何進(jìn)行旋轉(zhuǎn)的?通常這個(gè)過(guò)程有三個(gè)步驟。第一步,把圖像內(nèi)的坐標(biāo)點(diǎn)繞旋轉(zhuǎn)中心點(diǎn)旋轉(zhuǎn)到對(duì)應(yīng)的坐標(biāo)上。由于圖像是通過(guò)二維數(shù)組進(jìn)行保存的,所以圖像的坐標(biāo)點(diǎn)一定要落在坐標(biāo)系的第一象限內(nèi),并且要保證它們是整數(shù)坐標(biāo)點(diǎn)。通常情況下,進(jìn)行旋轉(zhuǎn)后得到的坐標(biāo)點(diǎn)不是整數(shù)點(diǎn)也不一定在第一象限內(nèi),因此需要對(duì)旋轉(zhuǎn)后得到的點(diǎn)進(jìn)行平移和取整,使得它們都是落在第一象限內(nèi)的整數(shù)點(diǎn)。圖像內(nèi)任意一點(diǎn)p(x,y)繞某個(gè)旋轉(zhuǎn)點(diǎn)(X,Y)逆時(shí)針旋轉(zhuǎn)角度后得到點(diǎn)p'(x',y')的計(jì)算公式如下:點(diǎn)旋轉(zhuǎn)的C++實(shí)現(xiàn)代碼如下。(OpenCV3.45+VS2019)//將點(diǎn)point2繞點(diǎn)point1逆時(shí)針旋轉(zhuǎn)angle度后得到新的點(diǎn)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)//做一個(gè)四舍五入取整 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)//做一個(gè)四舍五入取整 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;}用來(lái)平移坐標(biāo)點(diǎn)的代碼如下。voidtranslationPoint(cv::Point&point,intx,inty)//平移運(yùn)算{ point.x=point.x+x; point.y=point.y+y;}注:平移量x與y的大小,可以根據(jù)旋轉(zhuǎn)后圖像的四個(gè)頂點(diǎn)A'、B'、C'、D'獲得。第二步,把圖像對(duì)應(yīng)的坐標(biāo)像素大小賦給旋轉(zhuǎn)后的坐標(biāo)。即,圖像內(nèi)任意點(diǎn)p(x,y)對(duì)應(yīng)的像素值為I(x,y),那它旋轉(zhuǎn)后得到的點(diǎn)p'(x',y')的像素值I(x',y')=I(x,y)。如下圖2所示。當(dāng)我們需要旋轉(zhuǎn)的圖像區(qū)域在圖片內(nèi)時(shí)(這區(qū)域也可以是整張圖片),如何確定旋轉(zhuǎn)區(qū)域ABCD是很重要的,只有這樣才能判斷整張圖片內(nèi)的哪些點(diǎn)是四邊形ABCD區(qū)域內(nèi)的。圖2圖片中要旋轉(zhuǎn)的區(qū)域我們以圖片的左上頂點(diǎn)為原點(diǎn)建立如圖2所示的坐標(biāo)系,其中四邊形ABCD的四個(gè)頂點(diǎn)是已知的,分別為A(x0,y0)、B(x1,y1)、C(x2,y2)、D(x3,y3)。這時(shí)根據(jù)兩點(diǎn)式可得到四條邊的直線方程如下:根據(jù)線性規(guī)劃的知識(shí),可以通過(guò)直線方程來(lái)表示四邊形ABCD的區(qū)域。注:因?yàn)樗倪呅蜛BCD內(nèi)的任意點(diǎn)p在直線AB上方,所以直線AB方程大于等于0;點(diǎn)p在直線BC左側(cè),所以直線BC方程小于等于0。同理可得,直線CD方程小于等于0、直線AD方程大于等于0。實(shí)現(xiàn)代碼塊如下:std::vector<cv::Point>newPoints; cv::PointnewP; for(inti=0;i<4;++i) { if(points[i]!=point)//判斷輸入的4個(gè)頂點(diǎn)是否與旋轉(zhuǎn)點(diǎn)point相同 { rotatePoint(point,points[i],newP,angle);//頂點(diǎn)points[i]與旋轉(zhuǎn)點(diǎn)point不同,則進(jìn)行旋轉(zhuǎn)計(jì)算 newPoints.push_back(newP); } else { newPoints.push_back(points[i]); } } //獲取經(jīng)旋轉(zhuǎn)后,新圖像的大小,其中w表示圖像寬長(zhǎng),h表示圖像高長(zhǎng)。 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); //計(jì)算向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);//實(shí)現(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) { //四邊形頂點(diǎn)A為points[0],頂點(diǎn)B為points[1],頂點(diǎn)C為points[2],頂點(diǎn)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);//將點(diǎn)point0繞點(diǎn)point旋轉(zhuǎn)angle度得到新的點(diǎn)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]; } } } } }第三步,對(duì)旋轉(zhuǎn)后的圖像進(jìn)行插值。由于在第一步中對(duì)旋轉(zhuǎn)后的點(diǎn)進(jìn)行了取整,這難免會(huì)使得新圖像存在間隙,所以需要對(duì)這些間隙進(jìn)行填充。在OpenCV中常用的插值方法有以下5種:圖3常見(jiàn)的插值方法在本文中采用的插值方法與最近鄰插值類(lèi)似,即把最近四個(gè)方向(上下左右)的平均值作為插值。//灰度圖(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); } } }整個(gè)算法的完整代碼如下:#include<iostream>#include<opencv2/opencv.hpp>//計(jì)算點(diǎn)point2繞點(diǎn)point1逆時(shí)針旋轉(zhuǎn)angle度后得到新的點(diǎn)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)//做一個(gè)四舍五入 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)//做一個(gè)四舍五入 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)//平移運(yùn)算{ point.x=point.x+x; point.y=point.y+y;}intMax4(inta[4])//獲取四個(gè)數(shù)中的最大值{ intmax=a[0]; for(inti=1;i<4;i++) { if(max<a[i]) max=a[i]; } returnmax;}intMin4(inta[4])//獲取四個(gè)數(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個(gè)頂點(diǎn)是否與旋轉(zhuǎn)點(diǎn)point相同 { rotatePoint(point,points[i],newP,angle);//頂點(diǎn)points[i]與旋轉(zhuǎn)點(diǎn)point不同,則進(jìn)行旋轉(zhuǎn)計(jì)算 newPoints.push_back(newP); } else { newPoints.push_back(points[i]); } } //獲取經(jīng)旋轉(zhuǎn)后,新圖像的大小,其中w表示圖像寬長(zhǎng),h表示圖像高長(zhǎng)。 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); //計(jì)算向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);//實(shí)現(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) { //四邊形頂點(diǎn)A為points[0],頂點(diǎn)B為points[1],頂點(diǎn)C為points[2],頂點(diǎn)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);//將點(diǎn)point0繞點(diǎn)point旋轉(zhuǎn)angle度得到新的點(diǎn)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. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論