今天就來一招搞定數據增強(data_Augmentation),讓你在機器學習/深度學習圖像處理的路上,從此不再為數據不夠而發愁。且來看圖片從
250張>>>>任意張的華麗增強,每一張都與眾不同。開始之前呢,我們先把這件大事給細分下,一步一步的來:
首先,圖像讀取,需要對文件夾操作;
然後,增強圖像(重點,重點,重點);
最後,保存圖像。
來看下此次任務中,待增強的圖像和標籤,主要是為了做圖像分割做圖像準備。這個圖像懂的應該能看出來,這是一個嬰兒頭圍的醫學圖像,現實場景意義很強。上圖(以3張圖為例):
成雙成對,這樣在後續的文件讀取中會比較的方便(大神可以自己改改,練練動手能力)
那動手吧!!!
一.大殺氣之keras ImageDataGenerator
<code>from keras.preprocessing.image import ImageDataGenerator/<code>
ImageDataGenerator()是keras.preprocessing.image模塊中的圖片生成器,同時也可以在batch中對數據進行增強,擴充數據集大小,增強模型的泛化能力。比如進行旋轉,變形,歸一化等,它所能實現的功能且看下面的詳細部分吧。
<code>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)/<code>
這裡就以單張圖片為例,詳述下這個圖像增強大殺器的具體用法,分別以旋轉(rotation_range),長寬上平移(width_shift_range,height_shift_range)
輸入圖像:
先來看下兩者合併後的圖像:
到這裡,我們進行增強變換,演示下這裡增強部分是咋用的,且看:
(溫馨提示)
滑慢點,有GIF圖
(1)旋轉(rotation_range=1.2)
(2)寬度變換(width_shift_range=0.05)
(3)高度變換(height_shift_range=0.05)
這裡才只是演示了三個就那麼的強大,詳細,這要能增強多少圖片啊,想想都可怕,想都不敢想啊!!!
這裡是合併部分,單幅增強的大圖效果詳情看這裡:
這裡,且看單幅圖像的增強代碼(建議去下載仔細看,往後看,有方式):
<code>import osfrom keras.preprocessing.image import ImageDataGenerator,load_img,img_to_array,array_to_imgclass 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: breakif __name__=="__main__": aug=Augmentation() aug.augmentation()/<code>
這裡不做過多的解釋,打個廣告,歡迎關注:錢多多先森。對代碼中的詳細內容,我們且看第二部分
二.詳解單幅圖像增強
這裡先說下對圖像和標籤一起增強的步驟,有人該問為什麼還要標籤了。這裡針對的問題是圖像分割,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;
<code> # load_imageimg_t = load_img("../one/img/0.png")img_l = load_img("../one/label/0.png")/<code>
2.因為要講上述img_t和img_l進行合併,採用矩陣形式進行操作,這裡將讀取到的圖像轉換為矩陣形式;
<code> # img_to_arrayx_t = img_to_array(img_t) x_l = img_to_array(img_l)/<code>
3.train_img+train_label=merge.把label當做train的第三個通道
後面註釋部分,是對合並後的通道進行任意組合的形式,會出現不同的效果,如前文中三個特寫圖(具體自己可嘗試)
<code># 把label當做train的第三個通道x_t[:, :, 2] = x_l[:, :, 0] #x_t = x_t[..., [2,0,1]]#image-102,120,210/<code>
4.為了保存merge後圖像,此時該從array_to_image了,然後保存圖像文件;
<code>img_tmp = array_to_img(x_t)img_tmp.save("../one/merge/0.png") # 保存合併後的圖像/<code>
5.此時執行對merge圖像的增強操作;
開始前,既然我們要def do_augmentate(),我們先想想對一幅圖像的增強,需要些什麼:
image圖像文件;
save_to_dir保存增強後的文件夾地址;
批增強的數量。
至於別的,先看這裡
<code>flow(self, X, y, batch_size=32, shuffle=True, seed=None, save_to_dir=None, save_prefix=, save_format=png)x:樣本數據,秩應為4,在黑白圖像的情況下channel軸的值為1,在彩色圖像情況下值為3y:標籤batch_size:整數,默認32shuffle:布爾值,是否隨機打亂數據,默認為Truesave_to_dir:None或字符串,該參數能讓你將提升後的圖片保存起來,用以可視化save_prefix:字符串,保存提升後圖片時使用的前綴, 僅當設置了save_to_dir時生效save_format:"png"或"jpeg"之一,指定保存圖片的數據格式,默認"jpeg"yields:形如(x,y)的tuple,x是代表圖像數據的numpy數組.y是代表標籤的numpy數組.該迭代器無限循環.seed: 整數,隨機數種子/<code>
flow:接收numpy數組和標籤為參數,生成經過數據提升或標準化後的batch數據,並在一個無限循環中不斷的返回batch數據
6.由於flow的輸入X需要一個秩為4的數組,所以需要對他變形,加上img.shape=3
<code># 改變shape(1, 512, 512, 3)img = img.reshape((1,) + img.shape) /<code>
好了,這裡應該是對代碼部分描述的已經夠清楚了(哪裡還有不理解的,歡迎留言評論,大家一起進步哦)
三.最後的拆分分別保存train_img和train_label
話不多說,先看下拆分代碼部分,還是先說步驟:
1.讀取merge文件夾內圖片;2.按照之前組合的形式進行拆分為img_train和img_label,同時保存在兩個文件夾內,一一對應。
<code> def split_merge(self): # 讀入合併增強之後的數據(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")/<code>
代碼部分不做詳述了,和之前組合的形式差不多,著重說下這裡,是自己不懂的部分:
<code># 獲得文件名(不包含後綴)# rindex("/") 是返回/在字符串中最後一次出現的索引midname = imgname[imgname.rindex("/") + 1:imgname.rindex("." + "png")] /<code>
Python rindex() 返回子字符串 str 在字符串中最後出現的位置,如果沒有匹配的字符串會報異常,你可以指定可選參數[beg:end]設置查找的區間。
舉個栗子:
<code>import globpath_merge = "../one/aug_merge" # 合併增強之後的圖像print("imgname:",path_merge)print(path_merge.rindex("/"))/<code>
現在,把上文中的一段專門來看下打印結果
<code>import globpath_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)/<code>
最後,看下拆分後的圖片保存的結果吧!!!
這裡特意說下,圖像的數量是自己設置的,在這裡,imgnum數量,決定了對單幅圖像增強的數量。(如果你需要對其中增強的多一些,就把這塊給修改下)
<code> def do_augmentate(self, img, save_to_dir, save_prefix, batch_size=1, save_format=png, imgnum=30):/<code>
四.圖像增強之批處理
這塊的內容,不想做太多的解釋了,只是由單幅圖像的讀取,改為對文件夾內所有圖片的讀取。
但是,會把結果圖片這裡放一下,具體的代碼部分,歡迎去Github詳閱,地址:https://github.com/QianLingjun/Keras_image_aug,
最後,歡迎關注“錢多多先森”,一個關注更多AI、CV、數碼、個人理財領域知識的醫療行業從業者。關注我,一起成長。
在文章的最後,再重複一次。歡迎去Github詳閱,地址:https://github.com/QianLingjun/Keras_image_aug,碼字不易,給個免費的贊再走吧 。下期我們再見,拜
閱讀更多 錢多多先森 的文章