直接可以用的Python和OpenCV檢測及分割影象的目標區域例子
第一天
老師:你知道麼,今天有人問了我一個問題。
~.我:什麼?
老師:他說很難。
~.我:關於什麼的?
老師:影象處理。
~.我:喔,你說說看,我確實做了不少影象處理的東西(心裡默唸,你不知知道你給過我多少影象嗎?)
老師:好嘞!在用深度學習的時候,比如說面對一張影象,對某個區域感興趣怎麼辦?
~.我:他傻啊,切割出來啊,只需要訓練感興趣的部分就好啦。
老師:哎,那你給我一個教程,我正好順手把他的問題解決了。
~.我:好的(黑人臉.gif)
老師:我回頭把圖片資料發給你。
~.我:好的好的,老師,by the way, 有多少資料啊?
老師:也不多,一個U盤夠了,這樣吧,明天你過來拷一下吧。
~.我:好的(hello?一個U盤?)
第二天
有這麼一個檔案
看了裡面。。。我要爆炸了。。。
> 598M * 15 = 8970M = 8.97G 我的個媽呀。
開啟一看 全是密密麻麻的——蟲子!!!
為了視覺體驗,自動遮蔽,請大家自行去谷歌:蟲子、worm、bug、insects。。。
三天後
~.我: 老師, 我就給一個方法啊, 不同的蟲子他們可以自己調閾值和方法,我已經有寫說明檔案。
老師: 好的,我看看。
考慮到視覺忍受能力,我用一個可愛的蟲子做為一個示例,其他的都差不多,大家自行嘗試。
目標是把蟲子區域摳出來
環境:
例圖:谷歌,可愛的蟲子–image
軟體:Anaconda 4.20,Opencv3.2
OpenCv的安裝:
1.1安裝Python3.60
1.2
具體思路如下:
1.獲取圖片,這個簡單哈
img_path = r'C:\Users\aixin\Desktop\chongzi.png'
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
看,這不就是你處理初始的樣子?
2.轉換灰度並去噪聲
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (9, 9),0)
我們可以得到這兩張圖,第一張是灰度圖,第二張是去噪之後的,另外說一下,去噪咱們有很多種方法,均值濾波器、高斯濾波器、中值濾波器、雙邊濾波器等。
這裡取高斯是因為高斯去噪效果是最好的。
3.提取影象的梯度
gradX = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=1, dy=0)
gradY = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=0, dy=1)
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
以Sobel運算元計算x,y方向上的梯度,之後在x方向上減去y方向上的梯度,通過這個減法,我們留下具有高水平梯度和低垂直梯度的影象區域。
此時,我們會得到
4.我們繼續去噪聲
考慮到影象的孔隙 首先使用低通濾潑器平滑影象, 這將有助於平滑影象中的高頻噪聲。 低通濾波器的目標是降低影象的變化率。
如將每個畫素替換為該畫素周圍畫素的均值, 這樣就可以平滑並替代那些強度變化明顯的區域。
對模糊影象二值化,顧名思義,就是把影象數值以某一邊界分成兩種數值,細節我會附在文章底部,如果還是不懂,去cao文件吧。
blurred = cv2.GaussianBlur(gradient, (9, 9),0)
(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)
此時,我們會得到
其實就算手動分割我們也是需要找到一個邊界吧,可以看到輪廓出來了,但是我們最終要的是整個輪廓,所以內部小區域就不要了
5.影象形態學(牛逼吧、唬人的)
在這裡我們選取ELLIPSE核,採用CLOSE操作,具體細節你依舊可以參考我的附錄文件,及拓展。
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (25, 25))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
此時,我們會得到
6.細節刻畫
從上圖我們可以發現和原圖對比,發現有細節丟失,這會干擾之後的昆蟲輪廓的檢測,要把它們擴充,分別執行4次形態學腐蝕與膨脹(附錄文件)
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
此時,我們會得到
7.找出昆蟲區域的輪廓
此時用cv2.findContours()函式
第一個引數是要檢索的圖片,必須是為二值圖,即黑白的(不是灰度圖)
(_, cnts, _) = cv2.findContours(
引數一: 二值化影象
closed.copy(),
引數二:輪廓型別
# cv2.RETR_EXTERNAL, #表示只檢測外輪廓
# cv2.RETR_CCOMP, #建立兩個等級的輪廓,上一層是邊界
# cv2.RETR_LIST, #檢測的輪廓不建立等級關係
# cv2.RETR_TREE, #建立一個等級樹結構的輪廓
# cv2.CHAIN_APPROX_NONE, #儲存所有的輪廓點,相鄰的兩個點的畫素位置差不超過1
引數三:處理近似方法
# cv2.CHAIN_APPROX_SIMPLE, #例如一個矩形輪廓只需4個點來儲存輪廓資訊
# cv2.CHAIN_APPROX_TC89_L1,
# cv2.CHAIN_APPROX_TC89_KCOS
)
8.畫出輪廓
找到輪廓了,接下來,要畫出來的,即用cv2.drawContours()函式。
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.boxPoints(rect))
# draw a bounding box arounded the detected barcode and display the image
draw_img = cv2.drawContours(img.copy(), [box], -1, (0, 0, 255), 3)
cv2.imshow("draw_img", draw_img)
此時,我們會得到
9.裁剪出來就完成啦
方法嘛,這不就是麼,找到這四個點切出來就好啦
我們放大一點看一下細節
+
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
crop_img= img[y1:y1+hight, x1:x1+width]
cv2.imshow('crop_img', crop_img)
其實,box裡儲存的是綠色矩形區域四個頂點的座標。 我將按下圖紅色矩形所示裁剪昆蟲影象。
找出四個頂點的x,y座標的最大最小值。新影象的高=maxY-minY,寬=maxX-minX
終於我們得到了可愛的小蟲子。
得到了目標區域,那麼你想拿它幹什麼就幹什麼!我不管你哈。
考慮到現在的python教程一般都是一上來就是list、tuple什麼的,而不是檔案的讀寫和儲存,包括批量讀取等等,我特地加入了python版的檔案批量讀寫和儲存等附錄檔案。
快快快、誇我!
附錄1.實現程式碼
#-*- coding: UTF-8 -*-
'''
Author: Steve Wang
Time: 2017/12/8 10:00
Environment: Python 3.6.2 |Anaconda 4.3.30 custom (64-bit) Opencv 3.3
'''
import cv2
import numpy as np
def get_image(path):
#獲取圖片
img=cv2.imread(path)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
return img, gray
def Gaussian_Blur(gray):
# 高斯去噪
blurred = cv2.GaussianBlur(gray, (9, 9),0)
return blurred
def Sobel_gradient(blurred):
# 索比爾運算元來計算x、y方向梯度
gradX = cv2.Sobel(blurred, ddepth=cv2.CV_32F, dx=1, dy=0)
gradY = cv2.Sobel(blurred, ddepth=cv2.CV_32F, dx=0, dy=1)
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
return gradX, gradY, gradient
def Thresh_and_blur(gradient):
blurred = cv2.GaussianBlur(gradient, (9, 9),0)
(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)
return thresh
def image_morphology(thresh):
# 建立一個橢圓核函式
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (25, 25))
# 執行影象形態學, 細節直接查文件,很簡單
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
return closed
def findcnts_and_box_point(closed):
# 這裡opencv3返回的是三個引數
(_, cnts, _) = cv2.findContours(closed.copy(),
cv2.RETR_LIST,
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.boxPoints(rect))
return box
def drawcnts_and_cut(original_img, box):
# 因為這個函式有極強的破壞性,所有需要在img.copy()上畫
# draw a bounding box arounded the detected barcode and display the image
draw_img = cv2.drawContours(original_img.copy(), [box], -1, (0, 0, 255), 3)
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
crop_img = original_img[y1:y1+hight, x1:x1+width]
return draw_img, crop_img
def walk():
img_path = r'C:\Users\aixin\Desktop\chongzi.png'
save_path = r'C:\Users\aixin\Desktop\chongzi_save.png'
original_img, gray = get_image(img_path)
blurred = Gaussian_Blur(gray)
gradX, gradY, gradient = Sobel_gradient(blurred)
thresh = Thresh_and_blur(gradient)
closed = image_morphology(thresh)
box = findcnts_and_box_point(closed)
draw_img, crop_img = drawcnts_and_cut(original_img,box)
# 暴力一點,把它們都顯示出來看看
cv2.imshow('original_img', original_img)
cv2.imshow('blurred', blurred)
cv2.imshow('gradX', gradX)
cv2.imshow('gradY', gradY)
cv2.imshow('final', gradient)
cv2.imshow('thresh', thresh)
cv2.imshow('closed', closed)
cv2.imshow('draw_img', draw_img)
cv2.imshow('crop_img', crop_img)
cv2.waitKey(20171219)
cv2.imwrite(save_path, crop_img)
walk()
# 用來轉化影象格式的
img = cv2.cvtColor(src,
COLOR_BGR2HSV # BGR---->HSV
COLOR_HSV2BGR # HSV---->BGR
...)
# For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255]
# 返回一個閾值,和二值化影象,第一個閾值是用來otsu方法時候用的
# 不過現在不用了,因為可以通過mahotas直接實現
T = ret = mahotas.threshold(blurred)
ret, thresh_img = cv2.threshold(src, # 一般是灰度影象
num1, # 影象閾值
num2, # 如果大於或者num1, 畫素值將會變成 num2
# 最後一個二值化引數
cv2.THRESH_BINARY # 將大於閾值的灰度值設為最大灰度值,小於閾值的值設為0
cv2.THRESH_BINARY_INV # 將大於閾值的灰度值設為0,大於閾值的值設為最大灰度值
cv2.THRESH_TRUNC # 將大於閾值的灰度值設為閾值,小於閾值的值保持不變
cv2.THRESH_TOZERO # 將小於閾值的灰度值設為0,大於閾值的值保持不變
cv2.THRESH_TOZERO_INV # 將大於閾值的灰度值設為0,小於閾值的值保持不變
)
thresh = cv2.AdaptiveThreshold(src,
dst,
maxValue,
# adaptive_method
ADAPTIVE_THRESH_MEAN_C,
ADAPTIVE_THRESH_GAUSSIAN_C,
# thresholdType
THRESH_BINARY,
THRESH_BINARY_INV,
blockSize=3,
param1=5
)
# 一般是在黑色背景中找白色物體,所以原始影象背景最好是黑色
# 在執行找邊緣的時候,一般是threshold 或者是canny 邊緣檢測後進行的。
# warning:此函式會修改原始影象、
# 返回:座標位置(x,y),
(_, cnts, _) = cv2.findContours(mask.copy(),
# cv2.RETR_EXTERNAL, #表示只檢測外輪廓
# cv2.RETR_CCOMP, #建立兩個等級的輪廓,上一層是邊界
cv2.RETR_LIST, #檢測的輪廓不建立等級關係
# cv2.RETR_TREE, #建立一個等級樹結構的輪廓
# cv2.CHAIN_APPROX_NONE, #儲存所有的輪廓點,相鄰的兩個點的畫素位置差不超過1
cv2.CHAIN_APPROX_SIMPLE, #例如一個矩形輪廓只需4個點來儲存輪廓資訊
# cv2.CHAIN_APPROX_TC89_L1,
# cv2.CHAIN_APPROX_TC89_KCOS
)
img = cv2.drawContours(src, cnts, whichToDraw(-1), color, line)
img = cv2.imwrite(filename, dst, # 檔案路徑,和目標影象檔案矩陣
# 對於JPEG,其表示的是影象的質量,用0-100的整數表示,預設為95
# 注意,cv2.IMWRITE_JPEG_QUALITY型別為Long,必須轉換成int
[int(cv2.IMWRITE_JPEG_QUALITY), 5]
[int(cv2.IMWRITE_JPEG_QUALITY), 95]
# 從0到9,壓縮級別越高,影象尺寸越小。預設級別為3
[int(cv2.IMWRITE_PNG_COMPRESSION), 5])
[int(cv2.IMWRITE_PNG_COMPRESSION), 9])
# 如果你不知道用哪個flags,畢竟太多了哪能全記住,直接找找。
尋找某個函式或者變數
events = [i for i in dir(cv2) if 'PNG' in i]
print( events )
尋找某個變數開頭的flags
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print flags
批量讀取檔名字
import os
filename_rgb = r'C:\Users\aixin\Desktop\all_my_learning\colony\20170629'
for filename in os.listdir(filename_rgb): #listdir的引數是資料夾的路徑
print (filename)