Skip to content

Commit ba1c2b4

Browse files
authored
Merge pull request #6263 from hjmjohnson/ingest-MultipleImageIterator
ENH: Ingest MultipleImageIterator into Modules/Core
2 parents 0c91b66 + a1a0d48 commit ba1c2b4

12 files changed

Lines changed: 256 additions & 61 deletions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
project(MultipleImageIterator)
2+
3+
itk_module_impl()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# MultipleImageIterator
2+
3+
In-tree ITK module providing `itk::MultipleImageIterator`, a single
4+
iterator that walks any number of co-registered images in lockstep.
5+
The iterator returns a `std::vector` of pixel values (or references)
6+
at each position, simplifying multi-channel processing without the
7+
boilerplate of synchronizing N separate iterators.
8+
9+
## Origin
10+
11+
Ingested from the standalone remote module
12+
[**KitwareMedical/MultipleImageIterator**](https://github.com/KitwareMedical/MultipleImageIterator)
13+
on 2026-05-12, following the v4 ingestion guidelines defined in
14+
InsightSoftwareConsortium/ITK#6204. Placed under `Modules/Core/` to
15+
match the placement of other ITK iterators (`itkImageRegionIterator`,
16+
`itkNeighborhoodIterator`). The upstream repository will be archived
17+
read-only after this PR merges; it remains reachable at the URL above
18+
for historical reference.
19+
20+
## References
21+
22+
- Schaerer J. *A MultipleImageIterator for iterating over multiple
23+
images simultaneously.* The Insight Journal. December 2014.
24+
<https://doi.org/10.54294/e5lmyz>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#ifndef itkMultipleImageIterator_h
20+
#define itkMultipleImageIterator_h
21+
#include <vector>
22+
#include <itkImageRegionIterator.h>
23+
24+
25+
namespace itk
26+
{
27+
/** \class MultipleImageIterator
28+
* \brief An wrapper around image iterators to iterate over several images simultaneously
29+
* All iterators must
30+
* - point to images of the same type
31+
* - be of the same size (number of values from begin to end)
32+
* \ingroup ImageIterators
33+
* \ingroup MultipleImageIterator */
34+
template <typename TIterator>
35+
class MultipleImageIterator
36+
{
37+
public:
38+
using Self = MultipleImageIterator;
39+
using IteratorType = TIterator;
40+
using ImageType = typename IteratorType::ImageType;
41+
/// Access one of the iterators
42+
IteratorType &
43+
operator[](const unsigned int i)
44+
{
45+
return m_Iterators[i];
46+
}
47+
/// Add a new iterator
48+
void
49+
AddIterator(const IteratorType & it)
50+
{
51+
m_Iterators.push_back(it);
52+
}
53+
/// Advance all iterators
54+
Self &
55+
operator++()
56+
{
57+
for (auto it = m_Iterators.begin(); it != m_Iterators.end(); ++it)
58+
{
59+
++(*it);
60+
}
61+
return *this;
62+
}
63+
/// Rewind all iterators
64+
void
65+
GoToBegin()
66+
{
67+
for (auto it = m_Iterators.begin(); it != m_Iterators.end(); ++it)
68+
{
69+
it->GoToBegin();
70+
}
71+
}
72+
/** Check if the first iterator is at end. In debug mode, additionally check
73+
* that at least one iterator is present and that all iterators' IsAtEnd()
74+
* methods return the same thing */
75+
bool
76+
IsAtEnd()
77+
{
78+
#ifdef NDEBUG
79+
return m_Iterators[0].IsAtEnd();
80+
#else
81+
assert(m_Iterators.size());
82+
bool retval = m_Iterators[0].IsAtEnd();
83+
for (unsigned int i = 0; i < m_Iterators.size(); ++i)
84+
assert(m_Iterators[i].IsAtEnd() == retval);
85+
return retval;
86+
#endif
87+
}
88+
/// Returns the number of iterators
89+
unsigned int
90+
Size() const
91+
{
92+
return m_Iterators.size();
93+
}
94+
95+
protected:
96+
std::vector<IteratorType> m_Iterators;
97+
};
98+
} // namespace itk
99+
#endif // itkMultipleImageIterator_h
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Maintainer: Joel Schaerer
2+
itk_module(
3+
MultipleImageIterator
4+
DEPENDS
5+
ITKCommon
6+
TEST_DEPENDS
7+
ITKTestKernel
8+
EXCLUDE_FROM_DEFAULT
9+
DESCRIPTION
10+
"An iterator wrapper that walks multiple co-registered images in lockstep, returning a std::vector of pixel values at each position. https://doi.org/10.54294/e5lmyz"
11+
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
itk_module_test()
2+
3+
set(MultipleImageIteratorTests DumpIntensities.cxx)
4+
5+
createtestdriver(MultipleImageIterator "${MultipleImageIterator-Test_LIBRARIES}" "${MultipleImageIteratorTests}")
6+
7+
itk_add_test(
8+
NAME MultipleImageIteratorTest
9+
COMMAND
10+
MultipleImageIteratorTestDriver
11+
--compare
12+
DATA{randBase.nrrd}
13+
"${ITK_TEST_OUTPUT_DIR}/randOut.nrrd"
14+
DumpIntensities
15+
"${ITK_TEST_OUTPUT_DIR}/randOut.nrrd"
16+
DATA{img1.png}
17+
DATA{img2.png}
18+
DATA{img3.png}
19+
)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#include <iostream>
20+
#include <vector>
21+
#include <string>
22+
#include <cstdlib>
23+
#include <itkImageFileReader.h>
24+
#include <itkImageFileWriter.h>
25+
#include "itkMultipleImageIterator.h"
26+
27+
// Dumps random samples from files into a csv file
28+
29+
int
30+
DumpIntensities(int argc, char * argv[])
31+
{
32+
if (argc < 3)
33+
{
34+
std::cerr << "Usage: DumpIntensities outfile inImage [inImage ...]" << std::endl;
35+
return 1;
36+
}
37+
using PixelType = unsigned short;
38+
using ImageType = itk::Image<PixelType, 3>;
39+
using ReaderType = itk::ImageFileReader<ImageType>;
40+
41+
using IteratorType = itk::ImageRegionIterator<ImageType>;
42+
itk::MultipleImageIterator<IteratorType> it;
43+
44+
std::vector<ImageType::Pointer> images; // Need to keep a reference as iterators only have weak references
45+
for (int i = 2; i < argc; ++i)
46+
{
47+
ReaderType::Pointer r = ReaderType::New();
48+
r->SetFileName(argv[i]);
49+
r->Update();
50+
ImageType::Pointer im = r->GetOutput();
51+
im->DisconnectPipeline();
52+
images.push_back(im);
53+
it.AddIterator(itk::ImageRegionIterator<ImageType>(im, im->GetLargestPossibleRegion()));
54+
}
55+
56+
unsigned long long c = 0;
57+
using Vec3 = itk::FixedArray<PixelType, 3>;
58+
std::vector<Vec3> values;
59+
for (it.GoToBegin(); !it.IsAtEnd(); ++it, ++c)
60+
{
61+
if (c % 42 == 0)
62+
{
63+
Vec3 v;
64+
for (unsigned int i = 0; i < it.Size(); ++i)
65+
{
66+
v[i] = it[i].Get();
67+
}
68+
values.push_back(v);
69+
}
70+
}
71+
72+
using Image1D = itk::Image<Vec3, 1>;
73+
Image1D::RegionType region;
74+
region.SetIndex(0, 0);
75+
region.SetSize(0, values.size());
76+
Image1D::Pointer randImage = Image1D::New();
77+
randImage->SetRegions(region);
78+
randImage->Allocate();
79+
80+
int index = 0;
81+
itk::ImageRegionIterator<Image1D> oIt(randImage, randImage->GetLargestPossibleRegion());
82+
while (!oIt.IsAtEnd())
83+
{
84+
oIt.Set(values[index++]);
85+
++oIt;
86+
}
87+
88+
using WriterType = itk::ImageFileWriter<Image1D>;
89+
WriterType::Pointer writer = WriterType::New();
90+
writer->SetInput(randImage);
91+
writer->SetFileName(argv[1]);
92+
writer->Update();
93+
94+
return 0;
95+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bafkreicd6xi33oatbqzspxkeszliv3mmjnnmiiu6dj6qj6wuclkv7uokda
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bafkreiactetnzqheknc2zjeg6aoo6y7uqbid7oukpwttfjuj52stok6wbe
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bafkreibqkbie3ezhl2y2kvgfizpoj5qfz3y5xdbe5rqtjo35pb5zn5geti
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bafkreihso7ziayrcpwm22dbjctew7ju3a7egxdyoytdn4ueuwggu26tscq

0 commit comments

Comments
 (0)