本文仅提供Fasterrcnn对2021年天池大赛——广东工业智能创新大赛初赛的基本检测过程,代码可直接实现,但未添加有效的数据预处理过程,上传后评分结果很低。
本博文可供参考部分:
Fasterrcnn的代码;json文件转换为coco格式;检测结果转换为单独json文件;
提示:以下是本篇文章正文内容,下面案例可供参考
详细见2021年天池广东工业智能创新大赛
赛题聚焦瓷砖表面瑕疵智能检测,要求选手开发出高效可靠的计算机视觉算法,提升瓷砖表面瑕疵质检的效果和效率,降低对大量人工的依赖。要求算法尽可能快与准确的给出瓷砖疵点具体的位置和类别,主要考察疵点的定位和分类能力。
大赛深入到佛山瓷砖知名企业,在产线上架设专业拍摄设备,实地采集生产过程真实数据,解决企业真实的痛点需求。大赛数据覆盖到了瓷砖产线所有常见瑕疵,包括粉团、角裂、滴釉、断墨、滴墨、B孔、落脏、边裂、缺角、砖渣、白边等(实际数据集中分为6种缺陷)。实拍图示例如下:
初赛的数据集是白板瓷砖。花色简单,数量总共约12000张,包含训练集和测试集。
下面是训练集中缺陷标注的json示例(无缺陷图片在json文件中不显示)。
[
{
"name": "226_46_t20201125133518273_CAM1.jpg",
"image_height": 6000,
"image_width": 8192,
"category": 4, #缺陷类别
"bbox": [ #缺陷定位
1587,
4900,
1594,
4909
]
},
'''
'''
]
关于以上“category”指该图片中缺陷的类别,具体数字对应类别如下:
{
"0": "背景",
"1": "边异常",
"2": "角异常",
"3": "白色点瑕疵",
"4": "浅色块瑕疵",
"5": "深色点块瑕疵",
"6": "光圈瑕疵"
}
0.2ACC+0.8mAP
其中ACC:是有瑕疵或无瑕疵的分类指标,考察瑕疵检出能力;mAP:参照PASCALVOC的评估标准计算瑕疵的mAP值。
Faster-RCNN是一个非常有效的目标检测算法,虽然是一个比较早的论文, 但它至今仍是许多目标检测算法的基础。
Faster-RCNN作为一种two-stage的算法,与one-stage的算法相比,two-stage的算法更加复杂且速度较慢,但是检测精度会更高。
事实上也确实是这样,Faster-RCNN的检测效果非常不错,但是检测速度与训练速度有待提高。
(待补充。。。)
参考 Faster-RCNN CSDN
pytorch1.7
python3.8
Step1: 下载代码
大佬的代码链接:faster-rcnn-pytorch
本渣的代码链接:
链接:https://pan.baidu.com/s/1qsUOEjCiZOt0MmpoDEhP-w 密码:d12o
(本质相同,本渣的代码是自己在接口方面做了微调,更适合直接用于该赛题)
Step2: 用训练好的代码直接测试单张图片
运行predict.py文件,随后输入要测试的单张图片文件路径和名称,如img/test.jpeg(提前在img文件夹中放入要检测的图片)
Step3: 用训练好的代码对测试集中的文件进行测试
打开predict_folder.py文件,修改testpath参数,该参数是测试图片所在的文件夹路径。
运行predict_folder.py
运行结束后,检测结果将会存储在test_data.json文件中。
Step4: 训练自己的faster-rcnn网络
因为文件大小限制,请先下载赛题中给出的训练集图片9.1G。赛题训练集图片 并将训练集图片放置在faster-rcnn/VOCdevkit/VOC2007/JPEGImages文件夹中。
验证集图片2.77G 验证集图片,并将验证集图片放置在
faster-rcnn/test文件夹中。
点击train.py进行训练。
详见jsonxml文件夹。
其中JPEGJmages存放训练集的图片,用于进行训练。main.py是转化的代码,train_annos.json是大赛提供的训练集的标注。
main.py的内容如下:
import json
from xml.etree import ElementTree as ET
import cv2
import math
img_paths = '/home/XXX/桌面/fasterrcnn/faster-rcnn/VOCdevkit/VOC2007/JPEGImages/'
with open('train_annos.json', 'r') as f:
image_meta = json.load(f, encoding='etf-8')
each_img_meta = {
}
for each_item in image_meta:
each_img_meta[each_item['name']] = []
for idx, each_item in enumerate(image_meta):
bbox = each_item['bbox']
bbox.append(each_item['category'])
each_img_meta[each_item['name']].append(bbox)
# 创建xml文件的函数
def create_tree(image_name, h, w):
global annotation
# 创建树根annotation
annotation = ET.Element('annotation')
# 创建一级分支folder
folder = ET.SubElement(annotation, 'folder')
# 添加folder标签内容
folder.text = None
# 创建一级分支filename
filename = ET.SubElement(annotation, 'filename')
filename.text = image_name
# 创建一级分支source
source = ET.SubElement(annotation, 'source')
# 创建source下的二级分支database
database = ET.SubElement(source, 'database')
database.text = 'Unknown'
# 创建一级分支size
size = ET.SubElement(annotation, 'size')
# 创建size下的二级分支图像的宽、高及depth
width = ET.SubElement(size, 'width')
width.text = str(w)
height = ET.SubElement(size, 'height')
height.text = str(h)
depth = ET.SubElement(size, 'depth')
depth.text = '3'
# 创建一级分支segmented
segmented = ET.SubElement(annotation, 'segmented')
segmented.text = '0'
# 定义一个创建一级分支object的函数
def create_object(root, xi, yi, xa, ya, obj_name): # 参数依次,树根,xmin,ymin,xmax,ymax
# 创建一级分支object
_object = ET.SubElement(root, 'object')
# 创建二级分支
name = ET.SubElement(_object, 'name')
# print(obj_name)
name.text = str(obj_name)
pose = ET.SubElement(_object, 'pose')
pose.text = 'Unspecified'
truncated = ET.SubElement(_object, 'truncated')
truncated.text = '0'
difficult = ET.SubElement(_object, 'difficult')
difficult.text = '0'
# # 创建bndbox
bndbox = ET.SubElement(_object, 'bndbox')
xmin = ET.SubElement(bndbox, 'xmin')
xmin.text = '%s' % xi
ymin = ET.SubElement(bndbox, 'ymin')
ymin.text = '%s' % yi
xmax = ET.SubElement(bndbox, 'xmax')
xmax.text = '%s' % xa
ymax = ET.SubElement(bndbox, 'ymax')
ymax.text = '%s' % ya
import os
os.makedirs('./JPEGImages',exist_ok=True)
os.makedirs('./Annotations',exist_ok=True)
window_s = 1333
for idx, each_item in enumerate(image_meta):
# print(each_item)
bbox = each_item['bbox']
img = cv2.imread(img_paths + each_item['name'])
h, w = img.shape[:2]
# each_img_meta[each_item['name']].append(bbox)
center_x, center_y = int(bbox[0] + (bbox[2] - bbox[0]) / 2), int((bbox[3] - bbox[1]) / 2 + bbox[1])
x, y, r, b = center_x - window_s // 2, center_y - window_s // 2, center_x + window_s // 2, center_y + window_s // 2
x = max(0, x)
y = max(0, y)
r = min(r, w)
b = min(b, h)
boxes = each_img_meta[each_item['name']]
annotations = []
for e_box in boxes:
if x < e_box[0] < r and y < e_box[1] < b and x < e_box[2] < r and y < e_box[3] < b:
e_box = [int(i) for i in e_box]
e_box[0] = math.floor(e_box[0] - x)
e_box[1] = math.floor(e_box[1] - y)
e_box[2] = math.ceil(e_box[2] - x)
e_box[3] = math.ceil(e_box[3] - y)
annotations.append(e_box)
print('process id:', idx, "|", annotations)
slice_img = img[y:b, x:r]
create_tree(each_item['name'], window_s, window_s)
for anno in annotations:
create_object(annotation, anno[0], anno[1], anno[2], anno[3], anno[4])
tree = ET.ElementTree(annotation)
slice_name=each_item['name'][:-4]+'_'+str(x)+'_'+str(y)+'.jpg'
xml_name=each_item['name'][:-4]+'_'+str(x)+'_'+str(y)+'.xml'
cv2.imwrite('./JPEGImages/' + slice_name, slice_img)
tree.write('./Annotations/' + xml_name)
# for anno in annotations:
# cv2.putText(slice_img, str(anno[4]), (int(anno[0]) - 5, int(anno[1]) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
# (0, 255, 0))
# cv2.rectangle(slice_img, (int(anno[0]), int(anno[1])), (int(anno[2]), int(anno[3])), (0, 255, 0), 2)
# cv2.imshow('slice_img', slice_img)
#
# cv2.waitKey(1000)
if x < 0 or y < 0 or r > w or b > h:
print(idx)
print(each_item['name'])
在以上“二、2、(2)、Step3: 用训练好的代码对测试集中的文件进行测试”中对文件夹中图片进行测试时,predict_folder.py中已经展示了进行测试时顺便将测试结果添加到json文件中的过程了。
主要思路是: 从文件夹中依次读取图片——输入训练好的网络中进行检测——将检测结果根据大赛提交要求,将label,bbox,category,score等提取出来——创建包含这4项检测结果的字典——将字典写入json文件中——所有图片检测完成,关闭json文件。
具体实现代码为:
from frcnn import FRCNN
from PIL import Image
import os
import json
import numpy as np
import torch
from nets.frcnn_training import get_new_img_size
from utils.utils import DecodeBox
# ---------------------------------------------------------------------------------------
# 对测试集进行目标检测,并将其结果写入json文件中
# ---------------------------------------------------------------------------------------
test_path = 'testset' #放置测试图片的文件夹路径
images_totalnum = len([lists for lists in os.listdir(test_path) if os.path.isfile(os.path.join(test_path, lists))]) #测试集中图片的总数量
frcnn = FRCNN()
class NpEncoder(json.JSONEncoder):
'''
json文件写入操作防止出现编码错误
'''
def default(self, obj):
if isinstance(obj, np.integer):
return int(obj)
elif isinstance(obj, np.floating):
return float(obj)
elif isinstance(obj, np.ndarray):
return obj.tolist()
else:
return super(NpEncoder, self).default(obj)
image_num = 1
with open('test_data.json','w') as json_file:
for filename in os.listdir(r'./' + test_path):
image = Image.open(test_path + '/' + filename)
image = image.convert("RGB")
try:
label,score,bbox = frcnn.detect_image(image)
for i in range(len(label)):
test_dict = {
'name': filename,
"category": label[i],
"bbox": bbox[i],
"score": score[i]
}
json_str = json.dumps(test_dict, cls=NpEncoder)
json_file.write(json_str + ',' + '\n')
print(str(image_num)+"/"+str(images_totalnum)+ ' | label: ' + (label[i]) +' score:' + str(score[i]))
image_num += 1
except:
print(str(image_num)+"/"+str(images_totalnum)+' is no defect.')
image_num += 1
print("Writing to a JSONFile is finishing!")
虽然训练了100个epoch后,验证集的loss已经达到0.1943,但未对图片进行切图,滑窗等手段,使得faster-rcnn直接预测本赛题中的瑕疵效果很差,resize后更是看不到小的瑕疵点。
这里mark一下faster-rcnn的运行过程,下一步再进行尝试其他手段。
还望各位大佬多指点!