本文翻譯自pyimagesearch技術部落格上的一篇文章,《measuring size of objects in an image with opencv》,原文作者:adrian rosebrock 。
網址:https://www.pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/
圖像目标尺寸檢測類似于計算從我們的相機到一個物體的距離——在這兩種情況下,我們都需要事先定義一個比率來測量每個給定度量機關的像素數(pixels_per_metric)。在這裡所說的這個被稱為“pixels_per_metric”的比率名額,我在接下來的部分中對其更正式的定義。
為了确定圖像中物體的大小,我們首先需要使用一個參照物作為“校準”點。我們的參照物應該有兩個重要的屬性:
我們應該知道這個物體的真實尺寸(在寬度或高度上的毫米或英寸等值的大小)。
我們應該能夠輕松地在圖檔中找到這個參照物,要麼基于參照物的位置(如,參照物可以是一副圖像中左上角的物體)或基于參照物的外表(例如參照物可以是圖檔中具有最獨特的顔色或獨一無二的形狀,不同于所有其他的物體)。
在任何一種情況下,我們的參考應該以某種方式唯一可識别。
在這個例子中,我們将使用美分硬币作為我們的參照物,并且在所有示例中,確定它始終是我們圖像中最左邊的對象。
圖1:我們将使用美分硬币作為參照物,并確定它始終處于圖像最左側位置,這使得我們可以通過對它們位置的輪廓大小進行排序,進一步來提取資訊。
通過保證美分硬币是最左邊的物體,我們可以從左到右對我們的物體等高線區域進行排列,抓住這個硬币(它将始終對應于排序清單中的第一個等高線區域)。并使用它來定義我們的pixels_per_metric比率,我們将其定義為:
pixels_per_metric =物體像素寬 / 物體真實寬
美分硬币的真實寬度是0.955英寸。現在,假設我們圖像中硬币的像素寬為150像素(基于它的相關邊界框)。那麼這種情況下pixels_per_metric這樣計算:
pixels_per_metric = 150px / 0.955in = 157px
是以,在我們這幅圖像中,每英寸大約有157個像素。有了這個比率,我們可以計算圖像中其他物體的大小了。
既然我們已經了解了pixels_per_metric,我們就可以實作用于測量圖像中對象大小的python程式腳本了。
打開一個新的py檔案,插入以下代碼:
第2-8行用來導入我們所需的python包。在本例中,我們将大量使用imutils包,是以,如果您沒有安裝它,請確定在使用之前安裝它:
另外如果你已經安裝過這個imutils包,也請確定它為最新版本,在我這裡所使用最新的版本是0.3.6。
第10行和第11行定義了一個midpoint函數,顧名思義,它用于計算兩個(x,y)坐标之間的中點。
然後我們在第14-19行中解析我們的指令行參數。我們需要兩個參數,–image,它是我們輸入圖像的路徑,其中包含我們想要測量的對象,–width,也就是我們的參照物的寬度(英寸),–image路徑圖像中所認定的那個最左邊的物體。
我們現在可以加載我們的圖像并對其進行預處理:
第2-4行從磁盤加載我們的圖像,将其轉換為灰階,然後使用高斯過濾器平滑它。然後我們執行邊緣檢測和擴張+磨平,以消除邊緣圖中邊緣之間的任何間隙(第8-10行)。
第13-15行找到等高線,也就是我們邊緣圖中物體相對應的輪廓線。
然後,這些等高線區域從左到右(使得我們可以提取到參照物)在第19行中進行排列。然後我們在第20行時,對pixelspermetric值進行初始化。
下一步是對每一個等高線區域值大小進行檢查校驗。
在第2行,我們開始對每個單獨的輪廓值進行循環。如果等高線區域大小不夠大,我們就會丢棄該區域,認為它是邊緣檢測過程遺留下來的噪音(第4和5行)。
如果等高線區域足夠大,我們就會在第9-11行計算圖像的旋轉邊界框,特别注意:cv2.cv.boxpoints函數是針對于opencv2.4版本,而cv2.boxpoints函數是針對于opencv 3版本。
然後我們将旋轉的邊界框坐标按順序排列在左上角,右上角,右下角,左下角,正如上周的部落格文章(第17行)所讨論的。
最後,第18-22行用綠色畫出物體的輪廓,然後将邊界框矩形的頂點畫在小的紅色圓圈中。現在我們已經有了邊界框,接下來就可以計算出一系列的中點:
第4-6行将我們前面所得的有序邊界框各個值拆分出來,然後計算左上角和右上角之間的中點,然後是計算左下角和右下角之間的中點。
此外,我們還分别計算左上角與左下角,右上角和右下角的中點(第10和11行)。
第14-17行在我們的圖像上畫出藍色的中點,然後将各中間點用紫色線連接配接起來。
接下來,我們需要通過檢視我們的參照物來初始化pixelspermetric變量:
首先,我們計算中間點集之間的歐幾裡得距離(第2行和第3行)。
da變量将包含高度距離(以像素為機關),而db将保持我們的寬度距離。
然後,我們在第8行進行檢查,看看我們的pixelspermetric變量是否已經被初始化了,如果沒有,我們将db除以我們提供的寬度,進而得到每英寸的(近似)像素。
現在我們已經定義了pixelspermetric變量,我們可以測量圖像中各物體的大小:
第2行和第3行計算物體的尺寸(英寸),方法是通過pixelsper路徑成本劃分各自的歐幾裡得距離(參見上面的“pixels_per_metric ”一節,以獲得關于這個比率如何工作的更多資訊)。 第6-11行在我們的圖像上畫出物體的尺寸,而第14和15行顯示輸出結果。
為了測試我們的size.py腳本,隻需執行以下指令:
您的輸出應該如下圖所示:
圖2:使用opencv、python和計算機視覺+圖像處理技術來測量圖像中物體的大小
正如您所看到的,我們已經成功地計算出了我們圖像中的每個物體的大小——我們的名片被正确地報告為3.5in x 2in。類似地,我們的鎳币被準确地描述為0.8in x 0.8in。 然而,并不是所有的結果都是完美的。 兩個遊戲男孩的墨盒被報道有稍微不同的尺寸(實際上它們的尺寸是一樣的)。兩個鎳币的尺寸也下降了0.1英寸。 這是為什麼?為什麼物體的測量不是百分之百準确的?
有兩個原因:
首先,我匆忙用我的iphone拍了這張照片。這個角度當然不是一個完美的90度角“向下看”(就像鳥的眼睛一樣)在物體上。如果沒有一個完美的90度視圖(或者盡可能接近它),物體的尺寸就會被扭曲。
其次,我沒有使用相機的内在和外在參數來校準我的iphone。如果不确定這些參數,照片就會傾向于徑向和切向鏡頭失真。執行一個額外的校準步驟來發現這些參數可以“不扭曲”我們的圖像,并導緻更好的對象大小近似(但是我将把關于失真校正的讨論作為未來部落格文章的主題)。
換句話說,在拍攝對象的照片時,盡量接近90度的視角——這将有助于提高對物體大小估計的準确性。
讓我們看第二個測量物體大小的例子,這次測量的是藥片的尺寸:
圖3:使用opencv測量圖像中藥丸的大小
在美國,幾乎有一半的處方藥物是圓的和/或白色的,是以,如果我們能根據他們的測量結果來過濾藥片,我們就有更好的機會準确識别藥物。
最後,我們有一個最後的例子,這次用的是3.5in x 2in 的名片來測量兩個黑膠唱片和一個信封的大小:
同樣,結果也不是很完美,但這是由于(1)視角和(2)鏡頭失真,正如上述分析的兩個原因中所提到的。
本文還參考了另外一篇翻譯原英文部落格的部落格,網址如下:
https://blog.csdn.net/u010636181/article/details/80659700