Conversation
Handle output iterators that return pixel proxies instead of lvalue
references by copying the pixel, updating its components, and assigning
it back through the iterator.
Addresses compilation error:
```
/opt/local/include/ITK-6.0/itkCastImageFilter.hxx:167:84: error: non-const lvalue reference to type 'itk::Vector<double, 2>' cannot bind to an initializer list temporary
167 | std::conditional_t<isVariableLengthVector, OutputPixelType, OutputPixelType &> outputPixel{ *outputIt };
| ^ ~~~~~~~~~~~~~
```
|
| else | ||
| { | ||
| outputPixel[k] = static_cast<OutputPixelValueType>(inputPixel[k]); | ||
| OutputPixelType outputPixel{ *outputIt }; | ||
|
|
||
| for (unsigned int k = 0; k < componentsPerPixel; ++k) | ||
| { | ||
| outputPixel[k] = static_cast<OutputPixelValueType>(inputPixel[k]); | ||
| } | ||
|
|
||
| *outputIt = outputPixel; |
There was a problem hiding this comment.
The new copy-modify-assign path is not exercised by the direct CastImageFilter tests. Ordinary Image outputs dereference to references, and VectorImage uses VariableLengthVector, so both select the earlier branch instead. Add an adaptor-backed, non-VariableLengthVector proxy-output test that runs the filter and verifies every converted component. This is non-blocking, but without it a regression can reintroduce a proxy-write compilation or conversion failure without detection.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Artifacts
- The executed script searches the relevant tests and records iterator facts and unavailable test tools, providing a repeatable verification command.
Direct cast test coverage inspection
- Executed repository inspection shows the changed copy-modify-assign branch, direct tests limited to VectorImage and ordinary Image outputs, and no direct CastImageFilter/ImageAdaptor instantiation; the branch lacks an exact test.
Iterator branch and environment inspection
- Executed inspection proves VectorImage is a proxy but VariableLengthVector case and records absent CMake, Pixi, CTest, and test driver; no runtime result can be inferred.
Comments Outside DiffThese findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.
|
|
Looks promising, thanks @nilason, but we would really need to have this case added to our unit tests! |
| outputPixel[k] = static_cast<OutputPixelValueType>(inputPixel[k]); | ||
| } | ||
|
|
||
| *outputIt = outputPixel; |
There was a problem hiding this comment.
It looks like this statement in the else clause is the only fundamental difference between the if and the else clause. (Right?) If that is indeed the case, I think it would be better to merge the proposed if and the else clauses, and have a single if, saying something like:
if constexpr (<this problematic case>)
{
*outputIt = outputPixel;
}
There was a problem hiding this comment.
There are differences also before the (repetitive) for-loop. But an if-else-clause on either side would also work.
There was a problem hiding this comment.
A possible rewrite might look like:
if constexpr (std::is_reference_v<OutputPixelReferenceType> || isVariableLengthVector)
{
std::conditional_t<isVariableLengthVector, OutputPixelType, OutputPixelType &> outputPixel{ *outputIt };
}
else
{
OutputPixelType outputPixel{ *outputIt };
}
for (unsigned int k = 0; k < componentsPerPixel; ++k)
{
outputPixel[k] = static_cast<OutputPixelValueType>(inputPixel[k]);
}
if constexpr (!std::is_reference_v<OutputPixelReferenceType> && !isVariableLengthVector))
{
*outputIt = outputPixel;
}There was a problem hiding this comment.
Agreed the duplication is worth removing — I looked at merging into a single if constexpr and the clean way to do it changes the control flow more than a one-line fixup should (the two branches produce a reference vs. a value with different lifetimes, so a shared declaration needs its own follow-up). Left as-is in #6901 to keep that PR to the fix plus the test; happy to open a separate cleanup PR if you'd still like it done here.
|
What are the template parameters to itk::CastImageFilter that cased the issue? |
I'm too unfamiliar with ITK do implement tests, but feel free to add test to this PR or separately (in which case I can rebase this). |
It is a simple |
|
53e1acd to
3ecf6a0
Compare
3ecf6a0 to
53e1acd
Compare
|
Thank you for tracking this down and reporting it, @nilason — the reproduction steps and OTB source pointer made this easy to root-cause. Since it needed a test and I'm not sure how comfortable you are writing one, I've opened #6901 with your fix plus a regression test and the root-cause answer to @blowekamp's question, crediting you as co-author. Your branch here is untouched. Happy to keep discussing here if anything about the approach in #6901 needs adjusting. |
|
The general trigger is any subclass of Detail and a reproducer with no OTB dependency in #6901, which also adds the test @N-Dekker asked for. |
|
Thank you all for digging into this! As maintainer you could probably have continued with this PR, pushing commits to my PR branch (the "Allow edits and access to secrets by maintainers" checkbox is ticked) and I wouldn't have mind. Please continue with #6901, I'm glad to help if I can. I can always test with OTB again. |
Until now, ImageBufferRange and ImageRegionRange would use a proxy to access the data of a subclass of `itk::Image`. With this commit, they will do direct pixel access instead, for those subclasses. This commit may significantly improve the performance of iteration over the pixels of an itk::Image subclass. Moreover, it will address the compile errors, attempting to build OrfeoToolBox v10 against ITK 6.0 beta2, reported by Nicklas Larsson at InsightSoftwareConsortium#6898 saying: ``` itkCastImageFilter.hxx:167:84: error: non-const lvalue reference to type 'itk::Vector<double, 2>' cannot bind to an initializer list temporary 167 | std::conditional_t<isVariableLengthVector, OutputPixelType, OutputPixelType &> outputPixel{ *outputIt }; | ^ ~~~~~~~~~~~~~ ```
Until now, ImageBufferRange and ImageRegionRange would use a proxy to access the data of a subclass of `itk::Image`. With this commit, they will do direct pixel access instead, for those subclasses. This commit may significantly improve the performance of iteration over the pixels of an itk::Image subclass. Moreover, it will address the compile errors, attempting to build OrfeoToolBox v10 against ITK 6.0 beta2, reported by Nicklas Larsson at InsightSoftwareConsortium#6898 saying: ``` itkCastImageFilter.hxx:167:84: error: non-const lvalue reference to type 'itk::Vector<double, 2>' cannot bind to an initializer list temporary 167 | std::conditional_t<isVariableLengthVector, OutputPixelType, OutputPixelType &> outputPixel{ *outputIt }; | ^ ~~~~~~~~~~~~~ ```
Handle output iterators that return pixel proxies instead of lvalue references by copying the pixel, updating its components, and assigning it back through the iterator.
Addresses compilation error (attempting to build OrfeoToolBox v10 against ITK 6.0 beta2):
Note: this is created with assistance of Codex, as I'm not familiar with ITK code. This solves the compilation error, but needs to be reviewed with that in mind.
PR Checklist
Refer to the ITK Software Guide for
further development details if necessary.