【從零學習OpenCV 4】圖像透視變換
- 2019 年 12 月 13 日
- 筆記
經過幾個月的努力,小白終於完成了市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》。為了更讓小夥伴更早的了解最新版的OpenCV 4,小白與出版社溝通,提前在公眾號上連載部分內容,請持續關注小白。
本小節將介紹圖像的另一種變換——透視變換。透視變換是按照物體成像投影規律進行變換,即將物體重新投影到新的成像平面,示意圖如圖3-24所示。透視變換常用於機械人視覺導航研究中,由於相機視場與地面存在傾斜角使得物體成像產生畸變,通常通過透視變換實現對物體圖像的校正。透視變換中,透視前的圖像和透視後的圖像之間的變換關係可以用一個3×3的矩陣變換矩陣表示,該矩陣可以通過兩張圖像中四個對應點的坐標求取,因此透視變換又稱作「四點變換」。與仿射變換一樣,OpenCV 4中提供了根據四個對應點求取變換矩陣的getPerspectiveTransform()函數和進行透視變換的warpPerspective()函數,接下來將介紹這兩個函數的使用方法,兩個函數的函數原型在代碼清單3-35和代碼清單3-36中給出。

圖3-24 透視變換原理示意圖
代碼清單3-35 getPerspectiveTransform()函數原型 1. Mat cv::getPerspectiveTransform (const Point2f src[], 2. const Point2f dst[], 3. int solveMethod = DECOMP_LU 4. )
- src[]:原圖像中的四個像素坐標。
- dst[]:目標圖像中的四個像素坐標。
- solveMethod:選擇計算透視變換矩陣方法的標誌,可以選擇參數及含義在表3-6中給出。
該函數兩個輸入量都是存放浮點坐標的數組,在生成數組的時候像素點的輸入順序無關,但是需要注意像素點的對應關係,函數的返回值是一個3×3的變換矩陣。函數中最後一個參數是根據四個對應點坐標計算透視變換矩陣方法的選擇標誌,其可以選擇的參數標誌在表3-6中給出,默認情況下選擇的是最佳主軸元素的高斯消元法DECOMP_LU。
表3-6 getPerspectiveTransform()函數計算方法標誌
標誌參數 |
簡記 |
作用 |
---|---|---|
DECOMP_LU |
0 |
最佳主軸元素的高斯消元法 |
DECOMP_SVD |
1 |
奇異值分解(SVD)方法 |
DECOMP_EIG |
2 |
特徵值分解法 |
DECOMP_CHOLESKY |
3 |
Cholesky分解法 |
DECOMP_QR |
4 |
QR分解法 |
DECOMP_NORMAL |
16 |
使用正規方程公式,可以去前面的標誌一起使用 |
代碼清單3-36 warpPerspective()函數原型 1. void cv::warpPerspective(InputArray src, 2. OutputArray dst, 3. InputArray M, 4. Size dsize, 5. int flags = INTER_LINEAR, 6. int borderMode = BORDER_CONSTANT, 7. const Scalar & borderValue = Scalar() 8. )
- src:輸入圖像。
- dst:透視變換後輸出圖像,與src數據類型相同,但是尺寸與dsize相同。
- M:3×3的變換矩陣。
- dsize:輸出圖像的尺寸。
- flags:插值方法標誌。
- borderMode:像素邊界外推方法的標誌。
- borderValue:填充邊界使用的數值,默認情況下為0
該函數所有參數含義都與warpAffine()函數的參數含義相同,這裡不再進行贅述。為了說明該函數在實際應用中的作用,在代碼清單3-37中給出了將相機視線不垂直於二維碼平面拍攝的圖像經過透視變換變成相機視線垂直於二維碼平面拍攝的圖像。在圖3-25中給出了相機拍攝到的二維碼圖像和經過程序透視變換後的圖像。為了尋找透視變換關係,我們需要尋找拍攝圖像中二維碼四個角點的像素坐標和透視變換后角點對應的理想坐標。本程序中,我們事先通過Image Watch插件查看了拍攝圖像二維碼四個角點的坐標,並希望透視變換後二維碼可以充滿全部的圖像,因此我們在程序中手動輸入四對對應點的像素坐標。但是在實際工程中,二維碼的角點坐標可以通過角點檢測的方式獲取,具體方式我們將在後面介紹。
代碼清單3-37 myWarpPerspective.cpp二維碼圖像透視變換 1. #include <opencv2opencv.hpp> 2. #include <iostream> 3. 4. using namespace cv; 5. using namespace std; 6. 7. int main() 8. { 9. Mat img = imread("noobcvqr.png"); 10. if (img.empty()) 11. { 12. cout << "請確認圖像文件名稱是否正確" << endl; 13. return -1; 14. } 15. 16. Point2f src_points[4]; 17. Point2f dst_points[4]; 18. //通過Image Watch查看的二維碼四個角點坐標 19. src_points[0] = Point2f(94.0, 374.0); 20. src_points[1] = Point2f(507.0, 380.0); 21. src_points[2] = Point2f(1.0, 623.0); 22. src_points[3] = Point2f(627.0, 627.0); 23. //期望透視變換後二維碼四個角點的坐標 24. dst_points[0] = Point2f(0.0, 0.0); 25. dst_points[1] = Point2f(627.0, 0.0); 26. dst_points[2] = Point2f(0.0, 627.0); 27. dst_points[3] = Point2f(627.0, 627.0); 28. Mat rotation, img_warp; 29. rotation = getPerspectiveTransform(src_points, dst_points); //計算透視變換矩陣 30. warpPerspective(img, img_warp, rotation, img.size()); //透視變換投影 31. imshow("img", img); 32. imshow("img_warp", img_warp); 33. waitKey(0); 34. return 0; 35. }

圖3-25 myWarpPerspective.cpp程序運行結果