前言
下两篇:yolov5代码解读-网络架构、yolov5代码解读-训练 代码已上传到github,数据集和权重文件已上传到百度网盘(链接在github里),如需下载请移步:https://github.com/scc-max/yolov5-scc
yolov5在数据处理这方面已经做的较为完善了,去看看它实际是怎么写的,读懂它,再修改它。从yolov5源码中学习python,学习目标检测算法。
目录
- 前言
- 加载数据
- 设置标签缓存
- 训练时实际加载数据 __getitem__
- 马赛克增强
- 第一部分:坐标计算
- 第二部分:更新标签。
- 第三部分:图像增强。
- 图像增强
- 第一部分:构建随机仿射矩阵并变换。
- 第二部分:标签转换
- 总结
加载数据
在train中是通过creat_dataloader加载数据的
进入creat_dataloader
是通过load image and labels这个函数加载图片和标签的。
几个参数的解释,rect是矩形,表示不通过pad方式把原始数据用0填充,直接就把矩形图送进去训练,这也是为了避免pad增加一些不必要的参数。stride是降采样,这里是32.
进入load image and labels
实际走的是红色框这部分,这里可以打断点debug一下,最终f(img_files)是一个包含了图片名字的列表。
图片路径和标签路径的替换,这个真的是绝美,绝了。直接用字符串的替换方法。
设置标签缓存
先判断是否有缓存路径是否有文件,如果有就直接加载,如果检测到数据集改了就重新创建。如果路径不是文件就创建缓存。
得到的cache是一个压缩文件,是标签array和对应图像尺寸(448,448)的压缩.。因此后面又解压回来了。
训练时实际加载数据 getitem
这一步是在训练开始后才会实际的去读取读片和标签。__getitem__走一遍,处理一张图像和标签,但这个是按照batch走的,所以会循环batch_size次。
可以看到,在读完超参数之后,第一步就是做马赛克。这里mosaic的数值为1,random.random又是(0,1)之间的值,所以一定会做马赛克的。
马赛克这部分,请直接跳到‘马赛克增强’,maxup默认为False,也跳过了。
因为已经走了马赛克这跳指令了,后面的else就不走了,因为做马赛克的时候已经加载了数据了(第第一部分),然后再马赛克的时候也做过数据增强了,因此数据增强这条路也不走了。直接跳到后面。
因为走过马赛克,标签变了,这时再修改成xywh的模式并且给归一化。
这部分是做翻转操作,有上下反转和左右翻转。对图像应用的是numpy的flipud和fliplr,对标签直接相应维度求了个1-x。
这就是__getitem__最后的部分了,把numpy数据转换成pytorch类型的,再把不连续的内存转化成连续的内存,加快运行速度。
马赛克增强
进入这个load_mosaic实际看看。这个函数比较长,分成3部分来看,第一部分计算坐标,第二部分更新标签,第三部分对做好的马赛克大图做图像增强。
第一部分:坐标计算
前面两行代码就不说了,这里的yc,xc就是创建的马赛克大图的中心位置。我的图像尺寸是448,448,马赛克是将4张大图拼接到一起,因此总的长度就是896,896。如果我们要随机选个中心店,用来分割4张图片,会怎么分呢。要确定,这个点,不能太偏向一侧,否则会比例失衡。因此,它的范围最好在中间一点。这里用的是(224, 896-224)这样一个位置,保证了中心在图片(0.25,0.75)这样一个位置。
其次,indices就是随机选出另外3张图片,因为咱们的马赛克是4张图拼接到一起麻,现在一缺三。
循环里是要关注的主要内容:首先加载图片,加载后img是array数组。我们只看i==0,即左上角的情况,另外3个以此类推。第一行先创建一个固定数值,形状和img一样的数组,这里用114填充了,相当于padding。第二行计算这第一张图在大图中的坐标,其右下角的坐标就是刚才我们选出的中心坐标,其左上角的坐标就要分情况了,如果是这张图比较小,没有越界,那就中心坐标-它的宽高,如果越界了,就直接取0,超出的部分不要了。这就是为什么要用max()。第三行是小图中需要拿出来放进大图的部分的坐标,注意它和第二行不是一个坐标系的,先进算大图中刚才计算的左上角和右下角坐标之差,再用小图的宽高-它,如果差比较小,那就拿出来右下角的部分,最大,也就是和宽高一样,那样x1b,y1b就是0.
跳出循环,是一个赋值操作,即把img(小图)中坐标取出的部分赋给大图相应位置。经过这一步,坐标之外的部分还是刚才的114哦。然后计算一下pad(114)部分的宽高。
第二部分:更新标签。
做完这个马赛克后,一时标签的位置已经发生改变了,虽然形状和大小还没变。二是有些部分标签可能超出大图范围了,被截取了。这时就要做标签的更新。
先将xywh转成xyxy,这四个公式很好理解,中心坐标-宽高的一半=左上角,中心坐标+宽高的一半=右下角,*宽高是因为之前做了归一化,这一要计算行列值便于定位。padw和padh是之前114的那部分。做完后放进之前的空list中。
经过concatenate之后,维度由4变成了5,加了一个0索引。之后是做了一个剪切范围的操作。之前说有些标签可能超过大图范围了,这时就判断一下,范围是否在(0,896)之间,超出的就直接不要了。然后将剪切的结果原地返回给labels4
第三部分:图像增强。
这一部分也因人而已,有的是先做数据增强再拼接,有的是先拼接再做数据增强。但是效果应该差不多。这里进入函数看看,使用opencv做的,而不是pytorch自带的模块。
图像增强
上一步说了是先做了马赛克后做了数据增强,用opencv做的,这是马赛克处理的第三步,那具体是怎么做的呢?也是按照两个步骤来做,第一步构建随机仿射变换矩阵,对图像进行放射变换,第二部分对标签进行相应转换。
第一部分:构建随机仿射矩阵并变换。
旋转 平移 缩放等操作 都需要系数矩阵。先随机生成旋转、平移和缩放的矩阵,然后再得到一个大矩阵,再将这个矩阵作为参数送给opencv,对图像进行变换。
将上述所有矩阵相乘就是得到最终的变换矩阵M,这里提供了透视变换和仿射变换两种选择
第二部分:标签转换
图像做完变换,标签也要做相应变换,这部分实在是没搞懂。
也不贴图了。
总结
详细读了yolov5对数据的处理,就可以得出一个结论了,大多数图像处理都是用numpy的opencv完成的。虽然我可以用opencv读出来4通道的tiff数据,设置一下标志位就可以,但是后面还有不计其数的数据处理过程,它们都是基于3通道数据的,有些API只能处理3通道数据。修改的成本还是很大的。
下一部分就是网络结构了:
yolov5代码解读-网络架构