目錄
一.大殺氣之keras ImageDataGenerator
三.最後的拆分分別保存train_img和train_label
今天就來一招搞定數據增強(data_Augmentation),讓你在機器學習/深度學習圖像處理的路上,從此不再爲數據不夠而發愁。且來看圖片從250張>>>>任意張的華麗增強,每一張都與衆不同。
開始之前呢,我們先把這件大事給細分下,一步一步的來:
首先,圖像讀取,需要對文件夾操作;
然後,增強圖像(重點,重點,重點);
最後,保存圖像。
來看下此次任務中,待增強的圖像和標籤,主要是爲了做圖像分割做圖像準備。這個圖像懂的應該能看出來,這是一個嬰兒頭圍的醫學圖像,現實場景意義很強。上圖(以3張圖爲例):
train_img
train_label
成雙成對,這樣在後續的文件讀取中會比較的方便(大神可以自己改改,練練動手能力)
那動手吧!!!
一.大殺氣之keras ImageDataGenerator
from keras.preprocessing.image import ImageDataGenerator
ImageDataGenerator()是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_img
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改變通道排布方式
這裏,且看單幅圖像的增強代碼(建議去下載仔細看,往後看,有方式):
__author__ = "lingjun"
# E-mail: [email protected]
# 微信公衆號:小白CV
import os
from 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_image
img_t = load_img("../one/img/0.png")
img_l = load_img("../one/label/0.png")
2.因爲要講上述img_t和img_l進行合併,採用矩陣形式進行操作,這裏將讀取到的圖像轉換爲矩陣形式;
# img_to_array
x_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,同時保存在兩個文件夾內,一一對應。
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")
代碼部分不做詳述了,和之前組合的形式差不多,着重說下這裏,是自己不懂的部分:
# 獲得文件名(不包含後綴)
# rindex("/") 是返回'/'在字符串中最後一次出現的索引
midname = imgname[imgname.rindex("/") + 1:imgname.rindex("." + "png")]
Python rindex() 返回子字符串 str 在字符串中最後出現的位置,如果沒有匹配的字符串會報異常,你可以指定可選參數[beg:end]設置查找的區間。
舉個栗子:
import glob
path_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,或者關注微信公衆號:小白算法,回覆關鍵字:Keras_image_aug。歡迎你的光臨哦。
批處理部分train_img,2是文件名
批處理部分train_label,14是文件名
最後,歡迎關注“小白CV”公衆號,長按關注哦。在文章的最後,再重複一次。歡迎去Github詳閱,地址:https://github.com/QianLingjun/Keras_image_aug,或者關注微信公衆號:小白CV,回覆關鍵字:Keras_image_aug。想獲得更多福利,關注公衆號後續更新。
五、最後,補充單文件夾圖像的數據增強(法一)
__author__ = "lingjun"
# E-mail: [email protected]
# 微信公衆號:小白CV
from keras.preprocessing.image import ImageDataGenerator
path = 'D:/image' # 類別子文件夾的上一級
dst_path ='D:/image_gen'
# 圖片生成器
datagen = ImageDataGenerator(
rotation_range=5,
width_shift_range=0.02,
height_shift_range=0.02,
shear_range=0.02,
horizontal_flip=True,
vertical_flip=True
)
gen = datagen.flow_from_directory(
path,
target_size=(512, 512),
batch_size=30,
save_to_dir=dst_path, # 生成後的圖像保存路徑
save_prefix='xx',
save_format='jpg')
for i in range(15):
gen.next()
注意:待增強的圖像放在image文件夾下的子文件夾下,例如,待增強圖片在文件夾flower內,則此事flower的文件夾是image的子文件夾,這裏多進行嘗試就好。
原圖
增強後的圖像
同時呢,自己需要哪些變換,可以自行對ImageDataGenerator內容就行查詢修改,這裏不贅述;
六、基於Augmentor的數據增強
簡單粗暴,原圖是這樣的:
簡單的幾句代碼,就能夠實現許許多多的增強結果
import Augmentor
# 待增強圖像放在test文件夾下即可
# 會自動的創建output文件,用於保存增強後的圖像
p=Augmentor.Pipeline("./test")
# 增強的方式,逆時針隨機旋轉90度(隨機概率可自行設定)
p.rotate90(probability=0.5)
p.skew_corner(probability=0.7,magnitude=1)
# 增強的個數
p.sample(20)
來看下增強後的結果,代碼中的註釋部分很詳細,主要是做了這幾件事情:
- 導入庫,確定待增強圖像所在的位置
- 確定增強的方式
- 增強的個數
更加多樣的增強方式和數量可以自行增加。(美女都被拉變形了,罪惡啊罪惡)
之後也會持續的關注一些圖像增強的第三方好庫,也會都更新到這裏,持續關注,收藏哦
小白CV:公衆號旨在專注CV(計算機視覺)、AI(人工智能)領域相關技術,文章內容主要圍繞C++、Python編程技術,機器學習(ML)、深度學習(DL)、OpenCV等圖像處理技術,深度發掘技術要點,記錄學習工作中常用的操作,做你學習工作的問題小助手。只關注技術,做CV領域專業的知識分享平臺。
————————————————