"""
This file contains helper functions for evaluate_segmentation.py script.
"""

import os
import csv
import xml.etree.cElementTree as ET

import numpy as np

import config
from utils import get_paths


# compare segmentations based on the EvaluateSegmentation software of Taha
def segment_comparison(
    goldstandard_path,
    segmentation_path,
    executable_path,
    eval_result_path,
    threshold,
    measures,
):
    print(measures)
    print("goldstandard path: ", goldstandard_path)
    print("segmentation path: ", segmentation_path)
    print("executable path: ", executable_path)
    print("eval result path: ", eval_result_path)
    command_string = (
        executable_path
        + " "
        + goldstandard_path
        + " "
        + segmentation_path
        + " -use "
        + measures
        + " -xml "
        + eval_result_path
        + " -thd "
        + str(threshold)
        + " -unit millimeter"
    )
    print("command string: ", command_string)
    os.system(command_string)


# parse the xml file and create dataframes for the relevant metric data.
# Also, save the dataframe data into csvs
def parse_xml_to_csv(xml_path, csv_path, run_params=None):
    # get all the metrics as a list
    if run_params is None:
        run_params = {}
    list_of_measures = []
    measures_values = []
    tree = ET.parse(xml_path)
    root = tree.getroot()
    for child in root.findall(".//metrics/*"):
        list_of_measures.append(child.attrib["symbol"])
        value = child.attrib["value"]
        measures_values.append(value)

    with open(csv_path, "a+") as f1:
        writer = csv.writer(f1)
        if os.path.isfile(csv_path) and os.path.getsize(csv_path) == 0:
            writer.writerow(list(run_params.keys()) + list_of_measures)
        writer.writerow(list(run_params.values()) + measures_values)


# calculate_sensibility
def calculate_sensibility(metrics_dic):
    try:
        fp = int(metrics_dic["FP"])
        fn = int(metrics_dic["FN"])
        tp = int(metrics_dic["TP"])
        valsensibility = (1 - fp / (tp + fn)) * 100
    except (KeyError, ZeroDivisionError) as e:
        valsensibility = np.nan

    return round(valsensibility, 6)


# calculate_conformity
def calculate_conformity(metrics_dic):
    try:
        fp = int(metrics_dic["FP"])
        fn = int(metrics_dic["FN"])
        tp = int(metrics_dic["TP"])
        valconformity = (1 - (fp + fn) / tp) * 100
    except (KeyError, ZeroDivisionError) as e:
        valconformity = np.nan

    return round(valconformity, 6)


# create dictionary of
def create_dict_from_xml(xml_path, metrics_list=None):
    if metrics_list is None:
        metrics_list = ["TP", "FP", "TN", "FN"]

    value_metrics_dic = []
    tree = ET.parse(xml_path)
    root = tree.getroot()
    for child in root.findall(".//metrics/*"):
        if child.tag in metrics_list:
            value_metrics_dic.append(child.attrib["value"])
    metrics_dic = dict(zip(metrics_list, value_metrics_dic))
    return metrics_dic


def sensibility_conformity_to_xml(xml_path):
    """
    Insert Sensibility and Conformity values into Evaluation xml.

    :param xml_path: path to xml file generated by EvaluateSegmentation.exe
    """
    print("ADDING SENSBIL and CFM to:", xml_path)
    tree = ET.parse(xml_path)
    root = tree.getroot()

    metrics_dic = create_dict_from_xml(xml_path)

    valsensibility = calculate_sensibility(metrics_dic)
    valconformity = calculate_conformity(metrics_dic)

    sensibility_attributes = {
        "name": "sensibility",
        "value": str(valsensibility),
        "symbol": "SENSBIL",
        "type": "similarity",
        "unit": "voxel",
    }
    SENSBIL = ET.Element("SENSBIL", attrib=sensibility_attributes)
    conformity_attributes = {
        "name": "conformity",
        "value": str(valconformity),
        "symbol": "CFM",
        "type": "similarity",
        "unit": "voxel",
    }
    CFM = ET.Element("CFM", attrib=conformity_attributes)

    root[2].insert(2, SENSBIL)
    root[2].insert(3, CFM)
    tree.write(xml_path)


