Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented MISmetric #6

Open
wants to merge 6 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ The open-source and free to use Python package miseval was developed to establis
| ----------- | ----------- | ----------- |
| Dice Similarity Index | "DSC", "Dice", "DiceSimilarityCoefficient" | miseval.calc_DSC() |
| Intersection-Over-Union | "IoU", "Jaccard", "IntersectionOverUnion" | miseval.calc_IoU() |
| MISmetric | "MISm", "MISmetric" | miseval.calc_MISm() |
| Sensitivity | "SENS", "Sensitivity", "Recall", "TPR", "TruePositiveRate" | miseval.calc_Sensitivity() |
| Specificity | "SPEC", "Specificity", "TNR", "TrueNegativeRate" | miseval.calc_Specificity() |
| Weighted Specificity | "mSPEC", "WeightedSpecificity" | miseval.calc_Specificity_Weighted() |
| Precision | "PREC", "Precision" | miseval.calc_Precision() |
| Accuracy | "ACC", "Accuracy", "RI", "RandIndex" | miseval.calc_Accuracy() |
| Balanced Accuracy | "BACC", "BalancedAccuracy" | miseval.calc_BalancedAccuracy() |
Expand Down
8 changes: 7 additions & 1 deletion miseval/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
from miseval.volumetric_similarity import *
# Matthews Correlation Coefficient
from miseval.mcc import *
# MISmetric
from miseval.mism import *

#-----------------------------------------------------#
# Access Functions to Metric Functions #
Expand Down Expand Up @@ -95,6 +97,8 @@
"Specificity": calc_Specificity,
"TNR": calc_Specificity,
"TrueNegativeRate": calc_Specificity,
"wSPEC": calc_Specificity_Weighted,
"WeightedSpecificity": calc_Specificity_Weighted,
"PREC": calc_Precision,
"Precision": calc_Precision,
"VS": calc_VolumetricSimilarity,
Expand All @@ -108,7 +112,9 @@
"MCC_normalized": calc_MCC_Normalized,
"nMCC": calc_MCC_Normalized,
"MCC_absolute": calc_MCC_Absolute,
"aMCC": calc_MCC_Absolute
"aMCC": calc_MCC_Absolute,
"MISm": calc_MISm,
"MISmetric": calc_MISm
}

#-----------------------------------------------------#
Expand Down
48 changes: 48 additions & 0 deletions miseval/mism.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#==============================================================================#
# Author: Dominik Müller #
# Copyright: 2022 IT-Infrastructure for Translational Medical Research, #
# University of Augsburg #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
#==============================================================================#
#-----------------------------------------------------#
# Library imports #
#-----------------------------------------------------#
# External modules
import math
# Internal modules
from miseval.confusion_matrix import calc_ConfusionMatrix
from miseval import calc_DSC_Sets, calc_Specificity_Weighted

#-----------------------------------------------------#
# Calculate : MISmetric #
#-----------------------------------------------------#
"""
Combination of weighted Specificity for p=0 and Dice Similarity Coefficient
as Backbone for p>0.

Recommended for weak-labeled datasets.

References:
Coming soon.
"""
def calc_MISm(truth, pred, c=1, alpha=0.1):
# Obtain confusion mat
tp, tn, fp, fn = calc_ConfusionMatrix(truth, pred, c)
# Identify metric wing
p = tp + fn
# Compute & return normal dice if p > 0
if p > 0: return calc_DSC_Sets(truth, pred, c)
# Compute & return weighted specificity if p = 0
else : return calc_Specificity_Weighted(truth, pred, c, alpha)
12 changes: 12 additions & 0 deletions miseval/specificity.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,15 @@ def calc_Specificity_CM(truth, pred, c=1):
else : spec = 0.0
# Return specificity
return spec

#-----------------------------------------------------#
# Calculate : Weighted Specificity #
#-----------------------------------------------------#
def calc_Specificity_Weighted(truth, pred, c=1, alpha=0.1):
# Obtain confusion mat
tp, tn, fp, fn = calc_ConfusionMatrix(truth, pred, c)
# Compute weighted specificity
if (fp+tn) != 0 : wspec = (alpha*tn) / ((1-alpha)*fp + alpha*tn)
else : wspec = 0.0
# Return weighted specificity
return wspec
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name='miseval',
version='1.1.2',
version='1.1.3',
description='A Metric Library for Medical Image Segmentation Evaluation',
url='https://github.com/frankkramer-lab/miseval',
author='Dominik Müller',
Expand Down
58 changes: 58 additions & 0 deletions tests/test_mism.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#==============================================================================#
# Author: Dominik Müller #
# Copyright: 2022 IT-Infrastructure for Translational Medical Research, #
# University of Augsburg #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
#==============================================================================#
#-----------------------------------------------------#
# Library imports #
#-----------------------------------------------------#
# External modules
import numpy as np
import unittest
# Internal modules
from miseval import *

#-----------------------------------------------------#
# Unittest: MISmetric #
#-----------------------------------------------------#
class TEST_MISm(unittest.TestCase):
@classmethod
def setUpClass(self):
# Create ground truth
np.random.seed(1)
self.gt_bi = np.random.randint(2, size=(32,32))
self.gt_mc = np.random.randint(5, size=(32,32))
# Create prediction
np.random.seed(2)
self.pd_bi = np.random.randint(2, size=(32,32))
self.pd_mc = np.random.randint(5, size=(32,32))

#-------------------------------------------------#
# Calculate : MISmetric #
#-------------------------------------------------#
def test_calc_MISm(self):
# Check binary score
score_bi = calc_MISm(self.gt_bi, self.pd_bi, c=1)
self.assertTrue(isinstance(score_bi, np.float64))
# Check multi-class score
for i in range(5):
score_mc = calc_MISm(self.gt_mc, self.pd_mc, c=i)
self.assertTrue(isinstance(score_mc, np.float64))
# Check existance in metric_dict
self.assertTrue("MISm" in metric_dict)
self.assertTrue(callable(metric_dict["MISm"]))
self.assertTrue("MISmetric" in metric_dict)
self.assertTrue(callable(metric_dict["MISmetric"]))
20 changes: 20 additions & 0 deletions tests/test_specificity.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,23 @@ def test_calc_Specificity_CM(self):
score_mc = calc_Specificity_CM(self.gt_mc, self.pd_mc, c=i)
self.assertTrue(isinstance(score_mc, np.float64))
self.assertTrue(score_mc == calc_Specificity(self.gt_mc, self.pd_mc, c=i))

#-------------------------------------------------#
# Calculate : Weighted Specificity #
#-------------------------------------------------#
def test_calc_Specificity_Weighted(self):
# Check binary score
score_bi = calc_Specificity_Weighted(self.gt_bi, self.pd_bi, c=1)
self.assertTrue(isinstance(score_bi, np.float64))
self.assertTrue(score_bi == calc_Specificity_Weighted(self.gt_bi,
self.pd_bi, c=1))
# Check multi-class score
for i in range(5):
score_mc = calc_Specificity_Weighted(self.gt_mc, self.pd_mc, c=i)
self.assertTrue(isinstance(score_mc, np.float64))
self.assertTrue(score_mc == calc_Specificity_Weighted(self.gt_mc,
self.pd_mc, c=i))
self.assertTrue("wSPEC" in metric_dict)
self.assertTrue(callable(metric_dict["wSPEC"]))
self.assertTrue("WeightedSpecificity" in metric_dict)
self.assertTrue(callable(metric_dict["WeightedSpecificity"]))