1. 程式人生 > >利用OpenCV-python進行直線檢測

利用OpenCV-python進行直線檢測

 最近需要利用攝像頭對細小的偏移做矯正,由於之前的介面工具是用 PyQT 所寫,在當前的工具中加入攝像頭矯正程式,也打算用 python 直接完成。

OpenCV 簡介:

Python 處理影象有 OpenCV 庫。OpenCV 可以執行在 Linux,windows,macOS 上,由 C 函式和 C++ 類構成,用於實現計算機影象、視訊的編輯,應用於影象識別、運動跟蹤、機器視覺等領域。

OpenCV 安裝:

OpenCV 無法用 pip 或easy_install 安裝,需要手動下載 .whl 檔案安裝。

實際應用中安裝的OpenCV 庫版本為 2.4.13。之所以用 2.4.13 版本的OpenCV,不採用 3.4 版本,是因為專案中用到的直線檢測演算法——霍夫變換,在OpenCV 3.4 中只能檢測到一條結果,這條結果是所有線段中最長的一條。

從以下網址獲取 OpenCV 的安裝包(網頁載入的速度比較慢,需要靜候)

開啟連結,待載入完畢後,搜尋 OpenCV,可以找到如下頁面


根據實際安裝的 python 環境,下載對應的 OpenCV 安裝包。

輸入命令,完成 OpenCV 的安裝。

pip install opencv_python‑2.4.13.5‑cp27‑cp27m‑win_amd64.whl

OpenCV 使用:

我對影象的處理是對灰度影象進行處理,不考慮 RGB 值,為了減輕實際的計算量。OpenCV 提供的 RGB 轉灰度值的介面是

gray = cv2.cvtColor(cut_img, cv2.COLOR_BGR2GRAY)

得到的灰度影象與原圖的比較,如下圖所示


要得到圖片的線段,在灰度圖上繼續做後續操作。對影象線段進行檢測的演算法,是通過運算元進行邊緣檢測,常用運算元有Prewitt 運算元、sobel 運算元、Laplace 運算元以及 Canny 運算元。這裡就不對這些運算元的檢測結果進行詳述了,因為我也不是很明白幾種運算元實際效果的區別。本例中使用的是 canny 運算元。

OpenCV 中呼叫 canny 運算元的方式如下:

edge = cv2.Canny(image, threshold1,threshold2[, edges[, apertureSize[, L2gradient ]]])

必要引數:

第一個引數是需要處理的原影象,該影象必須為單通道的灰度圖;

第二個引數是閾值1;

第三個引數是閾值2。

其中較大的閾值2用於檢測影象中明顯的邊緣,但一般情況下檢測的效果不會那麼完美,邊緣檢測出來是斷斷續續的。所以這時候用較小的第一個閾值用於將這些間斷的邊緣連線起來。

可選引數中apertureSize就是Sobel運算元的大小。而L2gradient引數是一個布林值,如果為真,則使用更精確的L2範數進行計算(即兩個方向的倒數的平方和再開放),否則使用L1範數(直接將兩個方向導數的絕對值相加)。

隨後進行霍夫變換,得到線段的座標。

霍夫變換是經典的檢測直線的演算法。其最初用來檢測影象中的直線,同時也可以將其擴充套件,以用來檢測影象中簡單的結構。

cv2.HoughLines函式輸出的是[float,float]形式的ndarray,其中每個值表示檢測到的線(ρ , θ)中浮點點值的引數。在本例中,函式將通過步長為1的半徑和步長為π/180的角來搜尋所有可能的直線。然而用這種方法檢測出來的是貫穿整張影象的直線,需要檢測線段時,則要採用概率霍夫變換。

lines = cv2.HoughLinesP(edges, 1,np.pi/180, 80, minLineLength, maxLineGap)

它有兩個引數:

minLineLength-線的最短長度,比這個線短的都會被忽略。

maxLineGap-兩條線之間的最大間隔,如果小於此值,這兩條線就會被看成一條線。

這個函式的返回值就是直線的起點和終點。

最後將找到的線段均顯示在圖片上,即完成直線檢測。

由於圖片畫質因素,很多看似是直線的也未檢測出是直線,還有許多散碎的直線並沒有連線成長線。有待後續研究解決。


附上完整程式碼:

import cv2
import numpy as np

#img = cv2.imread("C:/AE_PUFF/python_vision/2018_04_27/kk-3.jpg")
img = cv2.imread("test.jpg")
cv2.imshow('origin_img', img)
height = img.shape[0]  # 高度
width  = img.shape[1]  # 寬度
cut_img = img

gray = cv2.cvtColor(cut_img, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray_img', gray)
cv2.waitKey(0)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)

lines = cv2.HoughLines(edges, 1, np.pi/180, 118)
result = cut_img.copy()
minLineLength = 30 # height/32
maxLineGap = 10 # height/40
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 80, minLineLength, maxLineGap)

for x1, y1, x2, y2 in lines[0]:
    cv2.line(result, (x1, y1), (x2, y2), (0,255,0), 2)

cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()