英文:Adrian Rosebrock 編譯:伯樂在線-G.K.
http://python.jobbole.com/84378/
幾天前,一個叫 Cameron 的 PyImageSearch 讀者發(fā)來郵件詢問攝像頭測距的方法。他花了一些時間研究,但是沒有找到解決辦法。
我很能體會 Cameron 的感受。幾年前我做過一個分析棒球離手飛向本壘的運動的小項目。
我通過使用運動分析和基于軌跡的跟蹤方法來確定或者估計小球在視頻幀中的位置。并且因為棒球的大小是已知的,所以我也能估計出其到本壘的距離。
那是個有趣的項目,雖然系統(tǒng)的精度沒有達到我的預期。——棒球運動太快所造成的“運動模糊”讓達到高精度變得十分困難。
我的項目完全算是一個個例,但是通常來說,在計算機視覺或者圖形處理領(lǐng)域計算從相機到目標的距離實際上是一個非常容易的問題。你可以找到一個像三角形相似這樣簡單粗暴的方法,或者你也可以用上相機模型的內(nèi)參這樣更復雜一點(但是更精確)的方法。
在這篇博客,我將會告訴大家我和 Cameron 是如果解決這個計算相機到已知物體或目標的距離。
千萬要看——你一定不想錯過。
OpenCV 和 Python 版本:這個例子可以在Python 2.7/Python 3.4+和OpenCV 2.4.X上運行。
用相似三角形計算物體或者目標到相機的距離
我們將使用相似三角形來計算相機到一個已知的物體或者目標的距離。
相似三角形就是這么一回事:假設(shè)我們有一個寬度為 W 的目標或者物體。然后我們將這個目標放在距離我們的相機為 D 的位置。我們用相機對物體進行拍照并且測量物體的像素寬度 P 。這樣我們就得出了相機焦距的公式:
F = (P x D) / W
舉個例子,假設(shè)我在離相機距離 D = 24 英寸的地方放一張標準的 8.5 x 11 英寸的 A4 紙(橫著放;W = 11)并且拍下一張照片。我測量出照片中 A4 紙的像素寬度為 P = 249 像素。
因此我的焦距 F 是:
F = (248px x 24in) / 11in = 543.45
當我繼續(xù)將我的相機移動靠近或者離遠物體或者目標時,我可以用相似三角形來計算出物體離相機的距離:
D’ = (W x F) / P
為了更具體,我們再舉個例子,假設(shè)我將相機移到距離目標 3 英尺(或者說 36 英寸)的地方并且拍下上述的 A4 紙。通過自動的圖形處理我可以獲得圖片中 A4 紙的像素距離為 170 像素。將這個代入公式得:
D’ = (11in x 543.45) / 170 = 35 英寸
或者約 36 英寸,合 3 英尺。
注意:當我給這次例子拍照時,我的卷尺有一點松,因此結(jié)果造成了大約 1 英寸的誤差。還有我也是很快速地拍下了照片并且沒有完全對齊卷尺上的腳標,這也會對最終結(jié)果的 1 英寸誤差產(chǎn)生影響。綜上所述,相似三角形的方法還是合理的,你也可以用這個方法很簡單地計算出物體或者目標距離你的相機的距離。
現(xiàn)在理解了?
太棒了。接下來讓我們用一些代碼來看看如何用 Python、OpenCV、圖像處理和計算機視覺技術(shù)來獲得相機到物體或者目標的距離。
用Python和OpenCV來測量相機到目標的距離
繼續(xù),我們開始這個項目。打開一個文件,命名為distance_to_camera.py,然后就可以開工了。
# import the necessary packages
importnumpyasnp
importcv2
deffind_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
returncv2.minAreaRect(c)
第一件要做的事情就是導入必要的包。我們將用NumPy來進行數(shù)值計算和cv2來綁定 OpenCV 。
在那之后我們定義find_marker函數(shù)。這個函數(shù)接收一個image參數(shù),并且這意味著我們將用它來找出將要計算距離的物體。
在這個例子中我們使用標準的 8.5 x 11 英寸的 A4 紙作為我們的目標。
目前我們的第一個任務(wù)是找出圖像中的這張紙。
我們先將圖像轉(zhuǎn)成灰度圖,用高斯模糊除去明顯的噪點,并且在第7-9 行使用邊緣檢測。
完成這幾步后,我們的圖像應(yīng)該長這樣:

