1.介紹
opencv入門之數米粒:給出一幅米粒圖數出圖中米粒數目以及最大米粒位置。在原圖上畫出最大米粒的位置。並輸出最大米粒的面積和最大米粒的周長。使用pycharm編碼,親測功能強大。
2.思路
首先利用opencv輪廓檢測函數來分別提取每一個米粒,並計算出每個米粒的面積,然後對最大米粒求周長,最後在原圖上標記最大米粒的位置,並且輸出米粒信息。
3.預處理
opencv輪廓檢測函數所需要的圖像爲二值化圖片,所以在進行輪廓檢測前,需要對圖像進行預處理。預處理過程爲:
(1).對圖片進行灰度化:
- #打開圖片 img=cv2.imread(‘Img/rice.jpg’,0) (-1爲原圖,0爲強制灰度圖,1爲強制彩圖)
- 灰度化函數gray=cv2.cvtColor(frame,cv2.COLOR_RGB2GRAY)
(2)獲取去背景的米粒圖
利用腐蝕膨脹,對原圖進行5次腐蝕,然後進行5次膨脹,得到背景圖
原圖減去背景圖可以得到去背景的米粒圖
- 腐蝕:就像土壤侵蝕一樣,這個操作會把前景物體的邊界腐蝕掉(但是前景仍然 是白色)。這是怎麼做到的呢?卷積核沿着圖像滑動,如果與卷積覈對應的原圖 像的所有像素值都是 1,那麼中心元素就保持原來的像素值,否則就變爲零。
import import cv2
import numpy as np
#打開圖片
img=cv2.imread('Img/rice.jpg',0)
#構造模板,5次腐蝕
kernel=np.ones((5,5),np.uint8)#構造5x5卷積核
erosion=cv2.erode(img,kernel,iterations=5)#進行5次腐蝕
- 膨脹:與腐蝕相反,與卷積覈對應的原圖像的像素值中只要有一個是 1,中心元 素的像素值就是 1。所以這個操作會增加圖像中的白色區域(前景)。一般在去 噪聲時先用腐蝕再用膨脹。因爲腐蝕在去掉白噪聲的同時,也會使前景對象變 小。
dilation=cv2.dilate(erosion,kernel,iterations=5) #進行5次膨脹
- 獲取去背景的米粒圖
用原圖減去5次腐蝕,5次膨脹後的背景圖
#原圖減去背景得到米粒形狀
backImg=dilation
rice=img-backImg
去背景米粒效果圖:
(3)圖像二值化
簡單閾值
與名字一樣,這種方法非常簡單。但像素值高於閾值時,我們給這個像素 賦予一個新值(可能是白色),否則我們給它賦予另外一種顏色(也許是黑色)。 這個函數就是 cv2.threshhold()。這個函數的第一個參數就是原圖像,原圖 像應該是灰度圖。第二個參數就是用來對像素值進行分類的閾值。第三個參數 就是當像素值高於(有時是小於)閾值時應該被賦予的新的像素值。OpenCV 提供了多種不同的閾值方法,這是有第四個參數來決定的。這些方法包括:
cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INVOSTU全局閾值
這裏用到到的函數還是 cv2.threshold(),但是需要多傳入一個參數 (flag):cv2.THRESH_OTSU。這時要把閾值設爲 0。然後算法會找到最 優閾值,這個最優閾值就是返回值 retVal。如果不使用 Otsu 二值化,返回的 retVal 值與設定的閾值相等。
#OSTU二值化,th1返回閾值,ret1返回二值化的結果圖像
th1,ret1=cv2.threshold(rice,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
二值化效果圖如下
4.輪廓分析
輪廓可以簡單認爲成將連續的點(連着邊界)連在一起的曲線,具有相同 的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。 python3.x-opencv開發指導用書
(1) 輪廓提取
- 爲了更加準確,要使用二值化圖像。在尋找輪廓之前,要進行閾值化處理 或者 Canny 邊界檢測。
- 查找輪廓的函數會修改原始圖像。如果你在找到輪廓之後還想使用原始圖 像的話,你應該將原始圖像存儲到其他變量中。
- 在 OpenCV 中,查找輪廓就像在黑色背景中超白色物體。你應該記住, 要找的物體應該是白色而背景應該是黑色。
**#輪廓檢測,注意輸出結果應有3個,其他著作上爲2個輸出結果,親測錯誤**
ret1,contours,hierarchy=cv2.findContours(ret1,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
(2)輪廓數據
- 輪廓面積
輪廓的面積可以使用函數 cv2.contourArea() 計算得到,也可以使用矩 (0 階矩),M[‘m00’]。
cv2.contourArea(cnt)
- 輪廓周長
也被稱爲弧長。可以使用函數 cv2.arcLength() 計算得到。這個函數 的第二參數可以用來指定對象的形狀是閉合的(True),還是打開的(一條曲 線)。
cv2.arcLength(cnt,True)
(3)輸出結果
遍歷所有輪廓,比較輪廓數據,得出最大輪廓,並在原圖上標註。
函數 cv2.drawContours() 可以被用來繪製輪廓。它可以根據你提供 的邊界點繪製任何形狀。它的第一個參數是原始圖像,第二個參數是輪廓,一 個 Python 列表。第三個參數是輪廓的索引(在繪製獨立輪廓是很有用,當設 置爲 -1 時繪製所有輪廓)。接下來的參數是輪廓的顏色和厚度等。
#遍歷得到最大面積的米粒
maxC=-1
maxS=-1
for cnt in contours:
tempS=cv2.contourArea(cnt)
if maxS<tempS:
maxS=tempS
maxC=tempC=cv2.arcLength(cnt,True)
contour=cnt
#在img中畫出最大面積米粒
cv2.drawContours(img,[contour],-1,(0,0,255,),1)
改變參數可以獲取更好的效果
整體代碼
import cv2
import numpy as np
#打開圖片
img=cv2.imread('Img/rice.jpg',0)
#構造模板,5次腐蝕,5次膨脹,得到背景
kernel=np.ones((5,5),np.uint8)
erosion=cv2.erode(img,kernel,iterations=5)
dilation=cv2.dilate(erosion,kernel,iterations=5)
#原圖減去背景得到米粒形狀
backImg=dilation
rice=img-backImg
#OSTU二值化
th1,ret1=cv2.threshold(rice,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#輪廓檢測
ret1,contours,hierarchy=cv2.findContours(ret1,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#遍歷得到最大面積的米粒
maxC=-1
maxS=-1
for cnt in contours:
tempS=cv2.contourArea(cnt)
if maxS<tempS:
maxS=tempS
maxC=tempC=cv2.arcLength(cnt,True)
contour=cnt
#在img中畫出最大面積米粒
cv2.drawContours(img,[contour],-1,(0,0,255,),1)
cv2.imshow('image',rice)
print('面積最大:',maxS)
print('對應米粒周長:',maxC)
cv2.waitKey(0)
cv2.destroyAllWindows()