Repository: InsightSoftwareConsortium/ITKVkFFTBackend (language: python)

Issue:
`itk.VkHalfHermitianToRealInverseFFTImageFilter` interface differs from base class
Seeing different behavior between `itk.VkHalfHermitianToRealInverseFFTImageFilter` and `itk.VnlHalfHermitianToRealInverseFFTImageFilter` due to the VkFFT filter defining a mandatory output size parameter.

## Steps to Reproduce

Define image with first dimension of size (2 ** n) - 1

```py
>>> import itk
>>> image.SetRegions([129,256])     # Taken from ITK FFT tests. First dimension must be size (2 ** n) - 1
>>> image.Allocate()
```

### Expected (Vnl)
```py
>>> fft_filter = itk.VnlHalfHermitianToRealInverseFFTImageFilter[itk.Image[itk.complex[itk.F],2],itk.Image[itk.F,2]].New()
>>> fft_filter.SetInput(image)
>>> fft_filter.Update()
>>> itk.size(fft_filter.GetOutput())
itkSize2 ([256, 256])
```

### Observed (VkFFT)
```py
>>> fft_filter = itk.VkHalfHermitianToRealInverseFFTImageFilter[itk.Image[itk.complex[itk.F],2]].New()
>>> fft_filter.SetInput(image)
>>> fft_filter.Update()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: C:\repos\ITKVkFFTBackend\include\itkVkHalfHermitianToRealInverseFFTImageFilter.hxx:68:
ITK ERROR: Input image's first dimension must equal floor((output image's first dimension)/2) + 1
```

From tracing through [`itkVkHalfHermitianToRealInverseFFTImageFilter.hxx`](https://github.com/InsightSoftwareConsortium/ITKVkFFTBackend/blob/master/include/itkVkHalfHermitianToRealInverseFFTImageFilter.hxx#L67) the issue is that the VkFFT filter expects the output region size to be explicitly set by the user in its [interface](https://github.com/InsightSoftwareConsortium/ITKVkFFTBackend/blob/master/include/itkVkHalfHermitianToRealInverseFFTImageFilter.h#L91). As this is not done in ITK classes consuming FFT filters internally, attempting to override the base FFT class with its VkFFT accelerated implementation results in failures.

## Next Steps

I am inclined to simply remove the output region parameter from the VkFFT class and rely on the underlying output size defined in [https://github.com/InsightSoftwareConsortium/ITK/blob/master/Modules/Filtering/FFT/include/itkHalfHermitianToRealInverseFFTImageFilter.hxx#L65](https://github.com/InsightSoftwareConsortium/ITK/blob/master/Modules/Filtering/FFT/include/itkHalfHermitianToRealInverseFFTImageFilter.hxx#L65).

@Leengit Could you please provide feedback on this proposal? Am I missing context for why we are requesting an explicit output size definition for the accelerated FFT class?


Discussion / hints:
I think you have it exactly right.  What the other implementations are doing here is what Vk should be doing too.
Thanks @Leengit , I'll work on a patch

Fix patch:
diff --git a/include/itkVkHalfHermitianToRealInverseFFTImageFilter.h b/include/itkVkHalfHermitianToRealInverseFFTImageFilter.h
index 8aaff86d..7147d1c0 100644
--- a/include/itkVkHalfHermitianToRealInverseFFTImageFilter.h
+++ b/include/itkVkHalfHermitianToRealInverseFFTImageFilter.h
@@ -85,12 +85,10 @@ class VkHalfHermitianToRealInverseFFTImageFilter
 
   static constexpr unsigned int ImageDimension = InputImageType::ImageDimension;
 
+  /** Platform identifier for accelerator backend */
   itkGetMacro(DeviceID, uint64_t);
   itkSetMacro(DeviceID, uint64_t);
 
-  itkSetMacro(OutputRegion, OutputImageRegionType);
-  itkGetConstMacro(OutputRegion, OutputImageRegionType);
-
   SizeValueType
   GetSizeGreatestPrimeFactor() const override;
 
@@ -107,8 +105,6 @@ class VkHalfHermitianToRealInverseFFTImageFilter
 private:
   uint64_t m_DeviceID{ 0UL };
 
-  OutputImageRegionType m_OutputRegion;
-
   VkCommon m_VkCommon{};
 };
 
diff --git a/include/itkVkHalfHermitianToRealInverseFFTImageFilter.hxx b/include/itkVkHalfHermitianToRealInverseFFTImageFilter.hxx
index ddf7c463..77b8b73c 100644
--- a/include/itkVkHalfHermitianToRealInverseFFTImageFilter.hxx
+++ b/include/itkVkHalfHermitianToRealInverseFFTImageFilter.hxx
@@ -52,7 +52,7 @@ VkHalfHermitianToRealInverseFFTImageFilter<TInputImage, TOutputImage>::GenerateD
   const ProgressReporter progress(this, 0, 1);
 
   // allocate output buffer memory
-  output->SetRegions(this->GetOutputRegion());
+  output->SetRegions(output->GetRequestedRegion());
   output->Allocate();
 
   const SizeType & outputSize{ output->GetBufferedRegion().GetSize() };


Test patch:
diff --git a/test/itkVkHalfHermitianFFTImageFilterTest.cxx b/test/itkVkHalfHermitianFFTImageFilterTest.cxx
index 0893da58..4661718d 100644
--- a/test/itkVkHalfHermitianFFTImageFilterTest.cxx
+++ b/test/itkVkHalfHermitianFFTImageFilterTest.cxx
@@ -86,6 +86,7 @@ itkVkHalfHermitianFFTImageFilterTest(int argc, char * argv[])
 
       size.Fill(0);
       size[0] = mySize;
+      bool xIsOdd = (mySize % 2 == 1);
 
       typename RealImageType::Pointer realImage{ RealImageType::New() };
       realImage->SetRegions(size);
@@ -149,8 +150,7 @@ itkVkHalfHermitianFFTImageFilterTest(int argc, char * argv[])
 
       thisTestPassed = true;
       inverseFilter->SetInput(output);
-      index[0] = 0;
-      inverseFilter->SetOutputRegion({ index, size });
+      inverseFilter->SetActualXDimensionIsOdd(xIsOdd);
       inverseFilter->Update();
       typename RealImageType::Pointer        output2{ inverseFilter->GetOutput() };
       const typename RealImageType::SizeType output2Size{ output2->GetLargestPossibleRegion().GetSize() };