###########################################################################################
#                                                                                         #
# This sample shows how to evaluate object detections applying the following metrics:     #
#  * Precision x Recall curve       ---->       used by VOC PASCAL 2012)                  #
#  * Average Precision (AP)         ---->       used by VOC PASCAL 2012)                  #
#                                                                                         #
# Developed by: Rafael Padilla (rafael.padilla@smt.ufrj.br)                               #
#        SMT - Signal Multimedia and Telecommunications Lab                               #
#        COPPE - Universidade Federal do Rio de Janeiro                                   #
#        Last modification: Oct 9th 2018                                                 #
###########################################################################################
import argparse
import glob
import os
import shutil
# from argparse import RawTextHelpFormatter
import sys

import _init_paths
from BoundingBox import BoundingBox
from BoundingBoxes import BoundingBoxes
from Evaluator import *
from utils import BBFormat
import json
from pprint import pprint as pp

inter_mode = None
if os.getenv('pr_inter'):
    if os.getenv('pr_inter') == '11':
        inter_mode = MethodAveragePrecision.ElevenPointInterpolation
    elif os.getenv('pr_inter') == 'every':
        inter_mode = MethodAveragePrecision.EveryPointInterpolation
    else:
        inter_mode = None
    print('inter mode is: {}'.format(inter_mode))


if not (os.getenv('hikmars_version') and os.getenv('hikmars_version') == '3'):
    try:
        from hikframe.dl.base.evaluate import Evaluation
        EL = Evaluation()
    except:
        pass


def ValidateFormats(argFormat, argName, errors):
    if argFormat == 'xywh':
        return BBFormat.XYWH
    elif argFormat == 'xyrb':
        return BBFormat.XYX2Y2
    elif argFormat is None:
        return BBFormat.XYWH  # default when nothing is passed
    else:
        errors.append(
            'argument %s: invalid value. It must be either \'xywh\' or \'xyrb\''
            % argName)


# Validate mandatory args
def ValidateMandatoryArgs(arg, argName, errors):
    if arg is None:
        errors.append('argument %s: required argument' % argName)
    else:
        return True


def ValidateImageSize(arg, argName, argInformed, errors):
    errorMsg = 'argument %s: required argument if %s is relative' % (
        argName, argInformed)
    ret = None
    if arg is None:
        errors.append(errorMsg)
    else:
        arg = arg.replace('(', '').replace(')', '')
        args = arg.split(',')
        if len(args) != 2:
            errors.append(
                '%s. It must be in the format \'width,height\' (e.g. \'600,400\')'
                % errorMsg)
        else:
            if not args[0].isdigit() or not args[1].isdigit():
                errors.append(
                    '%s. It must be in INdiaTEGER the format \'width,height\' (e.g. \'600,400\')'
                    % errorMsg)
            else:
                ret = (int(args[0]), int(args[1]))
    return ret


# Validate coordinate types
def ValidateCoordinatesTypes(arg, argName, errors):
    if arg == 'abs':
        return CoordinatesType.Absolute
    elif arg == 'rel':
        return CoordinatesType.Relative
    elif arg is None:
        return CoordinatesType.Absolute  # default when nothing is passed
    errors.append(
        'argument %s: invalid value. It must be either \'rel\' or \'abs\'' %
        argName)


def ValidatePaths(arg, nameArg, errors):
    if arg is None:
        errors.append('argument %s: invalid directory' % nameArg)
    elif os.path.isdir(arg) is False and os.path.isdir(
            os.path.join(currentPath, arg)) is False:
        errors.append('argument %s: directory does not exist \'%s\'' %
                      (nameArg, arg))
    elif os.path.isdir(os.path.join(currentPath, arg)) is True:
        arg = os.path.join(currentPath, arg)
    else:
        errors.append('argument %s: invalid directory' % nameArg)
    return arg