如你所見,我們的目標(A4 紙)的邊緣已經(jīng)很清晰了。現(xiàn)在我們只要找出這張紙的輪廓(比如:外形)。
我們用13 行的cv2.findContours函數(shù)找到目標,并且在14 行計算出面積最大的輪廓。
我們假設(shè)面積最大的輪廓是我們的那張 A4 紙。這個假設(shè)在我們的這個例子是成立的,但是實際上在圖像中找出目標是和是與應(yīng)用場景高度相關(guān)的。
在我們的例子中,簡單的邊緣檢測和計算最大的輪廓是可行的。我們可以通過使用輪廓近似法使系統(tǒng)更具魯棒性,排除不包含有4個頂點的輪廓(因為 A4 紙是矩形有四個頂點),然后計算面積最大的四點輪廓。
注意:更多這樣的方法見這篇文章,講述了如何做一個簡單粗暴的手機掃描儀。
其他找到圖像中目標可選的方法是利用顏色特征(目標的顏色和背景有著明顯的不同)。你還可以使用關(guān)鍵點檢測,局部不變性描述子,和關(guān)鍵點匹配來尋找目標。但是這些方法以及超出了這篇文章的范疇,并且具有高度定制化的特性。
不管怎樣,我們現(xiàn)在獲得了目標的輪廓,并且在第17 行返回包含(x, y)坐標和像素高度和寬度信息的邊界框給調(diào)用函數(shù)。
讓我們也快速定義一個用上述的相似三角形法計算距離的函數(shù):
defdistance_to_camera(knownWidth,focalLength,perWidth):
# compute and return the distance from the maker to the camera
return(knownWidth*focalLength)/perWidth
這個函數(shù)傳入目標的knownWidth,計算好的focalLength,和目標在圖像中的像素距離,并且使用上面推導的相似三角形公式來計算到物體的距離。
繼續(xù)讀下列代碼來看看我們是如何利用這些函數(shù)的:
#import the necessary packages
importnumpyasnp
importcv2
deffind_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
returncv2.minAreaRect(c)
defdistance_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
找到圖像中目標的距離的第一步是標定和計算焦距。我們需要知道以下參數(shù):
相機到物體的距離
這個物體的寬度(單位英尺或米)。注意:也可以用高度,這個例子中我們使用寬度。
這里不得不提示一下我們所做的并不是實質(zhì)意義上的攝像機標定。真正的攝像機標定包括攝像機的內(nèi)參,你可以從這里獲得更多相關(guān)知識。
在第25 行我們初始化了已知的KNOWN_DISTANCE,從相機到物體的距離為 24 英寸。在第29 行我們初始了物體的寬度KNOWN_WIDTH為 11 英寸(一張橫著放的標準 A4 紙)。
然后我們在第32 行定義要用到的圖片的路徑。
下一步比較重要:是一個簡單的標定。
第37 行從硬盤讀取第一張圖,——我們將用這張圖來作為標定圖片。
圖片加載以后,在第38 行計算圖中 A4 紙的輪廓信息,在第39 行使用三角形相似法計算出focalLength。
由于我們已經(jīng)“標定”了我們的系統(tǒng)并且獲得了focalLength,我們可以很容易地計算出相機離接下來圖片中目標的距離。
讓我們看看這個是這么做的:
41# loop over the images
42forimagePathinIMAGE_PATHS:
# load the image, find the marker in the image, then compute the
# distance to the marker from the camera
image=cv2.imread(imagePath)
46marker=find_marker(image)
47inches=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 行,我們簡單地畫出目標的邊框并且顯示出距離。
結(jié)果
來看看我們的腳本運作,打開一個終端,導航到你的代碼目錄,執(zhí)行以下命令:
$pythondistance_to_camera.py
如果一切正常你將會看到2ft.png的結(jié)果,這張圖是用來“標定”我們的系統(tǒng)并且計算初始的focalLength:
從上面的圖片我們可以看到我們的焦距被正確地計算出來并且按照代碼中的變量KNOWN_DISTANCE和KNOWN_WIDTH,A4 紙的距離是 2 英尺。
現(xiàn)在我們有了焦距,我們可以在接下來的圖片中計算出目標的距離:
上上面的例子,我們的相機大概離目標有 3 英尺遠。
讓我們退后一步:
再次需要注意的是,我在拍這個例子的時候動作很快并且卷尺并沒有繃緊。而且,我也沒有確保我的相機是百分之百地對準目標底部,因此,這些例子總會有大概 1 英寸的誤差。
以上是我要說的,這篇文章描述的三角形相似法仍然可以用,并且能夠讓你測量出圖像上的物體或目標到你相機的距離。
總結(jié)
在這篇博客我們學習了如何計算一個圖像上的已知物體到相機的距離。
為了完成這個任務(wù)我們利用了三角形相似法,并且需要知道兩個重要的參數(shù):
1、 目標的實際寬度(或高度),單位可以是英寸或者米。
2、 標定過程 1 中相機到目標的距離。
計算機視覺和圖像處理算法可以被用來自動檢測圖像中物體的像素寬度或高度并且完成相似三角形的計算,得出一個焦距。
然后在接下來的圖片中,我們只要提取出目標輪廓就可以利用得到的焦距測量出目標到相機的距離。
編輯:jq
-
硬盤
+關(guān)注
關(guān)注
3文章
1361瀏覽量
59881 -
圖像處理
+關(guān)注
關(guān)注
29文章
1342瀏覽量
59513 -
相機
+關(guān)注
關(guān)注
5文章
1578瀏覽量
55799 -
自動檢測
+關(guān)注
關(guān)注
0文章
121瀏覽量
16376 -
計算機視覺
+關(guān)注
關(guān)注
9文章
1715瀏覽量
47625
原文標題:用 Python 和 OpenCV 來測量相機到目標的距離
文章出處:【微信號:vision263com,微信公眾號:新機器視覺】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
法蘭距離:相機鏡頭系統(tǒng)的關(guān)鍵參數(shù)
沒有專利的opencv-python 版本
毫米波雷達如何用距離與角度分辨率感知世界
工業(yè)鏡頭測量中的“工作距離”是什么?
那些年我用OpenCV+Qt趟過哪些坑?寫給視覺應(yīng)用開發(fā)者的避坑指南
【開發(fā)實例】基于GM-3568JHF開發(fā)板安裝OpenCV并使用視頻目標跟蹤 ( CamShift)
【GM-3568JHF開發(fā)板免費體驗】OpenCV開發(fā)環(huán)境安裝和計數(shù)程序開發(fā)
【GM-3568JHF開發(fā)板免費體驗】OpenCV 視頻目標跟蹤 ( CamShift)
如何板端編譯OpenCV并搭建應(yīng)用--基于瑞芯微米爾RK3576開發(fā)板
【Milk-V Duo S 開發(fā)板免費體驗】SDK編譯、人臉檢測、OpenCV測試
如何用OpenCV的相機捕捉視頻進行人臉檢測--基于米爾NXP i.MX93開發(fā)板
如何用Python和OpenCV來測量相機到目標的距離
評論