iOS 根据路径获取图片路径 苹果根据图片识别位置_目标检测


使用 Pipcook 识别图片中的前端组件

前言

为了让大家更好地学习 Pipcook 和机器学习,我们准备了实战系列教程,会分别从前端组件识别、图片风格迁移、AI 作诗以及博客自动分类,这几个具体示例来讲解如何在我们日常开发中使用 Pipcook,如果需要了解 Pipcook 1.0,请前往我们的官方仓库

背景

您是否在前端业务中遇到过这样的场景:手中有一些图片,您想有一种自动的方式来识别这些图片这个图片里都包含哪些组件,这些组件都在图片的什么位置,属于哪种类型的组件,这种类型的任务一般在深度学习领域称为目标检测。

目标检测与识别是指从一幅场景(图片)中找出目标,包括检测(where) 和识别(what) 两个过程

这种检测是非常有用的,例如,在图片生成代码的研究里,前端代码主要就是由 div, img, span 组成的,我们可以识别图片里的形状,位图,和文本的位置,然后直接生成相应的描述代码即可。

这篇教程将会教你如何训练出一个模型来做这样一个检测任务。

场景示例

举个例子,如下图所示,这个图片包含着多个组件,包括按钮,开关,输入框等,我们想要识别出他们的位置和类型:


iOS 根据路径获取图片路径 苹果根据图片识别位置_数据集_02


对于训练好的模型来说,在输入这张图片之后,模型会输出如下的预测结果:


{
  boxes: [
    [83, 31, 146, 71],  // xmin, ymin, xmax, ymax
    [210, 48, 256, 78],
    [403, 30, 653, 72],
    [717, 41, 966, 83]
  ],
  classes: [
    0, 1, 2, 2  // class index
  ],
  scores: [
    0.95, 0.93, 0.96, 0.99 // scores
  ]
}


同时,我们会在训练的时候生成 labelmap,labelmap 是一个序号和实际类型的一个映射关系,这个的生成主要是由于现实世界我们的分类名是文本的,但是在进入模型之前,我们需要将文本转成数字。下面就是一个 labelmap:


{
  "button": 0,
  "switch": 1,
  "input": 2
}


我们对上面的预测结果做一个解释:

  • boxes:这个字段描述的是识别出来的每一个组件的位置,按照左上角和右下角的顺序展示,如 [83, 31, 146, 71],说明这个组件左上角坐标为 (83, 13), 右下角坐标为 (146, 71)
  • classes: 这个字段描述的是每一个组件的类别,结合 labelmap,我们可以看出识别出来的组件分别为按钮,开关,输入框和输入框
  • scores: 识别出来的每一个组建的置信度,置信度是模型对于自己识别出来的结果有多大的信息,一般我们会设置一个阈值,我们只取置信度大于这个阈值的结果

数据准备

当我们想要做这样一个目标检测的任务时,我们需要按照一定规范制作,收集和存储我们的数据集,当今业界主要有两种目标检测的数据集格式,分别是 Coco 数据集 和 Pascal Voc 数据集, 我们也分别提供了相应的数据收集插件来收集这两种格式的数据,下面我们以 Pascal voc 格式举例,文件目录为:


train
|- 1.jpg
|- 1.xml
|- 2.jpg
|- 2.xml
|- ...
validation
|- 1.jpg
|- 1.xml
|- 2.jpg
|- 2.xml
|- ...
test
|- 1.jpg
|- 1.xml
|- 2.jpg
|- 2.xml
|- ...


我们需要按照一定比例把我们的数据集分成训练集 (train),验证集 (validation) 和测试集 (test),其中,训练集主要用来训练模型,验证集和测试集用来评估模型。验证集主要用来在训练过程中评估模型,以方便查看模型过拟合和收敛情况,测试集是在全部训练结束之后用来对模型进行一个总体的评估的。

对于每一张图片,Pascal Voc 都指定有一个 xml 注解文件来记录这个图片里有哪些组件和每个组件的位置,一个典型的 xml 文件内容为:


<?xml version="1.0" encoding="UTF-8"?>
<annotation>
   <folder>less_selected</folder>
   <filename>0a3b6b38-fb11-451c-8a0d-b5503bc351e6.jpg</filename>
   <size>
      <width>987</width>
      <height>103</height>
   </size>
   <segmented>0</segmented>
   <object>
      <name>buttons</name>
      <pose>Unspecified</pose>
      <truncated>0</truncated>
      <difficult>0</difficult>
      <bndbox>
         <xmin>83</xmin>
         <ymin>31.90625</ymin>
         <xmax>146</xmax>
         <ymax>71.40625</ymax>
      </bndbox>
   </object>
   <object>
      <name>switch</name>
      <pose>Unspecified</pose>
      <truncated>0</truncated>
      <difficult>0</difficult>
      <bndbox>
         <xmin>210.453125</xmin>
         <ymin>48.65625</ymin>
         <xmax>256.453125</xmax>
         <ymax>78.65625</ymax>
      </bndbox>
   </object>
   <object>
      <name>input</name>
      <pose>Unspecified</pose>
      <truncated>0</truncated>
      <difficult>0</difficult>
      <bndbox>
         <xmin>403.515625</xmin>
         <ymin>30.90625</ymin>
         <xmax>653.015625</xmax>
         <ymax>72.40625</ymax>
      </bndbox>
   </object>
   <object>
      <name>input</name>
      <pose>Unspecified</pose>
      <truncated>0</truncated>
      <difficult>0</difficult>
      <bndbox>
         <xmin>717.46875</xmin>
         <ymin>41.828125</ymin>
         <xmax>966.96875</xmax>
         <ymax>83.328125</ymax>
      </bndbox>
   </object>
</annotation>


这个 xml 注解文件主要由以下几个部分组成:

  • folder / filename: 这两个字段主要定义了注解对应的图片位置和名称
  • size: 图片的宽高
  • object:
  • name: 组件的类别名
  • bndbox: 组件的位置

我们已经准备好了一个这样的数据集,您可以下载下来查看一下:下载地址

开始训练

在准备好数据集之后,我们就可以开始训练了,使用 Pipcook 可以很方便的进行目标检测的训练,您只需搭建下面这样的 pipeline,


{
  "plugins": {
    "dataCollect": {
      "package": "@pipcook/plugins-object-detection-pascalvoc-data-collect",
      "params": {
        "url": "http://ai-sample.oss-cn-hangzhou.aliyuncs.com/pipcook/datasets/component-recognition-detection/component-recognition-detection.zip"
      }
    },
    "dataAccess": {
      "package": "@pipcook/plugins-coco-data-access"
    },
    "modelDefine": {
      "package": "@pipcook/plugins-detectron-fasterrcnn-model-define"
    },
    "modelTrain": {
      "package": "@pipcook/plugins-detectron-model-train",
      "params": {
        "steps": 100000
      }
    },
    "modelEvaluate": {
      "package": "@pipcook/plugins-detectron-model-evaluate"
    }
  }
}


通过上面的插件,我们可以看到分别使用了:

  1. @pipcook/plugins-object-detection-pascalvoc-data-collect
  2. @pipcook/plugins-coco-data-access 我们现在已经下载好了数据集,我们需要将数据集接入成后续模型需要的格式,由于我们模型采用的 detectron2 框架需要 coco 数据集格式,所以我们采用此插件
  3. @pipcook/plugins-detectron-fasterrcnn-model-define
  4. @pipcook/plugins-detectron-model-train
  5. @pipcook/plugins-detectron-model-evaluate 我们使用此插件来进行模型训练效果的评估,只有提供了 test 测试集,此插件才会有效,最终给出的是各个类别的 average precision

由于目标监测模型,尤其是 rcnn 家族的模型非常大,需要在有 nvidia gpu 并且 cuda 10.2 环境预备好的机器上进行训练:


pipcook run object-detection.json --verbose --tuna


模型在训练的过程中会实时打印出每个迭代的 loss,请注意查看日志确定模型收敛情况:


[06/28 10:26:57 d2.data.build]: Distribution of instances among all 14 categories:
|   category   | #instances   |  category   | #instances   |  category  | #instances   |
|:------------:|:-------------|:-----------:|:-------------|:----------:|:-------------|
|     tags     | 3114         |    input    | 2756         |  buttons   | 3075         |
| imagesUpload | 316          |    links    | 3055         |   select   | 2861         |
|    radio     | 317          |  textarea   | 292          | datePicker | 316          |
|     rate     | 292          | rangePicker | 315          |   switch   | 303          |
|  timePicker  | 293          |  checkbox   | 293          |            |              |
|    total     | 17598        |             |              |            |              |

[06/28 10:28:32 d2.utils.events]:  iter: 0  total_loss: 4.649  loss_cls: 2.798  loss_box_reg: 0.056  loss_rpn_cls: 0.711  loss_rpn_loc: 1.084  data_time: 0.1073  lr: 0.000000  
[06/28 10:29:32 d2.utils.events]:  iter: 0  total_loss: 4.249  loss_cls: 2.198  loss_box_reg: 0.056  loss_rpn_cls: 0.711  loss_rpn_loc: 1.084  data_time: 0.1073  lr: 0.000000  
...
[06/28 12:28:32 d2.utils.events]:  iter: 100000  total_loss: 0.032 loss_cls: 0.122  loss_box_reg: 0.056  loss_rpn_cls: 0.711  loss_rpn_loc: 1.084  data_time: 0.1073  lr: 0.000000


训练完成后,会在当前目录生成 output,这是一个全新的 npm 包,那么我们首先安装依赖:


cd output
BOA_TUNA=1 npm install


安装好环境之后,我们就可以开始预测了:


const predict = require('./output');
(async () => {
  const v1 = await predict('./test.jpg');
  console.log(v1); 
  // {
  //   boxes: [
  //    [83, 31, 146, 71],  // xmin, ymin, xmax, ymax
  //     [210, 48, 256, 78],
  //     [403, 30, 653, 72],
  //     [717, 41, 966, 83]
  //   ],
  //   classes: [
  //    0, 1, 2, 2  // class index
  //   ],
  //   scores: [
  //    0.95, 0.93, 0.96, 0.99 // scores
  //   ]
  // }
})();


注意,给出的结果包含三个部分:

  • boxes: 此属性是一个数组,每个元素是另一个包含四个元素的数组,分别是 xmin, xmax, ymin, ymax
  • scores:此属性是一个数组,每个元素是对应的预测结果的置信度
  • classes:此属性是一个数组,每个元素是对应的预测出来的类别

制作自己的数据集

看完上面的描述,你是否已经迫不及待想要用目标检测解决自己的问题了呢,要想制作自己的数据集,主要有以下几步

收集图片

这一步比较好理解,要想有自己的训练数据,您需要先想办法收集到足够的训练图片,这一步,您不需要让您自己的图片有相应的标注,只需要原始的图片进行标注就好

标注

现在市面上有很多的标注工具,您可以使用这些标注工具在您原始的图片上标注出有哪些组件,每个组件的位置和类型是什么,下面我们拿 labelimg 为例,详细的介绍一下


iOS 根据路径获取图片路径 苹果根据图片识别位置_iOS 根据路径获取图片路径_03


您可以先从上面的 labelimg 官网上安装软件,然后按照以下步骤操作:

  • 按照官网的说明进行构建和启动。
  • 在菜单/文件中单击“更改默认保存的注释文件夹”
  • 点击“打开目录”
  • 点击“创建RectBox”
  • 单击并释放鼠标左键以选择一个区域来标注矩形框
  • 您可以使用鼠标右键拖动矩形框来复制或移动它

训练

在制作好上面的数据集之后,根据之前的章节中的介绍组织文件结构,之后,就可以启动 pipeline 进行训练了,赶快开始吧。

总结

读者到这里已经学会如识别一张图片中的多个前端组件了,可以适用于一些更加通用的场景了。那么在一篇,我们会介绍一个更有趣的例子,就是如何使用 Pipcook 实现图片风格迁移,比如将图片中的橘子都替换称苹果,或者将写实的照片风格替换为油画风格等。