1. 圖像的透視變換
1.1 簡介
圖像的透視變換(Perspective Transformation)是指將圖像投影到一個新的視平面(Viewing Plane),也稱作投影映射(Projective Mapping)。
透視變換是一種非線性變換,它可以將一個二維坐標系中的點映射到三維坐標系中的點,然后再將其投影到另一個二維坐標系中的點。透視變換可以改變圖像中的形狀,并可以模擬真實世界中的透視效果。
仿射變換可以看成是透視變換的特殊情況,下圖是對幾何變換的總結。
幾何變換的總結.png
透視變換的應用:
圖像矯正透視變換可以用于矯正圖像的透視失真,例如由于拍攝角度或鏡頭畸變導致的圖像傾斜或拉伸。
圖像配準透視變換可以用于將兩張或多張圖像進行配準,使其具有相同的幾何形狀。這在醫學圖像處理、衛星圖像處理等領域有著重要的應用。
3D 建模透視變換可以用于將二維圖像投影到三維空間,從而生成三維模型。
圖像增強透視變換可以用于調整圖像的視角,使其看起來更具吸引力。
圖像合成透視變換可以用于將不同的圖像合成在一起,創建新的圖像。
特效透視變換可以用于創建各種特效,例如虛擬場景、3D 動畫等。
1.2 原理
透視變換的定義為將圖像中的所有點按照一定的透視關系映射到新的圖像中。
透視變換.png
透視關系可以由一個3x3的透視變換矩陣來表示,透視變換的矩陣如下:
其中,、、、 表示線性變換,、 表示平移變換,、 表示透視變換。
透視變換的過程為:
此時,得到的不是最后的坐標,還需要進一步轉換:
最終的坐標為:
重新回顧一下整個透視變換的過程:
不難看出看出仿射變換是透視變換的一種特殊情況。
2. 透視變換的應用
2.1 商品圖位置矯正
下面的代碼,對圖中的沒有擺正的商品通過透視變換將其對齊,然后在原圖中將商品放正。主要用到了 OpenCV 的 findHomography()、warpPerspective()函數進行透視變換。findHomography()函數用于計算兩個平面之間進行透視變換的矩陣,warpPerspective() 函數用于對圖像進行透視變換。
#include簡單介紹一下 warpPerspective() 函數:#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);?//?將頂點按照順時針方向進行排序 ????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());
第一個參數 src: 輸入圖像。
第二個參數 dst: 輸出圖像,與 src 具有相同的類型和大小。
第三個參數 M: 3x3 的透視變換矩陣。
第四個參數 dsize: 輸出圖像的大小。
上述代碼,還需要注意調用 findHomography() 函數時,輸入點的集合和輸出點的集合順序要一致。
2.2 廣告牌內容替換
透視變換還有一個比較經典的例子,就是替換一張圖像中廣告牌的內容,下面的代碼展示了這個例子:
#include3. 總結#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轉換到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函數實現二值化 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);?//?將頂點按照順時針方向進行排序 ????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; }
透視變換是一種重要的圖像處理技術,它具有廣泛的應用價值。它可以改變圖像的視角,從而使圖像更加符合人眼的視覺感受,或滿足特定的應用需求。它可以用于圖像矯正、圖像配準、3D 建模、增強現實等領域。
透視變換是一種非線性變換,因此它可能會導致圖像變形。例如,如果透視變換矩陣不合適,可能會使圖像中的物體看起來拉伸或壓縮。此外,透視變換也可能會導致圖像中的物體出現重疊或遮擋。在使用透視變換時,需要考慮這些局限性,并選擇合適的參數來獲得最佳效果。
審核編輯:黃飛
-
OpenCV
+關注
關注
30文章
628瀏覽量
41260 -
透視變換
+關注
關注
0文章
3瀏覽量
1350
原文標題:OpenCV筑基之圖像的透視變換
文章出處:【微信號:CVSCHOOL,微信公眾號:OpenCV學堂】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論