英文:Adrian Rosebrock 編譯:伯樂(lè)在線-G.K.
http://python.jobbole.com/84378/
幾天前,一個(gè)叫 Cameron 的 PyImageSearch 讀者發(fā)來(lái)郵件詢問(wèn)攝像頭測(cè)距的方法。他花了一些時(shí)間研究,但是沒(méi)有找到解決辦法。
我很能體會(huì) Cameron 的感受。幾年前我做過(guò)一個(gè)分析棒球離手飛向本壘的運(yùn)動(dòng)的小項(xiàng)目。
我通過(guò)使用運(yùn)動(dòng)分析和基于軌跡的跟蹤方法來(lái)確定或者估計(jì)小球在視頻幀中的位置。并且因?yàn)榘羟虻拇笮∈且阎?,所以我也能估?jì)出其到本壘的距離。
那是個(gè)有趣的項(xiàng)目,雖然系統(tǒng)的精度沒(méi)有達(dá)到我的預(yù)期?!羟蜻\(yùn)動(dòng)太快所造成的“運(yùn)動(dòng)模糊”讓達(dá)到高精度變得十分困難。
我的項(xiàng)目完全算是一個(gè)個(gè)例,但是通常來(lái)說(shuō),在計(jì)算機(jī)視覺(jué)或者圖形處理領(lǐng)域計(jì)算從相機(jī)到目標(biāo)的距離實(shí)際上是一個(gè)非常容易的問(wèn)題。你可以找到一個(gè)像三角形相似這樣簡(jiǎn)單粗暴的方法,或者你也可以用上相機(jī)模型的內(nèi)參這樣更復(fù)雜一點(diǎn)(但是更精確)的方法。
在這篇博客,我將會(huì)告訴大家我和 Cameron 是如果解決這個(gè)計(jì)算相機(jī)到已知物體或目標(biāo)的距離。
千萬(wàn)要看——你一定不想錯(cuò)過(guò)。
OpenCV 和 Python 版本:這個(gè)例子可以在Python 2.7/Python 3.4+和OpenCV 2.4.X上運(yùn)行。
用相似三角形計(jì)算物體或者目標(biāo)到相機(jī)的距離
我們將使用相似三角形來(lái)計(jì)算相機(jī)到一個(gè)已知的物體或者目標(biāo)的距離。
相似三角形就是這么一回事:假設(shè)我們有一個(gè)寬度為 W 的目標(biāo)或者物體。然后我們將這個(gè)目標(biāo)放在距離我們的相機(jī)為 D 的位置。我們用相機(jī)對(duì)物體進(jìn)行拍照并且測(cè)量物體的像素寬度 P 。這樣我們就得出了相機(jī)焦距的公式:
F = (P x D) / W
舉個(gè)例子,假設(shè)我在離相機(jī)距離 D = 24 英寸的地方放一張標(biāo)準(zhǔn)的 8.5 x 11 英寸的 A4 紙(橫著放;W = 11)并且拍下一張照片。我測(cè)量出照片中 A4 紙的像素寬度為 P = 249 像素。
因此我的焦距 F 是:
F = (248px x 24in) / 11in = 543.45
當(dāng)我繼續(xù)將我的相機(jī)移動(dòng)靠近或者離遠(yuǎn)物體或者目標(biāo)時(shí),我可以用相似三角形來(lái)計(jì)算出物體離相機(jī)的距離:
D’ = (W x F) / P
為了更具體,我們?cè)倥e個(gè)例子,假設(shè)我將相機(jī)移到距離目標(biāo) 3 英尺(或者說(shuō) 36 英寸)的地方并且拍下上述的 A4 紙。通過(guò)自動(dòng)的圖形處理我可以獲得圖片中 A4 紙的像素距離為 170 像素。將這個(gè)代入公式得:
D’ = (11in x 543.45) / 170 = 35 英寸
或者約 36 英寸,合 3 英尺。
注意:當(dāng)我給這次例子拍照時(shí),我的卷尺有一點(diǎn)松,因此結(jié)果造成了大約 1 英寸的誤差。還有我也是很快速地拍下了照片并且沒(méi)有完全對(duì)齊卷尺上的腳標(biāo),這也會(huì)對(duì)最終結(jié)果的 1 英寸誤差產(chǎn)生影響。綜上所述,相似三角形的方法還是合理的,你也可以用這個(gè)方法很簡(jiǎn)單地計(jì)算出物體或者目標(biāo)距離你的相機(jī)的距離。
現(xiàn)在理解了?
太棒了。接下來(lái)讓我們用一些代碼來(lái)看看如何用 Python、OpenCV、圖像處理和計(jì)算機(jī)視覺(jué)技術(shù)來(lái)獲得相機(jī)到物體或者目標(biāo)的距離。
用Python和OpenCV來(lái)測(cè)量相機(jī)到目標(biāo)的距離
繼續(xù),我們開(kāi)始這個(gè)項(xiàng)目。打開(kāi)一個(gè)文件,命名為distance_to_camera.py,然后就可以開(kāi)工了。
# 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)
第一件要做的事情就是導(dǎo)入必要的包。我們將用NumPy來(lái)進(jìn)行數(shù)值計(jì)算和cv2來(lái)綁定 OpenCV 。
在那之后我們定義find_marker函數(shù)。這個(gè)函數(shù)接收一個(gè)image參數(shù),并且這意味著我們將用它來(lái)找出將要計(jì)算距離的物體。
在這個(gè)例子中我們使用標(biāo)準(zhǔn)的 8.5 x 11 英寸的 A4 紙作為我們的目標(biāo)。
目前我們的第一個(gè)任務(wù)是找出圖像中的這張紙。
我們先將圖像轉(zhuǎn)成灰度圖,用高斯模糊除去明顯的噪點(diǎn),并且在第7-9 行使用邊緣檢測(cè)。
完成這幾步后,我們的圖像應(yīng)該長(zhǎng)這樣:
如你所見(jiàn),我們的目標(biāo)(A4 紙)的邊緣已經(jīng)很清晰了?,F(xiàn)在我們只要找出這張紙的輪廓(比如:外形)。
我們用13 行的cv2.findContours函數(shù)找到目標(biāo),并且在14 行計(jì)算出面積最大的輪廓。
我們假設(shè)面積最大的輪廓是我們的那張 A4 紙。這個(gè)假設(shè)在我們的這個(gè)例子是成立的,但是實(shí)際上在圖像中找出目標(biāo)是和是與應(yīng)用場(chǎng)景高度相關(guān)的。
在我們的例子中,簡(jiǎn)單的邊緣檢測(cè)和計(jì)算最大的輪廓是可行的。我們可以通過(guò)使用輪廓近似法使系統(tǒng)更具魯棒性,排除不包含有4個(gè)頂點(diǎn)的輪廓(因?yàn)?A4 紙是矩形有四個(gè)頂點(diǎn)),然后計(jì)算面積最大的四點(diǎn)輪廓。
注意:更多這樣的方法見(jiàn)這篇文章,講述了如何做一個(gè)簡(jiǎn)單粗暴的手機(jī)掃描儀。
其他找到圖像中目標(biāo)可選的方法是利用顏色特征(目標(biāo)的顏色和背景有著明顯的不同)。你還可以使用關(guān)鍵點(diǎn)檢測(cè),局部不變性描述子,和關(guān)鍵點(diǎn)匹配來(lái)尋找目標(biāo)。但是這些方法以及超出了這篇文章的范疇,并且具有高度定制化的特性。
不管怎樣,我們現(xiàn)在獲得了目標(biāo)的輪廓,并且在第17 行返回包含(x, y)坐標(biāo)和像素高度和寬度信息的邊界框給調(diào)用函數(shù)。
讓我們也快速定義一個(gè)用上述的相似三角形法計(jì)算距離的函數(shù):
defdistance_to_camera(knownWidth,focalLength,perWidth):
# compute and return the distance from the maker to the camera
return(knownWidth*focalLength)/perWidth
這個(gè)函數(shù)傳入目標(biāo)的knownWidth,計(jì)算好的focalLength,和目標(biāo)在圖像中的像素距離,并且使用上面推導(dǎo)的相似三角形公式來(lái)計(jì)算到物體的距離。
繼續(xù)讀下列代碼來(lái)看看我們是如何利用這些函數(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
找到圖像中目標(biāo)的距離的第一步是標(biāo)定和計(jì)算焦距。我們需要知道以下參數(shù):
相機(jī)到物體的距離
這個(gè)物體的寬度(單位英尺或米)。注意:也可以用高度,這個(gè)例子中我們使用寬度。
這里不得不提示一下我們所做的并不是實(shí)質(zhì)意義上的攝像機(jī)標(biāo)定。真正的攝像機(jī)標(biāo)定包括攝像機(jī)的內(nèi)參,你可以從這里獲得更多相關(guān)知識(shí)。
在第25 行我們初始化了已知的KNOWN_DISTANCE,從相機(jī)到物體的距離為 24 英寸。在第29 行我們初始了物體的寬度KNOWN_WIDTH為 11 英寸(一張橫著放的標(biāo)準(zhǔn) A4 紙)。
然后我們?cè)诘?2 行定義要用到的圖片的路徑。
下一步比較重要:是一個(gè)簡(jiǎn)單的標(biāo)定。
第37 行從硬盤(pán)讀取第一張圖,——我們將用這張圖來(lái)作為標(biāo)定圖片。
圖片加載以后,在第38 行計(jì)算圖中 A4 紙的輪廓信息,在第39 行使用三角形相似法計(jì)算出focalLength。
由于我們已經(jīng)“標(biāo)定”了我們的系統(tǒng)并且獲得了focalLength,我們可以很容易地計(jì)算出相機(jī)離接下來(lái)圖片中目標(biāo)的距離。
讓我們看看這個(gè)是這么做的:
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 行開(kāi)始遍歷所有的圖片路徑。
然后,在第45 行我們將列表中所有的圖片從硬盤(pán)讀取下來(lái)。在第46 行提取目標(biāo)輪廓,并且在第47 行計(jì)算攝像機(jī)到物體的距離。
在第50-56 行,我們簡(jiǎn)單地畫(huà)出目標(biāo)的邊框并且顯示出距離。
結(jié)果
來(lái)看看我們的腳本運(yùn)作,打開(kāi)一個(gè)終端,導(dǎo)航到你的代碼目錄,執(zhí)行以下命令:
$pythondistance_to_camera.py
如果一切正常你將會(huì)看到2ft.png的結(jié)果,這張圖是用來(lái)“標(biāo)定”我們的系統(tǒng)并且計(jì)算初始的focalLength:
從上面的圖片我們可以看到我們的焦距被正確地計(jì)算出來(lái)并且按照代碼中的變量KNOWN_DISTANCE和KNOWN_WIDTH,A4 紙的距離是 2 英尺。
現(xiàn)在我們有了焦距,我們可以在接下來(lái)的圖片中計(jì)算出目標(biāo)的距離:
上上面的例子,我們的相機(jī)大概離目標(biāo)有 3 英尺遠(yuǎn)。
讓我們退后一步:
再次需要注意的是,我在拍這個(gè)例子的時(shí)候動(dòng)作很快并且卷尺并沒(méi)有繃緊。而且,我也沒(méi)有確保我的相機(jī)是百分之百地對(duì)準(zhǔn)目標(biāo)底部,因此,這些例子總會(huì)有大概 1 英寸的誤差。
以上是我要說(shuō)的,這篇文章描述的三角形相似法仍然可以用,并且能夠讓你測(cè)量出圖像上的物體或目標(biāo)到你相機(jī)的距離。
總結(jié)
在這篇博客我們學(xué)習(xí)了如何計(jì)算一個(gè)圖像上的已知物體到相機(jī)的距離。
為了完成這個(gè)任務(wù)我們利用了三角形相似法,并且需要知道兩個(gè)重要的參數(shù):
1、 目標(biāo)的實(shí)際寬度(或高度),單位可以是英寸或者米。
2、 標(biāo)定過(guò)程 1 中相機(jī)到目標(biāo)的距離。
計(jì)算機(jī)視覺(jué)和圖像處理算法可以被用來(lái)自動(dòng)檢測(cè)圖像中物體的像素寬度或高度并且完成相似三角形的計(jì)算,得出一個(gè)焦距。
然后在接下來(lái)的圖片中,我們只要提取出目標(biāo)輪廓就可以利用得到的焦距測(cè)量出目標(biāo)到相機(jī)的距離。
編輯:jq
-
硬盤(pán)
+關(guān)注
關(guān)注
3文章
1315瀏覽量
57389 -
圖像處理
+關(guān)注
關(guān)注
27文章
1296瀏覽量
56823 -
相機(jī)
+關(guān)注
關(guān)注
4文章
1359瀏覽量
53764 -
自動(dòng)檢測(cè)
+關(guān)注
關(guān)注
0文章
115瀏覽量
15685 -
計(jì)算機(jī)視覺(jué)
+關(guān)注
關(guān)注
8文章
1699瀏覽量
46052
原文標(biāo)題:用 Python 和 OpenCV 來(lái)測(cè)量相機(jī)到目標(biāo)的距離
文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺(jué)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論