/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkImageSeparableConvolution.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkImageSeparableConvolution.h" #include "vtkFloatArray.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkCxxRevisionMacro(vtkImageSeparableConvolution, "$Revision: 1.20 $"); vtkStandardNewMacro(vtkImageSeparableConvolution); vtkCxxSetObjectMacro(vtkImageSeparableConvolution,XKernel,vtkFloatArray); vtkCxxSetObjectMacro(vtkImageSeparableConvolution,YKernel,vtkFloatArray); vtkCxxSetObjectMacro(vtkImageSeparableConvolution,ZKernel,vtkFloatArray); // Actually do the convolution void ExecuteConvolve ( float* kernel, int kernelSize, float* image, float* outImage, int imageSize ) { // Consider the kernel to be centered at (int) ( (kernelSize - 1 ) / 2.0 ) int center = (int) ( (kernelSize - 1 ) / 2.0 ); int i, j, k, kStart, iStart, iEnd, count; for ( i = 0; i < imageSize; ++i ) { outImage[i] = 0.0; // iStart = i - center; // if ( iStart < 0 ) // { // iStart = 0; // } // iEnd = i + center; // if ( iEnd > imageSize - 1 ) // { // iEnd = imageSize - 1; // } // Handle padding iStart = i - center; k = kernelSize - 1; while ( iStart < 0 ) { outImage[i] += image[0] * kernel[k]; ++iStart; --k; } iEnd = i + center; k = 0; while ( iEnd > imageSize - 1 ) { outImage[i] += image[imageSize - 1] * kernel[k]; ++k; --iEnd; } kStart = center + i; if ( kStart > kernelSize - 1 ) { kStart = kernelSize - 1; } count = iEnd - iStart + 1; for ( j = 0; j < count; ++j ) { outImage[i] += image[j+iStart] * kernel[kStart-j]; } } } // Description: // Overload standard modified time function. If kernel arrays are modified, // then this object is modified as well. unsigned long vtkImageSeparableConvolution::GetMTime() { unsigned long mTime=this->vtkImageDecomposeFilter::GetMTime(); unsigned long kTime; if ( this->XKernel ) { kTime = this->XKernel->GetMTime(); mTime = kTime > mTime ? kTime : mTime; } if ( this->YKernel ) { kTime = this->YKernel->GetMTime(); mTime = kTime > mTime ? kTime : mTime; } if ( this->YKernel ) { kTime = this->YKernel->GetMTime(); mTime = kTime > mTime ? kTime : mTime; } return mTime; } //---------------------------------------------------------------------------- vtkImageSeparableConvolution::~vtkImageSeparableConvolution() { SetXKernel ( NULL ); SetYKernel ( NULL ); SetZKernel ( NULL ); } //---------------------------------------------------------------------------- vtkImageSeparableConvolution::vtkImageSeparableConvolution() { XKernel = YKernel = ZKernel = NULL; } //---------------------------------------------------------------------------- // This extent of the components changes to real and imaginary values. int vtkImageSeparableConvolution::IterativeRequestInformation( vtkInformation* vtkNotUsed(input), vtkInformation* output) { vtkDataObject::SetPointDataActiveScalarInfo(output, VTK_FLOAT, 1); return 1; } //---------------------------------------------------------------------------- // This method tells the superclass that the whole input array is needed // to compute any output region. int vtkImageSeparableConvolution::IterativeRequestUpdateExtent( vtkInformation* input, vtkInformation* output) { int *wholeExtent = input->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()); vtkFloatArray* KernelArray = NULL; switch ( this->GetIteration() ) { case 0: KernelArray = this->GetXKernel(); break; case 1: KernelArray = this->GetYKernel(); break; case 2: KernelArray = this->GetZKernel(); break; } int kernelSize = 0; if ( KernelArray ) { kernelSize = KernelArray->GetNumberOfTuples(); kernelSize = (int) ( ( kernelSize - 1 ) / 2.0 ); } int* outExt = output->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()); // Assumes that the input update extent has been initialized to output ... int inExt[6]; memcpy(inExt, outExt, 6 * sizeof(int)); inExt[this->Iteration * 2] = outExt[this->Iteration * 2] - kernelSize; if ( inExt[this->Iteration * 2] < wholeExtent[this->Iteration * 2] ) { inExt[this->Iteration * 2] = wholeExtent[this->Iteration * 2]; } inExt[this->Iteration * 2 + 1] = outExt[this->Iteration * 2 + 1] + kernelSize; if ( inExt[this->Iteration * 2 + 1] > wholeExtent[this->Iteration * 2 + 1] ) { inExt[this->Iteration * 2 + 1] = wholeExtent[this->Iteration * 2 + 1]; } input->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),inExt,6); return 1; } template void vtkImageSeparableConvolutionExecute ( vtkImageSeparableConvolution* self, vtkImageData* inData, vtkImageData* outData, T* vtkNotUsed ( dummy ), int* inExt, int* outExt) { T *inPtr0, *inPtr1, *inPtr2; float *outPtr0, *outPtr1, *outPtr2; vtkIdType inInc0, inInc1, inInc2; vtkIdType outInc0, outInc1, outInc2; int inMin0, inMax0, inMin1, inMax1, inMin2, inMax2; int outMin0, outMax0, outMin1, outMax1, outMin2, outMax2; int idx0, idx1, idx2; int i; unsigned long count = 0; unsigned long target; // Reorder axes (the in and out extents are assumed to be the same) // (see intercept cache update) self->PermuteExtent(outExt, outMin0, outMax0, outMin1, outMax1, outMin2, outMax2); self->PermuteExtent(inExt, inMin0, inMax0, inMin1, inMax1, inMin2, inMax2); self->PermuteIncrements(inData->GetIncrements(), inInc0, inInc1, inInc2); self->PermuteIncrements(outData->GetIncrements(), outInc0, outInc1, outInc2); target = (unsigned long)((inMax2-inMin2+1)*(inMax1-inMin1+1)/50.0); target++; vtkFloatArray* KernelArray = NULL; switch ( self->GetIteration() ) { case 0: KernelArray = self->GetXKernel(); break; case 1: KernelArray = self->GetYKernel(); break; case 2: KernelArray = self->GetZKernel(); break; } int kernelSize = 0; float* kernel = NULL; if ( KernelArray ) { // Allocate the arrays kernelSize = KernelArray->GetNumberOfTuples(); kernel = new float[kernelSize]; // Copy the kernel for ( i = 0; i < kernelSize; i++ ) { kernel[i] = KernelArray->GetValue ( i ); } } int imageSize = inMax0 + 1; float* image = new float[imageSize]; float* outImage = new float[imageSize]; float* imagePtr; // loop over all the extra axes inPtr2 = (T *)inData->GetScalarPointerForExtent(inExt); outPtr2 = (float *)outData->GetScalarPointerForExtent(outExt); for (idx2 = inMin2; idx2 <= inMax2; ++idx2) { inPtr1 = inPtr2; outPtr1 = outPtr2; for (idx1 = inMin1; !self->AbortExecute && idx1 <= inMax1; ++idx1) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; inPtr0 = inPtr1; imagePtr = image; for (idx0 = inMin0; idx0 <= inMax0; ++idx0) { *imagePtr = (float)(*inPtr0); inPtr0 += inInc0; ++imagePtr; } // Call the method that performs the convolution if ( kernel ) { ExecuteConvolve ( kernel, kernelSize, image, outImage, imageSize ); imagePtr = outImage; } else { // If we don't have a kernel, just copy to the output imagePtr = image; } // Copy to output, be aware that we only copy to the extent that was asked for outPtr0 = outPtr1; imagePtr = imagePtr + (outMin0 - inMin0); for (idx0 = outMin0; idx0 <= outMax0; ++idx0) { *outPtr0 = (*imagePtr); outPtr0 += outInc0; ++imagePtr; } inPtr1 += inInc1; outPtr1 += outInc1; } inPtr2 += inInc2; outPtr2 += outInc2; } delete [] image; delete [] outImage; if ( kernel ) { delete [] kernel; } } //---------------------------------------------------------------------------- // This is writen as a 1D execute method, but is called several times. int vtkImageSeparableConvolution::IterativeRequestData( vtkInformation* vtkNotUsed( request ), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkImageData *inData = vtkImageData::SafeDownCast( inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkImageData *outData = vtkImageData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); outData->SetExtent(outData->GetWholeExtent()); outData->AllocateScalars(); if ( XKernel ) { // Check for a filter of odd length if ( 1 - ( XKernel->GetNumberOfTuples() % 2 ) ) { vtkErrorMacro ( << "Execute: XKernel must have odd length" ); return 1; } } if ( YKernel ) { // Check for a filter of odd length if ( 1 - ( YKernel->GetNumberOfTuples() % 2 ) ) { vtkErrorMacro ( << "Execute: YKernel must have odd length" ); return 1; } } if ( ZKernel ) { // Check for a filter of odd length if ( 1 - ( ZKernel->GetNumberOfTuples() % 2 ) ) { vtkErrorMacro ( << "Execute: ZKernel must have odd length" ); return 1; } } if (inData->GetNumberOfScalarComponents() != 1) { vtkErrorMacro(<< "ImageSeparableConvolution only works on 1 component input for the moment."); return 1; } // this filter expects that the output be floats. if (outData->GetScalarType() != VTK_FLOAT) { vtkErrorMacro(<< "Execute: Output must be be type float."); return 1; } // choose which templated function to call. switch (inData->GetScalarType()) { vtkTemplateMacro( vtkImageSeparableConvolutionExecute( this, inData, outData, static_cast(0), inInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()), outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return 1; } return 1; } void vtkImageSeparableConvolution::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); if ( this->XKernel ) { os << indent << "XKernel:\n"; this->XKernel->PrintSelf ( os, indent.GetNextIndent() ); } else { os << indent << "XKernel: (not defined)\n"; } if ( this->YKernel ) { os << indent << "YKernel:\n"; this->YKernel->PrintSelf ( os, indent.GetNextIndent() ); } else { os << indent << "YKernel: (not defined)\n"; } if ( this->ZKernel ) { os << indent << "ZKernel:\n"; this->ZKernel->PrintSelf ( os, indent.GetNextIndent() ); } else { os << indent << "ZKernel: (not defined)\n"; } }