目标检测网络指标mAP的测试的python实现

背景:实现相应的目标检测网络需要能够测试mAP

目的:实现mAP的测试。

参考代码:https://github.com/Cartucho/mAP#create-the-ground-truth-files

目录

一、mAP概览

1.1 mAP概览

1.2 测试需要的步骤

二、GroundTruth文档的生成

三、网络预测结果生成

四、预测mAP代码

4.1 运算IoU

4.2 运算AP

4.3 mAP


一、mAP概览

1.1 mAP概览

mAP为目标检测领域的基础指标。

首先标签相同交并比IoU>0.5表示网络检测正确。

然后画出相应的查全率与查准率的曲线,积分得到的蓝色区域即为mAP。

各类的平均AP即mAP

1.2 测试需要的步骤

Create a separate ground-truth text file for each image. In these files, each line should be in the following format:<class_name> <left> <top> <right> <bottom> [<difficult>]

生成相应的标注文档,无论是GroundTruth还是网络生成的预测,都需要按照上面的格式标注。

Use matching names(e.g. image: "image_1.jpg", ground-truth: "image_1.txt"; "image_2.jpg", "image_2.txt"...).

然后图片放入相应的文件夹之中。

1.Create the ground-truth files创建相应的GroundTruth文件

2.Move the ground-truth files into the folderground-truth/  放入相应文件夹

3.Create the predicted objects files 创建相应的预测的文件

4.Move the predictions files into the folderpredicted/  放入相应文件夹

5.Run themAPcode 运行mAP的代码

二、GroundTruth文档的生成

按照要求将文件拷入文件夹与生成相应的标注。

<class_name> <left> <top> <right> <bottom> [<difficult>]标注需要与文件同名且每一行按照这种格式生成。

"""
author:xing xiangrui
time  :2018.9.26   10:34
copy image and change name to the new directory
write image name and labels to the list.txt
"""
import os
import shutil
oldDir = "data"
newDir = "head_data"
width = 352 
height = 288
fw = open('list.txt','w')
for root,dirs,files in os.walk(oldDir):
    if 'images' in root:
        continue
    for file in files:
        if 'jpg' in file:
            # copy jpg
            oldPic = root + os.path.sep + file
            newPic = newDir + os.path.sep + root.split(os.path.sep)[-2] + "_" + root.split(os.path.sep)[-1] + "_" + file
            shutil.copyfile(oldPic,newPic)
            
            fw.write(newPic)
            # write txt
            oldTxt = root + os.path.sep + file.replace('jpg','txt')
            fo = open(oldTxt,'r')
            lines = fo.readlines()
            for line in lines:
                line = line.split()
                cx, cy, w, h = float(line[1])*width, float(line[2])*height, float(line[3])*width, float(line[4])*height
                #[x, y, w, h] = [float(line[i]) for i in range(1,5)]
                x1 = round(cx - w/2, 2)
                y1 = round(cy - h/2, 2)
                x2 = round(cx + w/2, 2)
                y2 = round(cy + h/2, 2)
                print(x1,y1,x2,y2)
                fw.write(" " + str(x1) + " " + str(y1) + " " + str(x2) + " " + str(y2))
            fw.write("\n")
            fo.close()
            #print("fine")
print("done")
fw.close()

此部分代码实现了

  • 旧用于预测的图像重命名放入新目录下
  • 旧标签按照mAP的要求格式生成新标签

三、网络预测结果生成

                for tmp_file in jpg_list:
                    img=cv2.imread(tmp_file)
                    # add ROI region
                    ROI=img[ROI_idx[0]:ROI_idx[1],ROI_idx[2]:ROI_idx[3]]
                    ROI_temp=ROI.copy()
                    img[:,:,:]=0
                    img[ROI_idx[0]:ROI_idx[1],ROI_idx[2]:ROI_idx[3]]=ROI_temp
                    #create txt file
                    tmp_file=tmp_file.replace("jpg","txt")
                    txt_filename=tmp_file.replace("images","predicted")
                    print("LOACTION!!!predict:"+tmp_file)
                    
