思想很简单,将标注的yolo数据转下格式,转为[类别,xmin,ymin,xmax,ymax]
转换valid后的信息,两个信息进行对比 完事
具体的,在终端执行:
./darknet detector valid cfg/voc.data cfg/yolo-voc.cfg backup/yolo-voc_final.weights
tip:在后面加入-thresh 0.25可以设置显示置信度的大小
在result文件夹下会生成以类别命名的txt文档,如下,在这里我一共就三个类别:smoke、red、white
comp4_det_test_smoke.txt
comp4_det_test_red.txt
comp4_det_test_white.txt
解析几个文件中的信息,如comp4_det_test_smoke.txt里面保存的是所有图片中识别为smoke的信息[文件名、置信度、xmin,ymin,xmax,ymax]。
运行如下代码:
import os
# txt_file为配置文件.data中的valid
txt_file = '/home/share/liubo/darknet/yanhuo/eval.txt'
f = open(txt_file)
lines = f.readlines()
for line in lines:
line = line.split('/')[-1][0:-5]
# test_out_file 为转换后保存的结果地址
test_out_file = '/home/share/liubo/darknet-yolov3/results/test_acc_yanhuo'
# 下面3个with需要自己的修改,修改成自己对应的类别
with open(os.path.join(test_out_file , line + '.txt'), "a") as new_f:
f1 = open('/home/share/liubo/darknet-yolov3/results/comp4_det_test_smoke.txt', 'r')
f1_lines = f1.readlines()
for f1_line in f1_lines:
f1_line = f1_line.split()
if line == f1_line[0]:
new_f.write("%s %s %s %s %s %s\n" % ('smoke', f1_line[1], f1_line[2], f1_line[3], f1_line[4], f1_line[5]))
with open(os.path.join(test_out_file , line + '.txt'), "a") as new_f:
f1 = open('/home/share/liubo/darknet-yolov3/results/comp4_det_test_white.txt', 'r')
f1_lines = f1.readlines()
for f1_line in f1_lines:
f1_line = f1_line.split()
# print(line.split('.')[0] + ' ' + f1_line[0])
if line == f1_line[0]:
new_f.write("%s %s %s %s %s %s\n" % ('white', f1_line[1], f1_line[2], f1_line[3], f1_line[4], f1_line[5]))
with open(os.path.join(test_out_file , line + '.txt'), "a") as new_f:
f1 = open('/home/share/liubo/darknet-yolov3/results/comp4_det_test_red.txt', 'r')
f1_lines = f1.readlines()
for f1_line in f1_lines:
f1_line = f1_line.split()
if line == f1_line[0]:
new_f.write("%s %s %s %s %s %s\n" % ('red', f1_line[1], f1_line[2], f1_line[3], f1_line[4], f1_line[5]))
运行代码后,效果如下:
转换前 转换后
此时out_file中保存了所有的检测信息,接下来对yolo数据格式进行转换
执行以下代码:
import os
from PIL import Image
import numpy as np
# label_img为数据集的labels地址,img_path为数据集images的地址
label_img = '/home/share/liubo/darknet/yanhuo/labels/'
img_path = '/home/share/liubo/darknet/yanhuo/images/'
classes = {
0:'smoke',
1:'white',
2:'red'
}
for line in lines:
line = line.split('/')[-1][0:-5] + '.txt'
txt = label_img + line
img = np.array(Image.open(img_path + line.split('/')[-1][0:-4] + '.jpg'))
sh, sw = img.shape[0], img.shape[1]
# gt_out_file为转换后的地址
gt_out_file = '/home/share/liubo/darknet-yolov3/results/gt_yanhuo'
with open(os.path.join(gt_out_file , line ), "a") as new_f:
f1 = open(txt)
f1_lines = f1.readlines()
for f1_line in f1_lines:
f1_line = f1_line.split()
x = float(f1_line[1]) * sw
y = float(f1_line[2]) * sh
w = float(f1_line[3]) * sw
h = float(f1_line[4]) * sh
xmin = x+1-w/2
ymin = y+1-h/2
xmax = x+1+w/2
ymax = y+1+h/2
new_f.write("%s %s %s %s %s\n" % (classes[int(f1_line[0])], xmin ,ymin,xmax,ymax))
此时已经保存了所有检测结果和转换后的结果,接下来对应的txt进行比较计算。
import os
def compute_IOU(rec1, rec2):
"""
计算两个矩形框的交并比。
:param rec1: (x0,y0,x1,y1) (x0,y0)代表矩形左上的顶点,(x1,y1)代表矩形右下的顶点。下同。
:param rec2: (x0,y0,x1,y1)
:return: 交并比IOU.
"""
left_column_max = max(rec1[0], rec2[0])
right_column_min = min(rec1[2], rec2[2])
up_row_max = max(rec1[1], rec2[1])
down_row_min = min(rec1[3], rec2[3])
# 两矩形无相交区域的情况
if left_column_max >= right_column_min or down_row_min <= up_row_max:
return 0
# 两矩形有相交区域的情况
else:
S1 = (rec1[2] - rec1[0]) * (rec1[3] - rec1[1])
S2 = (rec2[2] - rec2[0]) * (rec2[3] - rec2[1])
S_cross = (down_row_min - up_row_max) * (right_column_min - left_column_max)
return S_cross / (S1 + S2 - S_cross)
# gt为yolo数据转换后的地址
gt = '/home/share/liubo/darknet-yolov3/results/gt_yanhuo/'
# test为检测结果转换后的地址
test = '/home/share/liubo/darknet-yolov3/results/test_acc_yanhuo/'
# count_gt为标注的所有数据框
count_gt = {
}
# count_test为检测的所有数据框
count_test = {
}
# count_yes_test为检测正确的数据框
count_yes_test = {
}
# count_no_test为检测错误的数据框
count_no_test = {
}
# 计数
for gt_ in os.listdir(gt):
txt = gt + gt_
f = open(txt)
lines = f.readlines()
for line in lines:
line = line.split()
name = line[0]
if name not in count_gt:
count_gt[name] = 0
count_gt[name] += 1
for test_ in os.listdir(test):
txt = test + test_
f = open(txt)
lines = f.readlines()
for line in lines:
line = line.split()
name = line[0]
if name not in count_test:
count_test[name] = 0
count_test[name] += 1
# 下面主要思想:遍历test结果,再遍历对应gt的结果,如果两个框的iou大于一定的阙址并且类别相同,视为正确
for test_ in os.listdir(test):
f_test_txt = test + test_
f_test = open(f_test_txt)
f_test_lines = f_test.readlines()
for f_test_line in f_test_lines:
f_test_line = f_test_line.split()
f_gt_txt = gt + test_
f_gt = open(f_gt_txt)
f_gt_lines = f_gt.readlines()
flag = 1
for f_gt_line in f_gt_lines:
f_gt_line = f_gt_line.split()
IOU = compute_IOU([float(f_gt_line[1]), float(f_gt_line[2]), float(f_gt_line[3]), float(f_gt_line[4])],
[float(f_test_line[2]), float(f_test_line[3]), float(f_test_line[4]), float(f_test_line[5])])
if f_gt_line[0] == f_test_line[0] and IOU >= 0.5 and float(f_test_line[1]) >= 0.3:
flag = 0
if f_test_line[0] not in count_yes_test:
count_yes_test[f_test_line[0]] = 0
count_yes_test[f_test_line[0]] += 1
if flag == 1:
if f_test_line[0] not in count_no_test:
count_no_test[f_test_line[0]] = 0
count_no_test[f_test_line[0]] += 1
# 有以下4个结果,就可以计算相关指标了
print(count_gt)
print(count_test)
print(count_yes_test)
print(count_no_test)
以上就是Yolov3计算准确率、误报率、漏检率等的全过程
写了好久之后才写的博客,有什么理解错误或者代码问题请及时交流 感谢~