深度学习搞CV?图像数据不足咋办?看这里!
今天就來一招搞定數據增強(data_Augmentation),讓你在機器學習/深度學習圖像處理的路上,從此不再為數據不夠而發愁。且來看圖片從250張>>>>任意張的華麗增強,每一張都與眾不同。
開始之前呢,我們先把這件大事給細分下,一步一步的來:
首先,圖像讀取,需要對文件夾操作;
然后,增強圖像(重點,重點,重點);
最后,保存圖像。
來看下此次任務中,待增強的圖像和標簽,主要是為了做圖像分割做圖像準備。這個圖像懂的應該能看出來,這是一個嬰兒頭圍的醫學圖像,現實場景意義很強。上圖(以3張圖為例):
train_label
成雙成對,這樣在后續的文件讀取中會比較的方便(大神可以自己改改,練練動手能力)
那動手吧!!!
一.大殺氣之keras ImageDataGenerator
from?keras.preprocessing.image?import?ImageDataGeneratorImageDataGenerator()是keras.preprocessing.image模塊中的圖片生成器,同時也可以在batch中對數據進行增強,擴充數據集大小,增強模型的泛化能力。比如進行旋轉,變形,歸一化等,它所能實現的功能且看下面的詳細部分吧。
keras.preprocessing.image.ImageDataGenerator(?????????????? featurewise_center=False,??
?????????????? samplewise_center=False,?
?????????????? featurewise_std_normalization=False,?
?????????????? samplewise_std_normalization=False,?
?????????????? zca_whitening=False,?
?????????????? zca_epsilon=1e-06,?
?????????????? rotation_range=0,?#整數。隨機旋轉的度數范圍。
?????????????? width_shift_range=0.0,?#浮點數、一維數組或整數
?????????????? height_shift_range=0.0,?#浮點數。剪切強度(以弧度逆時針方向剪切角度)。
?????????????? brightness_range=None,?
?????????????? shear_range=0.0,?
?????????????? zoom_range=0.0,?#浮點數?或?[lower,?upper]。隨機縮放范圍
?????????????? channel_shift_range=0.0,?#浮點數。隨機通道轉換的范圍。
?????????????? fill_mode='nearest',?#?{"constant",?"nearest",?"reflect"?or?"wrap"}?之一。默認為?'nearest'。輸入邊界以外的點根據給定的模式填充:
?????????????? cval=0.0,?
?????????????? horizontal_flip=False,?
?????????????? vertical_flip=False,?
?????????????? rescale=None,?
?????????????? preprocessing_function=None,?
?????????????? data_format=None,?
?????????????? validation_split=0.0,?
?????????????? dtype=None)
這里就以單張圖片為例,詳述下這個圖像增強大殺器的具體用法,分別以旋轉(rotation_range),長寬上平移(width_shift_range,height_shift_range)
輸入圖像:
train_label
先來看下兩者合并后的圖像:
merge
到這里,我們進行增強變換,演示下這里增強部分是咋用的,且看:
(溫馨提示)
滑慢點,有GIF圖(1)旋轉(rotation_range=1.2)
otation=1.2
(2)寬度變換(width_shift_range=0.05)
width_shift_range=0.05
(3)高度變換(height_shift_range=0.05)
eight_shift_range=0.05
這里才只是演示了三個就那么的強大,詳細,這要能增強多少圖片啊,想想都可怕,想都不敢想啊!!!
增強匯總
這里是合并部分,單幅增強的大圖效果詳情看這里:
merge改變通道排布方式
這里,且看單幅圖像的增強代碼(建議去下載仔細看,往后看,有方式):
import?osfrom?keras.preprocessing.image?import?ImageDataGenerator,load_img,img_to_array,array_to_img
class?Augmentation(object):
????def?__init__(self,img_type="png"):
????????self.datagen=ImageDataGenerator(
????????????#rotation_range=1.2,
????????????#width_shift_range=0.05,
????????????height_shift_range=0.05,
????????????#?shear_range=0.05,
????????????#?zoom_range=0.05,
????????????#?horizontal_flip=True,
????????????fill_mode='nearest')
????def?augmentation(self):
????????#?讀入3通道的train和label,?分別轉換成矩陣,?然后將label的第一個通道放在train的第2個通處,?做數據增強
????????print("運行?Augmentation")
????????#?Start?augmentation.....
????????img_t?=?load_img("../one/img/0.png")??#?讀入train
????????img_l?=?load_img("../one/label/0.png")??#?讀入label
????????x_t?=?img_to_array(img_t)??#?轉換成矩陣
????????x_l?=?img_to_array(img_l)
????????x_t[:,?:,?2]?=?x_l[:,?:,?0]??#?把label當做train的第三個通道
????????#x_t?=?x_t[...,?[2,0,1]]#image-102,120,210
????????img_tmp?=?array_to_img(x_t)
????????img_tmp.save("../one/merge/0.png")??#?保存合并后的圖像
????????img?=?x_t
????????img?=?img.reshape((1,)?+?img.shape)??#?改變shape(1,?512,?512,?3)
????????savedir?=?"../one/aug_merge"??#?存儲合并增強后的圖像
????????if?not?os.path.lexists(savedir):
????????????os.mkdir(savedir)
????????print("running?%d?doAugmenttaion"?%?0)
????????self.do_augmentate(img,?savedir,?str(0))??#?數據增強
????def?do_augmentate(self,?img,?save_to_dir,?save_prefix,?batch_size=1,?save_format='png',?imgnum=30):
????????#?augmentate?one?image
????????datagen?=?self.datagen
????????i?=?0
????????for?_?in?datagen.flow(
????????????????img,
????????????????batch_size=batch_size,
????????????????save_to_dir=save_to_dir,
????????????????save_prefix=save_prefix,
????????????????save_format=save_format):
????????????i?+=?1
????????????if?i?>?imgnum:
????????????????break
if?__name__=="__main__":
????aug=Augmentation()
????aug.augmentation()
這里不做過多的解釋,打個廣告,歡迎關注微信公眾號:小白算法。對代碼中的詳細內容,我們且看第二部分
二.詳解單幅圖像增強
這里先說下對圖像和標簽一起增強的步驟,有人該問為什么還要標簽了。這里針對的問題是圖像分割,pix2pix的任務,即輸入時一般圖像,輸出是目標分割后圖像,在上面就是train_img和train_label的一一對應關系,這里開始分解步驟來說增強:
1.train_img+train_label=merge,也就是圖像+橢圓形的那個;
2.對merge圖像進行增強;
3.將merge圖像按通道拆分,1的逆過程。
前面只涉及步驟1和2,故先對這兩塊做詳述,如下:
著重講下Augmentation類中augmentation函數部分和對單幅圖像增強部分。
1.讀取train_img,train_label;
?#?load_imageimg_t?=?load_img("../one/img/0.png")
img_l?=?load_img("../one/label/0.png")
2.因為要講上述img_t和img_l進行合并,采用矩陣形式進行操作,這里將讀取到的圖像轉換為矩陣形式;
?#?img_to_arrayx_t?=?img_to_array(img_t)?
????????x_l?=?img_to_array(img_l)
3.train_img+train_label=merge.把label當做train的第三個通道
后面注釋部分,是對合并后的通道進行任意組合的形式,會出現不同的效果,如前文中三個特寫圖(具體自己可嘗試)
#?把label當做train的第三個通道x_t[:,?:,?2]?=?x_l[:,?:,?0]??
#x_t?=?x_t[...,?[2,0,1]]#image-102,120,210
4.為了保存merge后圖像,此時該從array_to_image了,然后保存圖像文件;
img_tmp?=?array_to_img(x_t)img_tmp.save("../one/merge/0.png")??#?保存合并后的圖像
5.此時執行對merge圖像的增強操作;
開始前,既然我們要def do_augmentate(),我們先想想對一幅圖像的增強,需要些什么:
image圖像文件;
save_to_dir保存增強后的文件夾地址;
批增強的數量。
至于別的,先看這里
flow(self,?X,?y,?batch_size=32,?shuffle=True,?seed=None,?save_to_dir=None,?save_prefix='',?save_format='png')'''
x:樣本數據,秩應為4,在黑白圖像的情況下channel軸的值為1,在彩色圖像情況下值為3
y:標簽
batch_size:整數,默認32
shuffle:布爾值,是否隨機打亂數據,默認為True
save_to_dir:None或字符串,該參數能讓你將提升后的圖片保存起來,用以可視化
save_prefix:字符串,保存提升后圖片時使用的前綴,?僅當設置了save_to_dir時生效
save_format:"png"或"jpeg"之一,指定保存圖片的數據格式,默認"jpeg"
yields:形如(x,y)的tuple,x是代表圖像數據的numpy數組.y是代表標簽的numpy數組.該迭代器無限循環.
seed:?整數,隨機數種子
'''
flow:接收numpy數組和標簽為參數,生成經過數據提升或標準化后的batch數據,并在一個無限循環中不斷的返回batch數據
6.由于flow的輸入X需要一個秩為4的數組,所以需要對他變形,加上img.shape=3
#?改變shape(1,?512,?512,?3)img?=?img.reshape((1,)?+?img.shape)??
好了,這里應該是對代碼部分描述的已經夠清楚了(哪里還有不理解的,歡迎留言評論,大家一起進步哦)
三.最后的拆分分別保存train_img和train_label
話不多說,先看下拆分代碼部分,還是先說步驟:
1.讀取merge文件夾內圖片;
2.按照之前組合的形式進行拆分為img_train和img_label,同時保存在兩個文件夾內,一一對應。
????????#?讀入合并增強之后的數據(aug_merge),?對其進行分離,?分別保存至?aug_merge_img,?aug_merge_label
????????print("running?split_Merge_image")
????????#?split?merged?image?apart
????????path_merge?=?"../one/aug_merge"??#?合并增強之后的圖像
????????path_train?=?"../one/aug_merge_img"??#?增強之后分離出來的train
????????path_label?=?"../one/aug_merge_label"??#?增強之后分離出來的label
????????if?not?os.path.lexists(path_train):
????????????os.mkdir(path_train)
????????if?not?os.path.lexists(path_label):
????????????os.mkdir(path_label)
????????train_imgs?=?glob.glob(path_merge?+?"/*."?+?"png")??#?所有訓練圖像
????????savedir?=?path_train???#?保存訓練集的路徑
????????if?not?os.path.lexists(savedir):
????????????os.mkdir(savedir)
????????savedir?=?path_label??#?保存label的路徑
????????if?not?os.path.lexists(savedir):
????????????os.mkdir(savedir)
????????for?imgname?in?train_imgs:??#?rindex("/")?是返回'/'在字符串中最后一次出現的索引
????????????midname?=?imgname[imgname.rindex("/")?+?1:imgname.rindex("."?+?"png")]??#?獲得文件名(不包含后綴)
????????????#print("midname:",midname)
????????????img?=?cv2.imread(imgname)??#?讀入訓練圖像
????????????img_train?=?img[:,?:,?2]??#?訓練集是第2個通道,?label是第0個通道
????????????img_label?=?img[:,?:,?0]
????????????newname=midname.split('\')[1]
????????????#print("new:",new)
????????????cv2.imwrite(path_train?+?"/"??+?newname?+?"_train"?+?"."?+?"png",?img_train)??#?保存訓練圖像和label
????????????print(path_train?+?"/"??+?"/"?+?newname?+?"_train"?+?"."?+?"png")
????????????cv2.imwrite(path_label?+?"/"?+?newname?+?"_label"?+?"."?+?"png",?img_label)
????????????print(path_label?+?"/"??+?"/"?+?newname?+?"_label"?+?"."?+?"png")
代碼部分不做詳述了,和之前組合的形式差不多,著重說下這里,是自己不懂的部分:
#?獲得文件名(不包含后綴)#?rindex("/")?是返回'/'在字符串中最后一次出現的索引
midname?=?imgname[imgname.rindex("/")?+?1:imgname.rindex("."?+?"png")]??
Python rindex() 返回子字符串 str 在字符串中最后出現的位置,如果沒有匹配的字符串會報異常,你可以指定可選參數[beg:end]設置查找的區間。
舉個栗子:
import?globpath_merge?=?"../one/aug_merge"??#?合并增強之后的圖像
print("imgname:",path_merge)
print(path_merge.rindex("/"))
打印的結果
現在,把上文中的一段專門來看下打印結果import?glob
path_merge?=?"../one/aug_merge"??#?合并增強之后的圖像
train_imgs?=?glob.glob(path_merge?+?"/*."?+?"png")??#?所有訓練圖像
for?imgname?in?train_imgs:??#?rindex("/")?是返回'/'在字符串中最后一次出現的索引
????print("imgname:",imgname)
????print("imgname.rindex:",imgname.rindex("."?+?"png"))
????print(imgname.rindex("/"))
????midname?=?imgname[imgname.rindex("/")?+?1:imgname.rindex("."?+?"png")]??#?獲得文件名(不包含后綴)
????print("midname===",midname)
????print("*"*20)
截取圖像地址
最后,看下拆分后的圖片保存的結果吧!!!
aug_train_img
aug_train_label
這里特意說下,圖像的數量是自己設置的,在這里,imgnum數量,決定了對單幅圖像增強的數量。(如果你需要對其中增強的多一些,就把這塊給修改下)
?def?do_augmentate(self,?img,?save_to_dir,?save_prefix,?batch_size=1,?save_format='png',?imgnum=30):
四.圖像增強之批處理
這塊的內容,不想做太多的解釋了,只是由單幅圖像的讀取,改為對文件夾內所有圖片的讀取。
但是,會把結果圖片這里放一下,具體的代碼部分,歡迎去Github詳閱,地址:https://github.com/QianLingjun/Keras_image_aug。批處理部分train_label,14是文件名
文本轉載自公眾號:小白算法,作者Q.IAN。小白算法專注于學生和初從業者零碎時間充電,涉及基礎算法、計算機視覺(CV)、人工智能(AI)圖像領域。包含Opencv、C++、Python、TensorFlow、Keras語言庫框架等。只關注技術,做人人都能懂的知識公眾平臺。
互動主題:即將50天了,別gg了噢,認真讀完后用自己的語言給數據增強下個定義呀!來看看你有沒有好好學習哈~
這是一個小騷包,長按二維碼即可關注
總結
以上是生活随笔為你收集整理的深度学习搞CV?图像数据不足咋办?看这里!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 干货 | 机器学习入门方法和资料合集
- 下一篇: 中科院大牛带你玩转Python数据分析,