def parse_xml_to_csv_avg_for_patients(xml_paths, csv_path, run_params):
    measures_values_all_patients = []
    # get all the metrics as a list
    measures_symbols = []
    for i, path in enumerate(xml_paths):
        measures_values = []
        tree = ET.parse(path)
        root = tree.getroot()
        for child in root.findall(".//metrics/*"):
            if i == 0:
                measures_symbols.append(child.attrib["symbol"])
            measures_values.append(child.attrib["value"])
        # if list is empty because image only contains zeros
        if not measures_values:
            measures_values_all_patients.append(["-100.001", "-100.001"])
        else:
            # print(measures_values)
            measures_values_all_patients.append(measures_values)

    # count average for each metric
    print("measures values all patients: ", measures_values_all_patients)
    measures_values_avg = np.mean(
        np.asarray(measures_values_all_patients, dtype=np.float32), axis=0
    )
    measures_values_sd = np.std(
        np.asarray(measures_values_all_patients, dtype=np.float32), axis=0
    )
    print(measures_values_avg)
    print(measures_values_avg.shape)
    print(measures_values_avg.dtype)

    with open(csv_path, "a+") as f1:
        writer = csv.writer(f1)
        if os.path.isfile(csv_path) and os.path.getsize(csv_path) == 0:
            writer.writerow(list(run_params.keys()) + measures_symbols)
        writer.writerow(list(run_params.values()) + measures_values_avg.tolist())
    return measures_values_avg, measures_values_sd


def evaluate_segmentation(
    patients_segm,
    dataset,
    epoch,
    batchsize,
    lr,
    dropout,
    augm,
    train_input,
    measures,
    csv_path,
    csv_path_per_patient,
    executable_path,
    realonly=False,
    ext_run="",
    e=0,
):
    # create the name of current run
    run_name = get_paths.get_run_name(epoch, batchsize, lr, dropout, augm, train_input)

    print(run_name)

    xml_paths = []

    for patient in patients_segm:
        print("_______________________________________________________________")

        # load labels and segmentations
        label_path = (
            get_paths.get_original_data_path(dataset) + str(patient) + "_label.nii.gz"
        )
        segmentation_path = get_paths.get_prob_path(patient, run_name, dataset)

        # for saving results of evaluate segmentation to xml and to csv
        xml_path_patient = get_paths.get_result_xml_path(patient, run_name, dataset)
        xml_path_patient_quotes = "'" + xml_path_patient + "'"
        xml_paths.append(xml_path_patient)

        # compare the segmentation with ground truth and save the xml file in
        # the results folder
        segment_comparison(
            label_path,
            segmentation_path,
            executable_path,
            xml_path_patient_quotes,
            config.threshold_unet,
            measures,
        )

        print("xml path patient: ", xml_path_patient)
        # print("Is file?" + str(os.path.isfile(xml_path_patient)))

        # parse the generated xmls and insert two more metrics: Sensibility
        # and Conformity
        # sensibility_conformity_to_xml(xml_path_patient)

        # parse the xml files in each folder, do stats and save the dataframes
        # as csvs with the parse_xml function
        run_params = {
            "patch_size": config.PATCH_SIZE,
            "num_epochs": epoch,
            "batch_size": batchsize,
            "dropout": dropout,
            "lr": lr,
            "patient": patient,
            "nr_patches": config.NUM_PATCHES,
            "augm": augm,
            "train_input": train_input,
            "dataset": dataset,
        }
        parse_xml_to_csv(xml_path_patient, csv_path_per_patient, run_params)

    run_params = {
        "patch_size": config.PATCH_SIZE,
        "num_epochs": epoch,
        "batch_size": batchsize,
        "dropout": dropout,
        "lr": lr,
        "patient": patient,
        "nr_patches": config.NUM_PATCHES,
        "augm": augm,
        "train_input": train_input,
        "dataset": dataset,
    }

    avg, sd = parse_xml_to_csv_avg_for_patients(xml_paths, csv_path, run_params)
    return avg, sd

"""
This file contains helper functions for evaluate_segmentation.py script.
"""

import os
import csv
import xml.etree.cElementTree as ET

import numpy as np

import config
from utils import get_paths