#                    start_time = time.time()
                    #print("LOCATION!!!detect_face function start"+"\n")
                    rectangles, points = detect_face(img, args.minsize,
                                                     pnet_fun, rnet_fun, onet_fun,
                                                     args.threshold, args.factor)
                    #print("LOCATION!!!idetect_face function done"+"\n")
#                    duration = time.time() - start_time
    
#                    print("duration:"+str(duration))
                    #print(type(rectangles))
                    points = np.transpose(points)
                    #print("LOCATION!!!loop rectangles"+"\n")
                    with open(txt_filename,'w') as result_file:
                        for rectangle in rectangles:
                            result_file.write("head" + " " + str(rectangle[4]) + " " + str(rectangle[0]) + " " + str(rectangle[1]) + " " + str(rectangle[2]) +  " " + str(rectangle[3])+"\n")
                    #print("LOCATION!!!Write done!"+"\n")
                print(ROI_idx)
                os.chdir("mAP/")
                os.system("python main.py -na")

根据网络预测将所有图片运行一遍,结果写入相应txt文件之中,然后调用mAP测试函数对结果进行预测。

四、预测mAP代码

4.1 运算IoU

      # load prediction bounding-box
      bb = [ float(x) for x in prediction["bbox"].split() ]
      for obj in ground_truth_data:
        # look for a class_name match
        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] + 1
          if iw > 0 and ih > 0:
            # compute overlap (IoU) = area of intersection / area of union
            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

4.2 运算AP

先设置两个点,即precision为1的时候,recall为0;precision为0的时候,recall为1

def voc_ap(rec, prec):
  """
  --- Official matlab code VOC2012---
  mrec=[0 ; rec ; 1];
  mpre=[0 ; prec ; 0];
  for i=numel(mpre)-1:-1:1
      mpre(i)=max(mpre(i),mpre(i+1));
  end
  i=find(mrec(2:end)~=mrec(1:end-1))+1;
  ap=sum((mrec(i)-mrec(i-1)).*mpre(i));
  """
  rec.insert(0, 0.0) # insert 0.0 at begining of list
  rec.append(1.0) # insert 1.0 at end of list
  mrec = rec[:]
  prec.insert(0, 0.0) # insert 0.0 at begining of list
  prec.append(0.0) # insert 0.0 at end of list
  mpre = prec[:]

precision随着recall的增减,逐渐减少

  """
   This part makes the precision monotonically decreasing
    (goes from the end to the beginning)
    matlab:  for i=numel(mpre)-1:-1:1
                mpre(i)=max(mpre(i),mpre(i+1));
  """
  # matlab indexes start in 1 but python in 0, so I have to do:
  #   range(start=(len(mpre) - 2), end=0, step=-1)
  # also the python function range excludes the end, resulting in:
  #   range(start=(len(mpre) - 2), end=-1, step=-1)
  for i in range(len(mpre)-2, -1, -1):
    mpre[i] = max(mpre[i], mpre[i+1])

创建recall的变化

  """
   This part creates a list of indexes where the recall changes
    matlab:  i=find(mrec(2:end)~=mrec(1:end-1))+1;
  """
  i_list = []
  for i in range(1, len(mrec)):
    if mrec[i] != mrec[i-1]:
      i_list.append(i) # if it was matlab would be i + 1

AP即为曲线下积分的面积

  """
   The Average Precision (AP) is the area under the curve
    (numerical integration)
    matlab: ap=sum((mrec(i)-mrec(i-1)).*mpre(i));
  """
  ap = 0.0
  for i in i_list:
    ap += ((mrec[i]-mrec[i-1])*mpre[i])
  return ap, mrec, mpre

4.3 mAP

根据每类的AP算出mAP

"""
 Calculate the AP for each class
"""
sum_AP = 0.0
ap_dictionary = {}
# open file to store the results
with open(results_files_path + "/results.txt", 'w') as results_file:
  results_file.write("# AP and precision/recall per class\n")
  count_true_positives = {}

。。。代码中有大量的代码用于画出曲线和显示出ground truth和图像上的prediction等,我们省去此部分。

  results_file.write("\n# mAP of all classes\n")
  mAP = sum_AP / n_classes
  text = "mAP = {0:.2f}%".format(mAP*100)
  results_file.write(text + "\n")
  print(text)

版权声明:本文为weixin_36474809原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。