模型评价参数:
参考博客:
【YOLO学习】召回率(Recall),精确率(Precision),平均正确率(Average_precision(AP) ),交除并(Intersection-over-Union(IoU))
mAP计算代码链接:
https://github.com/Cartucho/mAP
在\mAP-master\input目录下有三个文件夹,如图。我们需将每个文件夹下的文件替换为自己的数据集所对应的文件。
1.detection-results文件夹
此文件夹下存放yolov3测试所生成的文本文件。
1.1测试命令及结果文件
darknet.exe detector valid data/train5bottle.data yolov3-bottle.cfg backup/yolov3-bottle_last.weights
在\darknet-master\build\darknet\x64\results目录下生成txt文件,每个类别自成一个txt文件。如图。
此文本文档的格式展示(图片命名,分数,框的位置(左上右下)):
1.2将上述文本文件转换为MAP计算所需要的格式。
每个图片都生成一个对应的文本文档。保存在 detection-results 文件夹下。
转换后的格式如下
附转换代码,参考
https://blog.csdn.net/qq_40297851/article/details/106999045
# detection_transfer.py
import os
def creat_mapping_dic(result_txt, threshold=0.0): # 设置一个阈值,用来删掉置信度低的预测框信息
mapping_dic = {} # 创建一个字典,用来存放信息
txt = open(result_txt, 'r').readlines() # 按行读取TXT文件
for info in txt: # 提取每一行
info = info.split() # 将每一行(每个预测框)的信息切分开
photo_name = info[0] # 图片名称
probably = float(info[1]) # 当前预测框的置信度
if probably < threshold:
continue
else:
xmin = int(float(info[2]))
ymin = int(float(info[3]))
xmax = int(float(info[4]))
ymax = int(float(info[5]))
position = [xmin, ymin, xmax, ymax, probably]
if photo_name not in mapping_dic: # mapping_dic的每个元素的key值为图片名称,value为一个二维list,其中存放当前图片的若干个预测框的位置
mapping_dic[photo_name] = []
mapping_dic[photo_name].append(position)
return mapping_dic
def creat_result_txt(raw_txt_path, target_path, threshold=0.0): # raw_txt_path为yolo按类输出的TXT的路径 target_path 为转换后的TXT存放路径
all_files = os.listdir(raw_txt_path) # 获取所以的原始txt
for each_file in all_files: # 遍历所有的原始txt文件,each_file为一个文件名,例如‘car.txt’
each_file_path = os.path.join(raw_txt_path, each_file) # 获取当前txt的路径
map_dic = creat_mapping_dic(each_file_path, threshold=threshold) # 对当前txt生成map_dic
for each_map in map_dic: # 遍历当前存放信息的字典
target_txt = each_map + '.txt' # 生成目标txt文件名
target_txt_path = os.path.join(target_path, target_txt) # 生成目标txt路径
if target_txt not in os.listdir(target_path):
txt_write = open(target_txt_path, 'w') # 如果目标路径下没有这个目标txt文件,则创建它,即模式设置为“覆盖”
else:
txt_write = open(target_txt_path, 'a') # 如果目标路径下有这个目标txt文件,则将模式设置为“追加”
class_name = each_file[:-4] # 获取当前原始txt的类名
# txt_write.write(class_name) # 对目标txt写入类名
# txt_write.write('\n') # 换行
for info in map_dic[each_map]: # 遍历某张图片的所有预测框信息
txt_write.write(class_name) # 对目标txt写入类名
txt_write.write(' ' + str(info[4]) + ' ' + str(info[0]) + ' ' + str(info[1]) +
' ' + str(info[2]) + ' ' + str(info[3]) + ' ') # 写入预测框信息
txt_write.write('\n') # 换行
creat_result_txt(r'E:\HISI\darknet-master\build\darknet\x64\results',
r'E:\MAP',
threshold=0.1)
# 第一个文件路径,结果文件。 输入路径
# 第二个文件路径,生成单独标签的保存路径。 输出路径
# 阈值,筛选阈值以上的保存。
2.ground-truth文件夹
ground-truth文件夹下存放验证集的真实框的数据,格式要求如下:
当时制作数据集的时候采用的是labellmg标注软件。数据类型直接选用了yolo数据,yolo数据标注格式为(分类的id, 中心点坐标x,中心点坐标y,框的宽,框的高),yolo的数据标注格式是进行了归一化的。如下图:
附yolo-map转换代码:
# Yolo txt标签格式转换至map txt标签格式
import os
import cv2
import time
mapPath = r'E:\MAP_bottle\kitti_label' # 新生成的map数据想存放的文件夹路径,根据需要修改
# 将txt中坐标还原到原始照片的坐标
def restore_coordinate(yolo_bbox, image_w, image_h):
box_w = float(yolo_bbox[3]) * image_w
box_h = float(yolo_bbox[4]) * image_h
x_mid = float(yolo_bbox[1]) * image_w + 1
y_mid = float(yolo_bbox[2]) * image_h + 1
xmin = int(x_mid - box_w / 2)
xmax = int(x_mid + box_w / 2)
ymin = int(y_mid - box_h / 2) + 5 # 增加了一个偏移的量5;
ymax = int(y_mid + box_h / 2) + 5
return [xmin, ymin, xmax, ymax]
result=list() #
with open(r'E:\HISI\darknet-master\build\darknet\x64\data\train5bottle.names', 'r') as f: #修改为自己的calss路径
for line in f:
result.append(line.strip().split(',')[0]) # a.append(b):是将b原封不动的追加到a的末尾上,会改变a的值
# strip()方法用于移除字符串头尾指定的字符(默认为空格或者换行符)或字符序列
print(result)
print(result[0])
# 获取照片的labels文件和images文件,并生成新的标签文件
def restore_results(images_folder, labels_folder):
labels = os.listdir(labels_folder) #os.listdir用于返回该文件夹下所有的文件及文件夹
for label in labels:
name = label.split('.')[0] #以o分割字符串,得到的是第一个o之前的内容
print(name)
with open(os.path.join(labels_folder, label), 'r') as f: #读取文件
img = cv2.imread(os.path.join(images_folder, name + '.jpg')) #读取图片
w = img.shape[1]
h = img.shape[0]
info = f.readline()
lines = ""
while info:
label = list(info.split(' '))
ori_box = restore_coordinate(label, w, h)
for i in range(30):
# d=list(map(int,label[0]))
if int(label[0]) == i:
new_info = result[i] + ' ' \
+ str(ori_box[0]) + ' ' + str(ori_box[1]) + ' ' + str(ori_box[2]) + ' ' + str(ori_box[3]) \
+ ' ' + '\n'
lines += new_info
info = f.readline()
with open(os.path.join(mapPath, name + '.txt'), 'w') as fn:
fn.writelines(lines)
fn.close()
# 将转换的坐标值绘制到原始图片上,并显示查看
# cv2.rectangle(img, (ori_box[0], ori_box[1]), (ori_box[2], ori_box[3]), (0, 255, 255), 2)
# cv2.imshow('Transfer_label', img)
# if cv2.waitKey(100) & 0XFF == ord('q'):
# break
f.close()
# cv2.destoryAllWindows()
if __name__ == '__main__':
s = time.time()
imagePath = r'E:\MAP_bottle\test_image' # 原本的yolo数据格式的images所在的文件夹,根据自己的修改
labelPath = r'E:\MAP_bottle\test_yolo' # 原本的yolo数据格式的labels所在的文件夹,根据自己的修改
print('----数据转换开始---')
restore_results(imagePath, labelPath)
print('---耗时:{:.3f}ms'.format(time.time() - s))
print('---数据转换成功---')
3.images_optional文件夹
此文件夹下存放测试的图片。
pycharm打开map-master,在运行main.py之前先运行/script/extra下的intersect-gt-and-dr.py,运行即可得到所需要的MAP等数据可视化曲线。