defvoc_ap(rec, prec):
rec.insert(0,0.0)
rec.append(1.0)
mrec = rec[:]
prec.insert(0,0.0)
prec.append(0.0)
mpre = prec[:]for i inrange(len(mpre)-2,-1,-1):
mpre[i]=max(mpre[i], mpre[i+1])
i_list =[]for i inrange(1,len(mrec)):if mrec[i]!= mrec[i-1]:
i_list.append(i)
ap =0.0for i in i_list:
ap +=((mrec[i]-mrec[i-1])*mpre[i])return ap
6. 处理真实标签文件,创建列表存储真实标签数据存在的所有类的数据
TEMP_FILES_PATH =".temp_files"ifnot os.path.exists(TEMP_FILES_PATH):
os.makedirs(TEMP_FILES_PATH)
ground_truth_files_list = glob.glob('ground-truth/*.txt')
ground_truth_files_list.sort()
gt_counter_per_class ={}
counter_images_per_class ={}for txt_file in ground_truth_files_list:
file_id = txt_file.split(".txt",1)[0]
file_id = os.path.basename(os.path.normpath(file_id))
f =open(txt_file)
lines_list = f.readlines()
f.close()
lines_list =[x.strip()for x in lines_list]
bounding_boxes =[]
already_seen_classes =[]for line in lines_list:
class_name, left, top, right, bottom = line.split()
bbox = left +" "+ top +" "+ right +" "+bottom
bounding_boxes.append({"class_name":class_name,"bbox":bbox,"used":False})if class_name in gt_counter_per_class:
gt_counter_per_class[class_name]+=1else:
gt_counter_per_class[class_name]=1if class_name notin already_seen_classes:if class_name in counter_images_per_class:
counter_images_per_class[class_name]+=1else:
counter_images_per_class[class_name]=1
already_seen_classes.append(class_name)# 将每个真实标签数据写入一个临时的.json文件中withopen(TEMP_FILES_PATH +"/"+ file_id +"_ground_truth.json",'w')as outfile:
json.dump(bounding_boxes, outfile)
gt_classes =list(gt_counter_per_class.keys())
gt_classes =sorted(gt_classes)
n_classes =len(gt_classes)
7. 处理预测文件,将每个预测数据写入一个临时的.json文件中
predicted_files_list = glob.glob('predicted/*.txt')
predicted_files_list.sort()for class_index, class_name inenumerate(gt_classes):
bounding_boxes =[]for txt_file in predicted_files_list:
file_id = txt_file.split(".txt",1)[0]
file_id = os.path.basename(os.path.normpath(file_id))
f =open(txt_file)
lines = f.readlines()
f.close()
lines =[x.strip()for x in lines]for line in lines:
tmp_class_name, confidence, left, top, right, bottom = line.split()if tmp_class_name == class_name:
bbox = left +" "+ top +" "+ right +" "+bottom
bounding_boxes.append({"confidence":confidence,"file_id":file_id,"bbox":bbox})
bounding_boxes.sort(key=lambda x:float(x['confidence']), reverse=True)withopen(TEMP_FILES_PATH +"/"+ class_name +"_predictions.json",'w')as outfile:
json.dump(bounding_boxes, outfile)
8. 设置iou阈值(默认0.5),计算mAP,结果写入results.txt文件中
MINOVERLAP =0.5# iou阈值
sum_AP =0.0
results_files_path ="results"if os.path.exists(results_files_path):
shutil.rmtree(results_files_path)
os.makedirs(results_files_path)withopen(results_files_path +"/results.txt",'w')as results_file:
results_file.write("# AP and precision/recall per class\n")
count_true_positives ={}for class_index, class_name inenumerate(gt_classes):
count_true_positives[class_name]=0#导入预测数据
predictions_file = TEMP_FILES_PATH +"/"+ class_name +"_predictions.json"
predictions_data = json.load(open(predictions_file))
nd =len(predictions_data)
tp =[0]* nd
fp =[0]* nd
for idx, prediction inenumerate(predictions_data):
file_id = prediction["file_id"]
gt_file = TEMP_FILES_PATH +"/"+ file_id +"_ground_truth.json"
ground_truth_data = json.load(open(gt_file))
ovmax =-1
gt_match =-1# 导入预测框
bb =[float(x)for x in prediction["bbox"].split()]for obj in ground_truth_data:if obj["class_name"]== class_name:
bbgt =[float(x)for x in obj["bbox"].split()]
bi =[max(bb[0],bbgt[0]),max(bb[1],bbgt[1]),min(bb[2],bbgt[2]),min(bb[3],bbgt[3])]
iw = bi[2]- bi[0]+1
ih = bi[3]- bi[1]+1if iw >0and ih >0:# 计算IOU
ua =(bb[2]- bb[0]+1)*(bb[3]- bb[1]+1)+(bbgt[2]- bbgt[0]+1)*(bbgt[3]- bbgt[1]+1)- iw * ih
ov = iw * ih / ua
if ov > ovmax:
ovmax = ov
gt_match = obj
min_overlap = MINOVERLAP
if ovmax >= min_overlap:ifnotbool(gt_match["used"]):
tp[idx]=1
gt_match["used"]=True
count_true_positives[class_name]+=1withopen(gt_file,'w')as f:
f.write(json.dumps(ground_truth_data))else:
fp[idx]=1else:
fp[idx]=1if ovmax >0:
status ="INSUFFICIENT OVERLAP"# 计算召回率和精度
cumsum =0for idx, val inenumerate(fp):
fp[idx]+= cumsum
cumsum += val
cumsum =0for idx, val inenumerate(tp):
tp[idx]+= cumsum
cumsum += val
rec = tp[:]for idx, val inenumerate(tp):
rec[idx]=float(tp[idx])/ gt_counter_per_class[class_name]
prec = tp[:]for idx, val inenumerate(tp):
prec[idx]=float(tp[idx])/(fp[idx]+ tp[idx])# 计算AP
ap = voc_ap(rec[:], prec[:])
sum_AP += ap
text ="{0:.2f}%".format(ap*100)+" = "+ class_name +" AP "# 结果写入results.txt文件中
rounded_prec =['%.2f'% elem for elem in prec ]
rounded_rec =['%.2f'% elem for elem in rec ]
results_file.write(text +"\n Precision: "+str(rounded_prec)+"\n Recall :"+str(rounded_rec)+"\n\n")
n_images = counter_images_per_class[class_name]
results_file.write("\n# mAP of all classes\n")# 计算mAP
mAP = sum_AP / n_classes
text ="mAP = {0:.2f}%".format(mAP*100)
results_file.write(text +"\n")
shutil.rmtree(TEMP_FILES_PATH)
9. 计算预测总数,将各个类别的总数写入results.txt
pred_counter_per_class ={}for txt_file in predicted_files_list:
f =open(txt_file)
lines_list = f.readlines()
f.close()
lines_list =[x.strip()for x in lines]for line in lines_list:
class_name = line.split()[0]if class_name in pred_counter_per_class:
pred_counter_per_class[class_name]+=1else:
pred_counter_per_class[class_name]=1
pred_classes =list(pred_counter_per_class.keys())withopen(results_files_path +"/results.txt",'a')as results_file:
results_file.write("\n# Number of ground-truth objects per class\n")for class_name insorted(gt_counter_per_class):
results_file.write(class_name +": "+str(gt_counter_per_class[class_name])+"\n")
10. 将各个类别的TP/FP数量写入results.txt
for class_name in pred_classes:if class_name notin gt_classes:
count_true_positives[class_name]=0withopen(results_files_path +"/results.txt",'a')as results_file:
results_file.write("\n# Number of predicted objects per class\n")for class_name insorted(pred_classes):
n_pred = pred_counter_per_class[class_name]
text = class_name +": "+str(n_pred)
text +=" (tp:"+str(count_true_positives[class_name])+""
text +=", fp:"+str(n_pred - count_true_positives[class_name])+")\n"
results_file.write(text)
results_file.close()