OpenCV測(cè)量物體的尺寸技能 get~
點(diǎn)擊上方“小白學(xué)視覺(jué)”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時(shí)間送達(dá)


測(cè)距原理
性質(zhì)1:參考物尺寸
性質(zhì)2:易于識(shí)別
我們應(yīng)該能夠很容易地在圖片中找到參照物體,無(wú)論是基于物體的位置(例如,參考物體總是放在圖片的左上角)還是通過(guò)外觀(例如,獨(dú)特的顏色或形狀,不同與圖片中的其他物體)。無(wú)論是哪種情況,我們的參照物都應(yīng)該以某種方式具有唯一的可識(shí)別性。
在下面的例子中,我們將使用美國(guó)硬幣作為我們的參考對(duì)象,在所有的示例中,確保它始終是圖片中的最左側(cè)的對(duì)象。

通過(guò)確保硬幣是最左邊的物體,我們可以從左到右對(duì)物體輪廓進(jìn)行排序,獲取硬幣(始終是排序列表中的第一個(gè)輪廓),并使用它定義每個(gè)單位的像素?cái)?shù),我們將其定義為:
已知硬幣的寬度為0.955英寸。現(xiàn)在假設(shè),物體的寬為150像素(基于其關(guān)聯(lián)的邊界框)。
pixels_per_metric可得:
pixels_per_metric=150px/0.955in = 157px/in
因此,在圖片中應(yīng)用每英寸所占的像素點(diǎn)為157個(gè)。使用這個(gè)比率,我們可以計(jì)算圖片中物體的大小。
利用計(jì)算機(jī)視覺(jué)測(cè)量物體的大小
現(xiàn)在我們理解了pixels_per_metric比率的含義,我們可以應(yīng)用python運(yùn)行代碼來(lái)測(cè)量圖片中的物體大小。
打開(kāi)一個(gè)文件,命名為 object_size.py,并插入以下代碼:
# 導(dǎo)入必要的包from scipy.spatial import distance as distfrom imutils import perspectivefrom imutils import contoursimport numpy as npimport argparseimport imutilsimport cv2def midpoint(ptA, ptB):return ((ptA[0]+ptB[0])*0.5, (ptA[1]+ptB[1])*0.5)# 構(gòu)造解析參數(shù)ap = argparse.ArugmentParser()ap.add_argument("-i", "--image", required=True,help="path to the input image")ap.add_argument("-w", "--width", required=True,help="width of the left-most object in the image(in inches)")args = vars(ap.parse_args()
pip3 install imutils除此之外,如果你安裝了imutils,請(qǐng)確保你安裝了最新版本的,本例中imutils的版本為“0.5.2”
pip3 install --upgrade imutils第10-11行定義個(gè)midpoint的輔助函數(shù),從它的名字可知,該函數(shù)是用于計(jì)算兩組(x,y)坐標(biāo)的中點(diǎn)。
在第14-19行構(gòu)造解析參數(shù)。我們需要兩個(gè)參數(shù),--image,我們需要測(cè)量的物體圖片的路徑,--width,參考物體的寬(單位in),假定它是我們圖片中的最左邊的對(duì)象。
# 導(dǎo)入圖片轉(zhuǎn)換為灰度圖,并進(jìn)行輕微的模糊image = cv2.imread(args["image"])gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)gray = cv2.GaussianBlur(gray, (7, 7), 0)# 執(zhí)行邊緣檢測(cè)# 然后在物體之間的邊緣執(zhí)行膨脹+腐蝕操作使其縫隙閉合edged = cv2.Canny(gray, 50, 100)edged = cv2.dilate(edged, None, iterations=1)edged = cv2.erode(edged, None, iterations=1)# 在邊緣圖中查找輪廓cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)# 從左往右對(duì)輪廓進(jìn)行排序# 初始化'pixels per metric' 校準(zhǔn)變量(cnts, _) = contours.sort_contours(cnts)pixelsPerMetric = None
2-4行 從磁盤(pán)中導(dǎo)入圖片,轉(zhuǎn)換為灰度圖,然后用高斯濾波進(jìn)行平滑。
然后,我們執(zhí)行邊緣檢測(cè)和膨脹+腐蝕操作以閉合邊緣圖片中所有邊緣之間的間隙。
13-15行在我們的邊緣圖片中找相應(yīng)物體的輪廓。
19行將這些輪廓從左往右排序。初始化'pixels per metric' 的值
下一步就是去檢測(cè)每個(gè)輪廓:
for c in cnts:if cv2.contourArea(c)<100:continueorig = image.copy()box = cv2.minAreaRect(c)box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)box = np.array(box, dtype="int")box = perspective.order_points(box)cv2.drawContours(orig, [box.astype("int")], -1, (0,255,0), 2)for (x, y) in box:cv2.circle(orig, (int(x), int(y)), 5, (0,0,255), -1)
2行開(kāi)始遍歷每個(gè)單獨(dú)的輪廓。
如果輪廓不夠大,則會(huì)丟棄該區(qū)域,認(rèn)為該區(qū)域是邊緣檢測(cè)過(guò)程中留下的噪聲(4-5行)。
如果輪廓區(qū)域足夠大,在第9-11行計(jì)算圖中的選擇邊界框,特別注意OpenCV2使用的是cv2.cv.BoxPoints函數(shù),OpenCV3使用的是cv2.boxPoints函數(shù)。
然后,我們按照左上、右上、右下和左下的順序排列旋轉(zhuǎn)的邊界框坐標(biāo)。
最后,第16-20行用綠色的線(xiàn)畫(huà)出物體的輪廓,然后用紅色的小圓圈繪制出邊界框矩形的頂點(diǎn)。
現(xiàn)在我們已經(jīng)對(duì)邊界框進(jìn)行了排序,我們可以計(jì)算一系列的中點(diǎn):
# 打開(kāi)有序的邊界框,然后計(jì)算左上和右上坐標(biāo)之間的中點(diǎn),# 再計(jì)算左下和右下坐標(biāo)之間的中點(diǎn)(tl, tr, br, bl) = box(tltrX, tltrY) = midpoint(tl, tr)(blbrX, blbrY) = midpoint(bl, br)# 計(jì)算左上點(diǎn)和右上點(diǎn)之間的中點(diǎn)# 然后是右上角和右下角之間的中點(diǎn)(tlblX, tlblY) = midpoint(tl, bl)(trbrX, trbrY) = midpoint(tr, br)# 在圖中畫(huà)出中點(diǎn)cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1)cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1)cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1)cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1)# 在中點(diǎn)之間繪制線(xiàn)cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),(255, 0, 255), 2)cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),(255, 0, 255), 2)
第3-5行取出有序的邊界框,然后計(jì)算左上和右上點(diǎn)之間的中點(diǎn),再計(jì)算左下和右下點(diǎn)之間的中點(diǎn)。
我們還將分別計(jì)算左上+左下和右上+右下之間的中點(diǎn)。
第13-16行在圖中畫(huà)出藍(lán)色的中點(diǎn),然后用紫色線(xiàn)連接中點(diǎn)。
接下來(lái),我們通過(guò)分析參考物體來(lái)初始化pixelsPerMetric值
# 計(jì)算中點(diǎn)間的歐式距離dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))# 如果pixels per metric還未初始化,# 則將其計(jì)算為像素與提供的度量的比率(本例中為英寸)if pixelsPerMetric is None:pixelsPerMetric = dB / args["width"]
第2-3行計(jì)算集合中的中點(diǎn)的歐式距離。
dA保存的是高度距離,dB保存的是寬度距離。
然后,我在第7行進(jìn)行檢測(cè)pixelsPerMetric是否被初始化了,如果未被初始化,我們通過(guò)用dB出于--width提供的值,得到我們需要每英寸的像素?cái)?shù)。
現(xiàn)在pixelsPerMetric的值已經(jīng)被定義,我們可以測(cè)量圖片中物體的大小
dimA = dA / pixelsPerMetricdimB = dB / pixelsPerMetriccv2.putText(orig, "{:.1f}in".format(dimA),(int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX,0.65, (255, 255, 255), 2)cv2.putText(orig, "{:.1f}in".format(dimB),(int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,0.65, (255, 255, 255), 2)cv2.imshow("Image", orig)cv2.waitKey(0)
第2-3行通過(guò)對(duì)應(yīng)的歐式距離除以pixelsPerMetric計(jì)算得到物體的尺寸。
第6-11行在圖中畫(huà)出物體的尺寸,而第14-15行為顯示輸出結(jié)果的圖片。
物體尺寸測(cè)量結(jié)果
在命令行中輸入
python3 object_size.py --image images/example_01.png --width 0.955輸出結(jié)果如下圖所示:

如上圖所示,我們已經(jīng)成功的計(jì)算出圖片中每個(gè)物體的尺寸。
然而,并非所有的結(jié)果都是完美的。
可能的原因
1、拍攝的角度并非是一個(gè)完美的90°俯視。如果不是90°拍攝,物體的尺寸可能會(huì)出現(xiàn)扭曲。
2、沒(méi)有使用相機(jī)內(nèi)在和外在參數(shù)來(lái)校準(zhǔn)。當(dāng)無(wú)法確定這些參數(shù)時(shí),照片很容易發(fā)生徑向和切向的透鏡變形。執(zhí)行額外的校準(zhǔn)步驟來(lái)找到這些參數(shù)可以消除圖片中的失真并得到更好的物體大小的近似值。

總結(jié)
在本文中,我們學(xué)習(xí)了如何通過(guò)使用python和OpenCV來(lái)測(cè)量圖片中的物體的大小。
我們需要確定pixels per metric比率(單位尺寸像素?cái)?shù)),即在給定的度量(如英寸、毫米、米等)下,像素的數(shù)量。
為了計(jì)算這個(gè)比率,我們需要一個(gè)參考物體,它需要兩點(diǎn)重要的性質(zhì):
1、參考物體需要有含測(cè)量單位(英寸、毫米等等)的尺寸
2、無(wú)論從物體的位置還是形狀,參考物體都需要容易被找到。
加入上面的性質(zhì)都能滿(mǎn)足,你可以使用參考物體計(jì)算pixels per metric比率,并根據(jù)這個(gè)計(jì)算圖片中物體的大小。

