1. 程式人生 > 程式設計 >使用Python和OpenCV檢測影象中的物體並將物體裁剪下來

使用Python和OpenCV檢測影象中的物體並將物體裁剪下來

介紹

碩士階段的畢設是關於昆蟲影象分類的,程式碼寫到一半,上週五導師又給我新的昆蟲圖片資料集了,新圖片中很多圖片很大,但是圖片中的昆蟲卻很小,所以我就想著先處理一下圖片,把圖片中的昆蟲裁剪下來,這樣除去大部分無關背景,應該可以提高識別率。

原圖片舉例(將紅色矩形框部分裁剪出來)):

這裡寫圖片描述

step1:載入圖片,轉成灰度圖

image = cv2.imread("353.jpg")
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

step2:用Sobel運算元計算x,y方向上的梯度,之後在x方向上減去y方向上的梯度,通過這個減法,我們留下具有高水平梯度和低垂直梯度的影象區域。

gradX = cv2.Sobel(gray,ddepth=cv2.cv.CV_32F,dx=1,dy=0,ksize=-1)
gradY = cv2.Sobel(gray,dx=0,dy=1,ksize=-1)

# subtract the y-gradient from the x-gradient
gradient = cv2.subtract(gradX,gradY)
gradient = cv2.convertScaleAbs(gradient)

執行完這一步,得到的影象如下:

這裡寫圖片描述

step3:去除影象上的噪聲。首先使用低通濾潑器平滑影象(9 x 9核心),這將有助於平滑影象中的高頻噪聲。低通濾波器的目標是降低影象的變化率。如將每個畫素替換為該畫素周圍畫素的均值。這樣就可以平滑並替代那些強度變化明顯的區域。

然後,對模糊影象二值化。梯度影象中不大於90的任何畫素都設定為0(黑色)。 否則,畫素設定為255(白色)。

# blur and threshold the image
blurred = cv2.blur(gradient,(9,9))
(_,thresh) = cv2.threshold(blurred,90,255,cv2.THRESH_BINARY)

執行完這一步,得到的影象如下:

這裡寫圖片描述

step4:在上圖中我們看到蜜蜂身體區域有很多黑色的空餘,我們要用白色填充這些空餘,使得後面的程式更容易識別昆蟲區域,這需要做一些形態學方面的操作。

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(25,25))
closed = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,kernel)

處理之後的影象如下:

這裡寫圖片描述

step5:從上圖我們發現影象上還有一些小的白色斑點,這會干擾之後的昆蟲輪廓的檢測,要把它們去掉。分別執行4次形態學腐蝕與膨脹。

# perform a series of erosions and dilations
closed = cv2.erode(closed,None,iterations=4)
closed = cv2.dilate(closed,iterations=4)

執行完這步,得到的圖形如下:

這裡寫圖片描述

step6:找出昆蟲區域的輪廓。cv2.findContours()函式第一個引數是要檢索的圖片,必須是為二值圖,即黑白的(不是灰度圖),所以讀取的影象要先轉成灰度的,再轉成二值圖,我們在第三步用cv2.threshold()函式已經得到了二值圖。第二個引數表示輪廓的檢索模式,有四種:

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

第三個引數為輪廓的近似方法

  • cv2.CHAIN_APPROX_NONE儲存所有的輪廓點,相鄰的兩個點的畫素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
  • cv2.CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來儲存輪廓資訊

cv2.findContours()函式返回兩個值,一個是輪廓本身,還有一個是每條輪廓對應的屬性。cv2.findContours()函式返回第一個值是list,list中每個元素都是影象中的一個輪廓,用numpy中的ndarray表示。每一個ndarray裡儲存的是輪廓上的各個點的座標。我們把list排序,點最多的那個輪廓就是我們要找的昆蟲的輪廓。
OpenCV中通過cv2.drawContours在影象上繪製輪廓。

  • 第一個引數是指明在哪幅影象上繪製輪廓
  • 第二個引數是輪廓本身,在Python中是一個list
  • 第三個引數指定繪製輪廓list中的哪條輪廓,如果是-1,則繪製其中的所有輪廓
  • 第四個引數是輪廓線條的顏色
  • 第五個引數是輪廓線條的粗細

cv2.minAreaRect()函式:
主要求得包含點集最小面積的矩形,這個矩形是可以有偏轉角度的,可以與影象的邊界不平行。

(cnts,_) = cv2.findContours(closed.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts,key=cv2.contourArea,reverse=True)[0]

# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
box = np.int0(cv2.cv.BoxPoints(rect))

# draw a bounding box arounded the detected barcode and display the image
cv2.drawContours(image,[box],-1,(0,0),3)
cv2.imshow("Image",image)
cv2.imwrite("contoursImage2.jpg",image)
cv2.waitKey(0)

執行完這步得到的圖形如下:

這裡寫圖片描述

step7:裁剪。box裡儲存的是綠色矩形區域四個頂點的座標。我將按下圖紅色矩形所示裁剪昆蟲影象。找出四個頂點的x,y座標的最大最小值。新影象的高=maxY-minY,寬=maxX-minX。

這裡寫圖片描述

Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
hight = y2 - y1
width = x2 - x1
cropImg = image[y1:y1+hight,x1:x1+width]

裁剪出的圖片如下:

這裡寫圖片描述

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。