Mercurial > repos > imgteam > segmetrics
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>
--- /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