# compare segmentations based on the EvaluateSegmentation software of Taha
def segment_comparison(
    goldstandard_path,
    segmentation_path,
    executable_path,
    eval_result_path,
    threshold,
    measures,
):
    print(measures)
    print("goldstandard path: ", goldstandard_path)
    print("segmentation path: ", segmentation_path)
    print("executable path: ", executable_path)
    print("eval result path: ", eval_result_path)
    command_string = (
        executable_path
        + " "
        + goldstandard_path
        + " "
        + segmentation_path
        + " -use "
        + measures
        + " -xml "
        + eval_result_path
        + " -thd "
        + str(threshold)
        + " -unit millimeter"
    )
    print("command string: ", command_string)
    os.system(command_string)


# parse the xml file and create dataframes for the relevant metric data.
# Also, save the dataframe data into csvs
def parse_xml_to_csv(xml_path, csv_path, run_params=None):
    # get all the metrics as a list
    if run_params is None:
        run_params = {}
    list_of_measures = []
    measures_values = []
    tree = ET.parse(xml_path)
    root = tree.getroot()
    for child in root.findall(".//metrics/*"):
        list_of_measures.append(child.attrib["symbol"])
        value = child.attrib["value"]
        measures_values.append(value)

    with open(csv_path, "a+") as f1:
        writer = csv.writer(f1)
        if os.path.isfile(csv_path) and os.path.getsize(csv_path) == 0:
            writer.writerow(list(run_params.keys()) + list_of_measures)
        writer.writerow(list(run_params.values()) + measures_values)


# calculate_sensibility
def calculate_sensibility(metrics_dic):
    try:
        fp = int(metrics_dic["FP"])
        fn = int(metrics_dic["FN"])
        tp = int(metrics_dic["TP"])
        valsensibility = (1 - fp / (tp + fn)) * 100
    except (KeyError, ZeroDivisionError) as e:
        valsensibility = np.nan

    return round(valsensibility, 6)


# calculate_conformity
def calculate_conformity(metrics_dic):
    try:
        fp = int(metrics_dic["FP"])
        fn = int(metrics_dic["FN"])
        tp = int(metrics_dic["TP"])
        valconformity = (1 - (fp + fn) / tp) * 100
    except (KeyError, ZeroDivisionError) as e:
        valconformity = np.nan

    return round(valconformity, 6)


# create dictionary of
def create_dict_from_xml(xml_path, metrics_list=None):
    if metrics_list is None:
        metrics_list = ["TP", "FP", "TN", "FN"]

    value_metrics_dic = []
    tree = ET.parse(xml_path)
    root = tree.getroot()
    for child in root.findall(".//metrics/*"):
        if child.tag in metrics_list:
            value_metrics_dic.append(child.attrib["value"])
    metrics_dic = dict(zip(metrics_list, value_metrics_dic))
    return metrics_dic


def sensibility_conformity_to_xml(xml_path):
    """
    Insert Sensibility and Conformity values into Evaluation xml.

    :param xml_path: path to xml file generated by EvaluateSegmentation.exe
    """
    print("ADDING SENSBIL and CFM to:", xml_path)
    tree = ET.parse(xml_path)
    root = tree.getroot()

    metrics_dic = create_dict_from_xml(xml_path)

    valsensibility = calculate_sensibility(metrics_dic)
    valconformity = calculate_conformity(metrics_dic)

    sensibility_attributes = {
        "name": "sensibility",
        "value": str(valsensibility),
        "symbol": "SENSBIL",
        "type": "similarity",
        "unit": "voxel",
    }
    SENSBIL = ET.Element("SENSBIL", attrib=sensibility_attributes)
    conformity_attributes = {
        "name": "conformity",
        "value": str(valconformity),
        "symbol": "CFM",
        "type": "similarity",
        "unit": "voxel",
    }
    CFM = ET.Element("CFM", attrib=conformity_attributes)

    root[2].insert(2, SENSBIL)
    root[2].insert(3, CFM)
    tree.write(xml_path)