def getBoundingBoxes(directory,
                     isGT,
                     bbFormat,
                     coordType,
                     allBoundingBoxes=None,
                     allClasses=None,
                     imgSize=(0, 0)):
    """Read txt files containing bounding boxes (ground truth and detections)."""
    if allBoundingBoxes is None:
        allBoundingBoxes = BoundingBoxes()
    if allClasses is None:
        allClasses = []
    # Read ground truths
    os.chdir(directory)
    files = glob.glob("*.txt")
    files.sort()
    # Read GT detections from txt file
    # Each line of the files in the groundtruths folder represents a ground truth bounding box
    # (bounding boxes that a detector should detect)
    # Each value of each line is  "class_id, x, y, width, height" respectively
    # Class_id represents the class of the bounding box
    # x, y represents the most top-left coordinates of the bounding box
    # x2, y2 represents the most bottom-right coordinates of the bounding box
    for f in files:
        nameOfImage = f.replace(".txt", "")
        fh1 = open(f, "r")
        for line in fh1:
            line = line.replace("\n", "")
            if line.replace(' ', '') == '':
                continue
            splitLine = line.split(" ")
            if isGT:
                # idClass = int(splitLine[0]) #class
                idClass = (splitLine[0])  # class
                x = float(splitLine[1])
                y = float(splitLine[2])
                w = float(splitLine[3])
                h = float(splitLine[4])
                index = float(splitLine[5])
                bb = BoundingBox(nameOfImage,
                                 idClass,
                                 x,
                                 y,
                                 w,
                                 h,
                                 index,
                                 coordType,
                                 imgSize,
                                 BBType.GroundTruth,
                                 format=bbFormat)
            else:
                # idClass = int(splitLine[0]) #class
                idClass = (splitLine[0])  # class
                confidence = float(splitLine[1])
                x = float(splitLine[2])
                y = float(splitLine[3])
                w = float(splitLine[4])
                h = float(splitLine[5])
                index = float(splitLine[6])
                bb = BoundingBox(nameOfImage,
                                 idClass,
                                 x,
                                 y,
                                 w,
                                 h,
                                 index,
                                 coordType,
                                 imgSize,
                                 BBType.Detected,
                                 confidence,
                                 format=bbFormat)
            allBoundingBoxes.addBoundingBox(bb)
            if idClass not in allClasses:
                allClasses.append(idClass)
        fh1.close()
    return allBoundingBoxes, allClasses


# Get current path to set default folders
currentPath = os.path.dirname(os.path.abspath(__file__))

VERSION = '0.1 (beta)'

parser = argparse.ArgumentParser(
    prog='Object Detection Metrics - Pascal VOC',
    description='This project applies the most popular metrics used to evaluate object detection '
    'algorithms.\nThe current implemention runs the Pascal VOC metrics.\nFor further references, '
    'please check:\nhttps://github.com/rafaelpadilla/Object-Detection-Metrics',
    epilog="Developed by: Rafael Padilla (rafael.padilla@smt.ufrj.br)")
# formatter_class=RawTextHelpFormatter)
parser.add_argument('-v',
                    '--version',
                    action='version',
                    version='%(prog)s ' + VERSION)
# Positional arguments
# Mandatory
parser.add_argument('-gt',
                    '--gtfolder',
                    dest='gtFolder',
                    default=os.path.join(currentPath, 'groundtruths'),
                    metavar='',
                    help='folder containing your ground truth bounding boxes')
parser.add_argument('-det',
                    '--detfolder',
                    dest='detFolder',
                    default=os.path.join(currentPath, 'detections'),
                    metavar='',
                    help='folder containing your detected bounding boxes')
# Optional
parser.add_argument('-st',
                    '--scoreThreshold',
                    dest='scoreThreshold',
                    type=float,
                    default=0.5,
                    metavar='',
                    help='IOU threshold. Default 0.5')

parser.add_argument('-it',
                    '--IOUThreshold',
                    dest='IOUThreshold',
                    type=float,
                    default=0.5,
                    metavar='',
                    help='IOU threshold. Default 0.5')
parser.add_argument(
    '-gtformat',
    dest='gtFormat',
    metavar='',
    default='xywh',
    help='format of the coordinates of the ground truth bounding boxes: '
    '(\'xywh\': <left> <top> <width> <height>)'
    ' or (\'xyrb\': <left> <top> <right> <bottom>)')
parser.add_argument(
    '-detformat',
    dest='detFormat',
    metavar='',
    default='xywh',
    help='format of the coordinates of the detected bounding boxes '
    '(\'xywh\': <left> <top> <width> <height>) '
    'or (\'xyrb\': <left> <top> <right> <bottom>)')
parser.add_argument(
    '-gtcoords',
    dest='gtCoordinates',
    default='abs',
    metavar='',
    help='reference of the ground truth bounding box coordinates: absolute '
    'values (\'abs\') or relative to its image size (\'rel\')')
