1. 圖像的透視變換
1.1 簡介
圖像的透視變換(Perspective Transformation)是指將圖像投影到一個新的視平面(Viewing Plane),也稱作投影映射(Projective Mapping)。
透視變換是一種非線性變換,它可以將一個二維坐標(biāo)系中的點(diǎn)映射到三維坐標(biāo)系中的點(diǎn),然后再將其投影到另一個二維坐標(biāo)系中的點(diǎn)。透視變換可以改變圖像中的形狀,并可以模擬真實(shí)世界中的透視效果。
仿射變換可以看成是透視變換的特殊情況,下圖是對幾何變換的總結(jié)。
幾何變換的總結(jié).png
透視變換的應(yīng)用:
圖像矯正透視變換可以用于矯正圖像的透視失真,例如由于拍攝角度或鏡頭畸變導(dǎo)致的圖像傾斜或拉伸。
圖像配準(zhǔn)透視變換可以用于將兩張或多張圖像進(jìn)行配準(zhǔn),使其具有相同的幾何形狀。這在醫(yī)學(xué)圖像處理、衛(wèi)星圖像處理等領(lǐng)域有著重要的應(yīng)用。
3D 建模透視變換可以用于將二維圖像投影到三維空間,從而生成三維模型。
圖像增強(qiáng)透視變換可以用于調(diào)整圖像的視角,使其看起來更具吸引力。
圖像合成透視變換可以用于將不同的圖像合成在一起,創(chuàng)建新的圖像。
特效透視變換可以用于創(chuàng)建各種特效,例如虛擬場景、3D 動畫等。
1.2 原理
透視變換的定義為將圖像中的所有點(diǎn)按照一定的透視關(guān)系映射到新的圖像中。
透視變換.png
透視關(guān)系可以由一個3x3的透視變換矩陣來表示,透視變換的矩陣如下:
其中,、、、 表示線性變換,、 表示平移變換,、 表示透視變換。
透視變換的過程為:
此時,得到的不是最后的坐標(biāo),還需要進(jìn)一步轉(zhuǎn)換:
最終的坐標(biāo)為:
重新回顧一下整個透視變換的過程:
不難看出看出仿射變換是透視變換的一種特殊情況。
2. 透視變換的應(yīng)用
2.1 商品圖位置矯正
下面的代碼,對圖中的沒有擺正的商品通過透視變換將其對齊,然后在原圖中將商品放正。主要用到了 OpenCV 的 findHomography()、warpPerspective()函數(shù)進(jìn)行透視變換。findHomography()函數(shù)用于計(jì)算兩個平面之間進(jìn)行透視變換的矩陣,warpPerspective() 函數(shù)用于對圖像進(jìn)行透視變換。
#include簡單介紹一下 warpPerspective() 函數(shù):#include #include #include usingnamespacestd; usingnamespacecv; boolascendSort(vector a,vector b) { returncontourArea(a)>contourArea(b); } longpointSideLine(Point&lineP1,Point&lineP2,Point&point){ longx1=lineP1.x; longy1=lineP1.y; longx2=lineP2.x; longy2=lineP2.y; longx=point.x; longy=point.y; return(x-x1)*(y2-y1)-(y-y1)*(x2-x1); } vector sortPointByClockwise(vector points){ if(points.size()!=4){ returnpoints; } PointunFoundPoint; vector result={unFoundPoint,unFoundPoint,unFoundPoint,unFoundPoint}; longminDistance=-1; for(autopoint:points){ longdistance=point.x*point.x+point.y*point.y; if(minDistance==-1||distance0){ result[1]=points[0]; result[3]=points[1]; }else{ result[1]=points[1]; result[3]=points[0]; } } if(result[0]!=unFoundPoint&&result[1]!=unFoundPoint&&result[2]!=unFoundPoint&&result[3]!=unFoundPoint){ returnresult; } returnpoints; } intmain(intargc,char*argv[]) { Matsrc=imread(".../product.jpg"); imshow("src",src); Matgray,binary; cvtColor(src,gray,COLOR_BGR2GRAY); threshold(gray,binary,0,255,THRESH_BINARY|THRESH_OTSU); imshow("binary",binary); vector >contours; vector hierarchy; findContours(binary,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE); sort(contours.begin(),contours.end(),ascendSort);//ascendingsort RotatedRectrrt=minAreaRect(contours[0]); Rectbbox=rrt.boundingRect(); if(bbox.height>2000){ rrt=minAreaRect(contours[1]); bbox=rrt.boundingRect(); } Matroi; try{ roi=src(bbox); } catch(...){ } imshow("roi",roi); intwidth=static_cast (rrt.size.width); intheight=static_cast (rrt.size.height); floatangle=rrt.angle; printf("height%d,width:%d,angle:%f ",height,width,angle); Point2fvertices[4]; rrt.points(vertices); vector src_pts; for(inti=0;i4;?i++)?{ ????????printf("x=%.2f,?y=%.2f ",?vertices[i].x,?vertices[i].y); ????????src_pts.push_back(vertices[i]); ????} ????src_pts?=?sortPointByClockwise(src_pts);?//?將頂點(diǎn)按照順時針方向進(jìn)行排序 ????vector dst_pts; dst_pts.push_back(Point(0,0)); dst_pts.push_back(Point(width,0)); dst_pts.push_back(Point(width,height)); dst_pts.push_back(Point(0,height)); MatM=findHomography(src_pts,dst_pts); Matresult=Mat::zeros(Size(width,height),CV_8UC3); warpPerspective(src,result,M,result.size()); imshow("result",result); resize(result,result,roi.size()); result.copyTo(roi); imshow("final",src); waitKey(0); return0; }
voidwarpPerspective(InputArraysrc,OutputArraydst,
InputArrayM,Sizedsize, intflags=INTER_LINEAR, intborderMode=BORDER_CONSTANT, constScalar&borderValue=Scalar());
第一個參數(shù) src: 輸入圖像。
第二個參數(shù) dst: 輸出圖像,與 src 具有相同的類型和大小。
第三個參數(shù) M: 3x3 的透視變換矩陣。
第四個參數(shù) dsize: 輸出圖像的大小。
上述代碼,還需要注意調(diào)用 findHomography() 函數(shù)時,輸入點(diǎn)的集合和輸出點(diǎn)的集合順序要一致。
2.2 廣告牌內(nèi)容替換
透視變換還有一個比較經(jīng)典的例子,就是替換一張圖像中廣告牌的內(nèi)容,下面的代碼展示了這個例子:
#include3. 總結(jié)#include #include #include usingnamespacestd; usingnamespacecv; boolascendSort(vector a,vector b) { returncontourArea(a)>contourArea(b); } longpointSideLine(Point&lineP1,Point&lineP2,Point&point){ longx1=lineP1.x; longy1=lineP1.y; longx2=lineP2.x; longy2=lineP2.y; longx=point.x; longy=point.y; return(x-x1)*(y2-y1)-(y-y1)*(x2-x1); } vector sortPointByClockwise(vector points){ if(points.size()!=4){ returnpoints; } PointunFoundPoint; vector result={unFoundPoint,unFoundPoint,unFoundPoint,unFoundPoint}; longminDistance=-1; for(autopoint:points){ longdistance=point.x*point.x+point.y*point.y; if(minDistance==-1||distance0){ result[1]=points[0]; result[3]=points[1]; }else{ result[1]=points[1]; result[3]=points[0]; } } if(result[0]!=unFoundPoint&&result[1]!=unFoundPoint&&result[2]!=unFoundPoint&&result[3]!=unFoundPoint){ returnresult; } returnpoints; } intmain(){ Matbillboard=imread(".../billboard.jpg"); imshow("billboard",billboard); Mathsv; cvtColor(billboard,hsv,cv::COLOR_BGR2HSV);//BGR轉(zhuǎn)換到HSV色彩空間 imshow("hsv",hsv); cv::Scalarlower_white(0,0,0); cv::Scalarupper_white(180,30,255); Matmask; inRange(hsv,lower_white,upper_white,mask);//通過inRange函數(shù)實(shí)現(xiàn)二值化 imshow("mask",mask); MatstructureElement=getStructuringElement(MORPH_RECT,Size(105,105),Point(-1,-1)); morphologyEx(mask,mask,MORPH_OPEN,structureElement,Point(-1,-1),1); imshow("mask2",mask); vector >contours; vector hierarchy; findContours(mask,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE); sort(contours.begin(),contours.end(),ascendSort);//ascendingsort RotatedRectrrt=minAreaRect(contours[0]);//獲取最大輪廓的最小外接矩形 Rectbbox=rrt.boundingRect(); intwidth=static_cast (rrt.size.width); intheight=static_cast (rrt.size.height); printf("width%d,height:%d ",width,height); Point2fpt[4]; rrt.points(pt); Matroi; try{ roi=billboard(bbox); } catch(...){ } imshow("roi",roi); Matgirl=imread(".../girl.jpg"); imshow("girl",girl); intwidth_girl=girl.cols; intheight_girl=girl.rows; vector src_pts; src_pts.push_back(Point(0,0)); src_pts.push_back(Point(width_girl,0)); src_pts.push_back(Point(width_girl,height_girl)); src_pts.push_back(Point(0,height_girl)); vector dst_pts; for(inti=0;i4;?i++)?{ ????????printf("x=%.2f,?y=%.2f ",?pt[i].x,?pt[i].y); ????????dst_pts.push_back(pt[i]); ????} ????dst_pts?=?sortPointByClockwise(dst_pts);?//?將頂點(diǎn)按照順時針方向進(jìn)行排序 ????Mat?M?=?findHomography(src_pts,dst_pts); ????Mat?result; ????warpPerspective(girl,?result,?M,?billboard.size()); ????imshow("result",?result); ????result.copyTo(billboard,mask); ????imshow("final",?billboard); ????waitKey(0); ????return?0; }
透視變換是一種重要的圖像處理技術(shù),它具有廣泛的應(yīng)用價值。它可以改變圖像的視角,從而使圖像更加符合人眼的視覺感受,或滿足特定的應(yīng)用需求。它可以用于圖像矯正、圖像配準(zhǔn)、3D 建模、增強(qiáng)現(xiàn)實(shí)等領(lǐng)域。
透視變換是一種非線性變換,因此它可能會導(dǎo)致圖像變形。例如,如果透視變換矩陣不合適,可能會使圖像中的物體看起來拉伸或壓縮。此外,透視變換也可能會導(dǎo)致圖像中的物體出現(xiàn)重疊或遮擋。在使用透視變換時,需要考慮這些局限性,并選擇合適的參數(shù)來獲得最佳效果。
審核編輯:黃飛
-
OpenCV
+關(guān)注
關(guān)注
31文章
635瀏覽量
41350 -
透視變換
+關(guān)注
關(guān)注
0文章
3瀏覽量
1354
原文標(biāo)題:OpenCV筑基之圖像的透視變換
文章出處:【微信號:CVSCHOOL,微信公眾號:OpenCV學(xué)堂】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論