cv::ximgproc::FastLineDetectors是opencv-contrib中用於檢測直線的模塊,該方法能在較短時間內獲得精度較高的直線檢測結果,且不需要調節參數。該函數是LineSegmentDetector因版權問題從OpenCV中移除後最易用的直線檢測小能手,沒有之一。本文介紹該功能的使用方法其輸出結果剖析。
本文範例運行環境
FastLineDetectors運行必要條件
FastLineDetectors屬於opencv-contrib中的模塊,需要安裝opencv-contrib-python。在python的opencv相關的安裝包中,opencv-python 包含主要模塊,opencv-contrib-python 包含主要模塊以及一些擴展模塊。但這兩個模塊並不兼容,如果已經安裝過opencv-python,需要先卸載,再安裝opencv-contrib-python。
1 pip uninstall opencv-python2 pip install opencv-contrib-python在OpenCV 4.1.0之前的版本中,有一個LineSegmentDetector模塊,功能強大,檢測直線非常便捷,但該函數因為版權問題已在4.1.X版本上已移除,故此處不討論此方案。LineSegmentDetector相關的問題可以參考:
any-implementation-of-linesegmentdetector-for-python
https://answers.opencv.org/question/215800/any-implementation-of-linesegmentdetector-for-python/
LineSegmentDetector needs to be removed for license
https://github.com/WPIRoboticsProjects/GRIP/issues/961
FastLineDetectors應用示例
1 import cv2 2 import numpy as np 3 img = cv2.imread('sudo.png') 4 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 5 #Create default Fast Line Detector (FSD) 6 fld = cv2.ximgproc.createFastLineDetector() 7 #Detect lines in the image 8 lines = fld.detect(gray) 9 #Draw detected lines in the image 10 drawn_img = fld.drawSegments(gray,lines) 11 cv2.imshow("FLD", drawn_img) 12 cv2.waitKey(0)
如以上代碼所示:
第1~2行導入python包。
第3行讀入測試圖片,本文中測試圖片與代碼文件在同一路徑,故只有圖片名稱。更改測試圖像時,更改該行代碼中的圖像名稱即可。
第4行將測試圖片轉換為灰度圖。
第6行創建FastLineDetectors檢測實例,這裡使用的是默認參數,其參數順序及意義如下:
retval = cv.ximgproc.createFastLineDetector( [, _length_threshold[, _distance_threshold[, _canny_th1[, _canny_th2[, _canny_aperture_size[, _do_merge]]]]]] ) Parameters _length_threshold 10 - Segment shorter than this will be discarded _distance_threshold 1.41421356 - A point placed from a hypothesis line segment farther than this will be regarded as an outlier _canny_th1 50 - First threshold for hysteresis procedure in Canny() _canny_th2 50 - Second threshold for hysteresis procedure in Canny() _canny_aperture_size 3 - Aperturesize for the sobel operator in Canny() _do_merge false - If true, incremental merging of segments will be perfomred
第8行使用FastLineDetectors檢測測試圖像中的直線。
第10行根據檢測結果繪製直線。
第11~12行用於顯示檢測結果。
如下動圖所示,我選取了兩張圖,均使用默認參數來檢測,圖中的直線基本被檢出,說明該函數的通用性還是比較強的。
以其中停車場的圖片為例,圖中有許多小短線,此時可以更改createFastLineDetector的默認參數,設置直線的長度來對檢測直線進行過濾。
FastLineDetectors檢測結果詳解
通常我們檢測直線並不只是為了將其繪製出來而已,我們可能需要知道該直線與其他直線的關係。這時我們就需要能取用檢測得到直線兩個端點的坐標。
接下來開始解析FastLineDetectors的檢測結果,學習如何取用檢測直線的端點坐標。
在"FastLineDetectors應用示例"小節中,在完成直線檢測代碼之後,添加如下代碼,先觀察一下FastLineDetectors檢測結果的各種屬性:
print("數據類型",type(lines)) #列印數組數據類型print("數組元素數據類型:",lines.dtype) #列印數組元素數據類型 print("數組元素總數:",lines.size) #列印數組尺寸,即數組元素總數 print("數組形狀:",lines.shape) #列印數組形狀 print("數組的維度數目",lines.ndim) #列印數組的維度數目
運行後,可以看到FastLineDetectors的檢測結果的各種輸入為:
數據類型 <class 'numpy.ndarray'> 數組元素數據類型: float32 數組元素總數: 724 數組形狀: (181, 1, 4) 數組的維度數目 3
然後我們到github查看一下drawSegments的實現函數,如下所示:
1 void FastLineDetectorImpl::drawSegments(InputOutputArray _image, InputArray lines, bool draw_arrow) 2 { 3 CV_INSTRUMENT_REGION(); 4 5 CV_Assert(!_image.empty() && (_image.channels() == 1 || _image.channels() == 3)); 6 7 Mat gray; 8 if (_image.channels() == 1) 9 {10 gray = _image.getMatRef();11 }12 else if (_image.channels() == 3)13 {14 cvtColor(_image, gray, COLOR_BGR2GRAY);15 }16 17 // Create a 3 channel image in order to draw colored lines18 std::vector<Mat> planes;19 planes.push_back(gray);20 planes.push_back(gray);21 planes.push_back(gray);22 23 merge(planes, _image);24 25 double gap = 10.0;26 double arrow_angle = 30.0;27 28 Mat _lines;29 _lines = lines.getMat();30 int N = _lines.checkVector(4);31 // Draw segments32 for(int i = 0; i < N; ++i)33 {34 const Vec4f& v = _lines.at<Vec4f>(i);35 Point2f b(v[0], v[1]);36 Point2f e(v[2], v[3]);37 line(_image.getMatRef(), b, e, Scalar(0, 0, 255), 1);38 if(draw_arrow)39 {40 SEGMENT seg;41 seg.x1 = b.x;42 seg.y1 = b.y;43 seg.x2 = e.x;44 seg.y2 = e.y;45 getAngle(seg);46 double ang = (double)seg.angle;47 Point2i p1;48 p1.x = cvRound(seg.x2 - gap*cos(arrow_angle * CV_PI / 180.0 + ang));49 p1.y = cvRound(seg.y2 - gap*sin(arrow_angle * CV_PI / 180.0 + ang));50 pointInboardTest(_image.getMatRef(), p1);51 line(_image.getMatRef(), Point(cvRound(seg.x2), cvRound(seg.y2)), p1, Scalar(0,0,255), 1);52 }53 }54}上述代碼中:
第28~37行用於繪製檢測到的直線,其中lines[i][0]~lines[i][3]中存儲的四個元素分別為第i條直線端點1的X,Y坐標和端點2的X,Y坐標。
第38行中的變量[draw_arrow],是函數drawSegments的參數之一,默認值為false,用於設定是否在檢測直線的其中一個端點處繪製箭頭。
根據以上信息,我們推測在python的FastLineDetectors檢測結果中,lines[i][0][0]~lines[i][0][3]分別對應第 i 條直線端點1的X,Y坐標和端點2的X,Y坐標。
為了證明上述推測,我們選取下圖作為測試圖片,修改一下相關代碼,實現如下功能:找出檢測結果中最長的一根直線,繪製該直線及其兩個端點,並且在該直線一個端點處繪製箭頭。
1 import cv2 2 import numpy as np 3 from scipy.spatial import distance as dist 4 5 img = cv2.imread('sDQLM.png') 6 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 7 8 #Create default Fast Line Detector (FSD) 9 fld = cv2.ximgproc.createFastLineDetector()10 11 #Detect lines in the image12 lines = fld.detect(gray)13 14 dMax = 015 bx_Max = 016 by_Max = 017 ex_Max = 018 ey_Max = 019 20 for L in lines:21 22 bx,by,ex,ey = L[0]23 24 # compute the Euclidean distance between the two points,25 D = dist.euclidean((bx, by), (ex, ey))26 27 if D > dMax:28 dMax = D29 bx_Max = bx30 by_Max = by31 ex_Max = ex32 ey_Max = ey33 34 lineMax = np.array([[[bx_Max, by_Max, ex_Max,ey_Max]]])35 #Draw detected lines in the image36 drawn_img = fld.drawSegments(gray,lineMax,True)37 cv2.circle(drawn_img, (bx_Max, by_Max), 1, (255,0,0), 2)#line begin38 cv2.circle(drawn_img, (ex_Max, ey_Max), 1, (0,255,0), 2)#line end39 40 cv2.imshow("FLD", drawn_img)41 cv2.waitKey(0)上述代碼中:
第25行用於計算直線的長度,便於我們找出檢測結果中最長的直線。
第34行用最長的直線的兩個端點重新構建一個np.array。
第36行繪製最長的直線,並且第三個參數設置為TRUE,即在直線其中一端繪製箭頭。
第37~38行,在圖中繪製兩個端點。
運行結果如下:
FastLineDetectors可以方便的檢測圖中的直線,並且可以簡單的取用直線兩個端點的坐標,接下來就可以用它實現更多有趣的功能。
本文到此結束,感謝閱讀。
原文連結:https://livezingy.com/fastlinedetectors-opencv-contrib/
Webinar 1: OpenCV Overview
Webinar 2: OpenCV DNN在ARM上的加速
Webinar 3: OpenCV深度學習應用與原理分析
Webinar 4: 谷歌編程夏令營2020 OpenCV文本識別項目開發
OpenCV中國團隊官方推薦和認證OpenCV在線課程
加入我們OpenCV志願者
(請註明「OpenCV志願者」)
OpenCV中國團隊於2019年9月由深圳市人工智慧與機器人研究院支持成立,非營利目的,致力於OpenCV的開發、維護和推廣工作。
長按下方QR碼關注我們獲取最新動態