英文原文:
https://www.pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/
其他參考項(xiàng)目:https://github.com/snsharma1311/object-size

詳細(xì)代碼鏈接:https://github.com/DWCTOD/AI_study/tree/master/%E5%90%88%E6%A0%BC%E7%9A%84CV%E5%B7%A5%E7%A8%8B%E5%B8%88/%E5%AE%9E%E6%88%98%E7%AF%87/opencv/%EF%BC%88%E5%85%AD%EF%BC%89%E5%88%A9%E7%94%A8python%E5%92%8COpenCV%E6%B5%8B%E9%87%8F%E7%89%A9%E4%BD%93%E7%9A%84%E5%A4%A7%E5%B0%8F
好消息!
小白學(xué)視覺(jué)知識(shí)星球
開(kāi)始面向外開(kāi)放啦??????
下載1:OpenCV-Contrib擴(kuò)展模塊中文版教程 在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):擴(kuò)展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴(kuò)展模塊教程中文版,涵蓋擴(kuò)展模塊安裝、SFM算法、立體視覺(jué)、目標(biāo)跟蹤、生物視覺(jué)、超分辨率處理等二十多章內(nèi)容。 下載2:Python視覺(jué)實(shí)戰(zhàn)項(xiàng)目52講 在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):Python視覺(jué)實(shí)戰(zhàn)項(xiàng)目,即可下載包括圖像分割、口罩檢測(cè)、車(chē)道線(xiàn)檢測(cè)、車(chē)輛計(jì)數(shù)、添加眼線(xiàn)、車(chē)牌識(shí)別、字符識(shí)別、情緒檢測(cè)、文本內(nèi)容提取、面部識(shí)別等31個(gè)視覺(jué)實(shí)戰(zhàn)項(xiàng)目,助力快速學(xué)校計(jì)算機(jī)視覺(jué)。 下載3:OpenCV實(shí)戰(zhàn)項(xiàng)目20講 在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):OpenCV實(shí)戰(zhàn)項(xiàng)目20講,即可下載含有20個(gè)基于OpenCV實(shí)現(xiàn)20個(gè)實(shí)戰(zhàn)項(xiàng)目,實(shí)現(xiàn)OpenCV學(xué)習(xí)進(jìn)階。 交流群
歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺(jué)、傳感器、自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN、算法競(jìng)賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱(chēng)+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺(jué)SLAM“。請(qǐng)按照格式備注,否則不予通過(guò)。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~

