changeset 0:0729657d9e4e draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tools/segmetrics/ commit 3b911df716a7b42115c6cd773f666bc90a2bb10f
author imgteam
date Fri, 07 Oct 2022 22:05:59 +0000
parents
children 3a7310406943
files run-segmetrics.py segmetrics.xml test-data/input1.png test-data/input1.zip test-data/input2.png test-data/input2.zip test-data/results1.csv test-data/results2.csv
diffstat 8 files changed, 235 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/run-segmetrics.py	Fri Oct 07 22:05:59 2022 +0000
@@ -0,0 +1,133 @@
+"""
+Copyright 2022 Leonid Kostrykin, Biomedical Computer Vision Group, Heidelberg University.
+
+Distributed under the MIT license.
+See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+
+"""
+
+import argparse
+import csv
+import itertools
+import pathlib
+import tempfile
+import zipfile
+
+import numpy as np
+import segmetrics as sm
+import skimage.io
+
+
+measures = [
+    ('dice', 'Dice', sm.regional.Dice()),
+    ('seg', 'SEG', sm.regional.ISBIScore()),
+    ('jc', 'Jaccard coefficient', sm.regional.JaccardSimilarityIndex()),
+    ('ji', 'Jaccard index', sm.regional.JaccardIndex()),
+    ('ri', 'Rand index', sm.regional.RandIndex()),
+    ('ari', 'Adjusted Rand index', sm.regional.AdjustedRandIndex()),
+    ('hsd_sym', 'HSD (sym)', sm.boundary.Hausdorff('sym')),
+    ('hsd_e2a', 'HSD (e2a)', sm.boundary.Hausdorff('e2a')),
+    ('hsd_a2e', 'HSD (a2e)', sm.boundary.Hausdorff('a2e')),
+    ('nsd', 'NSD', sm.boundary.NSD()),
+    ('o_hsd_sym', 'Ob. HSD (sym)', sm.boundary.ObjectBasedDistance(sm.boundary.Hausdorff('sym'))),
+    ('o_hsd_e2a', 'Ob. HSD (e2a)', sm.boundary.ObjectBasedDistance(sm.boundary.Hausdorff('e2a'))),
+    ('o_hsd_a2e', 'Ob. HSD (a2e)', sm.boundary.ObjectBasedDistance(sm.boundary.Hausdorff('a2e'))),
+    ('o_nsd', 'Ob. NSD', sm.boundary.ObjectBasedDistance(sm.boundary.NSD())),
+    ('fs', 'Split', sm.detection.FalseSplit()),
+    ('fm', 'Merge', sm.detection.FalseMerge()),
+    ('fp', 'Spurious', sm.detection.FalsePositive()),
+    ('fn', 'Missing', sm.detection.FalseNegative()),
+]
+
+
+def process_batch(study, gt_filelist, seg_filelist, namelist, gt_is_unique, seg_is_unique):
+    for gt_filename, seg_filename, name in zip(gt_filelist, seg_filelist, namelist):
+        img_ref = skimage.io.imread(gt_filename)
+        img_seg = skimage.io.imread(seg_filename)
+        study.set_expected(img_ref, unique=gt_is_unique)
+        study.process(img_seg, unique=seg_is_unique, chunk_id=name)
+
+
+def aggregate(measure, values):
+    fnc = np.sum if measure.ACCUMULATIVE else np.mean
+    return fnc(values)
+
+
+def is_zip_filepath(filepath):
+    return filepath.lower().endswith('.zip')
+
+
+def is_image_filepath(filepath):
+    suffixes = ['png', 'tif', 'tiff']
+    return any((filepath.lower().endswith(f'.{suffix}') for suffix in suffixes))
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description='Image segmentation and object detection performance measures for 2-D image data')
+    parser.add_argument('input_seg', help='Path to the segmented image or image archive (ZIP)')
+    parser.add_argument('input_gt', help='Path to the ground truth image or image archive (ZIP)')
+    parser.add_argument('results', help='Path to the results file (CSV)')
+    parser.add_argument('-unzip', action='store_true')
+    parser.add_argument('-seg_unique', action='store_true')
+    parser.add_argument('-gt_unique', action='store_true')
+    for measure in measures:
+        parser.add_argument(f'-measure-{measure[0]}', action='store_true', help=f'Include {measure[1]}')
+
+    args = parser.parse_args()
+    study = sm.study.Study()
+
+    used_measures = []
+    for measure in measures:
+        if getattr(args, f'measure_{measure[0]}'):
+            used_measures.append(measure)
+            study.add_measure(measure[2], measure[1])
+
+    if args.unzip:
+        zipfile_seg = zipfile.ZipFile(args.input_seg)
+        zipfile_gt = zipfile.ZipFile(args.input_gt)
+        namelist = [filepath for filepath in zipfile_seg.namelist() if is_image_filepath(filepath) and filepath in zipfile_gt.namelist()]
+        print('namelist:', namelist)
+        with tempfile.TemporaryDirectory() as tmpdir:
+            basepath = pathlib.Path(tmpdir)
+            gt_path, seg_path = basepath / 'gt', basepath / 'seg'
+            zipfile_seg.extractall(str(seg_path))
+            zipfile_gt.extractall(str(gt_path))
+            gt_filelist, seg_filelist = list(), list()
+            for filepath in namelist:
+                seg_filelist.append(str(seg_path / filepath))
+                gt_filelist.append(str(gt_path / filepath))
+            process_batch(study, gt_filelist, seg_filelist, namelist, args.gt_unique, args.seg_unique)
+
+    else:
+        namelist = ['']
+        process_batch(study, [args.input_gt], [args.input_seg], namelist, args.gt_unique, args.seg_unique)
+
+    # define header
+    rows = [[''] + [measure[1] for measure in used_measures]]
+
+    # define rows
+    if len(namelist) > 1:
+        for chunk_id in namelist:
+            row = [chunk_id]
+            for measure in used_measures:
+                measure_name = measure[1]
+                measure = study.measures[measure_name]
+                chunks = study.results[measure_name]
+                row += [aggregate(measure, chunks[chunk_id])]
+            rows.append(row)
+
+    # define footer
+    rows.append([''])
+    for measure in used_measures:
+        measure_name = measure[1]
+        measure = study.measures[measure_name]
+        chunks = study.results[measure_name]
+        values = list(itertools.chain(*[chunks[chunk_id] for chunk_id in chunks]))
+        val = aggregate(measure, values)
+        rows[-1].append(val)
+
+    # write results
+    with open(args.results, 'w', newline='') as fout:
+        csv_writer = csv.writer(fout, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL)
+        for row in rows:
+            csv_writer.writerow(row)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/segmetrics.xml	Fri Oct 07 22:05:59 2022 +0000
@@ -0,0 +1,96 @@
+<tool id="ip_segmetrics" name="SegMetrics" version="0.11.3" profile="20.05">
+   <description>image segmentation and object detection performance measures</description>
+   <requirements> 
+        <requirement type="package" version="0.11.3">segmetrics</requirement>
+        <requirement type="package" version="0.18.1">scikit-image</requirement>
+   </requirements>
+   <command detect_errors="aggressive">
+   <![CDATA[
+   python '$__tool_directory__/run-segmetrics.py'
+   '$input_seg'
+   '$input_gt'
+   ./results.csv
+   $unzip
+   $is_seg_unique
+   $is_gt_unique
+   $measures.dice
+   $measures.seg
+   $measures.jc
+   $measures.ji
+   $measures.ri
+   $measures.ari
+   $measures.hsd_sym
+   $measures.hsd_e2a
+   $measures.hsd_a2e
+   $measures.nsd
+   $measures.o_hsd_sym
+   $measures.o_hsd_e2a
+   $measures.o_hsd_a2e
+   $measures.o_nsd
+   $measures.fs
+   $measures.fm
+   $measures.fp
+   $measures.fn
+   ]]>
+   </command>
+   <inputs>
+
+        <param name="input_seg" type="data" format="tiff,png,zip" label="Segmented images" />
+        <param name="input_gt"  type="data" format="tiff,png,zip" label="Ground truth images" />
+
+        <param name="unzip" type="boolean" checked="false" truevalue="-unzip" falsevalue="" label="Unzip" />
+
+        <param name="is_seg_unique" type="boolean" checked="false" truevalue="-seg_unique" falsevalue="" label="Segmentation is uniquely labeled" />
+        <param name="is_gt_unique"  type="boolean" checked="false" truevalue="-gt_unique"  falsevalue="" label="Ground truth is uniquely labeled" />
+
+        <section name="measures" title="Performance measures" >
+            <param name="dice" type="boolean" checked="true"  truevalue="-measure-dice" falsevalue="" label="Dice" />
+            <param name="seg"  type="boolean" checked="true"  truevalue="-measure-seg"  falsevalue="" label="SEG" />
+            <param name="jc"   type="boolean" checked="false" truevalue="-measure-jc"   falsevalue="" label="Jaccard coefficient" />
+            <param name="ji"   type="boolean" checked="true"  truevalue="-measure-ji"   falsevalue="" label="Jaccard index" />
+            <param name="ri"   type="boolean" checked="false" truevalue="-measure-ri"   falsevalue="" label="Rand index" />
+            <param name="ari"  type="boolean" checked="false" truevalue="-measure-ari"  falsevalue="" label="Adjusted Rand index" />
+            <param name="hsd_sym" type="boolean" checked="false" truevalue="-measure-hsd_sym" falsevalue="" label="Hausdorff distance (symmetric)" />
+            <param name="hsd_e2a" type="boolean" checked="false" truevalue="-measure-hsd_e2a" falsevalue="" label="Hausdorff distance (ground truth to segmented)" />
+            <param name="hsd_a2e" type="boolean" checked="false" truevalue="-measure-hsd_a2e" falsevalue="" label="Hausdorff distance (segmented to ground truth)" />
+            <param name="nsd"     type="boolean" checked="false" truevalue="-measure-nsd"     falsevalue="" label="Normalized sum of distances" />
+            <param name="o_hsd_sym" type="boolean" checked="true"  truevalue="-measure-o_hsd_sym" falsevalue="" label="Object-based Hausdorff distance (symmetric)" />
+            <param name="o_hsd_e2a" type="boolean" checked="false" truevalue="-measure-o_hsd_e2a" falsevalue="" label="Object-based Hausdorff distance (ground truth to segmented)" />
+            <param name="o_hsd_a2e" type="boolean" checked="false" truevalue="-measure-o_hsd_a2e" falsevalue="" label="Object-based Hausdorff distance (segmented to ground truth)" />
+            <param name="o_nsd"     type="boolean" checked="true"  truevalue="-measure-o_nsd"     falsevalue="" label="Object-based normalized sum of distances" />
+            <param name="fs" type="boolean" checked="true" truevalue="-measure-fs" falsevalue="" label="Falsely split objects per image" />
+            <param name="fm" type="boolean" checked="true" truevalue="-measure-fm" falsevalue="" label="Falsely merged objects per image" />
+            <param name="fp" type="boolean" checked="true" truevalue="-measure-fp" falsevalue="" label="Spurious objects per image" />
+            <param name="fn" type="boolean" checked="true" truevalue="-measure-fn" falsevalue="" label="Missing objects per image" />
+        </section>
+
+    </inputs>
+    <outputs>
+       <data format="csv" name="results" from_work_dir="results.csv" />
+    </outputs>
+    <tests>
+        <test>
+            <param name="input_seg" value="input2.png"/>
+            <param name="input_gt"  value="input1.png"/>
+            <output name="results" value="results1.csv" ftype="csv" compare="diff"/>
+            <param name="is_seg_unique" value="True"/>
+            <param name="is_gt_unique"  value="True"/>
+        </test>
+        <test>
+            <param name="input_seg" value="input1.zip"/>
+            <param name="input_gt"  value="input2.zip"/>
+            <output name="results" value="results2.csv" ftype="csv" compare="diff"/>
+            <param name="is_seg_unique" value="True"/>
+            <param name="is_gt_unique"  value="True"/>
+            <param name="unzip" value="True"/>
+        </test>
+    </tests>
+    <help>
+        Image segmentation and object detection performance measures for 2-D image data.
+    </help>
+    <citations>
+        <citation type="doi">10.1093/bioinformatics/btu080</citation>
+        <citation type="doi">10.1109/ISBI.2009.5193098</citation>
+        <citation type="doi">10.1109/ICIP.2003.1246871</citation>
+    </citations>
+</tool>
Binary file test-data/input1.png has changed
Binary file test-data/input1.zip has changed
Binary file test-data/input2.png has changed
Binary file test-data/input2.zip has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/results1.csv	Fri Oct 07 22:05:59 2022 +0000
@@ -0,0 +1,2 @@
+;Dice;SEG;Jaccard index;Ob. HSD (sym);Ob. NSD;Split;Merge;Spurious;Missing
+;0.5473684210526316;0.291005291005291;1.6238374627624792;141.68949443548658;0.8175405481022531;0;1;2;0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/results2.csv	Fri Oct 07 22:05:59 2022 +0000
@@ -0,0 +1,4 @@
+;Dice;SEG;Jaccard index;Ob. HSD (sym);Ob. NSD;Split;Merge;Spurious;Missing
+directory/img1.png;0.5473684210526316;0.14285714285714285;1.6238374627624792;142.42099468121046;0.7765050343914003;1;0;0;2
+directory/img2.png;0.5473684210526316;0.291005291005291;1.6238374627624792;141.68949443548658;0.8175405481022531;0;1;2;0
+;0.5473684210526316;0.2169312169312169;1.6238374627624792;142.0552445583485;0.7970227912468267;1;1;2;2