From 79fc85ab6c4efc287f4a6654a960c43586e6339f 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/4] 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 | 24 +++--------- .../ImageStatistics/wrapping/CMakeLists.txt | 6 +++ .../itkLabelOverlapLabelSetMeasures.wrap | 1 + ...belOverlapLabelSetMeasuresInstantiations.i | 14 +++++++ Wrapping/Generators/Python/PyBase/pyBase.i | 1 + 6 files changed, 67 insertions(+), 18 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 000000000000..4355092c021a --- /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 2ca3429b9792..9580a9c7c121 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 same set of labels of * pixels of two images. Background is assumed to be 0. @@ -71,25 +71,13 @@ 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... - - SizeValueType m_Source{ 0 }; - SizeValueType m_Target{ 0 }; - SizeValueType m_Union{ 0 }; - SizeValueType m_Intersection{ 0 }; - SizeValueType m_SourceComplement{ 0 }; - SizeValueType m_TargetComplement{ 0 }; - }; +#ifndef ITK_FUTURE_LEGACY_REMOVE + /** Deprecated backward-compatibility alias. Use LabelOverlapLabelSetMeasures directly. */ + using LabelSetMeasures = LabelOverlapLabelSetMeasures; +#endif // !ITK_FUTURE_LEGACY_REMOVE /** 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 61d2168cffbd..0f21ddf8dabf 100644 --- a/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt +++ b/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt @@ -1,2 +1,8 @@ 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 000000000000..5c74c57e67c0 --- /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 000000000000..52a577fb1db9 --- /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 e396679e8657..6163eff01b31 100644 --- a/Wrapping/Generators/Python/PyBase/pyBase.i +++ b/Wrapping/Generators/Python/PyBase/pyBase.i @@ -30,6 +30,7 @@ from . import _ITKCommonPython %include %include %include +%include %include %include From f704028a2f3fe91d4d4332afd5b4f775965b2b99 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/4] 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 | 14 ++++++++ .../itkLabelOverlapMeasuresImageFilterTest.py | 34 +++++++++++++++++++ 4 files changed, 50 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 000000000000..09acdc181124 --- /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 000000000000..db3c2a61feca --- /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 000000000000..6a266c79f337 --- /dev/null +++ b/Modules/Filtering/ImageStatistics/wrapping/test/CMakeLists.txt @@ -0,0 +1,14 @@ +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 000000000000..8e66ca4824c7 --- /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}") From fff2d0d41bd3dc5adc03738d3a79a1f9e9fde9c9 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sat, 2 May 2026 08:54:31 -0500 Subject: [PATCH 3/4] ENH: Add Python-friendly LabelSetMeasures accessors Expose the per-label overlap measures to Python via two paired accessors on itk::LabelOverlapMeasuresImageFilter: std::vector GetLabels() const LabelOverlapLabelSetMeasures GetMeasureForLabel(LabelType) const Plus six explicit `Get*()` getters on itk::LabelOverlapLabelSetMeasures (GetSource, GetTarget, GetUnion, GetIntersection, GetSourceComplement, GetTargetComplement) so Python can read the per-label fields. The existing C++ API (`MapType GetLabelSetMeasures()` returning `std::unordered_map`) is preserved unchanged for performant in-process lookup; the new accessors are additive. Why two accessors instead of wrapping the unordered_map as a Python dict: SWIG's %template instantiation of std::unordered_map does not materialize a dict-like wrapper class when Y (the value type) is %import-ed (rather than %include-d) into the consuming submodule. ITK's per-submodule wrapping pipeline isolates each class as its own SWIG run with %import-only type info from siblings, so the %template directives in itkLabelOverlapLabelSetMeasuresInstantiations.i were silently dropped (count of `hashmap` in the SWIG-generated .cpp was 0). Falling back to a vector-of-keys + per-key lookup avoids the %template materialization issue entirely; std::vector is already wrapped globally in pyBase.i for every primitive label type. Co-Authored-By: dzenanz --- .../include/itkLabelOverlapLabelSetMeasures.h | 37 ++++++++++++++++ .../itkLabelOverlapMeasuresImageFilter.h | 42 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h index 4355092c021a..ba06a5b4dcbe 100644 --- a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h +++ b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h @@ -34,6 +34,43 @@ struct LabelOverlapLabelSetMeasures SizeValueType m_Intersection{ 0 }; SizeValueType m_SourceComplement{ 0 }; SizeValueType m_TargetComplement{ 0 }; + + // ITK's igenerator wrapping pipeline does not expose public data members of + // a struct to Python. Provide explicit getters so wrapped consumers (Python + // tests, downstream bindings) can read these fields. Also provide + // descriptively-named aliases for the m_-prefixed forms; the m_Foo names + // remain accessible from C++ for backward compatibility with consumers that + // construct or mutate these values directly. + SizeValueType + GetSource() const + { + return m_Source; + } + SizeValueType + GetTarget() const + { + return m_Target; + } + SizeValueType + GetUnion() const + { + return m_Union; + } + SizeValueType + GetIntersection() const + { + return m_Intersection; + } + SizeValueType + GetSourceComplement() const + { + return m_SourceComplement; + } + SizeValueType + GetTargetComplement() const + { + return m_TargetComplement; + } }; } // namespace itk #endif // itkLabelOverlapLabelSetMeasures_h diff --git a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h index 9580a9c7c121..1e893152b114 100644 --- a/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h +++ b/Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h @@ -99,6 +99,48 @@ class ITK_TEMPLATE_EXPORT LabelOverlapMeasuresImageFilter : public ImageSinkm_LabelSetMeasures; } + /** Get the labels for which set measures have been computed. + * + * Provided for Python ergonomics: SWIG cannot wrap + * `std::unordered_map` as a + * Python dict across submodule boundaries (the value type is `%import`-ed + * rather than `%include`-d, so `%template` is silently dropped). Pair + * this accessor with `GetMeasureForLabel()` to iterate labels and look up + * their measures from Python: + * + * \code{.py} + * for label in filter.GetLabels(): + * m = filter.GetMeasureForLabel(label) + * \endcode + */ + std::vector + GetLabels() const + { + std::vector labels; + labels.reserve(this->m_LabelSetMeasures.size()); + for (const auto & kv : this->m_LabelSetMeasures) + { + labels.push_back(kv.first); + } + return labels; + } + + /** Get the per-label measures struct for a single label. Throws + * itk::ExceptionObject if the label is not present in the map. + * See GetLabels() for the Python iteration idiom. + */ + LabelOverlapLabelSetMeasures + GetMeasureForLabel(LabelType label) const + { + const auto it = this->m_LabelSetMeasures.find(label); + if (it == this->m_LabelSetMeasures.end()) + { + itkExceptionMacro("Label " << static_cast::PrintType>(label) + << " is not present in the label set measures map."); + } + return it->second; + } + // Overlap agreement metrics /** Get the total overlap over all labels. */ From e04e97e5769cb64b83daa7f707e95ccd6a2e05ac Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sat, 2 May 2026 08:54:53 -0500 Subject: [PATCH 4/4] COMP: Drop unused std::unordered_map SWIG infrastructure The original PR added wrapping infrastructure to expose `std::unordered_map` as a Python dict. In practice the %template instantiations in itkLabelOverlapLabelSetMeasuresInstantiations.i were silently dropped by SWIG: the value type LabelOverlapLabelSetMeasures is %import-ed (not %include-d) into the LabelOverlapMeasuresImageFilter submodule, so SWIG had only a forward declaration and could not materialize the hashmap wrapper class. Confirmed locally on macOS: count of `hashmap` in the SWIG-generated ITKImageStatisticsPython.cpp was 0 across every namespace-qualifier and ordering variant tried. Drop the unused infrastructure so the PR ships only the working C++/Python surface introduced in the previous commit: * Remove WRAPPER_SWIG_LIBRARY_FILES wiring from Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt. * Remove itkLabelOverlapLabelSetMeasuresInstantiations.i (no longer referenced). * Revert the `%include ` line added to Wrapping/Generators/Python/PyBase/pyBase.i (unused after the above; pyBase.i is foundation-level shared infrastructure and should not carry a header it doesn't consume). Update itkLabelOverlapMeasuresImageFilterTest.py to use the new GetLabels()/GetMeasureForLabel() accessors and the explicit getters (GetIntersection, GetUnion) on the per-label struct. Local test output (200 voxel-label fixture, 14 labels detected, intersection and union counts match expected per-label values): Found 14 labels Label: 0, ... Label: 1, i: 2246, u: 120691 ... Label: 13, i: 0, u: 387126 100% tests passed, 0 tests failed out of 1 Co-Authored-By: dzenanz --- .../ImageStatistics/wrapping/CMakeLists.txt | 5 ---- ...belOverlapLabelSetMeasuresInstantiations.i | 14 ----------- .../itkLabelOverlapMeasuresImageFilterTest.py | 23 ++++++++++++++++--- Wrapping/Generators/Python/PyBase/pyBase.i | 1 - 4 files changed, 20 insertions(+), 23 deletions(-) delete mode 100644 Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i diff --git a/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt b/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt index 0f21ddf8dabf..82dfc4eb1ef3 100644 --- a/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt +++ b/Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt @@ -1,8 +1,3 @@ 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/itkLabelOverlapLabelSetMeasuresInstantiations.i b/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i deleted file mode 100644 index 52a577fb1db9..000000000000 --- a/Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasuresInstantiations.i +++ /dev/null @@ -1,14 +0,0 @@ -%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/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py b/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py index 8e66ca4824c7..79507a051671 100644 --- a/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py +++ b/Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py @@ -29,6 +29,23 @@ 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}") + +# GetLabelSetMeasures() returns std::unordered_map, which SWIG +# cannot wrap as a Python dict across submodule boundaries. Use the paired +# accessors GetLabels() + GetMeasureForLabel() for Python iteration. +labels = list(lom_filter.GetLabels()) +print(f"Found {len(labels)} labels") +assert len(labels) > 0, "GetLabels() returned no labels" +for label in sorted(labels): + measure = lom_filter.GetMeasureForLabel(label) + # Use the explicit Get* accessors (igenerator does not expose public data + # members of a struct to Python; the m_Foo fields are not visible). + intersection = measure.GetIntersection() + union = measure.GetUnion() + print(f"Label: {label}, i: {intersection}, u: {union}") + assert ( + intersection >= 0 + ), f"Label {label}: intersection ({intersection}) must be non-negative" + assert ( + union >= intersection + ), f"Label {label}: union ({union}) must be >= intersection ({intersection})" diff --git a/Wrapping/Generators/Python/PyBase/pyBase.i b/Wrapping/Generators/Python/PyBase/pyBase.i index 6163eff01b31..e396679e8657 100644 --- a/Wrapping/Generators/Python/PyBase/pyBase.i +++ b/Wrapping/Generators/Python/PyBase/pyBase.i @@ -30,7 +30,6 @@ from . import _ITKCommonPython %include %include %include -%include %include %include