用 Python 和 OpenCV 來測量相機到目標的距離
(點擊
上方藍字
,快速關注我們)
英文:Adrian Rosebrock
編譯:
伯樂在線
-
G.K.
http://python.jobbole.com/84378/
幾天前,一個叫 Cameron 的 PyImageSearch 讀者發來郵件詢問攝像頭測距的方法。他花了一些時間研究,但是沒有找到解決辦法。
我很能體會 Cameron 的感受。幾年前我做過一個分析棒球離手飛向本壘的運動的小項目。
我通過使用運動分析和基於軌跡的跟蹤方法來確定或者估計小球在視頻幀中的位置。並且因為棒球的大小是已知的,所以我也能估計出其到本壘的距離。
那是個有趣的項目,雖然系統的精度沒有達到我的預期。——棒球運動太快所造成的「運動模糊」讓達到高精度變得十分困難。
我的項目完全算是一個個例,但是通常來說,在計算機視覺或者圖形處理領域計算從相機到目標的距離實際上是一個非常容易的問題。你可以找到一個像三角形相似這樣簡單粗暴的方法,或者你也可以用上相機模型的內參這樣更複雜一點(但是更精確)的方法。
在這篇博客,我將會告訴大家我和 Cameron 是如果解決這個計算相機到已知物體或目標的距離。
千萬要看——你一定不想錯過。
OpenCV 和 Python 版本:
這個例子可以在
Python 2.7
/
Python 3.4+
和
OpenCV 2.4.X
上運行。
用相似三角形計算物體或者目標到相機的距離
我們將使用相似三角形來計算相機到一個已知的物體或者目標的距離。
相似三角形就是這麼一回事:假設我們有一個寬度為 W 的目標或者物體。然後我們將這個目標放在距離我們的相機為 D 的位置。我們用相機對物體進行拍照並且測量物體的像素寬度 P 。這樣我們就得出了相機焦距的公式:
F = (P x D) / W
舉個例子,假設我在離相機距離 D = 24 英寸的地方放一張標準的 8.5 x 11 英寸的 A4 紙(橫著放;W = 11)並且拍下一張照片。我測量出照片中 A4 紙的像素寬度為 P = 249 像素。
因此我的焦距 F 是:
F = (248px x 24in) / 11in = 543.45
當我繼續將我的相機移動靠近或者離遠物體或者目標時,我可以用相似三角形來計算出物體離相機的距離:
D』 = (W x F) / P
為了更具體,我們再舉個例子,假設我將相機移到距離目標 3 英尺(或者說 36 英寸)的地方並且拍下上述的 A4 紙。通過自動的圖形處理我可以獲得圖片中 A4 紙的像素距離為 170 像素。將這個代入公式得:
D』 = (11in x 543.45) / 170 = 35 英寸
或者約 36 英寸,合 3 英尺。
注意:
當我給這次例子拍照時,我的捲尺有一點松,因此結果造成了大約 1 英寸的誤差。還有我也是很快速地拍下了照片並且沒有完全對齊捲尺上的腳標,這也會對最終結果的 1 英寸誤差產生影響。綜上所述,相似三角形的方法還是合理的,你也可以用這個方法很簡單地計算出物體或者目標距離你的相機的距離。
現在理解了?
太棒了。接下來讓我們用一些代碼來看看如何用 Python、OpenCV、圖像處理和計算機視覺技術來獲得相機到物體或者目標的距離。
用Python和OpenCV來測量相機到目標的距離
繼續,我們開始這個項目。打開一個文件,命名為
distance_to_camera.py
,然後就可以開工了。
# import the necessary packages
import
numpy
as
np
import
cv2
def
find_marker
(
image
)
:
# convert the image to grayscale, blur it, and detect edges
gray
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2GRAY
)
gray
=
cv2
.
GaussianBlur
(
gray
,
(
5
,
5
),
0
)
edged
=
cv2
.
Canny
(
gray
,
35
,
125
)
# find the contours in the edged image and keep the largest one;
# we"ll assume that this is our piece of paper in the image
(
cnts
,
_
)
=
cv2
.
findContours
(
edged
.
copy
(),
cv2
.
RETR_LIST
,
cv2
.
CHAIN_APPROX_SIMPLE
)
c
=
max
(
cnts
,
key
=
cv2
.
contourArea
)
# compute the bounding box of the of the paper region and return it
return
cv2
.
minAreaRect
(
c
)
第一件要做的事情就是導入必要的包。我們將用
NumPy
來進行數值計算和
cv2
來綁定 OpenCV 。
在那之後我們定義
find_marker
函數。這個函數接收一個
image
參數,並且這意味著我們將用它來找出將要計算距離的物體。
在這個例子中我們使用標準的 8.5 x 11 英寸的 A4 紙作為我們的目標。
目前我們的第一個任務是找出圖像中的這張紙。
我們先將圖像轉成灰度圖,用高斯模糊除去明顯的噪點,並且在第
7-9 行
使用邊緣檢測。
完成這幾步後,我們的圖像應該長這樣:
如你所見,我們的目標(A4 紙)的邊緣已經很清晰了。現在我們只要找出這張紙的輪廓(比如:外形)。
我們用
13 行
的
cv2.findContours
函數找到目標,並且在
14 行
計算出面積最大的輪廓。
我們假設面積最大的輪廓是我們的那張 A4 紙。
這個假設在我們的這個例子是成立的,但是實際上在圖像中找出目標是和是與應用場景高度相關的。
在我們的例子中,簡單的邊緣檢測和計算最大的輪廓是可行的。我們可以通過使用輪廓近似法使系統更具魯棒性,排除不包含有4個頂點的輪廓(因為 A4 紙是矩形有四個頂點),然後計算面積最大的四點輪廓。
注意:
更多這樣的方法見
這篇文章,講述了如何做一個簡單粗暴的手機掃描儀。
其他找到圖像中目標可選的方法是利用顏色特徵(目標的顏色和背景有著明顯的不同)。你還可以使用關鍵點檢測,局部不變性描述子,和關鍵點匹配來尋找目標。但是這些方法以及超出了這篇文章的範疇,並且具有高度定製化的特性。
不管怎樣,我們現在獲得了目標的輪廓,並且在第
17 行
返回包含
(x, y)
坐標和像素高度和寬度信息的邊界框給調用函數。
讓我們也快速定義一個用上述的相似三角形法計算距離的函數:
def
distance_to_camera
(
knownWidth
,
focalLength
,
perWidth
)
:
# compute and return the distance from the maker to the camera
return
(
knownWidth
*
focalLength
)
/
perWidth
這個函數傳入目標的
knownWidth
,計算好的
focalLength
,和目標在圖像中的像素距離,並且使用上面推導的相似三角形公式來計算到物體的距離
。
繼續讀下列代碼來看看我們是如何利用這些函數的:
#import the necessary packages
import
numpy
as
np
import
cv2
def
find_marker
(
image
)
:
# convert the image to grayscale, blur it, and detect edges
gray
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2GRAY
)
gray
=
cv2
.
GaussianBlur
(
gray
,
(
5
,
5
),
0
)
edged
=
cv2
.
Canny
(
gray
,
35
,
125
)
# find the contours in the edged image and keep the largest one;
# we"ll assume that this is our piece of paper in the image
(
cnts
,
_
)
=
cv2
.
findContours
(
edged
.
copy
(),
cv2
.
RETR_LIST
,
cv2
.
CHAIN_APPROX_SIMPLE
)
c
=
max
(
cnts
,
key
=
cv2
.
contourArea
)
# compute the bounding box of the of the paper region and return it
return
cv2
.
minAreaRect
(
c
)
def
distance_to_camera
(
knownWidth
,
focalLength
,
perWidth
)
:
# compute and return the distance from the maker to the camera
return
(
knownWidth
*
focalLength
)
/
perWidth
# initialize the known distance from the camera to the object, which
# in this case is 24 inches
KNOWN_DISTANCE
=
24.0
# initialize the known object width, which in this case, the piece of
# paper is 11 inches wide
KNOWN_WIDTH
=
11.0
# initialize the list of images that we"ll be using
IMAGE_PATHS
=
[
"images/2ft.png"
,
"images/3ft.png"
,
"images/4ft.png"
]
# load the furst image that contains an object that is KNOWN TO BE 2 feet
# from our camera, then find the paper marker in the image, and initialize
# the focal length
image
=
cv2
.
imread
(
IMAGE_PATHS
[
0
])
marker
=
find_marker
(
image
)
focalLength
=
(
marker
[
1
][
0
]
*
KNOWN_DISTANCE
)
/
KNOWN_WIDTH
找到圖像中目標的距離的第一步是標定和計算焦距。我們需要知道以下參數:
相機到物體的距離
這個物體的寬度(單位英尺或米)。
注意:
也可以用高度,這個例子中我們使用寬度。
這裡不得不提示一下我們所做的並不是實質意義上的攝像機標定。真正的攝像機標定包括攝像機的內參,你可以從
這裡獲得更多相關知識。
在第
25 行
我們初始化了已知的
KNOWN_DISTANCE
,從相機到物體的距離為 24 英寸。在第
29 行
我們初始了物體的寬度
KNOWN_WIDTH
為 11 英寸(一張橫著放的標準 A4 紙)。
然後我們在第
32 行
定義要用到的圖片的路徑。
下一步比較重要:
是一個簡單的標定
。
第
37 行
從硬碟讀取第一張圖,——我們將用這張圖來作為標定圖片。
圖片載入以後,在第
38 行
計算圖中 A4 紙的輪廓信息,在第
39 行
使用三角形相似法計算出
focalLength
。
由於我們已經「標定」了我們的系統並且獲得了
focalLength
,我們可以很容易地計算出相機離接下來圖片中目標的距離。
讓我們看看這個是這麼做的:
41
# loop over the images
42
for
imagePath
in
IMAGE_PATHS
:
# load the image, find the marker in the image, then compute the
# distance to the marker from the camera
image
=
cv2
.
imread
(
imagePath
)
46
marker
=
find_marker
(
image
)
47
inches
=
distance_to_camera
(
KNOWN_WIDTH
,
focalLength
,
marker
[
1
][
0
])
# draw a bounding box around the image and display it
box
=
np
.
int0
(
cv2
.
cv
.
BoxPoints
(
marker
))
cv2
.
drawContours
(
image
,
[
box
],
-
1
,
(
0
,
255
,
0
),
2
)
cv2
.
putText
(
image
,
"%.2fft"
%
(
inches
/
12
),
(
image
.
shape
[
1
]
-
200
,
image
.
shape
[
0
]
-
20
),
cv2
.
FONT_HERSHEY_SIMPLEX
,
2.0
,
(
0
,
255
,
0
),
3
)
cv2
.
imshow
(
"image"
,
image
)
cv2
.
waitKey
(
0
)
在第
42 行
開始遍歷所有的圖片路徑。
然後,在第
45 行
我們將列表中所有的圖片從硬碟讀取下來。在第
46 行
提取目標輪廓,並且在第
47 行
計算攝像機到物體的距離。
在第
50-56 行
,我們簡單地畫出目標的邊框並且顯示出距離。
結果
來看看我們的腳本運作,打開一個終端,導航到你的代碼目錄,執行以下命令:
$
python
distance_to_camera
.
py
如果一切正常你將會看到
2ft.png
的結果,這張圖是用來「標定」我們的系統並且計算初始的
focalLength
:
從上面的圖片我們可以看到我們的焦距被正確地計算出來並且按照代碼中的變數
KNOWN_DISTANCE
和
KNOWN_WIDTH
,A4 紙的距離是 2 英尺。
現在我們有了焦距,我們可以在接下來的圖片中計算出目標的距離:
上上面的例子,我們的相機大概離目標有 3 英尺遠。
讓我們退後一步:
再次需要注意的是,我在拍這個例子的時候動作很快並且捲尺並沒有繃緊。而且,我也沒有確保我的相機是百分之百地對準目標底部,因此,這些例子總會有大概 1 英寸的誤差。
以上是我要說的,這篇文章描述的三角形相似法仍然可以用,並且能夠讓你測量出圖像上的物體或目標到你相機的距離。
總結
在這篇博客我們學習了如何計算一個圖像上的已知物體到相機的距離。
為了完成這個任務我們利用了三角形相似法,並且需要知道兩個重要的參數:
1、 目標的實際寬度
(或高度)
,單位可以是英寸或者米。2、 標定過程 1 中相機到目標的距離。
計算機視覺和圖像處理演算法可以被用來自動檢測圖像中物體的像素寬度或高度並且完成相似三角形的計算,得出一個焦距。
然後在接下來的圖片中,我們只要提取出目標輪廓就可以利用得到的焦距測量出目標到相機的距離。
看完本文有收穫?請轉
發分享給更多人
關注「P
ython開發者」,提升Python技能
※面向對象:葉子的離開是風的追求還是樹的不挽留?我若是飄離的葉子,你是否是溫潤我的春泥?
TAG:Python開發者 |