深度网络图像分割通俗指南

简介: 本文介绍如何使用当前最先进的深度学习方法来区分图像中的前景与背景、家具与非家具,并从一张照片中提取出椅子。

更多深度文章,请关注云计算频道:https://yq.aliyun.com/cloud


1.png

给定一张包含家具的照片,你可以让程序自动将家具与背景分开吗?

在这篇文章中,我将介绍如何使用当前最先进的深度学习来尝试解决这个问题。我不是机器学习的专家,所以我希望这个帖子对于其他希望使用这个强大新工具的非专家们有一定的帮助作用。

这个问题称为分割。也就是说,从这张图开始:

2.png

到这张图:

3.png

我们可以将这个遮罩应用到源图像上,获得没有背景的椅子。我们将使用一些工具来简化这个工作:

keras - 一个非常棒的用于创建神经网络的库。 Keras是像Tensorflow这样的较低级别库的前端,它能为用户处理构建神经网络过程中存在的大量繁琐的细节。。

U-Net - 用于图像分割的神经网络架构。 U-Net最初被设计用于生物医学图像分割(例如,在CT扫描中识别肺结节),但它也可用于分割常规2D图像。在下文将看到,即使没有大数据集,U-Net的强大功能也能让你大吃一惊。

Brine - 一个数据集管理器,可以利用该管理器轻松地共享和管理图像数据集。构建模型最令人讨厌的部分就是获取和选择用于训练模型的数据集。我创建了brine来轻松共享数据集,使之能应用在PyTorch/Keras模型上。我们将使用它来下载数据集并将其与Keras进行交互。

一个Github代码库 - Carvana图像遮罩挑战赛是Kaggle的一项赛事,它提出了类似的问题:将汽车从背景中扣出来。人们经常在Kaggle比赛中分享他们的解决方案,而在这个代码库中,有人分享了一个使用Keras和U-Net的解决方案。我们的目标是利用这个解决方案来解决我们当前这个家具分割问题。

一个数据集 - 这是一个朋友提供的数据集。请注意,它非常的小,只包含了97张椅子和相应的遮罩。一般来说我不会指望通过这么少的数据来做很多的工作(Carvana挑战赛中提供了数千个样本),但是让我们来看看最终到底可以做到何种程度吧。

代码

这里有一个jupyter笔记,其中包含了建立模型的所有代码。我将重点介绍其中最重要的部分,并解释它的原理。

第一步是安装数据集。由于它托管在Brine上,所以可以用一个简单的命令来实现:brine install rohan/chairs-with-masks

下一步是加载数据集。可以通过Brine的load_dataset函数来执行此操作:chairs = brine.load_dataset('rohan/chairs-with-masks')。该数据集包含了97个样本,每个样本是图像及其遮罩。遮罩是一个只有两种颜色的图像,蓝色代表背景,红色代表前景。

数据集加载了,现在来加载U-Net网络。把“Kaggle-Carvana-Image-Masking-Challenge”代码库中的model目录复制下来。导入这个网络,执行model = unet.get_unet_256() 。感谢petrosgk的工作,只需调用这一个函数即可返回一个Keras内置的U-Net网络。 Keras提供了model.summary()方法来查看网络的结构,虽然从中可以看到大量的信息,但最重要的是第一个和最后一个,它告诉了我们网络期望的输入和输出的形状。

我们可以看到输入的形状是(None, 256, 256, 3),输出的形状是(None, 256, 256, 1)。元组的第一个元素是批量的大小,所以我们现在可以忽略它。这告诉我们,网络期望的输入是一批256x256的三通道图像,并将输出一批256x256个单通道遮罩。我们的遮罩也需要匹配这个形状。

下一步是准备样本,使之与网络一起使用。我们将为训练数据定义一个处理函数,在样本传给网络之前需应用于每个样本。

def fix_mask(mask):
    mask[mask < 100] = 0.0
    mask[mask >= 100] = 255.0

def train_process(sample):
    img, mask = sample
    img = img[:,:,:3]
    mask = mask[:, :, :3]
    mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
    fix_mask(mask)
    img = cv2.resize(img, SIZE)
    mask = cv2.resize(mask, SIZE)
    img = randomHueSaturationValue(img,
                                   hue_shift_limit=(-50, 50),
                                   sat_shift_limit=(0, 0),
                                   val_shift_limit=(-15, 15))
    img, mask = randomShiftScaleRotate(img, mask,
                                       shift_limit=(-0.062, 0.062),
                                       scale_limit=(-0.1, 0.1),
                                       rotate_limit=(-20, 20))
    img, mask = randomHorizontalFlip(img, mask)
    fix_mask(mask)
    img = img/255.
    mask = mask/255.
    mask = np.expand_dims(mask, axis=2)
    return (img, mask)

