ITK icon indicating copy to clipboard operation
ITK copied to clipboard

Undefined behavior of iterators Get for VariableLengthVector

Open SimonRit opened this issue 1 month ago • 2 comments

Description

The Get function of an iterator on an itk::Image of itk::VariableLengthVector returns an itk::VariableLengthVector which data points to a pixel of the itk::Image. This is because the DefaultVectorPixelAccessor::Get calls the VariableLengthVector constructor parameterized by a const pointer. This leads to an unexpected behavior for the user.

Steps to Reproduce

This is a minimal example reproducing the issue:

#include <itkVectorImage.h>
#include <itkImageRegionIterator.h>

int
main(int argc, char * argv[])
{
  using ImageType = itk::VectorImage<float,2>;
  auto img = ImageType::New();
  img->SetVectorLength(3);
  ImageType::SizeType size = { { 2, 2 } };
  img->SetRegions(size);
  img->AllocateInitialized();
  itk::ImageRegionIterator<ImageType> it(img, img->GetLargestPossibleRegion());
  auto v { it.Get() };
  float c = 0.;
  while(!it.IsAtEnd())
  {
    v.Fill(c++);
    it.Set(v);
    ++it;
  }
  it.GoToBegin();
  while(!it.IsAtEnd())
  {
    std::cout << it.Get() << std::endl;
    ++it;
  }
  return EXIT_SUCCESS;
}

Expected behavior

It should display

[0, 0, 0]
[1, 1, 1]
[2, 2, 2]
[3, 3, 3]

Actual behavior

It actually displays

[3, 3, 3]
[1, 1, 1]
[2, 2, 2]
[3, 3, 3]

Reproducibility

100%

Versions

All versions since 6a603279d6fd2c4a914a028e272a135c5bf45325.

Environment

All OSs.

Additional Information

SimonRit avatar Nov 29 '25 09:11 SimonRit

I am uncertain if this setting with the ImageRegionIterator Get returned value is fully a bug, or just using a "reference" as an aggressive optimization.

However, the following with an ImageRegionConstIterator is also modifying the buffer:

 itk::ImageRegionConstIterator<ImageType> constIt(img, img->GetLargestPossibleRegion());
  auto                                  v{ constIt.Get() };
  
  itk::ImageRegionIterator<ImageType> it(img, img->GetLargestPossibleRegion());
  float                               c = 0.;
  while (!it.IsAtEnd())
  {
    v.Fill(c++);
    it.Set(v);
    ++it;
  }

And that is clearly non-const correct behavior. I am wondering if the returned type for Get is correct for the VectorImages, perhaps is should be const?

Here is an AI agent summary of the current Get methods for iterators:

`itkImageConstIterator.h` - Returns PixelTye - uses `m_PixelAccessorFunctor.Get()`
`itkConditionalConstIterator.h` - Returns const PixelType (pure virtual)
`itkLineConstIterator.h` - Returns const PixelType - uses `m_Image->GetPixel()`
`itkConstShapedNeighborhoodIterator.h` - Returns PixelType - uses `GetPixel()`
`itkImageConstIteratorWithIndex.h` - Returns PixelType - uses `m_PixelAccessorFunctor.Get()`
`itkImageReverseConstIterator.h` - Returns const PixelType - uses `m_PixelAccessorFunctor.Get()`
`itkFloodFilledImageFunctionConditionalIterator.h` - Returns const PixelType (override) - uses `GetPixel()`
`itkShapedFloodFilledFunctionConditionalConstIterator.h` - Returns const PixelType (override) - uses `GetPixel()`
`itkFloodFilledSpatialFunctionConditionalIterator.h` - Returns const PixelType (override) - uses `GetPixel()`
`itkShapedFloodFilledImageFunctionConditionalIterator.h` - Returns const PixelType (override) - uses `GetPixel()`
`itkFloodFilledFunctionConditionalConstIterator.h` - Returns const PixelType (override) - uses `GetPixel()`

If the return type was const PixelType, then the above code would cause a deep copy.

blowekamp avatar Dec 01 '25 15:12 blowekamp

It looks to me that there is a Value() method for having a reference to the value. The documentation clearly indicates that you can't use adaptors then. The value is const / should not be modified according to the adaptor API (see Get here).

If making the returned type const PixelType solves the problem at no cost, it sounds like the solution to this bug.

SimonRit avatar Dec 01 '25 15:12 SimonRit