parser.add_argument(
    '-detcoords',
    default='abs',
    dest='detCoordinates',
    metavar='',
    help='reference of the ground truth bounding box coordinates: '
    'absolute values (\'abs\') or relative to its image size (\'rel\')')
parser.add_argument(
    '-imgsize',
    dest='imgSize',
    metavar='',
    help='image size. Required if -gtcoords or -detcoords are \'rel\'')
parser.add_argument('-sp',
                    '--savepath',
                    dest='savePath',
                    metavar='',
                    help='folder where the plots are saved')
parser.add_argument('-np',
                    '--noplot',
                    dest='showPlot',
                    action='store_false',
                    help='no plot is shown during execution')

parser.add_argument('-methods',
                    dest='methods',
                    metavar='',
                    help='index to be saved')

args = parser.parse_args()

scoreThreshold = args.scoreThreshold
IOUThreshold = args.IOUThreshold

# Arguments validation
errors = []
# Validate formats
gtFormat = ValidateFormats(args.gtFormat, '-gtformat', errors)
detFormat = ValidateFormats(args.detFormat, '-detformat', errors)
# Groundtruth folder
if ValidateMandatoryArgs(args.gtFolder, '-gt/--gtfolder', errors):
    gtFolder = ValidatePaths(args.gtFolder, '-gt/--gtfolder', errors)
else:
    # errors.pop()
    gtFolder = os.path.join(currentPath, 'groundtruths')
    if os.path.isdir(gtFolder) is False:
        errors.append('folder %s not found' % gtFolder)
# Coordinates types
gtCoordType = ValidateCoordinatesTypes(args.gtCoordinates, '-gtCoordinates',
                                       errors)
detCoordType = ValidateCoordinatesTypes(args.detCoordinates, '-detCoordinates',
                                        errors)
imgSize = (0, 0)
if gtCoordType == CoordinatesType.Relative:  # Image size is required
    imgSize = ValidateImageSize(args.imgSize, '-imgsize', '-gtCoordinates',
                                errors)
if detCoordType == CoordinatesType.Relative:  # Image size is required
    imgSize = ValidateImageSize(args.imgSize, '-imgsize', '-detCoordinates',
                                errors)
# Detection folder
if ValidateMandatoryArgs(args.detFolder, '-det/--detfolder', errors):
    detFolder = ValidatePaths(args.detFolder, '-det/--detfolder', errors)
else:
    # errors.pop()
    detFolder = os.path.join(currentPath, 'detections')
    if os.path.isdir(detFolder) is False:
        errors.append('folder %s not found' % detFolder)
if args.savePath is not None:
    savePath = ValidatePaths(args.savePath, '-sp/--savepath', errors)
else:
    savePath = os.path.join(currentPath, 'results')
# Validate savePath
# If error, show error messages
if len(errors) != 0:
    print(
        """usage: Object Detection Metrics [-h] [-v] [-gt] [-det] [-t] [-gtformat]
                                [-detformat] [-save]""")
    print('Object Detection Metrics: error(s): ')
    [print(e) for e in errors]
    sys.exit()

# Show plot during execution
showPlot = args.showPlot

methods_show = args.methods
methods_show = methods_show.split(',')
print('methods:', methods_show)
# # Clear folder and save results
# shutil.rmtree(savePath, ignore_errors=True)
# os.makedirs(savePath)
# generate results
results_path = os.path.join(savePath, 'results')
metrics_path = os.path.join(savePath, 'metrics')
pr_path = os.path.join(metrics_path, 'PR')
# shutil.rmtree(results_path, ignore_errors=True)
# shutil.rmtree(metrics_path, ignore_errors=True)
os.makedirs(results_path, exist_ok=True)
os.makedirs(metrics_path, exist_ok=True)
os.makedirs(pr_path, exist_ok=True)

# print('IOUThreshold= %f' % IOUThreshold)
# print('savePath = %s' % savePath)
# print('gtFormat = %s' % gtFormat)
# print('detFormat = %s' % detFormat)
# print('gtFolder = %s' % gtFolder)
# print('detFolder = %s' % detFolder)
# print('gtCoordType = %s' % gtCoordType)
# print('detCoordType = %s' % detCoordType)
# print('showPlot %s' % showPlot)

# Get groundtruth boxes
allBoundingBoxes, allClasses = getBoundingBoxes(gtFolder,
                                                True,
                                                gtFormat,
                                                gtCoordType,
                                                imgSize=imgSize)
