布匹缺陷检测--笔记二--数据增强
baseline的情况
baseline的数据情况
baseline使用的数据其实只是一部分,仅用了guangdong1_round2_train2_20191004_images\defect下的1264份有缺陷的数据。normal数据和其他3个文件夹的数据都是没有用到的。
baseline使用的参数:
模型:yolov5x.pt(用了预训练权重)
划分后的训练集数量:1025(如果我没记错)
batchsize:8
image_size:640*640
epoch:200
训练时长:10个小时+
baseline的训练报告:
通过上面的图片可以发现,数据分布极其不均匀,从而导致部分缺陷的类别识别效果差、对应类别的AP值低,从而大大的影响了map。从排行榜的得分情况可以看出,map分值低是非常突出的情况,同时map得分的权重占80%。
baseline的得分
更多数据
除了我上面baseline用到的文件外,还有另外三个文件夹:
这三个文件其实是一份数据,猜测官方是为了方便我们下载(因为解压需要文件的双倍内存,就是20G文件的解压,需要40G的内存空间才能成功)。这些文件的结构其实也是这样的:
|-- defect Images #存放有瑕疵的图像数据
|-- normal Images #存放无疵点的图像数据,jpeg编码图像文件
|-- Annotations #存放属性标签标注数据
|-- README.md #对数据的详细介绍
这样子我们就可以和baseline所用到的文件进行一个合并,对应文件夹复制过去就好了,json文件只要一个,就要打开文件内容进行复制了。这里有地方需要注意,如下图:(可能表达不太好,结合自己观察应该不难理解)
数据合并后的情况
数据合并后,defect共有4372份数据,normal有895份数据。
数据是多了,但也不一定全是好事,合并后数据大小为21Gb+.
单单对defect的数据进行划分train和val就要30min:
谨慎合并啊…
对合并后的数据进一步分析
①从下图可以看出数据合并后,数据不均匀依旧是十分严重的问题。
②通过对图片数据的观察可以发现,图片的命名的前缀其实和图片的类型有关:
例如名字开头为:0818A1_ 的都是这类布匹:
经过统计可以发现共有存在缺陷的共有68类布匹。
如下图,进一步统计可以观察到布匹类型也是存在着不均匀的。
③每种布匹的缺陷种类个数情况:(图片太大了,放大看要加载好久,可以考虑下载到桌面)
通过下图可以发现不同布匹他们的缺陷种类分布区别比较大,由此可以有一个思路,就是专门对某一种布匹进行数据增强,这样子既可能对缺陷的种类进行增强,也可以对布匹种类不平衡做出调整。
使用整合后的defect数据进行进一步训练
对baseline得到的权重,进一步使用整合后的defect数据进行训练,epoch=70,训练时间=10h+
得分:
数据增强
通过分析,进行数据增强可能存在的问题:不是一张图片对应一个label,有可能某张图片对应很多种缺陷,如果想争对某类缺陷进行增强,那必须要同时对存在这种缺陷的图片的其他缺陷一块增强,增加了难度。
增强方式
由于时间关系,我最终还是以某一类缺陷为对象进行数据增强。例如:选取对存在"破洞"这种缺陷的图片,对这些图片进行数据增强,包括这些图片的其他缺陷。
增强方式有:缩放并且进行长和宽的扭曲、添加灰边、翻转、色域扭曲;这4个操作共同构成一张图片的增强。
增强效果的观察
原图:
增强后:
增强参数设置
对"沾污"不做数据增强
对[“错花”, “水印”]增强1倍
对[“花毛”, “缝头”, “缝头印”, “虫粘”, “破洞”, “褶子”, “织疵”, “漏印”, “蜡斑”, “色差”, “网折”, “其他”]增强4倍
增强后的数据统计
数据增强后,存在缺陷的图片数量共有:18174,是原来的4倍有多的数据量。
对数据增强后,对所有缺陷进行统计,如下图所示。数量的确得到了提升,但是由于沾污也会随着图片的增强而被动增加,各种缺陷类别的比例还是相差较大。
小总结
数据成功增强了,但是样本不均匀还是没有解决,一个原因是因为时间不够无法对增强的数量进行调整尝试,也有部分原因是数据使然。
比较遗憾的是没时间尝试以布匹种类为对象进行数据增强,有可能可以解决样本不均衡和布匹种类不均衡的问题。
更多的改进:除了数据增强外,我还添加了normal(无缺陷)的数据进行训练。
进行了调整之后,再次进行训练,这次的训练数据:15254 ,测试数据:3813 ,batchsize:30 ,epoch:90 ,一轮的训练时间:大约30min ,估计的总训练时间:大约45h(现在训练直接要训练到3月1日10点,真好家伙,哈哈哈)
冲一把,希望最终有个好成绩
代码
自己肝的代码,可能不太整洁和轻便,数据量也很大,这个数据增强也运行了挺长时间的。
from PIL import Image, ImageDraw
import numpy as np
from matplotlib.colors import rgb_to_hsv, hsv_to_rgb
import json
import os
def rand(a=0, b=1):
return np.random.rand()*(b-a) + a
def get_random_data(annotation_name, box_data,input_shape, random=True, max_boxes=20, jitter=.5, hue=.1, sat=1.5, val=1.5, proc_img=True):
'''random preprocessing for real-time data augmentation'''
# line = annotation_line.split()
image = Image.open(annotation_name)
iw, ih = image.size
w,h= input_shape
box = np.array(box_data)
# 对图像进行缩放并且进行长和宽的扭曲
new_ar = w/h * rand(1-jitter,1+jitter)/rand(1-jitter,1+jitter)
scale = rand(.5,1.5)
if new_ar < 1:
nh = int(scale*h)
nw = int(nh*new_ar)
else:
nw = int(scale*w)
nh = int(nw/new_ar)
image = image.resize((nw,nh), Image.BICUBIC)
# 将图像多余的部分加上灰条
dx = int(rand(0, w-nw))
dy = int(rand(0, h-nh))
new_image = Image.new('RGB', (w,h), (128,128,128))
new_image.paste(image, (dx, dy))
image = new_image
# 翻转图像
flip = rand()<.5
if flip: image = image.transpose(Image.FLIP_LEFT_RIGHT)
# 色域扭曲
hue = rand(-hue, hue)
sat = rand(1, sat) if rand()<.5 else 1/rand(1, sat)
val = rand(1, val) if rand()<.5 else 1/rand(1, val)
x = rgb_to_hsv(np.array(image)/255.)
x[..., 0] += hue
x[..., 0][x[..., 0]>1] -= 1
x[..., 0][x[..., 0]<0] += 1
x[..., 1] *= sat
x[..., 2] *= val
x[x>1] = 1
x[x<0] = 0
image_data = hsv_to_rgb(x) # numpy array, 0 to 1
# 将box进行调整
box_data = np.zeros((max_boxes,5))
if len(box)>0:
np.random.shuffle(box)
box[:, [0,2]] = box[:, [0,2]]*nw/iw + dx
box[:, [1,3]] = box[:, [1,3]]*nh/ih + dy
if flip: box[:, [0,2]] = w - box[:, [2,0]]
box[:, 0:2][box[:, 0:2]<0] = 0
box[:, 2][box[:, 2]>w] = w
box[:, 3][box[:, 3]>h] = h
box_w = box[:, 2] - box[:, 0]
box_h = box[:, 3] - box[:, 1]
box = box[np.logical_and(box_w>1, box_h>1)] # discard invalid box
if len(box)>max_boxes: box = box[:max_boxes]
box_data[:len(box)] = box
return image_data, box_data
def get_defect_name(i):
if temps[i]["defect_name"]=="沾污":
defect_name = 1
elif temps[i]["defect_name"]=="错花":
defect_name = 2
elif temps[i]["defect_name"] == "水印":
defect_name = 3
elif temps[i]["defect_name"] == "花毛":
defect_name = 4
elif temps[i]["defect_name"] == "缝头":
defect_name = 5
elif temps[i]["defect_name"] == "缝头印":
defect_name = 6
elif temps[i]["defect_name"] == "虫粘":
defect_name = 7
elif temps[i]["defect_name"] == "破洞":
defect_name = 8
elif temps[i]["defect_name"] == "褶子":
defect_name = 9
elif temps[i]["defect_name"] == "织疵":
defect_name = 10
elif temps[i]["defect_name"] == "漏印":
defect_name = 11
elif temps[i]["defect_name"] == "蜡斑":
defect_name = 12
elif temps[i]["defect_name"] == "色差":
defect_name = 13
elif temps[i]["defect_name"] == "网折":
defect_name = 14
elif temps[i]["defect_name"] == "其他":
defect_name = 15
return defect_name
def reduce_defect_name(i):
name = ["沾污", "错花", "水印", "花毛", "缝头", "缝头印", "虫粘", "破洞", "褶子", "织疵", "漏印", "蜡斑", "色差", "网折", "其他"]
return name[i-1]
if __name__ == "__main__":
josn_path = "./data/all_defect/Annotations/anno_train.json"
with open(josn_path, 'r') as f:
temps = json.loads(f.read())
new = temps#记录json变化
#选择数据增强哪些label
label = ["错花", "水印", "花毛", "缝头", "缝头印", "虫粘", "破洞", "褶子", "织疵", "漏印", "蜡斑", "色差", "网折", "其他"]
passed = []
for i in range(len(temps)):
if (temps[i]['name'] in passed):#确保一张图片只增强一次
continue
if (temps[i]['defect_name'] not in label):
continue
raw_box = []
x_l, y_l, x_r, y_r = temps[i]["bbox"]
defect_name = get_defect_name(i)
raw_box.append([x_l, y_l, x_r, y_r, defect_name])
#往上找该图片是否有box跳过了
i_ = i
while temps[i_-1]['name'] == temps[i]['name']:
x_l, y_l, x_r, y_r = temps[i_-1]["bbox"]
defect_name = get_defect_name(i_-1)
i_-=1
#往下找把该图片的所有box都添加上
i_ = i
while i_+1 < len(temps) and temps[i_+1]['name'] == temps[i]['name']:
x_l, y_l, x_r, y_r = temps[i_+1]["bbox"]
defect_name = get_defect_name(i_+1)
i_+=1
raw_box.append([x_l, y_l, x_r, y_r, defect_name])
passed.append(temps[i]['name'])
img_name = temps[i]['name']
img_path = './data/all_defect/detect/' + img_name.split('.')[0]+'/'+temps[i]['name']
if (temps[i]['defect_name'] in ["花毛", "缝头", "缝头印", "虫粘", "破洞", "褶子", "织疵", "漏印", "蜡斑", "色差", "网折", "其他"]):
num = 4
else:
num = 1
for t in range(num):
# print(raw_box)
image_data, box_data = get_random_data(img_path,raw_box,[1024,1024],max_boxes=len(raw_box))
os.makedirs('./data/all_defect/detect/'+img_name.split('.')[0] + '_'+ str(t))
img = Image.fromarray((image_data*255).astype(np.uint8))
img.save('./data/all_defect/detect/' +img_name.split('.')[0] + '_'+ str(t)+'/'+img_name.split('.')[0] + '_'+ str(t) + '.jpg')
for k in box_data:
tmp_k = list(k)
# print(type(k))
append = dict(name=img_name.split('.')[0] + '_'+ str(t) + '.jpg',defect_name=reduce_defect_name(int(tmp_k[4])),
bbox=[round(tmp_k[0],2),round(tmp_k[1],2),round(tmp_k[2],2),round(tmp_k[3],2)])
new.append(append)
with open("./data/all_defect/Annotations/new.json", 'w') as f:
json.dump(new,f,indent=4)
参考
深度学习小技巧-目标检测当中的数据增强(Bubbliiiing 深度学习 教程):
https://www.bilibili.com/video/BV1ZA411b7L8