Repository navigation
WIP: ENH: Step iterator positions with integer arithmetic at buffer markers #7111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -216,7 +216,7 @@ class ITK_TEMPLATE_EXPORT ImageLinearConstIteratorWithIndex : public ImageConstI | |
| operator++() | ||
| { | ||
| this->m_PositionIndex[m_Direction]++; | ||
| this->m_Position += m_Jump; | ||
| this->MovePosition(m_Jump); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same pattern as the slice iterator: the last |
||
| return *this; | ||
| } | ||
|
|
||
|
|
@@ -226,7 +226,7 @@ class ITK_TEMPLATE_EXPORT ImageLinearConstIteratorWithIndex : public ImageConstI | |
| operator--() | ||
| { | ||
| this->m_PositionIndex[m_Direction]--; | ||
| this->m_Position -= m_Jump; | ||
| this->MovePosition(-m_Jump); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 47 of 1368 operations put the pointer below the buffer, up to 30 pixels; direction 0 reaches |
||
| return *this; | ||
| } | ||
|
|
||
|
|
@@ -248,8 +248,8 @@ template <typename TImage> | |
| inline void | ||
| ImageLinearConstIteratorWithIndex<TImage>::NextLine() | ||
| { | ||
| this->m_Position -= | ||
| this->m_OffsetTable[m_Direction] * (this->m_PositionIndex[m_Direction] - this->m_BeginIndex[m_Direction]); | ||
| this->MovePosition(-this->m_OffsetTable[m_Direction] * | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| (this->m_PositionIndex[m_Direction] - this->m_BeginIndex[m_Direction])); | ||
|
|
||
| this->m_PositionIndex[m_Direction] = this->m_BeginIndex[m_Direction]; | ||
|
|
||
|
|
@@ -265,12 +265,12 @@ ImageLinearConstIteratorWithIndex<TImage>::NextLine() | |
| this->m_PositionIndex[n]++; | ||
| if (this->m_PositionIndex[n] < this->m_EndIndex[n]) | ||
| { | ||
| this->m_Position += this->m_OffsetTable[n]; | ||
| this->MovePosition(this->m_OffsetTable[n]); | ||
| this->m_Remaining = true; | ||
| break; | ||
| } | ||
|
|
||
| this->m_Position -= this->m_OffsetTable[n] * (this->m_Region.GetSize()[n] - 1); | ||
| this->MovePosition(-this->m_OffsetTable[n] * static_cast<OffsetValueType>(this->m_Region.GetSize()[n] - 1)); | ||
| this->m_PositionIndex[n] = this->m_BeginIndex[n]; | ||
| } | ||
| } | ||
|
|
@@ -282,8 +282,8 @@ template <typename TImage> | |
| inline void | ||
| ImageLinearConstIteratorWithIndex<TImage>::PreviousLine() | ||
| { | ||
| this->m_Position += | ||
| this->m_OffsetTable[m_Direction] * (this->m_EndIndex[m_Direction] - 1 - this->m_PositionIndex[m_Direction]); | ||
| this->MovePosition(this->m_OffsetTable[m_Direction] * | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mirror for |
||
| (this->m_EndIndex[m_Direction] - 1 - this->m_PositionIndex[m_Direction])); | ||
|
|
||
| this->m_PositionIndex[m_Direction] = this->m_EndIndex[m_Direction] - 1; | ||
|
|
||
|
|
@@ -299,12 +299,12 @@ ImageLinearConstIteratorWithIndex<TImage>::PreviousLine() | |
| this->m_PositionIndex[n]--; | ||
| if (this->m_PositionIndex[n] >= this->m_BeginIndex[n]) | ||
| { | ||
| this->m_Position -= this->m_OffsetTable[n]; | ||
| this->MovePosition(-this->m_OffsetTable[n]); | ||
| this->m_Remaining = true; | ||
| break; | ||
| } | ||
|
|
||
| this->m_Position += this->m_OffsetTable[n] * (this->m_Region.GetSize()[n] - 1); | ||
| this->MovePosition(this->m_OffsetTable[n] * static_cast<OffsetValueType>(this->m_Region.GetSize()[n] - 1)); | ||
| this->m_PositionIndex[n] = this->m_EndIndex[n] - 1; | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,7 +41,7 @@ ImageLinearConstIteratorWithIndex<TImage>::GoToReverseBeginOfLine() | |
| { | ||
| const OffsetValueType distanceToEnd = this->m_EndIndex[m_Direction] - this->m_PositionIndex[m_Direction] - 1; | ||
|
|
||
| this->m_Position += m_Jump * distanceToEnd; | ||
| this->MovePosition(m_Jump * distanceToEnd); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| this->m_PositionIndex[m_Direction] = this->m_EndIndex[m_Direction] - 1; | ||
| } | ||
|
|
||
|
|
@@ -54,7 +54,7 @@ ImageLinearConstIteratorWithIndex<TImage>::GoToBeginOfLine() | |
| { | ||
| const OffsetValueType distanceToBegin = this->m_PositionIndex[m_Direction] - this->m_BeginIndex[m_Direction]; | ||
|
|
||
| this->m_Position -= m_Jump * distanceToBegin; | ||
| this->MovePosition(-m_Jump * distanceToBegin); | ||
|
|
||
| this->m_PositionIndex[m_Direction] = this->m_BeginIndex[m_Direction]; | ||
| } | ||
|
|
@@ -68,7 +68,7 @@ ImageLinearConstIteratorWithIndex<TImage>::GoToEndOfLine() | |
| { | ||
| const OffsetValueType distanceToEnd = this->m_EndIndex[m_Direction] - this->m_PositionIndex[m_Direction]; | ||
|
|
||
| this->m_Position += m_Jump * distanceToEnd; | ||
| this->MovePosition(m_Jump * distanceToEnd); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| this->m_PositionIndex[m_Direction] = this->m_EndIndex[m_Direction]; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,11 +30,12 @@ ImageSliceConstIteratorWithIndex<TImage>::NextLine() | |
| { | ||
| // Move to next line | ||
| this->m_PositionIndex[m_Direction_B]++; | ||
| this->m_Position += m_LineJump; | ||
| this->MovePosition(static_cast<OffsetValueType>(m_LineJump)); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| // Move to beginning of line | ||
| this->m_PositionIndex[m_Direction_A] = this->m_BeginIndex[m_Direction_A]; | ||
| this->m_Position -= m_PixelJump * (this->m_EndIndex[m_Direction_A] - this->m_BeginIndex[m_Direction_A]); | ||
| this->MovePosition( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After the last line of a slice this lands on the end-of-slice marker along the second direction: 15 of the 48 configurations end |
||
| -static_cast<OffsetValueType>(m_PixelJump * (this->m_EndIndex[m_Direction_A] - this->m_BeginIndex[m_Direction_A]))); | ||
| } | ||
|
|
||
| //---------------------------------------------------------------------- | ||
|
|
@@ -46,11 +47,12 @@ ImageSliceConstIteratorWithIndex<TImage>::PreviousLine() | |
| { | ||
| // Move to previous line | ||
| this->m_PositionIndex[m_Direction_B]--; | ||
| this->m_Position -= m_LineJump; | ||
| this->MovePosition(-static_cast<OffsetValueType>(m_LineJump)); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mirror of |
||
|
|
||
| // Move to end of line | ||
| this->m_PositionIndex[m_Direction_A] = this->m_EndIndex[m_Direction_A] - 1; | ||
| this->m_Position += m_PixelJump * (this->m_EndIndex[m_Direction_A] - this->m_BeginIndex[m_Direction_A]); | ||
| this->MovePosition( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After the first line of a slice this lands on the reverse end-of-slice marker: 18 configurations end |
||
| static_cast<OffsetValueType>(m_PixelJump * (this->m_EndIndex[m_Direction_A] - this->m_BeginIndex[m_Direction_A]))); | ||
| } | ||
|
|
||
| //---------------------------------------------------------------------- | ||
|
|
@@ -62,7 +64,8 @@ ImageSliceConstIteratorWithIndex<TImage>::GoToBeginOfSlice() | |
| { | ||
| // Move to beginning of Slice | ||
| this->m_PositionIndex[m_Direction_B] = this->m_BeginIndex[m_Direction_B]; | ||
| this->m_Position -= m_LineJump * (this->m_EndIndex[m_Direction_B] - this->m_BeginIndex[m_Direction_B]); | ||
| this->MovePosition( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Called from the end-of-slice marker, which can be beyond the buffer (see |
||
| -static_cast<OffsetValueType>(m_LineJump * (this->m_EndIndex[m_Direction_B] - this->m_BeginIndex[m_Direction_B]))); | ||
| } | ||
|
|
||
| //---------------------------------------------------------------------- | ||
|
|
@@ -73,7 +76,8 @@ void | |
| ImageSliceConstIteratorWithIndex<TImage>::NextSlice() | ||
| { | ||
| // Move to beginning of Slice | ||
| this->m_Position -= m_LineJump * (this->m_PositionIndex[m_Direction_B] - this->m_BeginIndex[m_Direction_B]); | ||
| this->MovePosition(-static_cast<OffsetValueType>( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Starts from the end-of-slice marker (beyond the buffer in 15 configurations) and steps back from it (reasoned, see line 37). |
||
| m_LineJump * (this->m_PositionIndex[m_Direction_B] - this->m_BeginIndex[m_Direction_B]))); | ||
| this->m_PositionIndex[m_Direction_B] = this->m_BeginIndex[m_Direction_B]; | ||
|
|
||
| for (unsigned int n = 0; n < TImage::ImageDimension; ++n) | ||
|
|
@@ -88,12 +92,12 @@ ImageSliceConstIteratorWithIndex<TImage>::NextSlice() | |
| this->m_PositionIndex[n]++; | ||
| if (this->m_PositionIndex[n] < this->m_EndIndex[n]) | ||
| { | ||
| this->m_Position += this->m_OffsetTable[n]; | ||
| this->MovePosition(this->m_OffsetTable[n]); | ||
| this->m_Remaining = true; | ||
| break; | ||
| } | ||
|
|
||
| this->m_Position -= this->m_OffsetTable[n + 1] - this->m_OffsetTable[n]; | ||
| this->MovePosition(this->m_OffsetTable[n] - this->m_OffsetTable[n + 1]); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the traversal is exhausted the per-dimension resets leave the IsAtEnd marker far outside: 8 configurations end |
||
| this->m_PositionIndex[n] = this->m_BeginIndex[n]; | ||
| } | ||
| } | ||
|
|
@@ -107,7 +111,8 @@ ImageSliceConstIteratorWithIndex<TImage>::PreviousSlice() | |
| { | ||
| // Move to end of Slice | ||
| this->m_PositionIndex[m_Direction_B] = this->m_EndIndex[m_Direction_B] - 1; | ||
| this->m_Position += m_LineJump * (this->m_EndIndex[m_Direction_B] - this->m_BeginIndex[m_Direction_B]); | ||
| this->MovePosition( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Starts from the reverse end-of-slice marker (below the buffer in 18 configurations) and steps forward from it (reasoned, see line 54). |
||
| static_cast<OffsetValueType>(m_LineJump * (this->m_EndIndex[m_Direction_B] - this->m_BeginIndex[m_Direction_B]))); | ||
|
|
||
| for (unsigned int n = 0; n < TImage::ImageDimension; ++n) | ||
| { | ||
|
|
@@ -121,12 +126,12 @@ ImageSliceConstIteratorWithIndex<TImage>::PreviousSlice() | |
| this->m_PositionIndex[n]--; | ||
| if (this->m_PositionIndex[n] >= this->m_BeginIndex[n]) | ||
| { | ||
| this->m_Position -= this->m_OffsetTable[n]; | ||
| this->MovePosition(-this->m_OffsetTable[n]); | ||
| this->m_Remaining = true; | ||
| break; | ||
| } | ||
|
|
||
| this->m_Position += this->m_OffsetTable[n + 1] - this->m_OffsetTable[n]; | ||
| this->MovePosition(this->m_OffsetTable[n + 1] - this->m_OffsetTable[n]); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mirror of |
||
| this->m_PositionIndex[n] = this->m_EndIndex[n] - 1; | ||
| } | ||
| } | ||
|
|
@@ -211,7 +216,7 @@ ImageSliceConstIteratorWithIndex<TImage> & | |
| ImageSliceConstIteratorWithIndex<TImage>::operator++() | ||
| { | ||
| this->m_PositionIndex[m_Direction_A]++; | ||
| this->m_Position += m_PixelJump; | ||
| this->MovePosition(static_cast<OffsetValueType>(m_PixelJump)); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The last |
||
| return *this; | ||
| } | ||
|
|
||
|
|
@@ -223,7 +228,7 @@ ImageSliceConstIteratorWithIndex<TImage> & | |
| ImageSliceConstIteratorWithIndex<TImage>::operator--() | ||
| { | ||
| this->m_PositionIndex[m_Direction_A]--; | ||
| this->m_Position -= m_PixelJump; | ||
| this->MovePosition(-static_cast<OffsetValueType>(m_PixelJump)); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The last |
||
| return *this; | ||
| } | ||
| } // end namespace itk | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /*========================================================================= | ||
| * | ||
| * 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. | ||
| * | ||
| *=========================================================================*/ | ||
|
|
||
| #include "itkImage.h" | ||
| #include "itkImageLinearConstIteratorWithIndex.h" | ||
| #include "itkImageLinearIteratorWithIndex.h" | ||
| #include "itkImageBufferRange.h" | ||
| #include "itkGTest.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <vector> | ||
|
|
||
| namespace | ||
| { | ||
| using ImageType = itk::Image<float, 3>; | ||
| using RegionType = ImageType::RegionType; | ||
|
|
||
| ImageType::Pointer | ||
| MakeImage() | ||
| { | ||
| auto image = ImageType::New(); | ||
| image->SetRegions(RegionType({ { 10, 20, 30 } }, { { 6, 5, 4 } })); | ||
| image->Allocate(); | ||
| float value = 1.0F; | ||
| for (float & pixel : itk::ImageBufferRange<ImageType>{ *image }) | ||
| { | ||
| pixel = value++; | ||
| } | ||
| return image; | ||
| } | ||
|
|
||
| // Visits every pixel of `region` once, in both orders, checking index, value and position agreement. | ||
| template <typename TIterator> | ||
| void | ||
| ExpectTraversal(ImageType * image, const RegionType & region, unsigned int direction) | ||
| { | ||
| TIterator it(image, region); | ||
| it.SetDirection(direction); | ||
| for (const bool reverse : { false, true }) | ||
| { | ||
| SCOPED_TRACE(reverse ? "reverse" : "forward"); | ||
| std::vector<unsigned int> visits(image->GetBufferedRegion().GetNumberOfPixels(), 0); | ||
| reverse ? it.GoToReverseBegin() : it.GoToBegin(); | ||
| while (!(reverse ? it.IsAtReverseEnd() : it.IsAtEnd())) | ||
| { | ||
| while (!(reverse ? it.IsAtReverseEndOfLine() : it.IsAtEndOfLine())) | ||
| { | ||
| const auto index = it.GetIndex(); | ||
| ASSERT_TRUE(region.IsInside(index)); | ||
| EXPECT_EQ(it.Get(), image->GetPixel(index)); | ||
| EXPECT_EQ(visits[image->ComputeOffset(index)]++, 0U); | ||
| reverse ? --it : ++it; | ||
| } | ||
| reverse ? it.PreviousLine() : it.NextLine(); | ||
| } | ||
| EXPECT_EQ(std::count(visits.cbegin(), visits.cend(), 1U), region.GetNumberOfPixels()); | ||
| } | ||
| } | ||
|
|
||
| const RegionType regions[] = { RegionType({ { 10, 20, 30 } }, { { 6, 5, 4 } }), | ||
| RegionType({ { 11, 21, 31 } }, { { 4, 3, 2 } }), | ||
| RegionType({ { 10, 20, 30 } }, { { 3, 2, 2 } }), | ||
| RegionType({ { 13, 23, 32 } }, { { 3, 2, 2 } }) }; | ||
| } // namespace | ||
|
|
||
| TEST(ImageLinearIteratorWithIndex, VisitsEveryPixelOnceInEveryDirection) | ||
| { | ||
| const auto image = MakeImage(); | ||
| for (const auto & region : regions) | ||
| { | ||
| for (unsigned int direction = 0; direction < 3; ++direction) | ||
| { | ||
| SCOPED_TRACE(testing::Message() << "region " << region << " direction " << direction); | ||
| ExpectTraversal<itk::ImageLinearConstIteratorWithIndex<ImageType>>(image, region, direction); | ||
| ExpectTraversal<itk::ImageLinearIteratorWithIndex<ImageType>>(image, region, direction); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST(ImageLinearIteratorWithIndex, GoToBeginAndEndOfLineLandOnTheLine) | ||
| { | ||
| const auto image = MakeImage(); | ||
| const auto region = regions[1]; | ||
| itk::ImageLinearConstIteratorWithIndex<ImageType> it(image, region); | ||
| it.SetDirection(2); | ||
| it.GoToBegin(); | ||
| it.NextLine(); | ||
| it.GoToEndOfLine(); | ||
| EXPECT_EQ(it.GetIndex()[2], region.GetIndex(2) + static_cast<itk::IndexValueType>(region.GetSize(2))); | ||
| it.GoToReverseBeginOfLine(); | ||
| EXPECT_EQ(it.Get(), image->GetPixel(it.GetIndex())); | ||
| it.GoToBeginOfLine(); | ||
| EXPECT_EQ(it.GetIndex()[2], region.GetIndex(2)); | ||
| EXPECT_EQ(it.Get(), image->GetPixel(it.GetIndex())); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The step is done on the pointer's integer value so that no pointer arithmetic produces a value outside the buffer ([expr.add]/4). Measured against
mainwith the slice loop: Clang (arm64 23, x86 18) folds this back to the original pointer arithmetic, same instructions per pixel; GCC 13 keeps one extra instruction per pixel (10.3 -> 11.3) at +0% wall time on long lines and +2% to +9% on 2-pixel lines.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gdevenyi This seems like a lot of compiled away conversion to do the same math to avoid an un-observed problem.
This solution does not have the performance degredation that your PR 7032 had.