From 224c2267ddf83071653b24396c9efaa6f0f56602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Tue, 19 Sep 2023 16:01:10 -0400 Subject: [PATCH 1/2] ENH: Wrap LabelSetMeasures in LabelOverlapMeasuresImageFilter This exposes the return value from itk::LabelOverlapMeasuresImageFilter::GetLabelSetMeasures() as a Python dictionary instead of *'> This is needed for convenient use of the class from Python. The class has been un-nested to make wrapping easier/possible. --- .../include/itkLabelOverlapLabelSetMeasures.h | 39 +++++++++++++++++++ .../itkLabelOverlapMeasuresImageFilter.h | 23 +++-------- .../ImageStatistics/wrapping/CMakeLists.txt | 2 + .../itkLabelOverlapLabelSetMeasures.wrap | 1 + ...belOverlapLabelSetMeasuresInstantiations.i | 14 +++++++ Wrapping/Generators/Python/PyBase/pyBase.i | 1 + 6 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h create mode 100644 Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasures.wrap create mode 100644 Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i diff --git a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h new file mode 100644 index 00000000000..4355092c021 --- /dev/null +++ b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h @@ -0,0 +1,39 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkLabelOverlapLabelSetMeasures_h +#define itkLabelOverlapLabelSetMeasures_h + +#include "itkIntTypes.h" + +namespace itk +{ +/** \class LabelOverlapLabelSetMeasures + * \brief Metrics stored per label + * \ingroup ITKImageStatistics + */ +struct LabelOverlapLabelSetMeasures +{ + SizeValueType m_Source{ 0 }; + SizeValueType m_Target{ 0 }; + SizeValueType m_Union{ 0 }; + SizeValueType m_Intersection{ 0 }; + SizeValueType m_SourceComplement{ 0 }; + SizeValueType m_TargetComplement{ 0 }; +}; +} // namespace itk +#endif // itkLabelOverlapLabelSetMeasures_h diff --git a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h index c86c744ef97..87be975b27d 100644 --- a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h +++ b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h @@ -20,12 +20,12 @@ #include "itkImageSink.h" #include "itkNumericTraits.h" +#include "itkLabelOverlapLabelSetMeasures.h" #include #include namespace itk { - /** \class LabelOverlapMeasuresImageFilter * \brief Computes overlap measures between the set same set of labels of * pixels of two images. Background is assumed to be 0. @@ -73,25 +73,14 @@ class ITK_TEMPLATE_EXPORT LabelOverlapMeasuresImageFilter : public ImageSink::RealType; - /** \class LabelSetMeasures - * \brief Metrics stored per label - * \ingroup ITKImageStatistics - */ - class LabelSetMeasures - { - public: - // default constructor/copy/move etc... +#ifndef ITK_FUTURE_LEGACY_REMOVE + /** Type to use for computations. */ + using LabelSetMeasures = LabelOverlapLabelSetMeasures; +#endif // !ITK_FUTURE_LEGACY_REMOVE - SizeValueType m_Source{ 0 }; - SizeValueType m_Target{ 0 }; - SizeValueType m_Union{ 0 }; - SizeValueType m_Intersection{ 0 }; - SizeValueType m_SourceComplement{ 0 }; - SizeValueType m_TargetComplement{ 0 }; - }; /** Type of the map used to store data per label */ - using MapType = std::unordered_map; + using MapType = std::unordered_map; using MapIterator = typename MapType::iterator; using MapConstIterator = typename MapType::const_iterator; diff --git a/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt b/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt index 61d2168cffb..b7409746b17 100644 --- a/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt +++ b/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt @@ -1,2 +1,4 @@ itk_wrap_module(ITKImageStatistics) +set(WRAPPER_SUBMODULE_ORDER itkLabelOverlapLabelSetMeasures) +list(APPEND WRAPPER_SWIG_LIBRARY_FILES "${CMAKE_CURRENT_LIST_DIR}/itkLabelOverlapLabelSetMeasuresInstantiations.i") itk_auto_load_and_end_wrap_submodules() diff --git a/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasures.wrap b/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasures.wrap new file mode 100644 index 00000000000..5c74c57e67c --- /dev/null +++ b/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasures.wrap @@ -0,0 +1 @@ +itk_wrap_simple_class("itk::LabelOverlapLabelSetMeasures") diff --git a/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i b/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i new file mode 100644 index 00000000000..52a577fb1db --- /dev/null +++ b/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i @@ -0,0 +1,14 @@ +%include + +%{ +#include "itkLabelOverlapMeasuresImageFilter.h" +%} + +%template(hashmapUCLOLSM) std::unordered_map< unsigned char, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapSCLOLSM) std::unordered_map< signed char, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapUSLOLSM) std::unordered_map< unsigned short, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapSSLOLSM) std::unordered_map< signed short, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapULLOLSM) std::unordered_map< unsigned long, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapSLLOLSM) std::unordered_map< signed long, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapULLLOLSM) std::unordered_map< unsigned long long, itk::LabelOverlapLabelSetMeasures >; +%template(hashmapSLLLOLSM) std::unordered_map< signed long long, itk::LabelOverlapLabelSetMeasures >; diff --git a/Wrapping/Generators/Python/PyBase/pyBase.i b/Wrapping/Generators/Python/PyBase/pyBase.i index 4450284b7c0..120cbeb79ba 100644 --- a/Wrapping/Generators/Python/PyBase/pyBase.i +++ b/Wrapping/Generators/Python/PyBase/pyBase.i @@ -28,6 +28,7 @@ from . import _ITKCommonPython %include %include %include +%include %include %include From 828b59ab495727f686995952b6b1e1889b54a3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Wed, 27 Sep 2023 14:35:04 -0400 Subject: [PATCH 2/2] ENH: Add a Python test --- .../test/Input/DzZ_Seeds.seg.nrrd.sha512 | 1 + .../test/Input/DzZ_T1.seg.nrrd.sha512 | 1 + .../wrapping/test/CMakeLists.txt | 12 +++++++ .../itkLabelOverlapMeasuresImageFilterTest.py | 34 +++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 Modules/Filtering/ImageStatistics/test/Input/DzZ_Seeds.seg.nrrd.sha512 create mode 100644 Modules/Filtering/ImageStatistics/test/Input/DzZ_T1.seg.nrrd.sha512 create mode 100644 Modules/Filtering/ImageStatistics/wrapping/test/CMakeLists.txt create mode 100644 Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py diff --git a/Modules/Filtering/ImageStatistics/test/Input/DzZ_Seeds.seg.nrrd.sha512 b/Modules/Filtering/ImageStatistics/test/Input/DzZ_Seeds.seg.nrrd.sha512 new file mode 100644 index 00000000000..09acdc18112 --- /dev/null +++ b/Modules/Filtering/ImageStatistics/test/Input/DzZ_Seeds.seg.nrrd.sha512 @@ -0,0 +1 @@ +4106f7a97659761a7fd42594a4f19aa9dad312445a1c9ff63f397f0c32cb952ee5d6ba6c6582951d883e6f40587091437a872c5fce966670d57f4984125c7a5d diff --git a/Modules/Filtering/ImageStatistics/test/Input/DzZ_T1.seg.nrrd.sha512 b/Modules/Filtering/ImageStatistics/test/Input/DzZ_T1.seg.nrrd.sha512 new file mode 100644 index 00000000000..db3c2a61fec --- /dev/null +++ b/Modules/Filtering/ImageStatistics/test/Input/DzZ_T1.seg.nrrd.sha512 @@ -0,0 +1 @@ +eb93426d1ee5f00d722979fd9dbfd8701f27bbb4256522e3d25c4db0f88f33b6a2db1ce572613e38c1a91787ca911e2399d206a137f7e987421b357bd5925b5d diff --git a/Modules/Filtering/ImageStatistics/wrapping/test/CMakeLists.txt b/Modules/Filtering/ImageStatistics/wrapping/test/CMakeLists.txt new file mode 100644 index 00000000000..5fc096b9207 --- /dev/null +++ b/Modules/Filtering/ImageStatistics/wrapping/test/CMakeLists.txt @@ -0,0 +1,12 @@ +set(test_input_dir ${itk-module_SOURCE_DIR}/test/Input) + +# let's make sure 3D uchar images are wrapped +list(FIND ITK_WRAP_IMAGE_DIMS 3 wrap_3_index) +if(ITK_WRAP_PYTHON AND ITK_WRAP_unsigned_char AND wrap_3_index GREATER -1) + itk_python_add_test(NAME LabelOverlapMeasuresImageFilterTest + TEST_DRIVER_ARGS + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/itkLabelOverlapMeasuresImageFilterTest.py + DATA{${test_input_dir}/DzZ_T1.seg.nrrd} + DATA{${test_input_dir}/DzZ_Seeds.seg.nrrd} + ) +endif() diff --git a/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py b/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py new file mode 100644 index 00000000000..8e66ca4824c --- /dev/null +++ b/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py @@ -0,0 +1,34 @@ +# ========================================================================== +# +# Copyright NumFOCUS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ==========================================================================*/ + + +import itk +from sys import argv + +itk.auto_progress(2) + +ref = itk.imread(argv[1], itk.UC) +seg = itk.imread(argv[2], itk.UC) + +lom_filter = itk.LabelOverlapMeasuresImageFilter[itk.Image[itk.UC, 3]].New() +lom_filter.SetTargetImage(seg) +lom_filter.SetSourceImage(ref) +lom_filter.UpdateLargestPossibleRegion() +lsm = lom_filter.GetLabelSetMeasures() +for label, measure in lsm.items(): + print(f"Label: {label}, i: {measure.m_Intersection}, u: {measure.m_Union}")