# Get detected boxes
allBoundingBoxes, allClasses = getBoundingBoxes(detFolder,
                                                False,
                                                detFormat,
                                                detCoordType,
                                                allBoundingBoxes,
                                                allClasses,
                                                imgSize=imgSize)
allClasses.sort()

evaluator = Evaluator()
# acc_AP = 0

# macro_prec = 0
# macro_recall = 0
# macro_prec_recall85 = 0
# macro_f1score = 0

# all_tp = 0
# all_fp_duplicate = 0
# all_fp_location = 0
# all_fp_category = 0
# all_sample = 0
# result_path = os.path.join(savePath, 'results.txt')
# shutil.rmtree(result_path)


all_tp = 0
all_fp_duplicate = 0
all_fp_location = 0
all_fp_category = 0
all_sample = 0

# metrics.json
metric_dict = {
    "index": {
        "brief": {},
        "detail": {}
    },
    "chart": {
        "FP_view": {
            "right": 0,
            "wrong_duplicate": 0,
            "wrong_location": 0,
            "wrong_category": 0
        },
        "FN_view": {
            "right": 0,
            "wrong_location": 0,
            "wrong_category": 0
        }
    }
}
# metric_dict = {}

# """
# Macro-Precision,Macro-Recall,Macro-F1-Score
# """
# detections, gt_ret = evaluator.GetF1Metrics(results_path,
#                                             allBoundingBoxes,
#                                             scoreThreshold=scoreThreshold,
#                                             IOUThreshold=IOUThreshold)

# # f = open(result_path, 'a')
# # f.write('Precision/Recall/f1score/acc per class(scoreThreshold={},IOUThreshold={}):'.format(scoreThreshold, IOUThreshold))

# validClasses = 0
# # each detection is a class
# for metricsPerClass in detections:

#     # Get metric values per each class
#     cl = metricsPerClass['class']
#     precision = metricsPerClass['precision']
#     recall = metricsPerClass['recall']
#     f1score = metricsPerClass['f1score']
#     totalTP = metricsPerClass['total TP']
#     FP_duplicate = metricsPerClass['total FP_duplicate']
#     FP_category = metricsPerClass['total FP_category']
#     FP_location = metricsPerClass['total FP_location']
#     totalPositives = metricsPerClass['total positives']

#     macro_prec += precision
#     macro_recall += recall
#     macro_f1score += f1score

#     if totalPositives > 0:
#         validClasses = validClasses + 1
#         p_str = "{0:.2f}%".format(precision * 100)
#         r_str = "{0:.2f}%".format(recall * 100)
#         f_str = "{0:.2f}%".format(f1score * 100)
#         print('Precision: {} ({})'.format(p_str, cl))
#         print('Recall: {} ({})'.format(r_str, cl))
#         print('F1-Score: {} ({})'.format(f_str, cl))

#         metric_dict["index"]["detail"][cl] = {}
#         cls_detail = metric_dict["index"]["detail"][cl]
#         if "Macro-Precision" in methods_show:
#             cls_detail["Precision"] = p_str
#         if "Macro-Recall" in methods_show:
#             cls_detail["Recall"] = r_str
#         if "Macro-F1-Score" in methods_show:
#             cls_detail["F1-Score"] = f_str

#         all_tp += totalTP
#         all_fp_duplicate += FP_duplicate
#         all_fp_category += FP_category
#         all_fp_location += FP_location
#         all_sample += totalPositives

# macro_prec = macro_prec / validClasses
# macro_recall = macro_recall / validClasses
# macro_f1score = macro_f1score / validClasses

# macro_prec_str = "{0:.2f}%".format(macro_prec * 100)
# print('Macro-Precision: {}'.format(macro_prec_str))
# macro_recall_str = "{0:.2f}%".format(macro_recall * 100)
# print('Macro-Recall: {}'.format(macro_recall_str))
# macro_f1score_str = "{0:.2f}%".format(macro_f1score * 100)
# print('Macro-F1-Score: {}'.format(macro_f1score_str))
# # pdb.set_trace()
# # acc = all_tp / all_sample
# # acc_str = "{0:.2f}%".format(acc * 100)
# # print('Accuracy: {}'.format(acc_str))

