首先,您需要知道類似于比例的概念稱為每度量比的像素(pixels per metric ratio)。 近似含義是每個單位指標中包含的像素數(shù)。例如,圖表上的1厘米包含100張圖像。 實際上,相當于引用對象的作用,例如已知地圖上的引用材質(zhì),我們可以使用此引用對象將其轉(zhuǎn)換為地圖上其他對象的大小。 引用對象需要具有兩個重要屬性:
我們應該能夠輕松地在圖像中找到參考對象,無論是基于對象的位置(例如,參考對象總是放置在圖像的左上角)還是通過外觀(例如,獨特的顏色或形狀,與圖像不同)其他物品)。在任何一種情況下,我們的參考應該以某種方式唯一可識別。 在下面的示例中,我們將使用美國硬幣作為參考對象。在所有示例中,確保它始終是圖像中最左側(cè)的對象。 我們將使用美國四分之一作為參考對象,并確保它始終作為圖像中最左側(cè)的對象放置,使我們可以通過基于其位置對輪廓進行排序來輕松提取它。 通過保證四分之一是最左邊的對象,我們可以從左到右對對象輪廓進行排序,抓住四分之一(它將始終是排序列表中的第一個輪廓),并使用它來定義 pixel_per_metric,我們定義為: pixels_per_metric = object_width / know_width 美國四分之一的 已知寬度為0.955英寸?,F(xiàn)在,假設我們的 object_width(以像素為單位)計算為150像素寬(基于其關聯(lián)的邊界框)。 因此 pixels_per_metric是: pixels_per_metric = 150px / 0.955in = 157px 因此暗示在我們的圖像中每0.955英寸大約有157個像素。使用此比率,我們可以計算圖像中對象的大小。 用計算機視覺測量物體的大小 現(xiàn)在我們了解“每度量像素數(shù)”比率,我們可以實現(xiàn)用于測量圖像中對象大小的Python驅(qū)動程序腳本。 打開一個新文件,將其命名為 object_size 。py ,并插入以下代碼: # import the necessary packages from scipy.spatial import distance as dist from imutils import perspective from imutils import contours import numpy as np import argparse import imutils import cv2 def midpoint(ptA, ptB): return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5) # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument('-i', '--image', required=True, help='path to the input image') ap.add_argument('-w', '--width', type=float, required=True, help='width of the left-most object in the image (in inches)') args = vars(ap.parse_args()) 第2-8行導入我們所需的Python包。我們將在此示例中大量使用imutils包,因此如果您沒有安裝它,請確保在繼續(xù)之前安裝它: $ pip install imutils 請確保您擁有最新版本 pip install --upgrade imutils 第10行和第11行定義了一個稱為中點的輔助方法 ,顧名思義,它用于計算兩組(x,y)坐標之間 的中點。 然后我們在第14-19行解析命令行參數(shù) 。我們需要兩個參數(shù), - image ,它是包含我們想要測量的對象的輸入圖像的路徑,以及 - width ,它是我們的參考對象的寬度(以英寸為單位),被認為是最左邊的我們的形象 。 我們現(xiàn)在可以加載我們的圖像并預處理它: # load the image, convert it to grayscale, and blur it slightly image = cv2.imread(args['image']) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (7, 7), 0) # perform edge detection, then perform a dilation + erosion to # close gaps in between object edges edged = cv2.Canny(gray, 50, 100) edged = cv2.dilate(edged, None, iterations=1) edged = cv2.erode(edged, None, iterations=1) # find contours in the edge map cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) # sort the contours from left-to-right and initialize the # 'pixels per metric' calibration variable (cnts, _) = contours.sort_contours(cnts) pixelsPerMetric = None 第22-24行從磁盤加載我們的圖像,將其轉(zhuǎn)換為灰度,然后使用高斯濾波器對其進行平滑處理。然后,我們進行邊緣檢測以及擴張+侵蝕,以封閉邊緣圖中邊緣之間的任何間隙(第28-30行)。 第33-35行找到與我們的邊緣圖中的對象相對應的輪廓(即輪廓)。 然后在第39行從左到右(允許我們提取參考對象)對這些輪廓進行排序 。我們還在第40行初始化 pixelPerMetric 值 。 下一步是檢查每個輪廓: # loop over the contours individually for c in cnts: # if the contour is not sufficiently large, ignore it if cv2.contourArea(c) < 100: continue # compute the rotated bounding box of the contour orig = 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') # order the points in the contour such that they appear # in top-left, top-right, bottom-right, and bottom-left # order, then draw the outline of the rotated bounding # box box = perspective.order_points(box) cv2.drawContours(orig, [box.astype('int')], -1, (0, 255, 0), 2) # loop over the original points and draw them for (x, y) in box: cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1) 在 第43行,我們開始在每個輪廓上循環(huán)。如果輪廓不夠大,我們丟棄該區(qū)域,假設它是邊緣檢測過程遺留的噪聲(第45和46行)。 如果輪廓區(qū)域足夠大,我們計算第50-52行上圖像的旋轉(zhuǎn)邊界框 ,特別注意使用 cv2 。cv 。BoxPoints 功能適用于OpenCV 2.4和 cv2 。openCV 3的boxPoints方法。 然后,我們將旋轉(zhuǎn)的邊界框 坐標排列 在左上角,右上角,右下角和左下角,如上周的博文 (第58行)所述。 最后, 第59-63行以綠色繪制對象 的輪廓,然后以小的紅色圓圈繪制邊界框矩形的頂點。 現(xiàn)在我們已經(jīng)訂購了邊界框,我們可以計算出一系列中點: # unpack the ordered bounding box, then compute the midpoint # between the top-left and top-right coordinates, followed by # the midpoint between bottom-left and bottom-right coordinates (tl, tr, br, bl) = box (tltrX, tltrY) = midpoint(tl, tr) (blbrX, blbrY) = midpoint(bl, br) # compute the midpoint between the top-left and top-right points, # followed by the midpoint between the top-righ and bottom-right (tlblX, tlblY) = midpoint(tl, bl) (trbrX, trbrY) = midpoint(tr, br) # draw the midpoints on the image 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) # draw lines between the midpoints 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) 第68-70行打開我們訂購的邊界框,然后計算左上角和右上角之間的中點,然后是右下角之間的中點。 我們還將分別計算左上角+左下角和右上角+右下角之間的中點(第74和75行)。 第78-81行在我們的圖像上繪制 藍色中點 ,然后用紫色線連接中點 。 接下來,我們需要 通過調(diào)查我們的引用對象來初始化 pixelPerMetric變量: # compute the Euclidean distance between the midpoints dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY)) dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY)) # if the pixels per metric has not been initialized, then # compute it as the ratio of pixels to supplied metric # (in this case, inches) if pixelsPerMetric is None: pixelsPerMetric = dB / args['width'] 首先,我們計算我們的中點集之間的歐幾里德距離(第90和91行)。該 DA 變量將包含 高度距離(以像素為單位),而 分貝 將保存我們的 寬度的距離。 然后,我們就檢查 96號線,看看我們的 pixelsPerMetric 變量已經(jīng)初始化,如果沒有,我們把 分貝 由我們提供 - 寬度 ,從而使我們每英寸我們的(近似)的像素。 現(xiàn)在我們 已經(jīng)定義了 pixelPerMetric變量,我們可以測量圖像中對象的大?。?/p> # compute the size of the object dimA = dA / pixelsPerMetric dimB = dB / pixelsPerMetric # draw the object sizes on the image cv2.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) # show the output image cv2.imshow('Image', orig) cv2.waitKey(0) 第100行和第101行通過將相應的歐幾里德距離除以pixelsPerMetric 值來計算對象的尺寸(以英寸為單位) (有關此比率的工作原理的詳細信息,請參閱上面的 “每公制像素數(shù)”部分)。 第104-109行在圖像上繪制對象的尺寸 ,而 第112和113行顯示輸出結果。 物體尺寸測量結果 測試我們的 object_size 。py 腳本,只需發(fā)出以下命令: $ python object_size.py --image images/example_01.png --width 0.955 輸出應如下所示: 圖2:使用OpenCV,Python和計算機視覺+圖像處理技術測量圖像中對象的大小。 如您所見,我們已成功計算出圖像中每個對象的大小 - 我們的名片正確報告為 3.5in x 2in。同樣,我們的鎳被準確地描述為 0.8英寸x 0.8英寸。 然而,并非我們所有的結果都是 完美的。 據(jù)報道,Game Boy墨盒的尺寸略有不同(即使尺寸相同)。兩個季度的高度也 減去了0.1英寸。 那么為什么呢?為什么物體測量不是100%準確? 原因有兩方面: 首先,我匆匆用iPhone拍了這張照片。角度肯定 不是物體上“俯視”(如鳥瞰圖)的完美90度角。如果沒有完美的90度視圖(或盡可能接近它),對象的尺寸可能會出現(xiàn)扭曲。 其次,我沒有使用相機的內(nèi)在和外在參數(shù)來校準我的iPhone。在不確定這些參數(shù)的情況下,照片可能容易發(fā)生徑向和切向鏡頭失真。執(zhí)行額外的校準步驟來查找這些參數(shù)可以“扭曲”我們的圖像并導致更好的對象大小近似(但我將討論失真校正作為未來博客文章的主題)。 同時,在拍攝物體照片時盡量獲得盡可能接近90度的視角 - 這有助于提高物體尺寸估計的準確性。 那就是說,讓我們看一下測量物體尺寸的第二個例子,這次測量藥丸的尺寸: 在美國,所有20,000多種處方藥中有近50%是圓形和/或白色,因此如果我們可以根據(jù)它們的測量值來過濾藥丸,我們就有更好的機會準確識別藥物。 最后,我們有一個最后的例子,這次使用 3.5英寸x 2英寸名片來測量兩個乙烯基EP和一個信封的大小: $ python object_size.py --image images/example_03.png --width 3.5 同樣,結果不是很完美,但這是由于(1)視角和(2)透鏡畸變,如上所述。 總結 在本文中,我們學習了如何通過使用python和OpenCV來測量圖片中的物體的大小。 我們需要確定pixels per metric比率(單位尺寸像素數(shù)),即在給定的度量(如英寸、毫米、米等)下,像素的數(shù)量。 為了計算這個比率,我們需要一個參考物體,它需要兩點重要的性質(zhì): 1、參考物體需要有含測量單位(英寸、毫米等等)的尺寸 2、無論從物體的位置還是形狀,參考物體都需要容易被找到。 加入上面的性質(zhì)都能滿足,你可以使用參考物體計算pixels per metric比率,并根據(jù)這個計算圖片中物體的大小。 相關資源關注微信公眾號:“圖像算法”或者微信搜索imalg_cn 獲取 |
|
來自: 新用戶79878317 > 《opencv》