|
import sys |
|
import os |
|
from natsort import natsorted |
|
import argparse |
|
from collections import OrderedDict |
|
import io |
|
import contextlib |
|
import itertools |
|
import numpy as np |
|
import mmcv |
|
from mmdet.datasets.api_wrappers import COCO, COCOeval |
|
sys.path.append('/home/caduser/KOTORI/vin-ssl/source') |
|
os.chdir('/home/caduser/KOTORI/vin-ssl/source') |
|
|
|
from base_config_track import get_config |
|
from mmdet_tools import mmdet_test |
|
|
|
def print_log(msg, logger): |
|
pass |
|
|
|
|
|
def evaluate(dataset, results, metric='bbox', logger=None, jsonfile_prefix=None, classwise=False, proposal_nums=(100, 300, 1000), iou_thrs=None, metric_items=None): |
|
"""Evaluation in COCO protocol. |
|
Args: |
|
results (list[list | tuple]): Testing results of the dataset. |
|
metric (str | list[str]): Metrics to be evaluated. Options are |
|
'bbox', 'segm', 'proposal', 'proposal_fast'. |
|
logger (logging.Logger | str | None): Logger used for printing |
|
related information during evaluation. Default: None. |
|
jsonfile_prefix (str | None): The prefix of json files. It includes |
|
the file path and the prefix of filename, e.g., "a/b/prefix". |
|
If not specified, a temp file will be created. Default: None. |
|
classwise (bool): Whether to evaluating the AP for each class. |
|
proposal_nums (Sequence[int]): Proposal number used for evaluating |
|
recalls, such as recall@100, recall@1000. |
|
Default: (100, 300, 1000). |
|
iou_thrs (Sequence[float], optional): IoU threshold used for |
|
evaluating recalls/mAPs. If set to a list, the average of all |
|
IoUs will also be computed. If not specified, [0.50, 0.55, |
|
0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95] will be used. |
|
Default: None. |
|
metric_items (list[str] | str, optional): Metric items that will |
|
be returned. If not specified, ``['AR@100', 'AR@300', |
|
'AR@1000', 'AR_s@1000', 'AR_m@1000', 'AR_l@1000' ]`` will be |
|
used when ``metric=='proposal'``, ``['mAP', 'mAP_50', 'mAP_75', |
|
'mAP_s', 'mAP_m', 'mAP_l']`` will be used when |
|
``metric=='bbox' or metric=='segm'``. |
|
Returns: |
|
dict[str, float]: COCO style evaluation metric. |
|
""" |
|
metrics = metric if isinstance(metric, list) else [metric] |
|
allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast'] |
|
for metric in metrics: |
|
if metric not in allowed_metrics: |
|
raise KeyError(f'metric {metric} is not supported') |
|
if iou_thrs is None: |
|
iou_thrs = np.linspace( |
|
.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) |
|
if metric_items is not None: |
|
if not isinstance(metric_items, list): |
|
metric_items = [metric_items] |
|
|
|
result_files, tmp_dir = dataset.format_results(results, jsonfile_prefix) |
|
|
|
eval_results = OrderedDict() |
|
cocoGt = dataset.coco |
|
|
|
results_per_category = [] |
|
|
|
for metric in metrics: |
|
msg = f'Evaluating {metric}...' |
|
if logger is None: |
|
msg = '\n' + msg |
|
print_log(msg, logger=logger) |
|
|
|
if metric == 'proposal_fast': |
|
ar = dataset.fast_eval_recall( |
|
results, proposal_nums, iou_thrs, logger='silent') |
|
log_msg = [] |
|
for i, num in enumerate(proposal_nums): |
|
eval_results[f'AR@{num}'] = ar[i] |
|
log_msg.append(f'\nAR@{num}\t{ar[i]:.4f}') |
|
log_msg = ''.join(log_msg) |
|
print_log(log_msg, logger=logger) |
|
continue |
|
|
|
iou_type = 'bbox' if metric == 'proposal' else metric |
|
if metric not in result_files: |
|
raise KeyError(f'{metric} is not in results') |
|
try: |
|
predictions = mmcv.load(result_files[metric]) |
|
if iou_type == 'segm': |
|
|
|
|
|
|
|
|
|
|
|
|
|
for x in predictions: |
|
x.pop('bbox') |
|
warnings.simplefilter('once') |
|
warnings.warn( |
|
'The key "bbox" is deleted for more accurate mask AP ' |
|
'of small/medium/large instances since v2.12.0. This ' |
|
'does not change the overall mAP calculation.', |
|
UserWarning) |
|
cocoDt = cocoGt.loadRes(predictions) |
|
except IndexError: |
|
print_log( |
|
'The testing results of the whole dataset is empty.', |
|
logger=logger, |
|
level=logging.ERROR) |
|
break |
|
|
|
cocoEval = COCOeval(cocoGt, cocoDt, iou_type) |
|
cocoEval.params.catIds = dataset.cat_ids |
|
cocoEval.params.imgIds = dataset.img_ids |
|
cocoEval.params.maxDets = list(proposal_nums) |
|
cocoEval.params.iouThrs = iou_thrs |
|
|
|
coco_metric_names = { |
|
'mAP': 0, |
|
'mAP_50': 1, |
|
'mAP_75': 2, |
|
'mAP_s': 3, |
|
'mAP_m': 4, |
|
'mAP_l': 5, |
|
'AR@100': 6, |
|
'AR@300': 7, |
|
'AR@1000': 8, |
|
'AR_s@1000': 9, |
|
'AR_m@1000': 10, |
|
'AR_l@1000': 11 |
|
} |
|
if metric_items is not None: |
|
for metric_item in metric_items: |
|
if metric_item not in coco_metric_names: |
|
raise KeyError( |
|
f'metric item {metric_item} is not supported') |
|
|
|
if metric == 'proposal': |
|
cocoEval.params.useCats = 0 |
|
cocoEval.evaluate() |
|
cocoEval.accumulate() |
|
|
|
|
|
redirect_string = io.StringIO() |
|
with contextlib.redirect_stdout(redirect_string): |
|
cocoEval.summarize() |
|
print_log('\n' + redirect_string.getvalue(), logger=logger) |
|
|
|
if metric_items is None: |
|
metric_items = [ |
|
'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000', |
|
'AR_m@1000', 'AR_l@1000' |
|
] |
|
|
|
for item in metric_items: |
|
val = float( |
|
f'{cocoEval.stats[coco_metric_names[item]]:.3f}') |
|
eval_results[item] = val |
|
else: |
|
cocoEval.evaluate() |
|
cocoEval.accumulate() |
|
|
|
|
|
redirect_string = io.StringIO() |
|
with contextlib.redirect_stdout(redirect_string): |
|
cocoEval.summarize() |
|
print_log('\n' + redirect_string.getvalue(), logger=logger) |
|
|
|
if classwise: |
|
|
|
|
|
precisions = cocoEval.eval['precision'] |
|
|
|
assert len(dataset.cat_ids) == precisions.shape[2] |
|
|
|
for idx, catId in enumerate(dataset.cat_ids): |
|
|
|
|
|
nm = dataset.coco.loadCats(catId)[0] |
|
precision = precisions[:, :, idx, 0, -1] |
|
precision = precision[precision > -1] |
|
if precision.size: |
|
ap = np.mean(precision) |
|
else: |
|
ap = float('nan') |
|
results_per_category.append( |
|
(f'{nm["name"]}', float(ap))) |
|
|
|
num_columns = min(6, len(results_per_category) * 2) |
|
results_flatten = list( |
|
itertools.chain(*results_per_category)) |
|
headers = ['category', 'AP'] * (num_columns // 2) |
|
results_2d = itertools.zip_longest(*[ |
|
results_flatten[i::num_columns] |
|
for i in range(num_columns) |
|
]) |
|
|
|
if metric_items is None: |
|
metric_items = [ |
|
'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l' |
|
] |
|
|
|
for metric_item in metric_items: |
|
key = f'{metric}_{metric_item}' |
|
val = float( |
|
f'{cocoEval.stats[coco_metric_names[metric_item]]:.3f}' |
|
) |
|
eval_results[key] = val |
|
ap = cocoEval.stats[:6] |
|
eval_results[f'{metric}_mAP_copypaste'] = ( |
|
f'{ap[0]:.3f} {ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} ' |
|
f'{ap[4]:.3f} {ap[5]:.3f}') |
|
if tmp_dir is not None: |
|
tmp_dir.cleanup() |
|
return eval_results, results_per_category |
|
|
|
|
|
def get_args(): |
|
parser = argparse.ArgumentParser(description='Test trained object detection model') |
|
parser.add_argument( |
|
'--experiment_name', '-exp-name', type=str, default='no-exp',help='providing folder store checkpoint models') |
|
return parser.parse_args() |
|
|
|
if __name__ == "__main__": |
|
|
|
args = get_args() |
|
experiment_name = args.experiment_name |
|
print ("**********" * 3) |
|
print ('Staring evaluation process') |
|
checkpoints = os.listdir(os.path.join('../trained_weights', experiment_name)) |
|
checkpoints = natsorted(checkpoints) |
|
checkpoints = [p for p in checkpoints if 'epoch_' in p] |
|
|
|
|
|
selected_checkpoints = checkpoints[-1:] |
|
dict_results = {} |
|
valid_dict_results = {} |
|
eval_on_valid = False |
|
|
|
for checkpoint_name in selected_checkpoints: |
|
print ('-----'*5) |
|
print ('Processing for checkpoint', checkpoint_name) |
|
checkpoint = os.path.join('../trained_weights', experiment_name, checkpoint_name) |
|
|
|
results = {} |
|
results_dir = 'results' |
|
os.makedirs(results_dir, exist_ok=True) |
|
|
|
results_avg = [] |
|
results_avg_ar = [] |
|
results_classwise = [] |
|
|
|
cfg = get_config() |
|
|
|
if eval_on_valid: |
|
cfg.data.test['img_prefix'] = './data/' |
|
cfg.data.test['ann_file'] = './data/valid_annotations.json' |
|
|
|
args_result = argparse.Namespace(eval='bbox', out='results/' + experiment_name + '.pkl', checkpoint=None, work_dir=results_dir, fuse_conv_bn=None, |
|
gpu_ids=None, format_only=None, show=None, show_dir=None, show_score_thr=0.3, gpu_collect=None, |
|
tmpdir=None, cfg_options=None, options=None, launcher='none', eval_options=None, local_rank=0) |
|
|
|
dataset, outputs = mmdet_test.get_outputs(cfg, checkpoint, args_result) |
|
|
|
metrics, results_per_category = evaluate(dataset, outputs, metric='bbox', classwise=True) |
|
metrics_ar, _ = evaluate(dataset, outputs, metric='proposal') |
|
results_avg.append([experiment_name, metrics]) |
|
results_avg_ar.append([experiment_name, metrics_ar]) |
|
results_classwise.append([experiment_name, OrderedDict(results_per_category)]) |
|
|
|
print('--------------------------------') |
|
valid_dict_results[checkpoint_name] = [] |
|
print('Average Precision') |
|
print(list(results_avg[0][1].keys())[:-1]) |
|
|
|
valid_dict_results[checkpoint_name].append(list(results_avg[0][1].keys())[:-1]) |
|
|
|
for res in results_avg: |
|
print([res[0], list(res[1].values())[:-1]]) |
|
valid_dict_results[checkpoint_name].append([res[0], list(res[1].values())[:-1]]) |
|
|
|
dict_results[checkpoint_name] = list(results_avg[0][1].values())[1] |
|
print ("Results on testing set") |
|
print (valid_dict_results) |
|
print("**********" * 3) |
|
|