这里做了很多事情,我会一步一步进行解释。样本作为一个元组被传递进去,所以首先要进行解包。接下来的两行使用numpy切片来确保图像只有3个通道,如果有第四个alpha通道,则忽略。然后,使用cv2(OpenCV的python绑定)将遮罩转换为灰度图,这样,我们现在就有了一个单通道遮罩,这是网络所期望的。这里没有为两种颜色使用两个随机的灰度数字,而是强制使用fix_mask函数将掩码设置为0和255以表示背景和前景。然后,我们将图像和掩码的大小调整为256x256,以匹配网络指定的大小。

由于数据集较小,因此我们将使用数据扩充。数据扩充是指在训练期间在保留原始信息的基础上随机修改图像,人为地生成更多的数据。例如,旋转5度的椅子仍然是椅子,所以网络应该能够正确地识别出来。在代码中,我们使用petrosgk的Carvana示例中的3个函数来随机改变图像的色相、饱和度和值,并随机旋转和翻转图像。如果旋转或翻转了图像,则必须对遮罩执行相同的操作,以使遮罩与原始图像保持一致。

最后,我们通过将所有像素值除以255来对数据进行归一化操作,这样,所有的值都在0和1之间了。如果在此时打印image.shape,结果是256x256x3,这正是网络所需要的。尽管mask.shape是256x256,但网络是256x256x1,所以我们使用np.expand_dims来让遮罩匹配这个形状。最后,返回新的图像和遮罩。

在开始训练网络之前,还需要用一些样本进行验证。验证集不能用于训练,只能用于检查模型的性能。我们可以使用Brine的create_folds来创建:

validation_fold, train_fold = chairs.create_folds((20,))

我们将样本集中的20个图像作为验证集,剩下的77个图像作为训练集。

我们还定义了用于样本验证的处理函数,这与用于训练的处理函数非常相似,除了在验证时不使用数据扩充之外。

最后,Brine返回一个可用于训练和校验集的图片生成器:

train_generator = train_fold.to_keras(
    'image',
    'mask',
    batch_size=BATCH_SIZE,
    shuffle=True, 
    processing_function=train_process)

validation_generator = validation_fold.to_keras(
    'image',
    'mask',
    batch_size=BATCH_SIZE,
    shuffle=False,
    processing_function=validation_process)

这些生成器将返回样品的批处理,以及之前定义的处理函数。它们可以被直接传给Keras的fit_generator方法来训练模型。

现在,准备开始训练模型了:

callbacks = [EarlyStopping(monitor='val_loss',
                           patience=8,
                           verbose=1,
                           min_delta=1e-4),
             ReduceLROnPlateau(monitor='val_loss',
                               factor=0.1,
                               patience=4,
                               verbose=1,
                               epsilon=1e-4),
             ModelCheckpoint(monitor='val_loss',
                             filepath='weights/best_weights.hdf5',
                             save_best_only=True,
                             save_weights_only=True)]

这些回调会改变Keras的训练过程。 EarlyStopping将在验证损失的改进停止后停止训练,ReduceLROnPlateau将降低学习率,ModelCheckpoint将对在验证集上表现最佳的模型版本进行保存。

最后,可以开始训练了:

epochs=100
model.fit_generator(generator=train_generator,
                    steps_per_epoch=train_generator.steps_per_epoch(),
                    epochs=epochs,
                    callbacks=callbacks,
                    validation_data=validation_generator,
                    validation_steps=validation_generator.steps_per_epoch())

Keras开始训练模型了,它将在数据集上运行多次(由于数据增加和混洗,每次运行的结果会略有不同),并输出训练和验证集的损失和DICE分数。在某些时候,运行会停止,可能是因为EarlyStopping回调,或是因为达到了100次迭代。

好了,就这些!我们训练了一个U-Net网络,并尝试在图像中把椅子分割出来。让我们来看看它的实际表现如何吧。

生成预测

要产生一些预测,首先使用model.load_weights('weights/best_weights.hdf5')来加载最佳模型的权重,然后在验证集中的某些图像上使用模型的predict方法:

def predict_one():
    image_batch, mask_batch = next(validation_generator)
    predicted_mask_batch = model.predict(image_batch)
    image = image_batch[0]
    predicted_mask = predicted_mask_batch[0].reshape(SIZE)
    plt.imshow(image)
    plt.imshow(predicted_mask, alpha=0.6)

以下是一些样本的结果:

4.png
在原始图像上覆盖了网络预测出来的遮罩。颜色越黄,则说明网络对该像素的预测具有更高的置信度

虽然这对于生产系统来说还不够好,但网络已经学到了有关前景与背景、椅子与非椅子的一些内容。我个人觉得这个网络在没有进行预先训练,并且只有77张图片的情况下表现得已经非常不错了。如果有更大的数据集,网络的准确性可能会大大提高,或许就能用于生产系统了。

文章原标题《A Non-Expert’s Guide to Image Segmentation Using Deep Neural Nets》,作者:Rohan Relan,译者:夏天,审校:主题曲。

文章为简译,更为详细的内容请查看原文

本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。

相关文章
|
3月前
|
机器学习/深度学习 自然语言处理 算法
【模式识别】探秘判别奥秘:Fisher线性判别算法的解密与实战
【模式识别】探秘判别奥秘:Fisher线性判别算法的解密与实战
72 0
|
2月前
|
机器学习/深度学习 开发者
论文介绍:基于扩散神经网络生成的时空少样本学习
【2月更文挑战第28天】论文介绍:基于扩散神经网络生成的时空少样本学习
17 1
论文介绍:基于扩散神经网络生成的时空少样本学习
|
5月前
|
机器学习/深度学习 算法 计算机视觉
详细介绍CNN卷积层的原理、结构和应用,并探讨其在图像处理和计算机视觉任务中的重要性
详细介绍CNN卷积层的原理、结构和应用,并探讨其在图像处理和计算机视觉任务中的重要性
176 0
详细介绍CNN卷积层的原理、结构和应用,并探讨其在图像处理和计算机视觉任务中的重要性
|
9月前
|
机器学习/深度学习 编解码 索引
神经网络风格化过程的特征控制
翻译:《Controlling Perceptual Factors in Neural Style Transfer》
|
10月前
|
算法 计算机视觉 Python
计算机视觉实验:边缘提取与特征检测
计算机视觉实验:边缘提取与特征检测
|
12月前
|
编解码 自然语言处理 语音技术
概率图模型在真实世界中的应用
概率图模型有许多不同的实际应用。 为了激起大家对概率图模型的兴趣,也为了让大家能够对概率图模型有感性的认知,本章会分享概率图模型的诸多实际应用案例。
101 0
概率图模型在真实世界中的应用
|
12月前
|
机器学习/深度学习 人工智能 监控
【Pytorch神经网络理论篇】 35 GaitSet模型:步态识别思路+水平金字塔池化+三元损失
步态特征的距离匹配,对人在多拍摄角度、多行走条件下进行特征提取,得到基于个体的步态特征,再用该特征与其他个体进行比较,从而识别出该个体的具体身份。
255 0
|
机器学习/深度学习 传感器 算法
基于贝叶斯优化卷积神经网络结合长短记忆CNN-LSTM混合神经网络实现数据回归预测附Matlab代码
基于贝叶斯优化卷积神经网络结合长短记忆CNN-LSTM混合神经网络实现数据回归预测附Matlab代码
|
机器学习/深度学习 存储 算法
表情识别FER | 局部特征学习和Handcrafted特征结合
作者的BOVW模型,分测试和训练两个阶段。在训练阶段,作者对所有训练图片,提取了稠密SIFT特征,然后用K-mean聚类的方式,量化这些描述子,成为visual word(VW),视觉词汇,个人认为主要是针对bag这种投票集成算法,针对视觉图像定义的。这些VW存储在k-d树形成的随机森林里以减少搜索代价。在建立好词汇表后,训练和测试等价,意思就是训练和测试没有之前离线分类那种明显的区分了,后面有用到KNN,所以,训练和测试没有明显区分。
表情识别FER | 局部特征学习和Handcrafted特征结合
|
机器学习/深度学习 传感器 资源调度
【图像分割】基于局部高斯分布驱动的主动活动轮廓算法实现图像分割附matlab代码和论文
【图像分割】基于局部高斯分布驱动的主动活动轮廓算法实现图像分割附matlab代码和论文