EG version upgrade to 1.3
[ealt-edge.git] / example-apps / PDD / pcb-defect-detection / libs / val_libs / voc_eval.py
1 # --------------------------------------------------------
2 # Fast/er R-CNN
3 # Licensed under The MIT License [see LICENSE for details]
4 # Written by Bharath Hariharan
5 # --------------------------------------------------------
6 from __future__ import absolute_import
7 from __future__ import division
8 from __future__ import print_function
9
10 import xml.etree.ElementTree as ET
11 import os
12 import pickle
13 import numpy as np
14
15 import matplotlib.pyplot as plt
16 import pylab as pl
17 from sklearn.metrics import precision_recall_curve
18 from itertools import cycle
19
20 from libs.label_name_dict.label_dict import NAME_LABEL_MAP
21 from libs.configs import cfgs
22 from help_utils.tools import *
23
24 def write_voc_results_file(all_boxes, test_imgid_list, det_save_dir):
25   '''
26
27   :param all_boxes: is a list. each item reprensent the detections of a img.
28   the detections is a array. shape is [-1, 6]. [category, score, xmin, ymin, xmax, ymax]
29   Note that: if none detections in this img. that the detetions is : []
30
31   :param test_imgid_list:
32   :param det_save_path:
33   :return:
34   '''
35   for cls, cls_id in NAME_LABEL_MAP.items():
36     if cls == 'back_ground':
37       continue
38     print("Writing {} VOC resutls file".format(cls))
39
40     mkdir(det_save_dir)
41     det_save_path = os.path.join(det_save_dir, "det_"+cls+".txt")
42     with open(det_save_path, 'wt') as f:
43       for index, img_name in enumerate(test_imgid_list):
44         this_img_detections = all_boxes[index]
45
46         this_cls_detections = this_img_detections[this_img_detections[:, 0]==cls_id]
47         if this_cls_detections.shape[0] == 0:
48           continue # this cls has none detections in this img
49         for a_det in this_cls_detections:
50           f.write('{:s} {:.3f} {:.1f} {:.1f} {:.1f} {:.1f}\n'.
51                   format(img_name, a_det[1],
52                          a_det[2], a_det[3],
53                          a_det[4], a_det[5]))  # that is [img_name, score, xmin, ymin, xmax, ymax]
54
55
56 def parse_rec(filename):
57   """ Parse a PASCAL VOC xml file """
58   tree = ET.parse(filename)
59   objects = []
60   for obj in tree.findall('object'):
61     obj_struct = {}
62     obj_struct['name'] = obj.find('name').text
63     obj_struct['pose'] = obj.find('pose').text
64     obj_struct['truncated'] = int(obj.find('truncated').text)
65     obj_struct['difficult'] = int(obj.find('difficult').text)
66     bbox = obj.find('bndbox')
67     obj_struct['bbox'] = [int(bbox.find('xmin').text),
68                           int(bbox.find('ymin').text),
69                           int(bbox.find('xmax').text),
70                           int(bbox.find('ymax').text)]
71     objects.append(obj_struct)
72
73   return objects
74
75
76 def voc_ap(rec, prec, use_07_metric=False):
77   """ ap = voc_ap(rec, prec, [use_07_metric])
78   Compute VOC AP given precision and recall.
79   If use_07_metric is true, uses the
80   VOC 07 11 point method (default:False).
81   """
82   if use_07_metric:
83     # 11 point metric
84     ap = 0.
85     for t in np.arange(0., 1.1, 0.1):
86       if np.sum(rec >= t) == 0:
87         p = 0
88       else:
89         p = np.max(prec[rec >= t])
90       ap = ap + p / 11.
91   else:
92     # correct AP calculation
93     # first append sentinel values at the end
94     mrec = np.concatenate(([0.], rec, [1.]))
95     mpre = np.concatenate(([0.], prec, [0.]))
96
97     # compute the precision envelope
98     for i in range(mpre.size - 1, 0, -1):
99       mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
100
101     # to calculate area under PR curve, look for points
102     # where X axis (recall) changes value
103     i = np.where(mrec[1:] != mrec[:-1])[0]
104
105     # and sum (\Delta recall) * prec
106     ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
107   return ap
108
109
110 def voc_eval(detpath, annopath, test_imgid_list, cls_name, ovthresh=0.5,
111                  use_07_metric=False, use_diff=False):
112   '''
113
114   :param detpath:
115   :param annopath:
116   :param test_imgid_list: it 's a list that contains the img_name of test_imgs
117   :param cls_name:
118   :param ovthresh:
119   :param use_07_metric:
120   :param use_diff:
121   :return:
122   '''
123   # 1. parse xml to get gtboxes
124
125   # read list of images
126   imagenames = test_imgid_list
127
128   recs = {}
129   for i, imagename in enumerate(imagenames):
130     recs[imagename] = parse_rec(os.path.join(annopath, imagename+'.xml'))
131     # if i % 100 == 0:
132     #   print('Reading annotation for {:d}/{:d}'.format(
133     #     i + 1, len(imagenames)))
134
135   # 2. get gtboxes for this class.
136   class_recs = {}
137   num_pos = 0
138   # if cls_name == 'person':
139   #   print ("aaa")
140   for imagename in imagenames:
141     R = [obj for obj in recs[imagename] if obj['name'] == cls_name]
142     bbox = np.array([x['bbox'] for x in R])
143     if use_diff:
144       difficult = np.array([False for x in R]).astype(np.bool)
145     else:
146       difficult = np.array([x['difficult'] for x in R]).astype(np.bool)
147     det = [False] * len(R)
148     num_pos = num_pos + sum(~difficult)  # ignored the diffcult boxes
149     class_recs[imagename] = {'bbox': bbox,
150                              'difficult': difficult,
151                              'det': det} # det means that gtboxes has already been detected
152
153   # 3. read the detection file
154   detfile = os.path.join(detpath, "det_"+cls_name+".txt")
155   with open(detfile, 'r') as f:
156     lines = f.readlines()
157
158   # for a line. that is [img_name, confidence, xmin, ymin, xmax, ymax]
159   splitlines = [x.strip().split(' ') for x in lines]  # a list that include a list
160   image_ids = [x[0] for x in splitlines]  # img_id is img_name
161   confidence = np.array([float(x[1]) for x in splitlines])
162   BB = np.array([[float(z) for z in x[2:]] for x in splitlines])
163
164   nd = len(image_ids) # num of detections. That, a line is a det_box.
165   tp = np.zeros(nd)
166   fp = np.zeros(nd)
167
168   if BB.shape[0] > 0:
169     # sort by confidence
170     sorted_ind = np.argsort(-confidence)
171     sorted_scores = np.sort(-confidence)
172     BB = BB[sorted_ind, :]
173     image_ids = [image_ids[x] for x in sorted_ind]  #reorder the img_name
174
175     # go down dets and mark TPs and FPs
176     for d in range(nd):
177       R = class_recs[image_ids[d]]  # img_id is img_name
178       bb = BB[d, :].astype(float)
179       ovmax = -np.inf
180       BBGT = R['bbox'].astype(float)
181
182       if BBGT.size > 0:
183         # compute overlaps
184         # intersection
185         ixmin = np.maximum(BBGT[:, 0], bb[0])
186         iymin = np.maximum(BBGT[:, 1], bb[1])
187         ixmax = np.minimum(BBGT[:, 2], bb[2])
188         iymax = np.minimum(BBGT[:, 3], bb[3])
189         iw = np.maximum(ixmax - ixmin + 1., 0.)
190         ih = np.maximum(iymax - iymin + 1., 0.)
191         inters = iw * ih
192
193         # union
194         uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) +
195                (BBGT[:, 2] - BBGT[:, 0] + 1.) *
196                (BBGT[:, 3] - BBGT[:, 1] + 1.) - inters)
197
198         overlaps = inters / uni
199         ovmax = np.max(overlaps)
200         jmax = np.argmax(overlaps)
201
202       if ovmax > ovthresh:
203         if not R['difficult'][jmax]:
204           if not R['det'][jmax]:
205             tp[d] = 1.
206             R['det'][jmax] = 1
207           else:
208             fp[d] = 1.
209       else:
210         fp[d] = 1.
211
212   # 4. get recall, precison and AP
213   fp = np.cumsum(fp)
214   tp = np.cumsum(tp)
215   rec = tp / float(num_pos)
216   # avoid divide by zero in case the first detection matches a difficult
217   # ground truth
218   prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)
219   ap = voc_ap(rec, prec, use_07_metric=cfgs.USE_07_METRIC)
220
221   return rec, prec, ap
222
223
224 def do_python_eval(test_imgid_list, test_annotation_path):
225   AP_list = []
226   #import matplotlib.pyplot as plt
227   #import matplotlib.colors as colors
228   #color_list = colors.cnames.keys()[::6]
229
230   for cls, index in NAME_LABEL_MAP.items():
231     if cls == 'back_ground':
232       continue
233     recall, precision, AP = voc_eval(detpath=os.path.join(cfgs.EVALUATE_DIR, cfgs.VERSION),
234                                      test_imgid_list=test_imgid_list,
235                                      cls_name=cls,
236                                      annopath=test_annotation_path)
237     AP_list += [AP]
238     pl.plot(recall, precision, lw=2, label='{} (AP = {:.4f})'''.format(cls, AP))
239     print(10*"__")
240   pl.xlabel('Recall')
241   pl.ylabel('Precision')
242   pl.grid(True)
243   pl.ylim([0.0, 1.05])
244   pl.xlim([0.0, 1.0])
245   pl.title('Precision-Recall')
246   pl.legend(loc="lower left")     
247   pl.show()
248   pl.savefig(cfgs.VERSION+'_eval.jpg')
249   print("hello")
250   print("mAP is : {}".format(np.mean(AP_list)))
251
252
253 def voc_evaluate_detections(all_boxes, test_annotation_path, test_imgid_list):
254   '''
255
256   :param all_boxes: is a list. each item reprensent the detections of a img.
257
258   The detections is a array. shape is [-1, 6]. [category, score, xmin, ymin, xmax, ymax]
259   Note that: if none detections in this img. that the detetions is : []
260   :return:
261   '''
262   test_imgid_list = [item.split('.')[0] for item in test_imgid_list]
263
264   write_voc_results_file(all_boxes, test_imgid_list=test_imgid_list,
265                          det_save_dir=os.path.join(cfgs.EVALUATE_DIR, cfgs.VERSION))
266   do_python_eval(test_imgid_list, test_annotation_path=test_annotation_path)
267
268
269
270
271
272
273
274