OpenCV 識別圖片中的米粒個數,並計算米粒的平均面積和長度

介紹

OpenCV+Python

使用OpenCV構建圖像識別算法,識別圖片中的米粒個數,並計算米粒的平均面積和長度

軟件架構

模塊:OpenCV 4.0.0.21

編程語言:Python 3.7.2

編譯器:PyCharm 2018

程序設計思路

首先介紹一下程序設計的思路:

  1. 圖像採集(取到圖像):可以用攝像頭拍攝或者圖片直接導入
  2. 圖像預處理:對圖像進行灰度化
  3. 基於灰度的閾值分割:使用局部大津算法進行閾值分割二值化,形態學去噪
  4. 圖像特徵描述及目標分析:使用灰度直方圖計算輪廓,並通過面積條件進行篩選
  5. 得到最終結果:統計識別的米粒個數,並將米粒用矩形框標記出來,並打上編號

先看看效果圖:

效果圖
左邊是原始圖像,右邊是識別之後的結果。

下面介紹程序實現:

第一步:圖像採集``
img = cv2.imread("test.png")  #導入圖片,圖片放在程序所在目錄

使用cv2.imread()函數導入圖片,括號內輸入圖片路徑,如果圖片位於程序所在目錄,就可以直接寫圖片文件名。

第二步:圖像預處理

gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #轉換爲灰度圖

主要使用cv2.cvtColor()函數將彩色圖片轉化爲灰度圖

第三步:基於灰度的閾值分割

#使用局部閾值的大津算法進行圖像二值化
dst = cv2.adaptiveThreshold(gray,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,101, 1)

element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3, 3))#形態學去噪
dst=cv2.morphologyEx(dst,cv2.MORPH_OPEN,element)  #開運算去噪

使用局部大津算法對圖像進行二值化,全局大津法的優點在於可以快速有效的找到類間分割閾值,但其缺點也很明顯,就是隻能針對單一目標分割,或者感興趣的目標都屬於同一灰度範圍,若需探測目標灰度範圍分佈較大,則必將有一部分目標探測丟失(例如上圖中黑色和白色的汽車)。局部分割的優點在於可以進行多目標分割,缺點在於基於局部閾值分割出的目標連結性較差,包含噪聲。
在這個米粒圖片中,下部的圖像亮度較暗,如果使用全局大津算法,下部的米粒將會被認爲是背景,而被屏蔽掉。在這裏插入圖片描述
如圖所示:左邊是使用局部大津算法,右邊是使用全局大津算法,可以看到左邊的效果比右邊的效果好。

函數原型:

   cv2. adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)

參數解釋:
**InputArray src:**源圖像

**OutputArray dst:**輸出圖像,與源圖像大小一致

**int adaptiveMethod:**在一個鄰域內計算閾值所採用的算法,有兩個取值,分別爲 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C 。

ADAPTIVE_THRESH_MEAN_C的計算方法是計算出領域的平均值再減去第七個參數double C的值

ADAPTIVE_THRESH_GAUSSIAN_C的計算方法是計算出領域的高斯均值再減去第七個參數double C的值

**int thresholdType:**這是閾值類型,只有兩個取值,分別爲 THRESH_BINARY 和THRESH_BINARY_INV 具體的請看官方的說明,這裏不多做解釋

**int blockSize:**adaptiveThreshold的計算單位是像素的鄰域塊,鄰域塊取多大,就由這個值作決定

**double C:**在對參數int adaptiveMethod的說明中,我已經說了這個參數的作用,從中可以看出,這個參數實際上是一個偏移值調整量

然後對圖像進行形態學去噪,形態學去噪有一下幾種:

  • 開運算:先腐蝕再膨脹,用來消除小物體
  • 閉運算:先膨脹再腐蝕,用於排除小型黑洞
  • 形態學梯度:就是膨脹圖與俯視圖之差,用於保留物體的邊緣輪廓。
  • 頂帽:原圖像與開運算圖之差,用於分離比鄰近點亮一些的斑塊。
  • 黑帽:閉運算與原圖像之差,用於分離比鄰近點暗一些的斑塊。

