什么是图像分割问题呢? 简单的来讲就是给一张图像,检测是用框出框出物体,而图像分割分出一个物体的准确轮廓。也这样考虑,给出一张图像 I,这个问题就是求一个函数,从I映射到Mask。至于怎么求这个函数有多种方法。我们可以看到这个图,左边是给出图像,可以看到人和摩托车,右边是分割结果。
求这个函数有很多方法,但是第一次将深度学习结合起来的是这篇文章全卷积网络(FCN),利用深度学习求这个函数。在此之前深度学习一般用在分类和检测问题上。由于用到CNN,所以最后提取的特征的尺度是变小的。和我们要求的函数不一样,我们要求的函数是输入多大,输出有多大。为了让CNN提取出来的尺度能到原图大小,FCN网络利用上采样和反卷积到原图像大小。然后做像素级的分类。可以看图二,输入原图,经过VGG16网络,得到特征map,然后将特征map上采样回去。再将预测结果和ground truth每个像素一一对应分类,做像素级别分类。也就是说将分割问题变成分类问题,而分类问题正好是深度学习的强项。如果只将特征map直接上采样或者反卷积,明显会丢失很多信息。
FCN采取解决方法是将pool4、pool3、和特征map融合起来,由于pool3、pool4、特征map大小尺寸是不一样的,所以融合应该前上采样到同一尺寸。这里的融合是拼接在一起,不是对应元素相加。由于网络中只有卷积没有全连接,所以这个网络又叫全卷积网络。
特征提取部分,每经过一个池化层就一个尺度,包括原图尺度一共有5个尺度。
上采样部分,每上采样一次,就和特征提取部分对应的通道数相同尺度融合,但是融合之前要将其crop。这里的融合也是拼接。
一个大神用keras进行图像分割的github实例
最好的是这个数据集很小啊哈哈,30张512*512的训练图像
一个试验者的经验
一个和我配置一样的人的分割实例(数据同)
现在在尝试上面的东西之前,我先瞧瞧学长的代码
model.py 里面定义了u-net模型,第一个参数是pretrained_weights(预设权重值)
main.py 模型训练的时候用到了一个陌生的fit_generator函数训练,
history = model.fit_generator(myGene, steps_per_epoch = 5000,epochs=40, validation_data=valGene,validation_steps=5000,callbac
在这个之前还有一些奇奇怪怪的文件操作
但是有些文章也没有用这个,比如这个
#对模型进行训练和保存
model.fit(
x = im_array,
y = la_array,
batch_size = 10, #batch_size代表每次从im_array中的n个图片中选10个来进行训练
epochs = 8, #操作8次
validation_split = 0.2, #取训练集中的0.2作为验证集
shuffle = True
)
model.save("model_v1.h5") #保存模型
但它前面做了预处理
def load_png_files(image_path,start,end): # 加载图片成numpy的list
im_array = []
for i in range(start,end+1):
im = Image.open(os.path.join(image_path,str(i)+'.png'))
tmp_array = np.array(im) #图片转numpy数组
tmp_array = tmp_array[np.newaxis,:,:] #numpy数组添加一维,为了把二维图片转成三维图片集
if len(im_array) == 0:
im_array = tmp_array
else:
im_array = np.concatenate((im_array,tmp_array),axis=0) #将新的图片加入到之前的图片集
return im_array
im_array = im_array[:,:,:,np.newaxis]#给代码增加了一维的通道
model = Unet('resnet34', input_shape = (512, 512, 1), encoder_weights = None)#1代表通道数 model.compile('Adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
#预编译模型
效果:数据集较少, 数据未进行处理等问题, 生成的图像效果不佳
比较两种方法区别:
model.fit(trainX, trainY, batch_size=32, epochs=50)
下一个函数:fit_generator
简易代码如下
认识生成器:
由于训练的时候数据比较大, 直接加载进入内存可能会超内存, 所以需要采用生成器, 其原理是每次返回一定数量的数据; 比如我model.fit的时候, batch_size是10, 一般直接10张图就塞进去了。而用生成器的话,生成器每次返回2个数据, 然后model就用这2个去进行训练。重复调用5次生成器, 这样就实现了10个数据的训练.
再回看一下学长代码:
def trainGenerator(batch_size,train_path,original_dir,mask_dir,aug_dict,target_size,image_color_mode = "grayscale",aug_image_save_dir=None,aug_mask_save_dir=None,original_aug_prefix="image",mask_aug_prefix="mask",seed=1):
original_datagen = ImageDataGenerator(**aug_dict)
mask_datagen = ImageDataGenerator(**aug_dict)
aug_dict = dict(rotation_range=0.02,
width_shift_range=0.05,
height_shift_range=0.05,
shear_range=0.05,
zoom_range=0.05,
horizontal_flip=True,
fill_mode='nearest')
original_generator = original_datagen.flow_from_directory(
train_path,
classes = [original_dir],
class_mode = None,
color_mode = image_color_mode,
target_size = target_size,
batch_size = batch_size,
save_to_dir = aug_image_save_dir,
save_prefix = original_aug_prefix,
seed = seed)
mask_generator = mask_datagen.flow_from_directory(
train_path,
classes = [mask_dir],
class_mode = None,
color_mode = image_color_mode,
target_size = target_size,
batch_size = batch_size,
save_to_dir = aug_mask_save_dir,
save_prefix = mask_aug_prefix,
seed = seed)
train_generator = zip(original_generator,mask_generator)
----
myGene = trainGenerator(BATCH_SIZE, train_path, type, "GTM", aug_dict, target_size=data_size,
aug_image_save_dir=Aug_originall, aug_mask_save_dir=Aug_GTM1)#获得一个训练数据生成器
history = model.fit_generator(myGene,#训练集生成器
steps_per_epoch = 5000,epochs=40, validation_data=valGene,#验证集生成器
validation_steps=5000,callbacks=[model_checkpoint])
执行fit-generater时,original_datagen数据流返回BATCH_SIZE个经过各种变形的样本(每次取这么多),形成一个batch。重复这一过程5000(fit_generator的steps_per_epoch参数)次,一个epoch(一轮训练)结束。一个epoch所用样本batch_size乘以steps_per_epoch。当epoch=40(fit_generator的epochs参数)时,模型训练结束。
附上一篇我看不懂的数据流讲解
上面那个代码,先是初始化了一个叫original_datagen的数据生成器,对数据进行各种变形,flow_from_directory(以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据)设计了从训练图片文件夹中取数据的方式,参数讲解如下
注意 : 我们在进行数据增强的时候, 一定是图像和标签同时进行增强的!我终于知道mask是干什么的了!!实现方法: 为图像设置一个生成器, 标签设置一个生成器, 然后赋予他们同样的seed, 这样他们就会进行相同变化
贴上大神讲解代码
aug = ImageDataGenerator( #定义一个数据增强生成器
rotation_range = 0.05, # 定义旋转范围
zoom_range = 0.05, # 按比例随机缩放图像尺寸
width_shift_range = 0.05, # 图片水平偏移幅度
height_shift_range = 0.05, # 图片竖直偏移幅度
shear_range = 0.05, # 水平或垂直投影变换
horizontal_flip = True, # 水平翻转图像
fill_mode = "reflect" # 填充像素,出现在旋转或平移之后
)
我想像model.fit中一样从训练集中随机抽出一些作为验证集怎么办?
这个问题我在网上找了很久, 并没有发现相关的方法, 可能想要实现的话需要自己手写.