Skip to content

Commit 903ca57

Browse files
committed
WIP: Comparing performance in CastImageFilter
Comparing perfomance with using the cast image filter to convert between different vector pixel type. There are two option to compare: - Using ImageRegionRange appears to have performnace benifits, compared to the ImageScanelineIterators. - Using NumericTraits::GetLength is nearly a constant /compile time expression which is also performant. The challenge is choosing input vs output pixel type when only one has a compile time size.
1 parent 4fcc20a commit 903ca57

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

Modules/Filtering/ImageFilterBase/include/itkCastImageFilter.hxx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "itkProgressReporter.h"
2222
#include "itkImageAlgorithm.h"
23+
#include "itkImageRegionRange.h"
2324

2425
namespace itk
2526
{
@@ -139,18 +140,21 @@ CastImageFilter<TInputImage, TOutputImage>::DynamicThreadedGenerateDataDispatche
139140

140141
this->CallCopyOutputRegionToInputRegion(inputRegionForThread, outputRegionForThread);
141142

142-
const unsigned int componentsPerPixel = outputPtr->GetNumberOfComponentsPerPixel();
143143

144+
#if 0
144145
// Define the iterators
145146
ImageScanlineConstIterator inputIt(inputPtr, inputRegionForThread);
146147
ImageScanlineIterator outputIt(outputPtr, outputRegionForThread);
147148

149+
150+
const unsigned int componentsPerPixel = inputPtr->GetNumberOfComponentsPerPixel();
148151
while (!inputIt.IsAtEnd())
149152
{
150153
while (!inputIt.IsAtEndOfLine())
151154
{
152155
const InputPixelType & inputPixel = inputIt.Get();
153156
OutputPixelType value{ outputIt.Get() };
157+
154158
for (unsigned int k = 0; k < componentsPerPixel; ++k)
155159
{
156160
value[k] = static_cast<typename OutputPixelType::ValueType>(inputPixel[k]);
@@ -163,6 +167,32 @@ CastImageFilter<TInputImage, TOutputImage>::DynamicThreadedGenerateDataDispatche
163167
inputIt.NextLine();
164168
outputIt.NextLine();
165169
}
170+
#else
171+
172+
auto inputRange = ImageRegionRange<const TInputImage>(*inputPtr, inputRegionForThread);
173+
auto outputRange = ImageRegionRange<TOutputImage>(*outputPtr, outputRegionForThread);
174+
175+
auto inputIt = inputRange.begin();
176+
auto outputIt = outputRange.begin();
177+
const auto inputEnd = inputRange.end();
178+
179+
180+
OutputPixelType outputPixel{ *outputIt };
181+
const unsigned int componentsPerPixel = NumericTraits<OutputPixelType>::GetLength(outputPixel);
182+
while (inputIt != inputEnd)
183+
{
184+
InputPixelType inputPixel = *inputIt;
185+
for (unsigned int k = 0; k < componentsPerPixel; ++k)
186+
{
187+
outputPixel[k] = static_cast<typename OutputPixelType::ValueType>(inputPixel[k]);
188+
}
189+
*outputIt = outputPixel;
190+
191+
++inputIt;
192+
++outputIt;
193+
}
194+
195+
#endif
166196
}
167197

168198
} // end namespace itk

Modules/Filtering/ImageFilterBase/test/itkCastImageFilterTest.cxx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "itkFloatingPointExceptions.h"
2828
#include "itkRGBPixel.h"
2929
#include "itkRGBAPixel.h"
30+
#include "itkTimeProbe.h"
3031

3132
namespace itk
3233
{}
@@ -444,6 +445,47 @@ TestVectorImageCast3()
444445
}
445446

446447

448+
template <typename TInputImage, typename TOutputImage>
449+
void
450+
TimeImageCast()
451+
{
452+
std::cout << "Timing cast from " << GetCastTypeName<typename TInputImage::PixelType>() << " to "
453+
<< GetCastTypeName<typename TOutputImage::PixelType>() << " ... ";
454+
455+
// Create a 512^3 image
456+
auto image = TInputImage::New();
457+
458+
typename TInputImage::SizeType size;
459+
size.Fill(512);
460+
typename TInputImage::RegionType region{ size };
461+
image->SetRegions(region);
462+
image->SetNumberOfComponentsPerPixel(3);
463+
image->AllocateInitialized();
464+
465+
466+
using CastImageFilterType = itk::CastImageFilter<TInputImage, TOutputImage>;
467+
auto castImageFilter = CastImageFilterType::New();
468+
castImageFilter->SetInput(image);
469+
castImageFilter->SetNumberOfWorkUnits(1); // Single-threaded for consistent timing
470+
471+
// Warm-up run
472+
castImageFilter->Update();
473+
474+
// Timed run
475+
itk::TimeProbe timer;
476+
timer.Start();
477+
castImageFilter->Modified();
478+
castImageFilter->Update();
479+
timer.Stop();
480+
481+
const double totalPixels = size.CalculateProductOfElements();
482+
const double timeInSeconds = timer.GetMean();
483+
const double megapixelsPerSecond = (totalPixels / 1e6) / timeInSeconds;
484+
485+
std::cout << megapixelsPerSecond << " Mpixels/sec (" << timeInSeconds << " seconds)" << std::endl;
486+
}
487+
488+
447489
int
448490
itkCastImageFilterTest(int, char *[])
449491
{
@@ -477,6 +519,14 @@ itkCastImageFilterTest(int, char *[])
477519
success &= TestVectorImageCast2();
478520
success &= TestVectorImageCast3();
479521

522+
523+
std::cout << std::endl;
524+
std::cout << "Performance timing tests (512^3 images):" << std::endl;
525+
TimeImageCast<itk::Image<itk::Vector<float, 3>, 3>, itk::Image<itk::RGBPixel<double>, 3>>();
526+
TimeImageCast<itk::Image<itk::Vector<float, 3>, 3>, itk::VectorImage<double, 3>>();
527+
528+
TimeImageCast<itk::VectorImage<float, 3>, itk::Image<itk::RGBPixel<double>, 3>>();
529+
480530
std::cout << std::endl;
481531
if (!success)
482532
{

0 commit comments

Comments
 (0)