def parse_xml_to_csv_avg_for_patients(xml_paths, csv_path, run_params):
    measures_values_all_patients = []
    # get all the metrics as a list
    measures_symbols = []
    for i, path in enumerate(xml_paths):
        measures_values = []
        tree = ET.parse(path)
        root = tree.getroot()
        for child in root.findall(".//metrics/*"):
            if i == 0:
                measures_symbols.append(child.attrib["symbol"])
            measures_values.append(child.attrib["value"])
        # if list is empty because image only contains zeros
        if not measures_values:
            measures_values_all_patients.append(["-100.001", "-100.001"])
        else:
            # print(measures_values)
            measures_values_all_patients.append(measures_values)

    # count average for each metric
    print("measures values all patients: ", measures_values_all_patients)
    measures_values_avg = np.mean(
        np.asarray(measures_values_all_patients, dtype=np.float32), axis=0
    )
    measures_values_sd = np.std(
        np.asarray(measures_values_all_patients, dtype=np.float32), axis=0
    )
    print(measures_values_avg)
    print(measures_values_avg.shape)
    print(measures_values_avg.dtype)

    with open(csv_path, "a+") as f1:
        writer = csv.writer(f1)
        if os.path.isfile(csv_path) and os.path.getsize(csv_path) == 0:
            writer.writerow(list(run_params.keys()) + measures_symbols)
        writer.writerow(list(run_params.values()) + measures_values_avg.tolist())
    return measures_values_avg, measures_values_sd


def evaluate_segmentation(
    patients_segm,
    dataset,
    epoch,
    batchsize,
    lr,
    dropout,
    augm,
    train_input,
    measures,
    csv_path,
    csv_path_per_patient,
    executable_path,
    realonly=False,
    ext_run="",
    e=0,
):
    # create the name of current run
    run_name = get_paths.get_run_name(epoch, batchsize, lr, dropout, augm, train_input)

    print(run_name)

    xml_paths = []

    for patient in patients_segm:
        print("_______________________________________________________________")

        # load labels and segmentations
        label_path = (
            get_paths.get_original_data_path(dataset) + str(patient) + "_label.nii.gz"
        )
        segmentation_path = get_paths.get_prob_path(patient, run_name, dataset)

        # for saving results of evaluate segmentation to xml and to csv
        xml_path_patient = get_paths.get_result_xml_path(patient, run_name, dataset)
        xml_path_patient_quotes = "'" + xml_path_patient + "'"
        xml_paths.append(xml_path_patient)

        # compare the segmentation with ground truth and save the xml file in
        # the results folder
        segment_comparison(
            label_path,
            segmentation_path,
            executable_path,
            xml_path_patient_quotes,
            config.threshold_unet,
            measures,
        )

        print("xml path patient: ", xml_path_patient)
        # print("Is file?" + str(os.path.isfile(xml_path_patient)))

        # parse the generated xmls and insert two more metrics: Sensibility
        # and Conformity
        # sensibility_conformity_to_xml(xml_path_patient)

        # parse the xml files in each folder, do stats and save the dataframes
        # as csvs with the parse_xml function
        run_params = {
            "patch_size": config.PATCH_SIZE,
            "num_epochs": epoch,
            "batch_size": batchsize,
            "dropout": dropout,
            "lr": lr,
            "patient": patient,
            "nr_patches": config.NUM_PATCHES,
            "augm": augm,
            "train_input": train_input,
            "dataset": dataset,
        }
        parse_xml_to_csv(xml_path_patient, csv_path_per_patient, run_params)

    run_params = {
        "patch_size": config.PATCH_SIZE,
        "num_epochs": epoch,
        "batch_size": batchsize,
        "dropout": dropout,
        "lr": lr,
        "patient": patient,
        "nr_patches": config.NUM_PATCHES,
        "augm": augm,
        "train_input": train_input,
        "dataset": dataset,
    }

    avg, sd = parse_xml_to_csv_avg_for_patients(xml_paths, csv_path, run_params)
    return avg, sd

This file contains helper functions for evaluate_segmentation.py script. # compare segmentations based on the EvaluateSegmentation software of Taha # parse the xml file and create dataframes for the relevant metric data. # Also, save the dataframe data into csvs # get all the metrics as a list # calculate_sensibility # calculate_conformity # create dictionary of Insert Sensibility and Conformity values into Evaluation xml.      :param xml_path: path to xml file generated by EvaluateSegmentation.exe # get all the metrics as a list # if list is empty because image only contains zeros # print(measures_values) # count average for each metric # create the name of current run # load labels and segmentations # for saving results of evaluate segmentation to xml and to csv # compare the segmentation with ground truth and save the xml file in # the results folder # print("Is file?" + str(os.path.isfile(xml_path_patient))) # parse the generated xmls and insert two more metrics: Sensibility # and Conformity # sensibility_conformity_to_xml(xml_path_patient) # parse the xml files in each folder, do stats and save the dataframes # as csvs with the parse_xml function