我們使用開運算去除小噪聲

第四步:圖像特徵描述及目標分析

主要步驟爲:
1、檢測輪廓- cv2.findContours()函數
2、提取輪廓的水平矩形座標- rect = cv2.boundingRect( ) 函數
3、繪製矩形- cv2.rectangle()函數
4、 在米粒左上角寫上編號-cv2.putText( ) 函數

contours, hierarchy = cv2.findContours(dst,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)  #輪廓檢測函數
cv2.drawContours(dst,contours,-1,(120,0,0),2)  #繪製輪廓

count=0 #米粒總數
ares_avrg=0  #米粒平均
#遍歷找到的所有米粒
for cont in contours:

    ares = cv2.contourArea(cont)#計算包圍性狀的面積

    if ares<50:   #過濾面積小於10的形狀
        continue
    count+=1    #總體計數加1
    ares_avrg+=ares

    print("{}-blob:{}".format(count,ares),end="  ") #打印出每個米粒的面積

    rect = cv2.boundingRect(cont) #提取矩形座標

    print("x:{} y:{}".format(rect[0],rect[1]))#打印座標

    cv2.rectangle(img,rect,(0,0,0xff),1)#繪製矩形

    y=10 if rect[1]<10 else rect[1] #防止編號到圖片之外

    cv2.putText(img,str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1) #在米粒左上角寫上編號

print("米粒平均面積:{}".format(round(ares_avrg/ares,2))) #打印出每個米粒的面積

1、檢測輪廓

cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  

參數:

  • image參數是尋找輪廓的圖像;

  • mode參數表示輪廓的檢索模式,有四種(本文介紹的都是新的cv2接口):

    cv2.RETR_EXTERNAL表示只檢測外輪廓
    cv2.RETR_LIST檢測的輪廓不建立等級關係
    cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層爲外邊界,裏面的一層爲內孔的邊界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。
    cv2.RETR_TREE建立一個等級樹結構的輪廓。

  • method參數method爲輪廓的近似辦法

    cv2.CHAIN_APPROX_NONE存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
    cv2.CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來保存輪廓信息
    cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

返回值:

cv2.findContours()函數返回兩個值,一個是輪廓本身,還有一個是每條輪廓對應的屬性。

  • contour返回值:cv2.findContours()函數首先返回一個list,list中每個元素都是圖像中的一個輪廓,用numpy中的ndarray表示。
  • hierarchy返回值:此外,該函數還可返回一個可選的hiararchy結果,這是一個ndarray,其中的元素個數和輪廓個數相同,每個輪廓contours[i]對應4個hierarchy元素hierarchy[i][0]
    ~hierarchy[i][3],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,則該值爲負數。

2、提取輪廓的水平矩形座標

  cv2.boundingRect(InputArray points)

參數:

  • points:輸入信息,可以爲包含點的容器(vector)或是Mat。
  • 返回包覆輸入信息的最小正矩形。

3、繪製矩形

cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)

參數解釋:

  • img是原圖
  • (x,y)是矩陣的左上點座標
  • (x+w,y+h)是矩陣的右下點座標
  • (0,255,0)是畫線對應的rgb顏色
  • 2是所畫的線的寬度

4、 在米粒左上角寫上編號

cv2.putText(img,str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1) 

各參數依次是:圖片輸入/添加的文字/左上角座標/字體/字體大小/顏色/字體粗細

第五步:統計米粒的編號、面積、長度

ares = cv2.contourArea(cont)

計算包圍形狀的面積,並使用一個for循環來計算面積平均值和長度平均值

效果圖:
效果圖
在這裏插入圖片描述

完整程序代碼請點擊這裏下載:OpenCV 識別圖片中的米粒python程序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章