日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图像透视变换应用

發(fā)布時(shí)間:2024/1/1 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图像透视变换应用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

簡介

透視變換是將成像投影到一個(gè)新的視平面,也稱作投影映射。投影變換是三維空間上的非線性變換,可看做是仿射變換的更一般形式,簡單講即通過一個(gè)3x3的變換矩陣將原圖投影到一個(gè)新的視平面(Viewing Plane),在視覺上的直觀表現(xiàn)就是產(chǎn)生或消除了遠(yuǎn)近感。

OpenCV透視變換的透視變換

1、warpPerspective

利用透視矩陣對圖像進(jìn)行透視變換

  • 說明
    OpenCV提供了warpPerspective( )函數(shù)來實(shí)現(xiàn)圖片的透視變換,只需要輸入梯形四個(gè)頂點(diǎn)的坐標(biāo)和目標(biāo)畫布四個(gè)角的坐標(biāo),即可自動(dòng)完成轉(zhuǎn)換。核心代碼只有兩行:首先讀取兩個(gè)坐標(biāo)數(shù)組,計(jì)算變換矩陣;然后根據(jù)變換矩陣對原圖進(jìn)行透視變換,并輸出到目標(biāo)畫布。

    函數(shù)warpPerspective使用指定的矩陣轉(zhuǎn)換源圖像:
    dst(x,y)=src(M11x+M12y+M13M31x+M32y+M33,M21x+M22y+M23M31x+M32y+M33)\texttt{dst} (x,y) = \texttt{src} \left ( \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} , \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} \right ) dst(x,y)=src(M31?x+M32?y+M33?M11?x+M12?y+M13??,M31?x+M32?y+M33?M21?x+M22?y+M23??)
    當(dāng)設(shè)置標(biāo)志W(wǎng)ARP_INVERSE_MAP時(shí)。否則,首先使用invert反轉(zhuǎn)轉(zhuǎn)換,然后將其放在上面的公式中,而不是M中。

  • 聲明

    void warpPerspective(InputArray src,OutputArray dst,InputArray M,Size dsize,int flags=INTER_LINEAR,int borderMode= BORDER_CONSTANT,const Scalar &borderValue=Scalar() )
  • 參數(shù)

    src輸入圖像
    dst輸出圖像,其大小為dsize,并且類型與src相同。
    M3×3的轉(zhuǎn)換矩陣。
    dsize輸出圖像的尺寸大小。
    flags插值方法(INTER_LINEAR或INTER_NEAREST)與可選標(biāo)志W(wǎng)ARP_INVERSE_MAP的組合,設(shè)置M為逆變換(dst→src\texttt{dst}\rightarrow\texttt{src}dstsrc)。
    borderMode邊界補(bǔ)償方式,BORDER_CONSTANT 或者BORDER_REPLICATE
    borderValue邊界補(bǔ)償大小,常值,默認(rèn)為0。

關(guān)于變換矩陣M,OpenCV提供兩種方法計(jì)算:

1.1、findHomography–計(jì)算透視矩陣