# cls_brief = metric_dict["index"]["brief"]
# if "Macro-Precision" in methods_show:
#     cls_brief["Macro-Precision"] = macro_prec_str
# if "Macro-Recall" in methods_show:
#     cls_brief["Macro-Recall"] = macro_recall_str
# if "Macro-F1-Score" in methods_show:
#     cls_brief["Macro-F1-Score"] = macro_f1score_str
# # cls_brief["Accuracy"] = acc_str

# FP_view = metric_dict["chart"]["FP_view"]
# FP_view["right"] = int(all_tp)
# FP_view["wrong_duplicate"] = int(all_fp_duplicate)
# FP_view["wrong_location"] = int(all_fp_location)
# FP_view["wrong_category"] = int(all_fp_category)

# detected_gt = gt_ret["detected_gt"]
# FN_category = gt_ret["FN_category"]
# FN_location = gt_ret["FN_location"]
# FN_view = metric_dict["chart"]["FN_view"]
# FN_view["right"] = int(detected_gt)
# FN_view["wrong_category"] = int(FN_category)
# FN_view["wrong_location"] = int(FN_location)

# print('---')

# """
# mAP
# """
# # Plot Precision x Recall curve
# detections = evaluator.PlotPrecisionRecallCurve(
#     # Object containing all bounding boxes (ground truths and detections)
#     allBoundingBoxes,
#     IOUThreshold=IOUThreshold,  # IOU threshold
#     method=MethodAveragePrecision.EveryPointInterpolation,
#     showAP=True,  # Show Average Precision in the title of the plot
#     showInterpolatedPrecision=False,  # Don't plot the interpolated precision curve
#     savePath=pr_path,
#     showGraphic=showPlot)

# # f = open(os.path.join(savePath, 'results.txt'), 'w')
# # f.write('Object Detection Metrics\n')
# # f.write('https://github.com/rafaelpadilla/Object-Detection-Metrics\n\n\n')
# # f.write('Average Precision (AP), Precision and Recall per class:')

# # each detection is a class
# validClasses = 0
# for metricsPerClass in detections:
#     # Get metric values per each class
#     cl = metricsPerClass['class']
#     ap = metricsPerClass['AP']
#     totalPositives = metricsPerClass['total positives']
#     total_TP = metricsPerClass['total TP']
#     total_FP = metricsPerClass['total FP']

#     if totalPositives > 0:
#         validClasses = validClasses + 1
#         acc_AP = acc_AP + ap
#         # prec = ['%.2f' % p for p in precision]
#         # rec = ['%.2f' % r for r in recall]
#         ap_str = "{0:.2f}%".format(ap * 100)
#         # ap_str = "{0:.4f}%".format(ap * 100)
#         print('AP: %s (%s)' % (ap_str, cl))
#         # f.write('\n\nClass: %s' % cl)
#         # f.write('\nAP: %s' % ap_str)
#         # f.write('\nPrecision: %s' % prec)
#         # f.write('\nRecall: %s' % rec)
#         cls_detail = metric_dict["index"]["detail"][cl]
#         if "mAP" in methods_show:
#             cls_detail["AP"] = ap_str
#             pr_img_path = os.path.join(pr_path, '{}.png'.format(cl))
#             if os.path.exists(pr_img_path):
#                 cls_detail["pr_img_path"] = pr_img_path

# mAP = acc_AP / validClasses
# mAP_str = "{0:.2f}%".format(mAP * 100)
# print('mAP: %s' % mAP_str)
# cls_brief = metric_dict["index"]["brief"]
# if "mAP" in methods_show:
#     cls_brief["mAP"] = mAP_str
# print('---')

# tmp_list = []
# for k, v in metric_dict["index"]["detail"].items():
#     tmp_list.append({k: v})
# metric_dict["index"]["detail"] = tmp_list
# pp(metric_dict)

metric_file = os.path.join(metrics_path, 'metrics.json')
# shutil.rmtree(metric_file, ignore_errors=True)


"""
Precision,Recall, mAP
"""
# Plot Precision x Recall curve
detections, gt_ret = evaluator.PlotPrecisionRecallCurve(
    # Object containing all bounding boxes (ground truths and detections)
    allBoundingBoxes,
    IOUThreshold=IOUThreshold,  # IOU threshold
    scoreThreshold=scoreThreshold,  # scoreThreshold
    # method=MethodAveragePrecision.,
    method=inter_mode,
    showAP=True,  # Show Average Precision in the title of the plot
    showInterpolatedPrecision=False,  # Don't plot the interpolated precision curve
    results_path=results_path,
    savePath=pr_path,
    showGraphic=showPlot)

