Wrap LabelSetMeasures in LabelOverlapMeasuresImageFilter#4221
Wrap LabelSetMeasures in LabelOverlapMeasuresImageFilter#4221hjmjohnson merged 4 commits intoInsightSoftwareConsortium:mainfrom
Conversation
76a9429 to
bf14387
Compare
32dbcad to
5f66a80
Compare
|
Currently running into: |
c585070 to
6962a00
Compare
|
What is |
|
|
|
@dzenanz could you please rebase and push? I am wondering if recent SWIG updates have helped. |
828b59a to
f823335
Compare
|
Rebased onto current |
end-of-file-fixer pre-commit hook flags a trailing empty line on this upstream-tracked file. Same one-character fix as PR InsightSoftwareConsortium#6032; included here so PR InsightSoftwareConsortium#4221's pre-commit CI can pass without waiting on that PR to merge first.
Same upstream-inherited end-of-file-fixer hit blocking PR InsightSoftwareConsortium#6032 / InsightSoftwareConsortium#4221. One-character fix included here so this PR's pre-commit CI can pass.
`Documentation/docs/releases/5.4.6.md` carries a trailing empty line that the `end-of-file-fixer` pre-commit hook flags every time a PR rebases onto current `main`. Affected at least PRs InsightSoftwareConsortium#6032, InsightSoftwareConsortium#4221, and InsightSoftwareConsortium#6186, where each had to carry an identical one-character fix. Apply once on `main` to stop the spurious `pre-commit` failures.
c0b6f1e to
46406bf
Compare
|
Pushed two commits on top of Diagnosis (verified locally, macOS Python wrapping build): SWIG silently drops Pivot to a wrapping-friendly C++ surface: added two small accessors that don't rely on std::vector<LabelType> GetLabels() const;
LabelOverlapLabelSetMeasures GetMeasureForLabel(LabelType) const;Plus six Test now passes locally against the existing fixture: Commits (2, on top of your existing 2)
Both commits carry What I tried before pivoting (so this isn't a black box)
Root cause is architectural: ITK's wrapping pipeline gives each submodule its own SWIG run with CI will re-run on |
|
Re-triggering ARMBUILD-x86_64-rosetta — |
|
| Filename | Overview |
|---|---|
| Modules/Filtering/ImageStatistics/include/itkLabelOverlapLabelSetMeasures.h | New standalone header extracting LabelOverlapLabelSetMeasures struct with explicit Get* accessors for Python wrapping; well-structured and self-contained. |
| Modules/Filtering/ImageStatistics/include/itkLabelOverlapMeasuresImageFilter.h | Adds GetLabels() + GetMeasureForLabel() accessors for Python, moves struct to standalone header; copy-pasted misleading doc comment on legacy typedef and a double blank line. |
| Modules/Filtering/ImageStatistics/wrapping/itkLabelOverlapLabelSetMeasures.wrap | Minimal one-liner wrap file using itk_wrap_simple_class; correct approach for a plain struct. |
| Modules/Filtering/ImageStatistics/wrapping/CMakeLists.txt | Sets WRAPPER_SUBMODULE_ORDER to ensure LabelOverlapLabelSetMeasures is wrapped before the filter that uses it; correct ordering. |
| Modules/Filtering/ImageStatistics/wrapping/test/itkLabelOverlapMeasuresImageFilterTest.py | Python smoke test exercising GetLabels() + GetMeasureForLabel() and Get* accessors; no value assertions so numerical regressions would go undetected. |
| Modules/Filtering/ImageStatistics/test/Input/DzZ_Seeds.seg.nrrd.sha512 | Valid 128-character SHA-512 hash for new test input file. |
| Modules/Filtering/ImageStatistics/test/Input/DzZ_T1.seg.nrrd.sha512 | Valid 128-character SHA-512 hash for new test input file. |
| Modules/Filtering/ImageStatistics/wrapping/test/CMakeLists.txt | New wrapping test CMakeLists with proper guards for 3D uchar wrapping; correct structure. |
Sequence Diagram
sequenceDiagram
participant Py as Python caller
participant F as LabelOverlapMeasuresImageFilter
participant M as std::unordered_map (m_LabelSetMeasures)
participant S as LabelOverlapLabelSetMeasures
Py->>F: UpdateLargestPossibleRegion()
F->>M: populate per-label entries
Py->>F: GetLabels()
F->>M: iterate keys
F-->>Py: std::vector<LabelType>
loop for each label
Py->>F: GetMeasureForLabel(label)
F->>M: find(label)
M-->>F: iterator → LabelOverlapLabelSetMeasures
F-->>Py: LabelOverlapLabelSetMeasures (by value)
Py->>S: GetIntersection() / GetUnion() / …
S-->>Py: SizeValueType
end
Reviews (1): Last reviewed commit: "COMP: Drop unused std::unordered_map SWI..." | Re-trigger Greptile
dzenanz
left a comment
There was a problem hiding this comment.
Looks good. I can't approve my own PR.
This exposes the return value from itk::LabelOverlapMeasuresImageFilter::GetLabelSetMeasures() as a Python dictionary instead of <Swig Object of type 'std::unordered_map< unsigned char,itkLabelOverlapMeasuresImageFilterIUC3::LabelSetMeasures > *'> This is needed for convenient use of the class from Python. The class has been un-nested to make wrapping easier/possible.
Expose the per-label overlap measures to Python via two paired accessors on itk::LabelOverlapMeasuresImageFilter: std::vector<LabelType> 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<LabelType, LabelOverlapLabelSetMeasures>`) 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<X,Y> 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<LabelType> is already wrapped globally in pyBase.i for every primitive label type. Co-Authored-By: dzenanz <dzenanz@gmail.com>
The original PR added wrapping infrastructure to expose
`std::unordered_map<LabelType, LabelOverlapLabelSetMeasures>` 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 <std_unordered_map.i>` 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 <dzenanz@gmail.com>
2e88212 to
e04e97e
Compare
This exposes the return value from
itk::LabelOverlapMeasuresImageFilter::GetLabelSetMeasures()as a Python dictionary instead of<Swig Object of type 'std::unordered_map< unsigned char,itkLabelOverlapMeasuresImageFilterIUC3::LabelSetMeasures > *'>. This is needed for convenient use of the class from Python.PR Checklist