通過輸入和輸出圖像中兩組點(diǎn)計(jì)算透視矩陣

  • 說明
    查找兩個(gè)平面之間的透視轉(zhuǎn)換。
    通過變換前、后兩個(gè)平面的點(diǎn)尋找出一個(gè)單應(yīng)性變換矩陣H:

    使得反投影誤差:
    ∑i(xi′?h11xi+h12yi+h13h31xi+h32yi+h33)2+(yi′?h21xi+h22yi+h23h31xi+h32yi+h33)2\sum _i \left ( x'_i- \frac{h_{11} x_i + h_{12} y_i + h_{13}}{h_{31} x_i + h_{32} y_i + h_{33}} \right )^2+ \left ( y'_i- \frac{h_{21} x_i + h_{22} y_i + h_{23}}{h_{31} x_i + h_{32} y_i + h_{33}} \right )^2i?(xi??h31?xi?+h32?yi?+h33?h11?xi?+h12?yi?+h13??)2+(yi??h31?xi?+h32?yi?+h33?h21?xi?+h22?yi?+h23??)2被最小化。

    如果參數(shù)方法設(shè)置為默認(rèn)值0,則該函數(shù)使用所有點(diǎn)對通過簡單的最小二乘方案計(jì)算初始單應(yīng)性估計(jì)。

    但是,如果不是所有的點(diǎn)對(srcPointsisrcPoints_isrcPointsi?, dstPointsidstPoints_idstPointsi?)都適合嚴(yán)格的透視圖轉(zhuǎn)換(也就是說,存在一些異常值),那么這個(gè)初始估計(jì)就會(huì)很差。在這種情況下,您可以使用三種健壯方法中的一種。RANSAC方法,LMeDS和 RHO嘗試許多不同的隨機(jī)對應(yīng)點(diǎn)對的子集(四雙,共線雙被丟棄),估計(jì)單應(yīng)性矩陣使用這個(gè)子集和一個(gè)簡單的最小二乘算法,然后計(jì)算計(jì)算單應(yīng)性的質(zhì)量/善良的數(shù)量(這是內(nèi)圍層RANSAC或至少值重新投影誤差LMeDS)。然后使用最佳子集生成單應(yīng)矩陣的初始估計(jì)inliers/outliers的掩碼。

    無論采用何種方法,無論是否健壯,都可以使用Levenberg-Marquardt方法進(jìn)一步精簡計(jì)算出的單應(yīng)性矩陣(僅在穩(wěn)健方法中使用inliers),以進(jìn)一步降低重投影誤差。

    RANSAC和RHO的方法幾乎可以處理任何異常值比率,但是需要一個(gè)閾值來區(qū)分異常值和異常值。LMeDS方法不需要任何閾值,但是只有在有超過50%的內(nèi)部值時(shí),它才能正確運(yùn)行。最后,如果沒有異常值并且噪聲很小,請使用默認(rèn)方法(method= 0)。

  • 聲明

    Mat findHomography( InputArray srcPoints, InputArray dstPoints,int method = 0, double ransacReprojThreshold = 3,OutputArray mask=noArray(), const int maxIters = 2000,const double confidence = 0.995 );Mat findHomography(InputArray srcPoints, InputArray dstPoints,OutputArray mask, int method = 0, double ransacReprojThreshold =3 );
  • 參數(shù)

    srcPoint原始平面中點(diǎn)的坐標(biāo),其類型為CV_32FC2或vector 的矩陣。
    dstPoint目標(biāo)平面中點(diǎn)的坐標(biāo),類型為CV_32FC2的矩陣或vector 。
    method用于計(jì)算單應(yīng)矩陣的方法。
    ransacReprojThreshold
    mask通過可靠的方法(RANSAC或LMEDS)設(shè)置的可選輸出掩碼。請注意,輸入掩碼值將被忽略。
    maxItersRANSAC的最大迭代次數(shù)。
    confidence置信度,介于0和1之間。
    method取值
    • 0: 使用所有點(diǎn)的常規(guī)方法,即最小二乘法
    • RANSAC: 基于RANSAC的魯棒方法
    • LMEDS: 最小中值穩(wěn)健方法
    • RHO: 基于PROSAC穩(wěn)健的方法
1.2、getPerspectiveTransform–根據(jù)四對對應(yīng)點(diǎn)計(jì)算透視變換

通過原圖和變換后圖像的4個(gè)對應(yīng)點(diǎn)(即對應(yīng)的四邊形)計(jì)算出透視變換矩陣。

  • 說明
    The function calculates the 3×3 matrix of a perspective transform so that:

  • 聲明

    Mat cv::getPerspectiveTransform ( InputArray src,InputArray dst,int solveMethod = DECOMP_LU ) Mat cv::getPerspectiveTransform ( const Point2f src[],const Point2f dst[],int solveMethod = DECOMP_LU )
  • 參數(shù)

    src源圖像中四邊形頂點(diǎn)的坐標(biāo)。
    dst目標(biāo)圖像中相應(yīng)四邊形頂點(diǎn)的坐標(biāo)。
    solveMethod傳遞給cv :: solve(DecompTypes)的方法。
2、perspectiveTransform–點(diǎn)的透視變換

利用透視矩陣對點(diǎn)進(jìn)行透視變換。

  • 說明
    執(zhí)行向量的透視矩陣轉(zhuǎn)換。

    函數(shù)cv :: perspectiveTransform通過將src的每個(gè)元素視為2D或3D向量來對其進(jìn)行轉(zhuǎn)換,其方式如下:

    這里顯示了3D矢量變換。在2D矢量變換的情況下,將省略z分量。

    該函數(shù)轉(zhuǎn)換2D或3D向量的稀疏集合。如果要使用透視變換來變換圖像,請使用warpPerspective。如果存在反問題,也就是說,您想從幾對對應(yīng)點(diǎn)中計(jì)算出最可能的透視變換,則可以使用getPerspectiveTransform或findHomography。

  • 聲明

    void perspectiveTransform( InputArray src,OutputArray dst,InputArray m )
  • 參數(shù)

    src輸入兩通道或三通道浮點(diǎn)數(shù)組;每個(gè)元素都是要轉(zhuǎn)換的2D / 3D向量。
    dst與src具有相同大小和類型的輸出數(shù)組。
    m3x3或4x4浮點(diǎn)轉(zhuǎn)換矩陣。

