# -------------------------------------------------------- # Fast R-CNN # Copyright (c) 2015 Microsoft # Licensed under The MIT License [see LICENSE for details] # Written by Ross Girshick and Xinlei Chen # -------------------------------------------------------- from __future__ import absolute_import from __future__ import division from __future__ import print_function import os from datasets.imdb import imdb import datasets.ds_utils as ds_utils import xml.etree.ElementTree as ET import numpy as np import scipy.sparse import scipy.io as sio import pickle import subprocess import uuid from .mydataset_eval import mydataset_eval from model.config import cfg import matplotlib.pyplot as plt import cv2 class mydataset(imdb): def __init__(self, image_set, dataset): name = dataset + '_' + image_set imdb.__init__(self, name) self._image_set = image_set self._root_path = '/media/rgh/rgh-data/Dataset/'+dataset#.capitalize() self._data_path = os.path.join(self._root_path, 'image', image_set) self._parsing_label_path = os.path.join(self._root_path, 'label', image_set) # self._classes = ('__background__', # always index 0 # 'face', 'hair', 'U-clothes', 'L-arm','R-arm', # 'pants', 'L-leg', 'R-leg', 'dress','L-shoe', # 'R-shoe') self._classes = ('__background__', 'Hat', 'Hair', 'Glove', 'Sunglasses', 'Upper-clothes', 'Dress', 'Coat', 'Socks', 'Pants', 'Jumpsuits', 'Scarf', 'Skirt', 'Face', 'Left-arm', 'Right-arm', 'Left-leg', 'Right-leg', 'Left-shoe', 'Right-shoe') self._class_to_ind = dict(list(zip(self.classes, list(range(self.num_classes))))) self._image_ext = '.jpg' self._parsing_label_ext = '.png' self._image_index = self._load_image_set_index() # Default to roidb handler self._roidb_handler = self.gt_roidb self.config = {'cleanup': False, 'use_diff': True, 'matlab_eval': False, 'rpn_file': None, 'min_size': 2} assert os.path.exists(self._root_path), \ 'dataset path does not exist: {}'.format(self._root_path) assert os.path.exists(self._data_path), \ 'Path does not exist: {}'.format(self._data_path) def parsing_label_path_at(self, i): return self.parsing_label_path_from_index(self._image_index[i]) def parsing_label_path_from_index(self, index): parsing_label_path = os.path.join(self._parsing_label_path, index + self._parsing_label_ext) assert os.path.exists(parsing_label_path), \ 'Path does not exist: {}'.format(parsing_label_path) return parsing_label_path def image_path_at(self, i): """ Return the absolute path to image i in the image sequence. """ return self.image_path_from_index(self._image_index[i]) def image_path_from_index(self, index): """ Construct an image path from the image's "index" identifier. """ image_path = os.path.join(self._data_path, index + self._image_ext) assert os.path.exists(image_path), \ 'Path does not exist: {}'.format(image_path) return image_path def _load_image_set_index(self): """ Load the indexes listed in this dataset's image set file. """ image_set_file = os.path.join(self._root_path, self._image_set + '.txt') assert os.path.exists(image_set_file), \ 'Path does not exist: {}'.format(image_set_file) with open(image_set_file) as f: image_index = [x.strip() for x in f.readlines()] return image_index def gt_roidb(self): """ Return the database of ground-truth regions of interest. This function loads/saves from/to a cache file to speed up future calls. """ cache_file = os.path.join(self.cache_path, self.name + '_gt_roidb.pkl') # if os.path.exists(cache_file): # with open(cache_file, 'rb') as fid: # try: # roidb = pickle.load(fid) # except: # roidb = pickle.load(fid, encoding='bytes') # print('{} gt roidb loaded from {}'.format(self.name, cache_file)) # return roidb # gt_roidb = [self._load_annotation(index) # for index in self.image_index] gt_roidb = [] error_index = [] for index in self.image_index: roi = self._load_annotation(index) if roi is not None: gt_roidb.append(roi) else: error_index.append(index) for index in error_index: self.image_index.remove(index) print('error images number: ', len(error_index)) print(error_index) with open(cache_file, 'wb') as fid: pickle.dump(gt_roidb, fid, pickle.HIGHEST_PROTOCOL) print('wrote gt roidb to {}'.format(cache_file)) return gt_roidb def rpn_roidb(self): if int(self._year) == 2007 or self._image_set != 'test': gt_roidb = self.gt_roidb() rpn_roidb = self._load_rpn_roidb(gt_roidb) roidb = imdb.merge_roidbs(gt_roidb, rpn_roidb) else: roidb = self._load_rpn_roidb(None) return roidb def _load_rpn_roidb(self, gt_roidb): filename = self.config['rpn_file'] print('loading {}'.format(filename)) assert os.path.exists(filename), \ 'rpn data not found at: {}'.format(filename) with open(filename, 'rb') as f: box_list = pickle.load(f) return self.create_roidb_from_box_list(box_list, gt_roidb) def _load_annotation(self, img_index): """ Load image and bounding boxes info from txt file. """ bbox_txt = open(os.path.join(self._root_path, 'bbox', self._image_set, img_index+'.txt'),'r') bboxs = [] num_objs = 0 for index, line in enumerate(bbox_txt): line = line.strip() if line != '': num_objs += 1 #print(line.split(' ')) x1, y1, x2, y2 = line.split(' ') # Make pixel indexes 0-based x1 = float(x1)# - 1 #if float(x1) > 1 else float(x1) y1 = float(y1)# - 1 #if float(y1) > 1 else float(y1) x2 = float(x2)# - 1 y2 = float(y2)# - 1 # 0 bk bboxs.append([index+1, [x1, y1, x2, y2],(x2 - x1 + 1) * (y2 - y1 + 1)]) boxes = np.zeros((num_objs, 4), dtype=np.uint16) gt_classes = np.zeros((num_objs), dtype=np.int32) overlaps = np.zeros((num_objs, self.num_classes), dtype=np.float32) seg_areas = np.zeros((num_objs), dtype=np.float32) for index, bbox in enumerate(bboxs): boxes[index, :] = bbox[1] gt_classes[index] = bbox[0] overlaps[index, bbox[0]] = 1.0 seg_areas[index] = bbox[2] overlaps = scipy.sparse.csr_matrix(overlaps) if cfg.SUB_CATEGORY: sub_category_txt = open(os.path.join(self._root_path, 'sub_category_3', self._image_set, img_index + '.txt'), 'r') subs = [] num_subs = 0 for index, line in enumerate(sub_category_txt): line = line.strip() if line != '': num_subs += 1 main, sub = line.split(' ') main = int(main) sub = int(sub) subs.append((main-1)*3+sub) # print(img_index, ' ', num_objs,' ', num_subs) if num_objs !=num_subs: return None sub_categorys = np.zeros((num_subs), dtype=np.int32) for index, sub in enumerate(subs): sub_categorys[index] = sub return {'boxes': boxes, 'gt_classes': gt_classes, 'sub_categorys': sub_categorys, 'gt_overlaps': overlaps, 'flipped': False, 'seg_areas': seg_areas, 'do_parsing': cfg.DO_PARSING} return {'boxes': boxes, 'gt_classes': gt_classes, 'gt_overlaps': overlaps, 'flipped': False, 'seg_areas': seg_areas, 'do_parsing': cfg.DO_PARSING} def _get_results_file_template(self): # VOCdevkit/results/VOC2007/Main/<comp_id>_det_test_aeroplane.txt filename = self._image_set + '_{:s}.txt' path = os.path.join( self._root_path,'detection_result', filename) return path def _write_results_file(self, all_boxes): for cls_ind, cls in enumerate(self.classes): if cls == '__background__': continue print('Writing {} results file'.format(cls)) filename = self._get_results_file_template().format(cls) with open(filename, 'wt') as f: for im_ind, index in enumerate(self.image_index): dets = all_boxes[cls_ind][im_ind] if dets == []: continue # the VOCdevkit expects 1-based indices for k in range(dets.shape[0]): f.write('{:s} {:.3f} {:.1f} {:.1f} {:.1f} {:.1f}\n'. format(index, dets[k, -1], dets[k, 0] + 1, dets[k, 1] + 1, dets[k, 2] + 1, dets[k, 3] + 1)) def _do_python_eval(self, output_dir='output'): annopath = os.path.join(self._root_path, 'bbox',self._image_set, '{:s}.txt') imagesetfile = os.path.join(self._root_path, self._image_set + '.txt') cachedir = os.path.join(self._root_path, 'annotations_cache') aps = [] recs = [] ### ADDED precs = [] ### ADDED nposes = [] ### ADDED tps = [] ### ADDED fps = [] ### ADDED # The PASCAL VOC metric changed in 2010 #use_07_metric = True if int(self._year) < 2010 else False #print('VOC07 metric? ' + ('Yes' if use_07_metric else 'No')) use_07_metric = False if not os.path.isdir(output_dir): os.mkdir(output_dir) for i, cls in enumerate(self._classes): if cls == '__background__': continue filename = self._get_results_file_template().format(cls) # add npos, tp, fp rec, prec, ap, npos, tp, fp = mydataset_eval( filename, annopath, imagesetfile, cls, cachedir, ovthresh=cfg.TEST.IOU_THRESH, use_07_metric=use_07_metric, use_diff=self.config['use_diff']) # plt.plot(rec, prec, lw=2, label=cls) # plt.xlabel('Recall') # plt.ylabel('Precision') # plt.grid(True) # plt.ylim([0.0, 1.05]) # plt.xlim([0.0, 1.0]) # plt.title('Precision-Recall') # plt.legend(loc="upper right") # plt.show() tps += [tp[-1]] ### ADDED fps += [fp[-1]] ### ADDED nposes += [npos] ### ADDED aps += [ap] recs += [rec[-1]] precs += [prec[-1]] #print(('AP for {} = {:.4f}'.format(cls, ap))) print(('{} AP: {:.4f}, Rec: {:.4f}, Prec: {:.4f}'.format(cls, ap, rec[-1], prec[-1]))) with open(os.path.join(output_dir, cls + '_pr.pkl'), 'wb') as f: pickle.dump({'rec': rec, 'prec': prec, 'ap': ap}, f) overall_recs = np.sum(tps) / float(np.sum(nposes)) ### ADDED overall_precs = np.sum(tps) / np.maximum(np.sum(tps) + np.sum(fps), np.finfo(np.float64).eps) ### ADDED print('Mean AP = {:.4f}'.format(np.mean(aps))) print('Overall Recall = {:.4f}'.format(overall_recs)) ### ADDED print('Overall Precision = {:.4f}'.format(overall_precs)) ### ADDED print('Overall F1 = {:.4f}'.format((2 * overall_recs * overall_precs) / (overall_recs + overall_precs))) ### ADDED print('~~~~~~~~') print('AP Results:') for ap in aps: print(('{:.3f}'.format(ap))) print(('{:.3f}'.format(np.mean(aps)))) print('~~~~~~~~') print('Recall Results:') for rec in recs: print(('{:.3f}'.format(rec))) print(('{:.3f}'.format(np.mean(overall_recs)))) # print('~~~~~~~~') # print('Prec Results:') # for prec in precs: # print(('{:.3f}'.format(prec))) # print(('{:.3f}'.format(np.mean(overall_precs)))) # print('~~~~~~~~') # print('') print('--------------------------------------------------------------') print('Results computed with the **unofficial** Python eval code.') print('Results should be very close to the official MATLAB eval code.') print('Recompute with `./tools/reval.py --matlab ...` for your paper.') print('-- Thanks, The Management') print('--------------------------------------------------------------') def evaluate_detections(self, all_boxes, output_dir): self._write_results_file(all_boxes) self._do_python_eval(output_dir) if self.config['matlab_eval']: self._do_matlab_eval(output_dir) if self.config['cleanup']: for cls in self._classes: if cls == '__background__': continue filename = self._get_results_file_template().format(cls) os.remove(filename) if __name__ == '__main__': from mydataset.mydataset import mydataset d = mydataset('lip') res = d.roidb from IPython import embed; embed()