# f = open(os.path.join(savePath, 'results.txt'), 'w')
# f.write('Object Detection Metrics\n')
# f.write('https://github.com/rafaelpadilla/Object-Detection-Metrics\n\n\n')
# f.write('Average Precision (AP), Precision and Recall per class:')

# each detection is a class
validClasses = 0
acc_pre = []
acc_rec = []
acc_f1 = []
acc_pre_for_rc85 = []
acc_AP = []

for metricsPerClass in detections:
    # Get metric values per each class
    cl = metricsPerClass['class']
    ap = metricsPerClass['AP']
    # precision = metricsPerClass['precision']
    # recall = metricsPerClass['recall']

    mpre = metricsPerClass['ret_prec']
    mrec = metricsPerClass['ret_rec']
    mf1 = metricsPerClass['ret_f1']
    mprec_for_rc85 = metricsPerClass['ret_prec85']

    totalPositives = metricsPerClass['total positives']
    total_TP = metricsPerClass['total TP']
    total_FP = metricsPerClass['total FP']

    if totalPositives > 0:
        validClasses = validClasses + 1
        acc_AP.append(ap)
        acc_pre.append(mpre)
        acc_rec.append(mrec)
        acc_f1.append(mf1)
        acc_pre_for_rc85.append(mprec_for_rc85)

        ap_str = "{0:.2f}%".format(ap*100)
        pre_str = "{0:.2f}%".format(mpre*100)
        recall_str = "{0:.2f}%".format(mrec*100)
        f1_str = "{0:.2f}%".format(mf1*100)
        pre_f_rc85_str = "{0:.2f}%".format(mprec_for_rc85*100)
        print('-------------------')
        print(f'class id: {cl}')
        print(f'ap: {ap_str}')
        print(f'precision: {pre_str}')
        print(f'recall: {recall_str}')
        print(f'f1score: {f1_str}')
        print(f'precision_for_recall0.85: {pre_f_rc85_str}')
        print('-------------------')
        metric_dict["index"]["detail"][cl] = {}
        cls_detail = metric_dict["index"]["detail"][cl]
        if "mAP" in methods_show:
            cls_detail["AP"] = ap_str
            pr_img_path = os.path.join(pr_path, '{}.png'.format(cl))
            if os.path.exists(pr_img_path):
                cls_detail["pr_img_path"] = pr_img_path
        if "Macro-Precision" in methods_show:
            cls_detail["Precision"] = pre_str
            cls_detail["precision_for_recall0.85"] = pre_f_rc85_str
        if "Macro-Recall" in methods_show:
            cls_detail["Recall"] = recall_str
        if "Macro-F1-Score" in methods_show:
            cls_detail["F1-Score"] = f1_str

        totalTP = metricsPerClass['total TP']
        FP_duplicate = metricsPerClass['total FP_duplicate']
        FP_category = metricsPerClass['total FP_category']
        FP_location = metricsPerClass['total FP_location']
        totalPositives = metricsPerClass['total positives']
        all_tp += totalTP
        all_fp_duplicate += FP_duplicate
        all_fp_category += FP_category
        all_fp_location += FP_location
        all_sample += totalPositives


mAP = sum(acc_AP) / validClasses
mAP_str = "{0:.2f}%".format(mAP * 100)

mpre = sum(acc_pre) / validClasses
mpre_str = "{0:.2f}%".format(mpre * 100)

mrec = sum(acc_rec) / validClasses
mrec_str = "{0:.2f}%".format(mrec * 100)

mf1 = sum(acc_f1) / validClasses
mf1_str = "{0:.2f}%".format(mf1 * 100)

mpre_f_rec85 = sum(acc_pre_for_rc85) / validClasses
mpre_f_rec85_str = "{0:.2f}%".format(mpre_f_rec85 * 100)
print('[!!!]calculate detail:')
print('validClasses nums:', validClasses)
print('mAP(%)=sum(', acc_AP, ')/', validClasses)
print('mPrecision(%)=sum(', acc_pre, ')/', validClasses)
print('mRecall(%)=sum(', acc_rec, ')/', validClasses)
print('mF1score(%)=sum(', acc_f1, ')/', validClasses)
print('mPrecision_for_recall85(%)=sum(', acc_pre_for_rc85, ')/', validClasses)
print('\n')
# # print('mAP: %s' % mAP_str)
cls_brief = metric_dict["index"]["brief"]
if "mAP" in methods_show:
    cls_brief["mAP"] = mAP_str