應(yīng)用

?簡單變換

void onMouse(int event, int x, int y, int flags, void* utsc) {if (event==EVENT_LBUTTONUP){//畫出源圖像中需要透視到四邊形中的4個(gè)待轉(zhuǎn)換點(diǎn)circle(src2, Point(x, y), 2, Scalar(0, 0, 255), 2);imshow("wait", src2);//記錄原圖中待轉(zhuǎn)換的點(diǎn)坐標(biāo)srcTri[clickTimes].x = x;srcTri[clickTimes].y = y;clickTimes++;cout << "The" << clickTimes << " point: [ " << srcTri[clickTimes - 1].x << "," << srcTri[clickTimes - 1].y << "]" << endl;}if (clickTimes == 4) {//將原圖像中的四個(gè)點(diǎn)轉(zhuǎn)換到目的圖形的對應(yīng)四個(gè)點(diǎn)上//第一個(gè)點(diǎn)——左上角dstTri[0].x = 0;dstTri[0].y = 0;//第二個(gè)點(diǎn)——右上角dstTri[1].x = 480;dstTri[1].y = 0;//第三個(gè)點(diǎn)——右下角dstTri[2].x = 480;dstTri[2].y = 600;//第四個(gè)點(diǎn)——左下角dstTri[3].x = 0;dstTri[3].y = 600;//通過原圖像和目標(biāo)圖像中4個(gè)對應(yīng)點(diǎn)求出透視矩陣Mat m = findHomography(srcTri, dstTri, RANSAC);warpPerspective(src, dst, m, Size(480, 600));imshow("dst", dst);} }int main() {src = imread("D:/test/m.png");src2 = src.clone();namedWindow("src", WINDOW_AUTOSIZE);imshow("src", src2);setMouseCallback("src", onMouse);waitKey(0);return 0; }



注意:需要注意透視變換中點(diǎn)與點(diǎn)的對應(yīng)順序,應(yīng)該順時(shí)針方向進(jìn)行點(diǎn)的轉(zhuǎn)換。

?復(fù)雜的透視變換
可以結(jié)合第一個(gè)應(yīng)用的方式,使用點(diǎn)擊事件進(jìn)行獲取需要在目標(biāo)圖片上轉(zhuǎn)換的具體坐標(biāo)。

void warpPerspectiveTest() {//1 讀取廣告圖Mat src = imread("D:/test/src.jpg");imshow("src", src);//2 讀取廣告牌背景圖Mat bg = imread("D:/test/bg.jpg");Mat bg_temp = bg.clone();imshow("bg", bg);//3 透視變換4對坐標(biāo)確定vector<Point2f> srcTri(4), dstTri(4);//3.1 源圖的四個(gè)坐標(biāo)srcTri[0].x = 0;srcTri[0].y = 0;srcTri[1].x = src.cols-1;srcTri[1].y = 0;srcTri[2].x = src.cols-1;srcTri[2].y = src.rows-1;srcTri[3].x = 0;srcTri[3].y = src.rows-1;//3.2 目標(biāo)圖片的四個(gè)坐標(biāo)//用畫圖工具測試的4個(gè)點(diǎn)的像素坐標(biāo)dstTri[0].x = 218;dstTri[0].y = 31;dstTri[1].x = 392;dstTri[1].y = 182;dstTri[2].x = 399;dstTri[2].y = 258;dstTri[3].x = 218;dstTri[3].y = 194;//4 求出透視變換的矩陣//Mat m=getPerspectiveTransform(srcTri, dstTri);Mat m = findHomography(srcTri, dstTri);//5 進(jìn)行透視變換warpPerspective(src, bg_temp, m, bg.size());imshow("bg_temp", bg_temp);//fillConvexPoly(InputOutputArray img, const Point * pts, int npts,// const Scalar & color, int lineType = LINE_8,// int shift = 0);Point pt[4];for (size_t i = 0; i < 4; i++){pt[i] = dstTri[i];}//6 在目標(biāo)圖像中去除多邊形區(qū)域,并合并fillConvexPoly(bg, pt, 4,Scalar(0));Mat dst;add(bg,bg_temp , dst);imshow("dst", dst); }





?自動(dòng)檢測源映射坐標(biāo)
上面都是手動(dòng)或者自己輸入的變換源坐標(biāo),下面讓計(jì)算機(jī)自己檢測需要的4個(gè)坐標(biāo)。
步驟:

  • 圖片預(yù)處理
    1.1 灰度
    1.2 二值化分割
    1.3 去噪聲
    1.4 閉操作
  • 尋找輪廓
  • 霍夫直線檢測
  • 定位邊沿輪廓直線
  • 直線方程
  • 直線交點(diǎn)
  • 透視矩陣
  • 透視變換
  • void warpPerspectiveTest2() {Mat src = imread("D:/test/kak.png");imshow("src", src);//1. 圖片預(yù)處理//1.1 灰度化Mat gray_src;cvtColor(src, gray_src, COLOR_BGR2GRAY);//1.2 去噪聲blur(gray_src, gray_src, Size(3, 3));//1.3 二值化Mat binary_src;threshold(gray_src, binary_src, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);imshow("binary_src", binary_src);//1.4 閉操作Mat closed_src;Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));morphologyEx(binary_src, closed_src, MORPH_CLOSE,kernel );imshow("closed_src", closed_src);//2. 尋找輪廓vector<vector<Point>> contours;Mat draw = Mat::zeros(src.size(), src.type());//注意尋找輪廓的方法,只尋找最外圍的輪廓findContours(closed_src, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//2.1 遍歷每一個(gè)輪廓for (size_t i = 0; i < contours.size(); i++){Rect rect = boundingRect(contours[i]);if (rect.width < src.cols / 2 && rect.height < src.rows / 2)continue;//2.2 畫出篩選后的輪廓drawContours(draw, contours, i, Scalar(0, 0, 255),3);}imshow("draw", draw);//3. 霍夫直線檢測--為了匹配四個(gè)邊vector<Vec4i> lines;cvtColor(draw, draw, COLOR_BGR2GRAY);//Each line is represented by a 4-element vector,(x1, y1, x2, y2), where (x1,y1)and(x2, y2)are the ending points of each detected//3.1 找出所有的直線HoughLinesP(draw, lines, 1, CV_PI / 180, src.rows / 2, src.rows / 2, 0);Mat draw2 = Mat::zeros(src.size(), src.type());for (int j = 0; j < lines.size(); j++) {Vec4i ln = lines[j];//3.2 畫出所有的直線line(draw2, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 255, 0), 2);}cout << "lines.size=" << lines.size() << endl;imshow("draw2", draw2);//4. 定位邊沿輪廓直線Vec4i topLine, bottomLine, leftLine, rightLine;for (size_t i = 0; i < lines.size(); i++){Vec4i ln = lines[i];//4.1找最左側(cè)的直線,確定左側(cè)直線的兩個(gè)點(diǎn)的橫坐標(biāo)在圖像中心的左側(cè)區(qū)域if (ln[0] < src.cols / 2 && ln[2] < src.cols / 2) {leftLine = ln;}//4.2找頂部的直線,確定頂部直線的兩個(gè)點(diǎn)的縱坐標(biāo)在圖像中心的上方區(qū)域內(nèi)if (ln[1] < src.rows / 2 && ln[3] < src.rows / 2) {topLine = ln;}//4.3找最右側(cè)的直線,確定右側(cè)直線的兩個(gè)點(diǎn)的橫坐標(biāo)在圖像中心的右側(cè)區(qū)域if (ln[0] > src.cols / 2 && ln[2] > src.cols / 2) {rightLine = ln;}//4.4找底部的直線,確定底部直線的兩個(gè)點(diǎn)的縱坐標(biāo)在圖像中心的下方區(qū)域內(nèi)if (ln[1] > src.rows / 2 && ln[3] > src.rows / 2) {bottomLine = ln;}}cout << "leftLine:p1(x,y)=" << leftLine[0] << "," << leftLine[1] << " " << "p2(x,y)=" << leftLine[2] << "," << leftLine[3] << endl;cout << "topLine:p1(x,y)=" << topLine[0] << "," << topLine[1] << " " << "p2(x,y)=" << topLine[2] << "," << topLine[3] << endl;cout << "rightLine:p1(x,y)=" << rightLine[0] << "," << rightLine[1] << " " << "p2(x,y)=" << rightLine[2] << "," << rightLine[3] << endl;cout << "bottomLine:p1(x,y)=" << bottomLine[0] << "," << bottomLine[1] << " " << "p2(x,y)=" << bottomLine[2] << "," << bottomLine[3] << endl;//5. 直線方程//5.1求出左側(cè)直線的方程 y=kx+b: k=(y2-y1)/(x2-x1) b=y-kx double kl = double(leftLine[3] - leftLine[1]) / double(leftLine[2] - leftLine[0]);double bl = double(leftLine[1] - kl * leftLine[0]);//5.2求出頂側(cè)直線的方程 double kt = double(topLine[3] - topLine[1]) / double(topLine[2] - topLine[0]);double bt = double(topLine[1] - kt * topLine[0]);//5.3求出右側(cè)直線的方程 double kr = double(rightLine[3] - rightLine[1]) / double(rightLine[2] - rightLine[0]);double br = double(rightLine[1] - kr * rightLine[0]);//5.4求出底部直線的方程 double kb = double(bottomLine[3] - bottomLine[1]) / double(bottomLine[2] - bottomLine[0]);double bb = double(bottomLine[1] - kb * bottomLine[0]);//6. 直線交點(diǎn)Point p1, p2, p3, p4;//6.1左側(cè)直線和頂部直線的交點(diǎn): x=(b2-b1)/(k1-k2) y=k1*x+b1p1.x = int((bt - bl) / (kl - kt));p1.y = int(kl * p1.x + bl);//6.2頂部直線和右側(cè)直線的交點(diǎn)p2.x = int((br - bt) / (kt - kr));p2.y = int(kt * p2.x + bt);//6.3右側(cè)直線和底部直線的交點(diǎn)p3.x = int((bb - br) / (kr - kb));p3.y = int(kr * p3.x + br);//6.4底部直線和左側(cè)直線的交點(diǎn)p4.x = int((bl - bb) / (kb - kl));p4.y = int(kb * p4.x + bb);cout << "左上角:[" << p1.x << "," << p1.y <<"]"<< endl;cout << "右上角:[" << p2.x << "," << p2.y <<"]"<< endl;cout << "右下角:[" << p3.x << "," << p3.y <<"]"<< endl;cout << "左下角:[" << p4.x << "," << p4.y <<"]"<< endl;//6.5畫出交點(diǎn)circle(draw2, p1, 2, Scalar(0, 0, 255));circle(draw2, p2, 2, Scalar(0, 0, 255));circle(draw2, p3, 2, Scalar(0, 0, 255));circle(draw2, p4, 2, Scalar(0, 0, 255));imshow("draw2-point", draw2);//7. 透視矩陣 //將檢測出來的4個(gè)點(diǎn)變換到新的對應(yīng)的4個(gè)點(diǎn)坐標(biāo)//7.1源圖像的四個(gè)待映射的點(diǎn)vector<Point2f> srcTri(4);srcTri[0] = p1;srcTri[1] = p2;srcTri[2] = p3;srcTri[3] = p4;//7.2目標(biāo)圖像中相對應(yīng)的點(diǎn)vector<Point2f> dstTri(4);dstTri[0] = Point(0, 0);dstTri[1] = Point(src.cols, 0);dstTri[2] = Point(src.cols, src.rows);dstTri[3] = Point(0, src.rows);//7.3獲取透視變換的矩陣Mat m_FindH=findHomography(srcTri, dstTri);Mat m_getPT=getPerspectiveTransform(srcTri, dstTri);//8 透視變換Mat dst_findH = Mat::zeros(src.size(), src.type());Mat dst_getPT = Mat::zeros(src.size(), src.type());warpPerspective(src, dst_findH, m_FindH, src.size());warpPerspective(src, dst_getPT, m_FindH, src.size());imshow("dst_findH", dst_findH);imshow("dst_getPT", dst_getPT);}









    學(xué)習(xí):
    【OpenCV3.3】通過透視變換矯正變形圖像

    Opencv日常之Homography

    【OpenCV】透視變換 Perspective Transformation(續(xù))

    【圖像處理】透視變換 Perspective Transformation

    圖像透視變換原理及實(shí)現(xiàn)

    詳解 OpenCV 透視變換原理 及 實(shí)例

    【圖像處理】透視變換 Perspective Transformation

    opencv學(xué)習(xí)筆記五十:透視變換綜合實(shí)例

    總結(jié)

    以上是生活随笔為你收集整理的图像透视变换应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。