0%

目标检测篇:数据预处理

反正跑数据也要等待,不如来写几篇博客,这也是我第一次参加这样类型的比赛,做一下相关经验的总结。在天池的2021广东工业智造创新大赛,智能算法赛:瓷砖表面瑕疵质检中,提供了基础的 json 数据和文件,这里介绍三种预处理方式:

  1. 普通数据制作 COCO 数据集
  2. 普通数据制作 mask 数据
  3. 因图片尺寸过大显存溢出,所以需要切割图片使得切割图片包含目标区域
  4. 自适应切割
  5. 类别平衡处理

题目中,提供的 json 格式文件如下,即使在文件名相同的情况下,也是一个目标对应一条 json 数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"name": "235_2_t20201127123021723_CAM2.jpg",
"image_height": 6000,
"image_width": 8192,
"category": 5,
"bbox": [
1655.06,
1094.04,
1663.06,
1102.04
]
},
{
"name": "235_2_t20201127123021723_CAM2.jpg",
"image_height": 6000,
"image_width": 8192,
"category": 5,
"bbox": [
1909.06,
1379.04,
1920.06,
1388.04
]
},

制作 COCO 数据集

COCO 数据集格式可以参考这里。在真实训练的过程中,info, version, licenses等信息没啥卵用,可以直接设为 None

对于 images 字段,由『图片名、图片ID、图片宽高』组成,图片宽高和图片名可以直接获取。所以需要为每一个图片设置 ID,图片名相同,则 ID 名相同且唯一,这个用 python 的字典不难实现。

对于 annotations 字段,由『segmentation, area, iscrowd, image_id, bbox, category_id, id』组成。其中,这里的 id 是盒子的 id,全局唯一,遍历数据集的时候设置自增变量便可实现。

  • segmentation 表示分割的多边形,如果是矩形就写顺时针切割:x1, y1, x2, y1, x2, y2, x1, y2;如果不是矩形,题中的数据会提供的,自己获取即可;
  • area 表示面积,矩形的话就长乘宽;
  • iscrowd 为 1 时,表示用起始像素加一段其它像素来框住 mask 区域;为 0 时,表示 segmentation 为多边形;
  • image_id 表示这个字段对应哪个图片;
  • bbox 表示盒子区域;
  • category_id 表示这个目标区域的类别;

获取起来也不是很难的样子,一次 for 循环就可以搞定。代码

制作 mask 数据集

mask 数据格式可以参考我的上篇文章。这里,因为一张图片里面有好几个目标。所以,先用字典按照 name 排序,再用 groupby 进行分组,使得一张图片下面能包括所有的目标区域。新json文件的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
"name": "197_100_t20201119093903298_CAM1.jpg",
"image_height": 6000,
"image_width": 8192,
"category": [
3,
3,
3,
5
],
"bbox": [
[
3485.51,
5037.54,
3492.51,
5046.54
],
[
4225.65,
1587.67,
4233.65,
1597.67
],
[
4362.51,
4815.54,
4370.51,
4823.54
],
[
1931.72,
3621.12,
1960.72,
3725.12
]
]
},

遍历得到的新字典(注意:groupby 后得到的字典只能迭代一次)。这里为了节省内存,不打开原始图片文件,按照 json 文件提供生成背景为黑色、尺寸为原图像尺寸的图片,在对图片的每个目标区域生成掩码颜色,把掩码颜色贴到背景图片在保存即可。代码

mask 颜色注意事项

这里需要注意的是,如果要用颜色来区分类别(取决于训练数据的代码如何写),一定不能写成这样。因为读取图片后,想要根据目标的数量来设置训练使用的 label,此时用np.unique()统计类别数量时,所有类别的数值都一样,便无法获取准确类别。

1
2
3
4
5
{
'1': '#FF0000',
'2': '#00FF00',
'3': '#0000FF',
}

可以写成这样,总之保证颜色不一致就可以了。

1
2
3
4
5
{
'1': '#110000',
'2': '#002200',
'3': '#000033',
}

切割图片

这个是最头疼的一步。假设我们要切割 512 X 512 的图片,使切割图片包含目标区域。FasrerRCNN等主流目标检测工具有负样本的机制,所以不用切割无关区域。初试的裁剪想法是,随机生成一个点的坐标,这个点是左上角的顶点,这个顶点的坐标加上 512 后能覆盖目标区域。点的坐标由两部分组成,分别是 lefttop。在生成lefttop时加入限制,保证裁剪区域会覆盖目标区域的像素点。但切割中仍然会有很多问题存在,溢出、损坏区域大于 512 等,如下图所示:

所以总结后的裁剪逻辑:

  1. 先处理损坏区域大于 512 的情况
  2. 处理顶点左侧溢出边界
  3. 若不满足以上情况,随机生成顶点的横坐标
  4. 处理顶点下侧溢出边界
  5. 若不满足以上情况,随机生成顶点的纵坐标
  6. 如果横向裁剪溢出右边界,那么缩小横坐标
  7. 如果竖向裁剪溢出下边界,那么缩小纵坐标
  8. 判断裁剪区域和目标区域是否有交集,没有交集的情况下,移动坐标点

裁剪完毕后,需要对裁剪区域进行检验,防止裁剪失败。这里就是用np.unique(array)来获取裁剪区域中的所有像素点,判断返回值的长度是否大于1来判断是否含有目标区域。因为背景元素肯定占一个元素,所以不能用大于0来判断。代码

自适应切割

图片的大小大约为 6000X8000,是不小存在了。在处理图片的时候我们发现,有的坏点(目标区域)很小,只有 5X5 的规模,而有的目标区域很大,有 512X512的规模。除了网络要就加入自适应机制外,我们在图像预处理部分也加入了相关处理。

首先定位到原始的盒子B1,用随机数生成一个更大的B2框,B2框在允许的情况下是 B1 的 10 倍左右(这里用随机数实现)。最后裁剪B2框,裁剪后将裁剪的图片设置为统一大小,也就是所有的目标区域在512尺寸的图片中大小都会一致。防止了特别大和特别小的极端对立,也省去了在 FasterRCNNAncthor 数量和规模的设置,训练和推理更加迅速。在切割的时候,把原始盒子B1的位置重新定位下,保存到新的 json,预处理就做完了。代码

类别平衡处理

在训练集中,我们发现5号类有8886个样本,6号类只有331个样本。可能是由于制作工艺的原因,6号类对应的损坏很难出现,但容易出现5号类对应的损坏。为了平衡样本,直接采取了一种简单粗暴的解决方案:

  • 每个类共需要 2000 张图片;
  • 对于样本数量大于 2000 的类来说,就随机选择;
  • 对于样本数量低于 2000 的类来说,就做数据增强,翻转或每个盒子多裁剪几张都是可以的

最后,每个类都有 2000 张图片,这样样本就均衡了许多。

代码

完整项目代码:
https://github.com/XDU-Bigbing/Simple-Key-Point-Detection/tree/main/code

经验总结

参加比赛的时候距离比赛开始过去半个月了,and实验室一直有破事,比赛打的断断续续,没有弃赛胜似弃赛,这次就当积累经验和踩坑了,准确率23%,排名460/4700。生命不息,踩坑无数,记录参赛经验毕竟纯做论文得不到更加贴近实际的经验

  1. 写完代码后要检查,不要怕麻烦,不要等到最后执行完采取看结果,或者执行小规模的代码,发现错误后可能两个小时已经浪费了,这一点上我的确吃了不少亏;
  2. 数据与代码分离,不要将代码和数据放在一个文件夹。虽然读取数据方便,可万一认为当前的模型不好使想删除时,一不小心直接 rm -rf /* 了,连数据一起删没了。数据应该放在单独文件夹,不在任何代码的文件夹下。这一点在使用服务器进行炼丹的时候要尤为注意;
  3. 写出通用性强的代码,做出一次结果后,很可能不满意需要再次调整数据和模型,比如额外增加功能或选择判断。需求可能会一直在变,所以代码不要一次性写死,要写的更加灵活和可维护。当然这需要一些程序设计经验,只可意会不可言传。这里只是推荐 argparse 这个库,不用一次次去程序里面改参数了;
  4. 模型大体上都一样,学懂理论后,先找个别人实现好的直接套用观察结果就好实力硬可以自己重头写;其他的印象不是很深刻了,只要python功底强大,还是能应付各种场景需求和读懂他人源代码的。

参考

  1. COCO 数据集格式:https://blog.yuxinzhao.top/coco-dataset-format/
  2. segmentation写法:http://www.xyu.ink/3612.html
  3. iscrowd解释:https://github.com/cocodataset/cocoapi/issues/184
  4. PIL缩放图像注释事项:https://jdhao.github.io/2020/11/18/pillow_image_resize_pitfall/
感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章