编者按:发展至今,计算机视觉已经产生了不少令人惊叹的应用,但一提到它,人们首先想到的总是人脸检测、物品识别……除去已经发展得很成熟的人脸识别技术,我们难道只能用检测室内物品来练习技巧吗?作为一个极具工程价值的领域,也许大家应当扩张技术应用的广度,把它和现实场景结合起来,真正做一些更接地气的尝试。
本文旨在构建一个自定义Mask R-CNN模型,它可以检测汽车车身的损坏区域(如下图所示)。这背后的应用理念是,购买二手车时,消费者首先会关注车身刮擦情况,有了这个模型,他们足不出户就能大致了解车子情况,避免被坑。而对于日常生活中的小事故,如果用户只需上传图片就能完成车辆破损鉴定,保险公司的索赔效率也会大幅提高。
Mask R-CNN是一个实例分割模型,它能确定图片中各个目标的位置和类别,给出像素级预测。所谓“实例分割”,指的是对场景内的每种兴趣对象进行分割,无论它们是否属于同一类别——比如模型可以从街景视频中识别车辆、人员等单个目标。下图是在COCO数据集上训练好的Mask R-CNN,如图所示,大到每一辆车,小到单根香蕉,它都能用窗口标出目标物品在画面中的像素位置。
不同于Faster R-CNN这样的经典对象检测模型,Mask R-CNN的一个特点是可以给窗口内表示对象轮廓的像素着色。可能有人会觉得这是个鸡肋功能,但它对自动驾驶汽车和机器人控制意义非凡:
着色可以帮助汽车明确道路上各目标的具体像素位置,从而避免发生碰撞;
如果机器人想抓取某个目标物品,它就需要知道位置信息(如亚马逊的无人机)。
如果只是单纯想在COCO上训练Mask R-CNN模型,最简单的方法是调用Tensorflow Object Detection API,具体内容Github都有,此处不再详谈。
在构建Mask R-CNN模型之前,我们首先来了解一下它的工作机制。
事实上,Mask R-CNN是Faster R-CNN和FCN的结合,前者负责物体检测(分类标签+窗口),后者负责确定目标轮廓。如下图所示:
它的概念很简单:对于每个目标对象,Faster R-CNN都有两个输出,一是分类标签,二是候选窗口;为了分割目标像素,我们可以在前两个输出的基础上增加第三个输出——指示对象在窗口中像素位置的二进制掩模(mask)。和前两个输出不同,这个新输出需要提取更精细的空间布局,为此,Mask R-CNN在Faster-RCNN上添加一个分支网络:Fully Convolution Networ(FCN)。
FCN是一种流行的语义分割算法,所谓语义分割,就是机器自动从图像中分割出对象区域,并识别其中的内容。该模型首先通过卷积和最大池化层把输入图像压缩到原始大小的1/32,然后在这个细粒度级别进行分类预测。最后,它再用上采样和deconvolution层把图还原成原始大小。
因此简而言之,我们可以说Mask R-CNN结合了两个网络——把Faster R-CNN和FCN纳入同一巨型架构。模型的损失函数计算的是分类、生成窗口、生成掩模的总损失。
此外,Mask R-CNN还做了一些基础改进,使其比FCN更精确,具体可以阅读论文。
GitHub:github.com/matterport/Mask_RCNN
这是一个在Python 3、Keras和TensorFlow上实现Mask R-CNN的现成资源。虽然最近TensorFlow目标检测库也更新了和Mask R-CNN相关的资源,但如果想一帆风顺地搭建模型,我们还是推荐这个,用TensorFlow太容易出bug了。
当然,这不是不鼓励大家去勇敢试错,毕竟熟悉了这些错误,我们才能更好地理解整个过程。但这个实现绝对值得收藏。
收集数据
考虑到本文只是演示,这里我们只Google了66张受损车辆图像(50张训练集,16张验证集)。如果想做个大点的数据集,建议把搜索关键词设为“damaged car painting”,用中文容易出现一大堆补漆广告,搜车祸则是大量引擎盖变形图。下面是一些图像样本
注释数据
为了构建Mask R-CNN模型,首先我们要对图像进行注释,标出其中的损坏区域。我们使用的注释工具是VGG Image Annotator — v 1.0.6,它有一个在线版本。除了常规图形,它也允许我们绘制多边形蒙版:
注释完后,记得把它们下载下来,保存为.json格式。这里是注释好的66幅图。
训练模型
完成上述步骤,现在我们就可以训练模型了。首先,把GitHub里的东西复制下来,然后加载我们的图像和注释。
class CustomDataset(utils.Dataset):
def load_custom(self, dataset_dir, subset):
"""Load a subset of the Balloon dataset.
dataset_dir: Root directory of the dataset.
subset: Subset to load: train or val
"""
# 添加类别标签,我们只有一个
self.add_class("damage", 1, "damage")
# 训练集和验证集
assert subset in ["train", "val"]
dataset_dir = os.path.join(dataset_dir, subset)
# 我们主要关心每个区域的x和y坐标
annotations1 = json.load(open(os.path.join(dataset_dir, "via_region_data.json")))
annotations = list(annotations1.values()) # don't need the dict keys
# 即使没有任何注释工具也会把图像保存在JSON中
# 跳过未注释图像。
annotations = [a for a in annotations if a['regions']]
# 添加图像
for a in annotations:
# 获取构成每个对象实例轮廓多边形点的x,y坐标
# 它们在shape_attributes里((参见上面的json格式))
polygons = [r['shape_attributes'] for r in a['regions'].values()]
# 输入图像大小后,load_mask()才能把多边形转成蒙版
image_path = os.path.join(dataset_dir, a['filename'])
image = skimage.io.imread(image_path)
height, width = image.shape[:2]
self.add_image(
"damage", ## 如果只有一类,只需在此处添加名称即可
image_id=a['filename'], # use file name as a unique image id
path=image_path,
width=width, height=height,
polygons=polygons)
整段代码在这里。我们复制了资源里的balloon.py文件,并对它做了修改。需要注意的是,这些代码只适合包含一个类的问题。
此外,你也可以用这个笔记本内容可视化给定图像上的蒙版。
如果要训练,运行以下代码:
## 如果用的是在COCO上预训练的模型
python3 custom.py train --dataset=/path/to/datasetfolder --weights=coco
## 如果是继续训练您之前训练过的模型
python3 custom.py train --dataset=/path/to/datasetfolder --weights=last
注:用一个GPU训练10个epoch需要20-30分钟。
验证模型
至于模型的权重和偏差是不是正确的,大家可以参考这个笔记本
。里面列出了许多设置,可以作为辅助检查工具。
实验结果
如图所示,模型准确标出了漆面损坏位置。当然,这并不是全部,我们在这个示例里只用了66幅图,划痕也比较明显,所以这个模型的应用性非常有限。如果数据集够大,我们完全可以期待一个能检测微小伤痕的模型,当消费者在家里观察汽车3D图时,系统能自动标出一些不显眼的痕迹,避免乘兴而去,败兴而归。
除了检测车辆划痕,你认为Mask R-CNN模型还有什么潜在应用呢?
原文地址:www.analyticsvidhya.com/blog/2018/07/building-mask-r-cnn-model-detecting-damage-cars-python/