diff --git a/Code/BasicFilters/otbOverlapSaveConvolutionImageFilter.h b/Code/BasicFilters/otbOverlapSaveConvolutionImageFilter.h new file mode 100755 index 0000000000000000000000000000000000000000..00c18ec5a11207584fe9fa0afa82c06bcce4efd4 --- /dev/null +++ b/Code/BasicFilters/otbOverlapSaveConvolutionImageFilter.h @@ -0,0 +1,144 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt 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 notices for more information. + +=========================================================================*/ +#ifndef __otbOverlapSaveConvolutionImageFilter_h +#define __otbOverlapSaveConvolutionImageFilter_h + +#include "itkImageToImageFilter.h" +#include "itkImage.h" +#include "itkNumericTraits.h" +#include "itkArray.h" +#include "itkFFTWCommon.h" + + +/*NB: This program gives a texture image by using gabor filter. Suddently, this program implements a block convolution procedure in the aim to deal huge image. This procedure is called "overlap-save method".*/ + +namespace otb +{ +template <class TInputImage,class TOutputImage> class ITK_EXPORT OverlapSaveConvolutionImageFilter : public itk::ImageToImageFilter< TInputImage, TOutputImage > +{ + + +public: + /** Extract dimension from input and output image. */ + itkStaticConstMacro(InputImageDimension, unsigned int,TInputImage::ImageDimension); + itkStaticConstMacro(OutputImageDimension, unsigned int,TOutputImage::ImageDimension); + + /** Convenient typedefs for simplifying declarations. */ + typedef TInputImage InputImageType; + typedef TOutputImage OutputImageType; + + /** Standard class typedefs. */ + typedef OverlapSaveConvolutionImageFilter Self; + typedef itk::ImageToImageFilter< InputImageType, OutputImageType> Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(OverlapSaveConvolutionImageFilter, ImageToImageFilter); + + /** Image typedef support. */ + typedef typename InputImageType::PixelType InputPixelType; + typedef typename OutputImageType::PixelType OutputPixelType; + typedef typename itk::NumericTraits<InputPixelType>::RealType InputRealType; + + typedef typename InputImageType::RegionType InputImageRegionType; + typedef typename OutputImageType::RegionType OutputImageRegionType; + + typedef typename InputImageType::SizeType InputSizeType; + + typedef typename itk::Array<InputRealType> ArrayType; + + /** Set the radius of the neighborhood used to compute the mean. */ + virtual void SetRadius (const InputSizeType rad) + { + itkDebugMacro("setting radius to " << rad); + if (this->m_Radius != rad) + { + this->m_Radius = rad; + unsigned int arraySize=1; + for (unsigned int i=0; i<m_Radius.GetSizeDimension(); i++) + { + arraySize *= 2*this->m_Radius[i]+1; + } + this->m_Filter.SetSize(arraySize); + this->m_Filter.Fill(1); + this->Modified(); + } + } + + /** Get the radius of the neighborhood used to compute the mean */ + itkGetConstReferenceMacro(Radius, InputSizeType); + + /** Set the input filter */ + void SetFilter(ArrayType filter) + { + if((filter.Size()!= m_Filter.Size())) + { + itkExceptionMacro("Error in SetFilter, invalid filter size:"<< filter.Size()<<" instead of 2*(m_Radius[0]+1)*(2*m_Radius[1]+1): "<<m_Filter.Size()); + } + else + { + m_Filter = filter; + } + this->Modified(); + } + /** Get the filter */ + itkGetConstReferenceMacro(Filter,ArrayType); + + /** Set/Get methods for the normalization of the filter */ + itkSetMacro(NormalizeFilter, bool); + itkGetMacro(NormalizeFilter, bool); + itkBooleanMacro(NormalizeFilter); + + virtual void GenerateInputRequestedRegion() throw(itk::InvalidRequestedRegionError); + +#ifdef ITK_USE_CONCEPT_CHECKING + /** Begin concept checking */ + itkConceptMacro(InputHasNumericTraitsCheck,(itk::Concept::HasNumericTraits<InputPixelType>)); + /** End concept checking */ +#endif + +protected: + OverlapSaveConvolutionImageFilter(); + virtual ~OverlapSaveConvolutionImageFilter() {} + void PrintSelf(std::ostream& os, itk::Indent indent) const; + void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,int threadId); + /** Process a square piece */ + void ProcessRegion(const OutputImageRegionType& region,const OutputImageRegionType& outputRegionForThread); + + +private: + OverlapSaveConvolutionImageFilter(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + + InputSizeType m_Radius; + ArrayType m_Filter; + bool m_NormalizeFilter; //Flag for normalisation + +}; + +} // end namespace otb + +#ifndef ITK_MANUAL_INSTANTIATION +#include "otbOverlapSaveConvolutionImageFilter.txx" +#endif + +#endif diff --git a/Code/BasicFilters/otbOverlapSaveConvolutionImageFilter.txx b/Code/BasicFilters/otbOverlapSaveConvolutionImageFilter.txx new file mode 100755 index 0000000000000000000000000000000000000000..78df55a038586a5340da589917913e895d8416d5 --- /dev/null +++ b/Code/BasicFilters/otbOverlapSaveConvolutionImageFilter.txx @@ -0,0 +1,393 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt 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 notices for more information. + +=========================================================================*/ +#ifndef _otbOverlapSaveConvolutionImageFilter_txx +#define _otbOverlapSaveConvolutionImageFilter_txx + +#include "otbOverlapSaveConvolutionImageFilter.h" + +#include "itkConstNeighborhoodIterator.h" +#include "itkImageRegionIteratorWithIndex.h" +#include "itkNeighborhoodAlgorithm.h" +#include "itkZeroFluxNeumannBoundaryCondition.h" +#include "itkOffset.h" +#include "itkProgressReporter.h" + +//debug +#include "itkImageRegionIterator.h" +#include "otbMath.h" + +namespace otb +{ + +template <class TInputImage, class TOutputImage> +OverlapSaveConvolutionImageFilter<TInputImage, TOutputImage> +::OverlapSaveConvolutionImageFilter() + { + m_Radius.Fill(1); + m_Filter.SetSize(3*3); + m_Filter.Fill(1); + m_NormalizeFilter = false; + } + +template <class TInputImage, class TOutputImage> +void +OverlapSaveConvolutionImageFilter<TInputImage, TOutputImage> +::GenerateInputRequestedRegion() throw (itk::InvalidRequestedRegionError) + { + // call the superclass' implementation of this method + Superclass::GenerateInputRequestedRegion(); + + // get pointers to the input and output + typename Superclass::InputImagePointer inputPtr = const_cast< TInputImage * >( this->GetInput() ); + typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); + + if ( !inputPtr || !outputPtr ) + { + return; + } + + // get a copy of the input requested region (should equal the output + // requested region) + typename TInputImage::RegionType inputRequestedRegion; + inputRequestedRegion = inputPtr->GetRequestedRegion(); + + typename TInputImage::RegionType::IndexType index=inputRequestedRegion.GetIndex(); + typename TInputImage::RegionType::SizeType size=inputRequestedRegion.GetSize(); + + // pad the input requested region by the operator radius + for (unsigned int i = 0; i < 2; i++) //for dimension 2 + { + size[i] += 2*m_Radius[i]+1; //increase size of input images'region by size of filter + index[i] -= static_cast<long>(m_Radius[i]); //translate origin + } + inputRequestedRegion.SetSize(size); + inputRequestedRegion.SetIndex(index); + + // crop the input requested region at the input's largest possible region + if(inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion())) + { + //std::cout<<"InputRequestedRegion: "<<inputRequestedRegion<<std::endl; + inputPtr->SetRequestedRegion(inputRequestedRegion); + return; + } + else + { + // Couldn't crop the region (requested region is outside the largest + // possible region). Throw an exception. + + // store what we tried to request (prior to trying to crop) + inputPtr->SetRequestedRegion(inputRequestedRegion); + + // build an exception + itk::InvalidRequestedRegionError e(__FILE__, __LINE__); + e.SetLocation(ITK_LOCATION); + e.SetDescription("Requested region is (at least partially) outside the largest possible region."); + e.SetDataObject(inputPtr); + throw e; + } + } + +template< class TInputImage, class TOutputImage> +void +OverlapSaveConvolutionImageFilter< TInputImage, TOutputImage> +::ProcessRegion(const OutputImageRegionType& region, const OutputImageRegionType& outputRegionForThread) +{ + itk::ImageRegionIteratorWithIndex<OutputImageType> it; + + // Input/Output pointers + typename OutputImageType::Pointer output = this->GetOutput(); + typename InputImageType::ConstPointer input = this->GetInput(); + + //****************************** + // int m=0,n=0; + //****************************** + typename InputImageType::SizeType sizeOfFilter; + sizeOfFilter[0]=2*m_Radius[0]+1; + sizeOfFilter[1]=2*m_Radius[1]+1; + + //InputRealType sum; + InputRealType norm; + + OutputImageRegionType outputRegion = region; + outputRegion.Crop(outputRegionForThread); + + + OutputImageRegionType inputRegionForThread = outputRegionForThread; + + typename TInputImage::RegionType::IndexType index=inputRegionForThread.GetIndex(); + typename TInputImage::RegionType::SizeType size=inputRegionForThread.GetSize(); + + // pad the input requested region by the operator radius + for (unsigned int i = 0; i < 2; i++) //for dimension 2 + { + size[i] += 2*m_Radius[i]+1; //increase size of input images'region by size of filter + index[i] -= static_cast<long>(m_Radius[i]); //translate origin + } + inputRegionForThread.SetSize(size); + inputRegionForThread.SetIndex(index); + + + typename InputImageType::RegionType pieceRegion; + typename InputImageType::SizeType pieceSize = region.GetSize(); + typename InputImageType::IndexType pieceIndex = region.GetIndex(); + + pieceIndex[0]-=m_Radius[0]; + pieceIndex[1]-=m_Radius[1]; + pieceSize[0]+=sizeOfFilter[0]; + pieceSize[1]+=sizeOfFilter[1]; + + pieceRegion.SetIndex(pieceIndex); + pieceRegion.SetSize(pieceSize); + + typename InputImageType::RegionType inputRegion = pieceRegion; + inputRegion.Crop(inputRegionForThread); + inputRegion.Crop(input->GetLargestPossibleRegion()); + typename InputImageType::IndexType inputIndex = inputRegion.GetIndex(); + typename InputImageType::SizeType inputSize = inputRegion.GetSize(); + + + unsigned int pieceNbOfPixel = pieceRegion.GetNumberOfPixels(); + unsigned int sizeFFT=(pieceSize[0]/2+1)*pieceSize[1]; + + //Iterator of input image + + itk::ZeroFluxNeumannBoundaryCondition<InputImageType> nbc; + itk::ConstNeighborhoodIterator<InputImageType> bit(m_Radius,input,inputRegion); + bit.OverrideBoundaryCondition(&nbc); + bit.GoToBegin(); + + //Iterator of output image + it = itk::ImageRegionIteratorWithIndex<OutputImageType>(output,outputRegion); + + typedef typename itk::fftw::Proxy<double> FFTWProxyType; + //variables for loops + int i,j,k,l; + + //memory allocation + InputPixelType* resampledFilterPiece; + resampledFilterPiece=static_cast<FFTWProxyType::PixelType*>(fftw_malloc(pieceNbOfPixel*sizeof(InputPixelType))); + + FFTWProxyType::ComplexType* filterPieceFFT; + filterPieceFFT=static_cast<FFTWProxyType::ComplexType*>(fftw_malloc(sizeFFT*sizeof(FFTWProxyType::ComplexType))); + + InputPixelType *inputPiece; + inputPiece=static_cast<FFTWProxyType::PixelType*>(fftw_malloc(pieceNbOfPixel*sizeof(InputPixelType))); + + FFTWProxyType::ComplexType* inputPieceFFT; + inputPieceFFT=static_cast<FFTWProxyType::ComplexType*>(fftw_malloc(sizeFFT*sizeof(FFTWProxyType::ComplexType))); + + //resampling filter (fill array with filter's values + zero padding) + for(k=0;k<pieceNbOfPixel;k++) + { + resampledFilterPiece[k]=0.0;//Fill filter with zero + } + + k=0; + for(j=0;j<sizeOfFilter[1];j++) + { + for(i=0;i<sizeOfFilter[0];i++) + { + resampledFilterPiece[i+j*pieceSize[0]]=m_Filter.GetElement(k);//Copy values + k++; + } + } + + // left zero padding + unsigned int leftskip = static_cast<unsigned int>(std::max(0L,inputIndex[0]-pieceIndex[0])); + unsigned int topskip = pieceSize[0]*static_cast<unsigned int>(std::max(0L,inputIndex[1]-pieceIndex[1])); + + // for( l = 0;l<pieceSize[1]*pieceSize[0];++l) +// { +// inputPiece[l]=itk::NumericTraits<InputPixelType>::Zero; +// } + + + + //Compute FFTs of one piece (part of input image) and of filter + + //Generating plans + FFTWProxyType::PlanType inputPlan=FFTWProxyType::Plan_dft_r2c_2d(pieceSize[0],pieceSize[1],inputPiece,inputPieceFFT,FFTW_ESTIMATE); + + for(l = 0;l<inputSize[1];++l) + { + for(k = 0;k<inputSize[0];++k) + { + inputPiece[topskip+pieceSize[0]*l+k+leftskip]=bit.GetCenterPixel(); + ++bit; + } + } + +// typename InputImageType::IndexType currentIndex; +// unsigned int linearindex = 0; +// for(l=0;l<pieceSize[1];++l) +// { +// for(k=0;k<pieceSize[0];++k) +// { +// currentIndex[0]=pieceIndex[0]+k; +// currentIndex[1]=pieceIndex[1]+l; +// if(inputRegion.IsInside(currentIndex)) +// { +// inputPiece[linearindex]=bit.GetCenterPixel(); +// ++bit; +// } +// else // reimplement the zero flux neumann boundary condition. +// { +// inputPiece[linearindex]=0; +// } +// ++linearindex; +// } +// } + + FFTWProxyType::PlanType filterPlan=FFTWProxyType::Plan_dft_r2c_2d(pieceSize[0],pieceSize[1],resampledFilterPiece,filterPieceFFT,FFTW_ESTIMATE); + + FFTWProxyType::Execute(inputPlan); + FFTWProxyType::Execute(filterPlan); + + //memory allocation + FFTWProxyType::ComplexType* multipliedFFTarray; + multipliedFFTarray=static_cast<FFTWProxyType::ComplexType*>(fftw_malloc(sizeFFT*sizeof(FFTWProxyType::ComplexType))); + + //inverse FFT + + // memory allocation + FFTWProxyType::PixelType* inverseFFTpiece; + inverseFFTpiece=static_cast<FFTWProxyType::PixelType*>(fftw_malloc(pieceNbOfPixel*sizeof(FFTWProxyType::PixelType))); + + FFTWProxyType::PlanType outputPlan=FFTWProxyType::Plan_dft_c2r_2d(pieceSize[0],pieceSize[1],multipliedFFTarray,inverseFFTpiece,FFTW_ESTIMATE); + + for(k=0;k<sizeFFT;k++) + { + //complex mutiplication + multipliedFFTarray[k][0]=inputPieceFFT[k][0]*filterPieceFFT[k][0]-inputPieceFFT[k][1]*filterPieceFFT[k][1]; + multipliedFFTarray[k][1]=inputPieceFFT[k][0]*filterPieceFFT[k][1]+inputPieceFFT[k][1]*filterPieceFFT[k][0]; + } + + FFTWProxyType::Execute(outputPlan); + + norm = itk::NumericTraits<InputRealType>::Zero; + for(i=0;i<sizeOfFilter[0]*sizeOfFilter[1];i++) + { + norm = itk::NumericTraits<InputRealType>::Zero; + norm += static_cast<InputRealType>(m_Filter(i)); + } + it.GoToBegin(); + while(!it.IsAtEnd()) + { + typename InputImageType::IndexType index = it.GetIndex(); + unsigned int linearIndex = (index[1]+sizeOfFilter[1]-1-outputRegion.GetIndex()[1])*pieceSize[0]+index[0]+sizeOfFilter[0]-1-outputRegion.GetIndex()[0]; + if (m_NormalizeFilter) + { + it.Set( static_cast<OutputPixelType>((inverseFFTpiece[linearIndex]/pieceNbOfPixel)/double(norm)) ); + } + else + { + it.Set( static_cast<OutputPixelType>(inverseFFTpiece[linearIndex]/pieceNbOfPixel) );//normalize pixels'values + } + ++it; + } + + + // std::cout<<"Piece region: "<<pieceRegion<<std::endl; +// std::cout<<"Output region: "<<outputRegion<<std::endl; + +// it.GoToBegin(); + +// typename InputImageType::IndexType currentIndex; +// unsigned int linearindex = 0; +// for(l=0;l<pieceSize[1];++l) +// { +// for(k=0;k<pieceSize[0];++k) +// { +// currentIndex[0]=pieceIndex[0]+k; +// currentIndex[1]=pieceIndex[1]+l; +// if(outputRegion.IsInside(currentIndex)) +// { +// // std::cout<<currentIndex<<" inside."<<std::endl; +// if (m_NormalizeFilter) +// { +// it.Set( static_cast<OutputPixelType>((inverseFFTpiece[linearindex]/pieceNbOfPixel)/double(norm)) ); +// } +// else +// { +// // std::cout<<currentIndex<<" inside : "<< inverseFFTpiece[linearindex]/pieceNbOfPixel <<std::endl; +// it.Set( static_cast<OutputPixelType>(inverseFFTpiece[linearindex]/pieceNbOfPixel) );//normalize pixels'values +// } +// ++it; +// } +// ++linearindex; +// } +// } + + + //frees memory + fftw_free(resampledFilterPiece); + fftw_free(inputPiece); + + FFTWProxyType::DestroyPlan(inputPlan); + FFTWProxyType::DestroyPlan(filterPlan); + FFTWProxyType::DestroyPlan(outputPlan); + + fftw_free(filterPieceFFT); + fftw_free(inputPieceFFT); + fftw_free(multipliedFFTarray); + fftw_free(inverseFFTpiece); + + } + + template< class TInputImage, class TOutputImage> void OverlapSaveConvolutionImageFilter< TInputImage, TOutputImage>::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,int threadId) + { + typename OutputImageRegionType::SizeType size = outputRegionForThread.GetSize(); + unsigned long minSize = std::min(size[0],size[1]); + unsigned int tilexdivision = static_cast<unsigned int>(vcl_ceil(static_cast<double>(size[0])/static_cast<double>(minSize))); + unsigned int tileydivision = static_cast<unsigned int>(vcl_ceil(static_cast<double>(size[1])/static_cast<double>(minSize))); + + std::cout<<"Tile: "<<outputRegionForThread<<std::endl; + std::cout<<"Min tile size: "<<minSize<<std::endl; + std::cout<<"tilexdivision: "<<tilexdivision<<std::endl; + std::cout<<"tileydivision: "<<tileydivision<<std::endl; + + + for(unsigned int i = 0;i<tilexdivision;++i) + { + for(unsigned int j = 0;j<tileydivision;++j) + { + OutputImageRegionType newRegion; + typename OutputImageRegionType::SizeType newSize; + newSize.Fill(minSize); + newRegion.SetSize(newSize); + typename OutputImageRegionType::IndexType newIndex = outputRegionForThread.GetIndex(); + newIndex[0]+=i*minSize; + newIndex[1]+=j*minSize; + newRegion.SetIndex(newIndex); + std::cout<<"Processing region: "<<i<<" "<<j<<std::endl; + this->ProcessRegion(newRegion,outputRegionForThread); + } + } + +// this->ProcessRegion(outputRegionForThread,outputRegionForThread); + } + + /** Standard "PrintSelf" method */ + template <class TInputImage, class TOutput> void OverlapSaveConvolutionImageFilter<TInputImage, TOutput>::PrintSelf(std::ostream& os,itk::Indent indent) const + { + Superclass::PrintSelf( os, indent ); + os << indent << "Radius: " << m_Radius << std::endl; + } +} // end namespace otb + +#endif