if "Macro-Precision" in methods_show:
    cls_brief["Macro-Precision"] = mpre_str
    cls_brief["Macro-Precision_for_recall85"] = mpre_f_rec85_str
if "Macro-Recall" in methods_show:
    cls_brief["Macro-Recall"] = mrec_str
if "Macro-F1-Score" in methods_show:
    cls_brief["Macro-F1-Score"] = mf1_str


FP_view = metric_dict["chart"]["FP_view"]
FP_view["right"] = int(all_tp)
FP_view["wrong_duplicate"] = int(all_fp_duplicate)
FP_view["wrong_location"] = int(all_fp_location)
FP_view["wrong_category"] = int(all_fp_category)

detected_gt = gt_ret["detected_gt"]
FN_category = gt_ret["FN_category"]
FN_location = gt_ret["FN_location"]
FN_view = metric_dict["chart"]["FN_view"]
FN_view["right"] = int(detected_gt)
FN_view["wrong_category"] = int(FN_category)
FN_view["wrong_location"] = int(FN_location)


# modify metric_dict
f_metric_dict = {
    "index": {
        "brief": {},
        "detail": {}
    },
    "chart": {
        "误检性分析": {
            "正确检测": 0,
            "重复检测": 0,
            "位置误检": 0,
            "类别误检": 0
        },
        "漏检性分析": {
            "正确检出": 0,
            "位置漏检": 0,
            "类别漏检": 0
        }
    }
}

f_metric_dict["index"] = metric_dict["index"]
f_metric_dict["chart"]["误检性分析"] = {
    "正确检测": metric_dict["chart"]["FP_view"]["right"],
    "重复检测": metric_dict["chart"]["FP_view"]["wrong_duplicate"],
    "位置误检": metric_dict["chart"]["FP_view"]["wrong_location"],
    "类别误检": metric_dict["chart"]["FP_view"]["wrong_category"]}
f_metric_dict["chart"]["漏检性分析"] = {
    "正确检出": metric_dict["chart"]["FN_view"]["right"],
    "位置漏检": metric_dict["chart"]["FN_view"]["wrong_location"],
    "类别漏检": metric_dict["chart"]["FN_view"]["wrong_category"]}


# fit old
f_metric_dict["chart"]["FP_view"] = {
    "right": metric_dict["chart"]["FP_view"]["right"],
    "wrong_duplicate": metric_dict["chart"]["FP_view"]["wrong_duplicate"],
    "wrong_location": metric_dict["chart"]["FP_view"]["wrong_location"],
    "wrong_category": metric_dict["chart"]["FP_view"]["wrong_category"]}
f_metric_dict["chart"]["FN_view"] = {
    "right": metric_dict["chart"]["FN_view"]["right"],
    "wrong_location": metric_dict["chart"]["FN_view"]["wrong_location"],
    "wrong_category": metric_dict["chart"]["FN_view"]["wrong_category"]}

pp(f_metric_dict)

# fit hikmars3
if os.getenv('hikmars_version') and os.getenv('hikmars_version') == '3':
    tmp = f_metric_dict["index"]["detail"]
    f_metric_dict["index"]["detail"] = [{k: v} for k, v in tmp.items()]
    with open(os.path.join(metric_file), "w") as f:
        json.dump(f_metric_dict, f)
else:
    brief = f_metric_dict["index"]["brief"]
    index_names = []
    index_values = []
    for k, v in brief.items():
        index_names.append(k)
        index_values.append(v)
    EL.brief_index(index_names, index_values)

    detail = f_metric_dict["index"]["detail"]
    for k, v in detail.items():
        label_name = k
        data = v
        index_names = []
        index_values = []
        for name, value in data.items():
            if name == 'pr_img_path':
                continue
            index_names.append(name)
            index_values.append(value)
        image_path = data['pr_img_path']
        EL.detail_index(label_name, index_names, index_values, image_path)

    for k, v in f_metric_dict["chart"].items():
        name = k
        data = []
        for name, value in v.items():
            data.append({"name": name, "value": int(value)})
        mode = 'bar'
        EL.chart(name, data, mode)
