/*========================================================================= * * 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. * *=========================================================================*/ #ifndef itkGaussianBlurImageFunction_hxx #define itkGaussianBlurImageFunction_hxx #include "itkImageLinearIteratorWithIndex.h" #include "itkMath.h" namespace itk { /** Set the Input Image */ template GaussianBlurImageFunction::GaussianBlurImageFunction() { typename GaussianFunctionType::ArrayType mean; mean[0] = 0.0f; for (unsigned int i = 0; i < Self::ImageDimension; ++i) { m_Sigma[i] = 1.0f; m_MaximumError[i] = 0.001f; m_MaximumKernelWidth = 32; m_Extent[i] = 1.0f; } m_GaussianFunction = GaussianFunctionType::New(); m_GaussianFunction->SetMean(mean); m_GaussianFunction->SetNormalized(false); // faster m_OperatorImageFunction = OperatorImageFunctionType::New(); m_OperatorInternalImageFunction = OperatorInternalImageFunctionType::New(); m_InternalImage = InternalImageType::New(); this->RecomputeGaussianKernel(); } /** Set the input image */ template void GaussianBlurImageFunction::SetInputImage(const InputImageType * ptr) { Superclass::SetInputImage(ptr); } /** Print self method */ template void GaussianBlurImageFunction::PrintSelf(std::ostream & os, Indent indent) const { this->Superclass::PrintSelf(os, indent); for (unsigned int i = 0; i < Self::ImageDimension; ++i) { os << indent << "Sigma[" << i << "] : " << m_Sigma[i] << std::endl; os << indent << "MaximumError[" << i << "] : " << m_MaximumError[i] << std::endl; os << indent << "Extent[" << i << "] : " << m_Extent[i] << std::endl; } os << indent << "MaximumKernelWidth: " << m_MaximumKernelWidth << std::endl; os << indent << "UseImageSpacing: " << (m_UseImageSpacing ? "On" : "Off") << std::endl; os << indent << "Internal Image : " << m_InternalImage << std::endl; } /** Set the variance of the gaussian in each direction */ template void GaussianBlurImageFunction::SetSigma(const double * sigma) { unsigned int i; for (i = 0; i < Self::ImageDimension; ++i) { if (sigma[i] != m_Sigma[i]) { break; } } if (i < Self::ImageDimension) { for (i = 0; i < Self::ImageDimension; ++i) { m_Sigma[i] = sigma[i]; } this->RecomputeGaussianKernel(); } } /** Set the variance of the gaussian in each direction */ template void GaussianBlurImageFunction::SetSigma(const double sigma) { unsigned int i; for (i = 0; i < Self::ImageDimension; ++i) { if (Math::NotExactlyEquals(sigma, m_Sigma[i])) { break; } } if (i < Self::ImageDimension) { for (i = 0; i < Self::ImageDimension; ++i) { m_Sigma[i] = sigma; } this->RecomputeGaussianKernel(); } } /** Set the extent of the gaussian in each direction */ template void GaussianBlurImageFunction::SetExtent(const double * extent) { unsigned int i; for (i = 0; i < Self::ImageDimension; ++i) { if (extent[i] != m_Extent[i]) { break; } } if (i < Self::ImageDimension) { for (i = 0; i < Self::ImageDimension; ++i) { m_Extent[i] = extent[i]; } this->RecomputeGaussianKernel(); } } /** Set the extent of the gaussian in each direction */ template void GaussianBlurImageFunction::SetExtent(const double extent) { unsigned int i; for (i = 0; i < Self::ImageDimension; ++i) { if (Math::NotExactlyEquals(extent, m_Extent[i])) { break; } } if (i < Self::ImageDimension) { for (i = 0; i < Self::ImageDimension; ++i) { m_Extent[i] = extent; } this->RecomputeGaussianKernel(); } } /** Recompute the gaussian kernel used to evaluate indexes * And allocate the internal image for processing depending on * the size of the operator */ template void GaussianBlurImageFunction::RecomputeGaussianKernel() { typename InternalImageType::SizeType size; // Compute the convolution of each kernel in each direction for (unsigned int direction = 0; direction < Self::ImageDimension; ++direction) { GaussianOperatorType gaussianOperator; gaussianOperator.SetDirection(direction); gaussianOperator.SetMaximumError(m_MaximumError[direction]); gaussianOperator.SetMaximumKernelWidth(m_MaximumKernelWidth); if (m_UseImageSpacing && (this->GetInputImage())) { if (this->GetInputImage()->GetSpacing()[direction] == 0.0) { itkExceptionMacro("Pixel spacing cannot be zero"); } else { gaussianOperator.SetVariance(m_Sigma[direction] * m_Sigma[direction] / this->GetInputImage()->GetSpacing()[direction]); } } else { gaussianOperator.SetVariance(m_Sigma[direction] * m_Sigma[direction]); } gaussianOperator.CreateDirectional(); m_OperatorArray[direction] = gaussianOperator; size[direction] = gaussianOperator.GetSize()[direction]; } // Allocate the internal image m_InternalImage = InternalImageType::New(); typename InternalImageType::RegionType region; region.SetSize(size); m_InternalImage->SetRegions(region); m_InternalImage->AllocateInitialized(); } /** Evaluate the function at the specified point */ template TOutput GaussianBlurImageFunction::EvaluateAtIndex(const IndexType & index) const { return this->EvaluateAtIndex(index, m_OperatorArray); } /** Evaluate the function at the specified point */ template TOutput GaussianBlurImageFunction::EvaluateAtIndex(const IndexType & index, const OperatorArrayType & operatorArray) const { const InputImageType * inputImage = this->GetInputImage(); // First time we use the complete image and fill the internal image m_OperatorImageFunction->SetInputImage(inputImage); m_OperatorImageFunction->SetOperator(operatorArray[0]); // if 1D Image we return the result if (Self::ImageDimension == 1) { return m_OperatorImageFunction->EvaluateAtIndex(index); } // Compute the centered index of the neighborhood IndexType centerIndex; for (unsigned int i = 0; i < Self::ImageDimension; ++i) { centerIndex[i] = (IndexValueType)(static_cast(m_InternalImage->GetBufferedRegion().GetSize()[i]) / 2.0f); } // first direction typename InternalImageType::IndexType ind; ind = index; // Define the region of the iterator typename InternalImageType::RegionType region; typename InternalImageType::SizeType size = m_InternalImage->GetBufferedRegion().GetSize(); size[0] = 1; region.SetSize(size); for (unsigned int i = 0; i < Self::ImageDimension; ++i) { if (i != 0) { ind[i] -= centerIndex[i]; } } region.SetIndex(ind); typename InternalImageType::RegionType regionN; regionN.SetSize(size); ind = centerIndex; for (unsigned int i = 0; i < Self::ImageDimension; ++i) { if (i != 0) { ind[i] = 0; } } regionN.SetIndex(ind); typename InternalImageType::RegionType regionS = region; regionS.Crop(inputImage->GetBufferedRegion()); itk::ImageLinearConstIteratorWithIndex it(inputImage, regionS); itk::ImageLinearIteratorWithIndex itN(m_InternalImage, regionN); it.SetDirection(1); itN.SetDirection(1); it.GoToBeginOfLine(); itN.GoToBeginOfLine(); while (!it.IsAtEnd()) { while (!it.IsAtEndOfLine()) { itN.Set(m_OperatorImageFunction->EvaluateAtIndex(it.GetIndex())); ++it; ++itN; } it.NextLine(); itN.NextLine(); } // Do the convolution in other directions for (unsigned int direction = 1; direction < Self::ImageDimension; ++direction) { size[direction] = 1; ind = centerIndex; for (unsigned int i = 0; i < Self::ImageDimension; ++i) { if (i > direction) { ind[i] = 0; } } region.SetSize(size); region.SetIndex(ind); m_OperatorInternalImageFunction->SetInputImage(m_InternalImage); m_OperatorInternalImageFunction->SetOperator(operatorArray[direction]); itk::ImageLinearIteratorWithIndex itr(m_InternalImage, region); unsigned int dir = direction + 1; if (dir == Self::ImageDimension) { dir = Self::ImageDimension - 1; } itr.SetDirection(dir); itr.GoToBeginOfLine(); while (!itr.IsAtEnd()) { while (!itr.IsAtEndOfLine()) { itr.Set(m_OperatorInternalImageFunction->EvaluateAtIndex(itr.GetIndex())); ++itr; } itr.NextLine(); } } return m_InternalImage->GetPixel(centerIndex); } /** Recompute the gaussian kernel used to evaluate indexes * The variance should be uniform */ template void GaussianBlurImageFunction::RecomputeContinuousGaussianKernel(const double * offset) const { for (unsigned int direction = 0; direction < Self::ImageDimension; ++direction) { typename NeighborhoodType::SizeType size; size.Fill(0); size[direction] = static_cast(m_Sigma[direction] * m_Extent[direction]); NeighborhoodType gaussianNeighborhood; gaussianNeighborhood.SetRadius(size); itk::FixedArray s; s[0] = m_Sigma[direction]; m_GaussianFunction->SetSigma(s); unsigned int i = 0; float sum = 0; typename NeighborhoodType::Iterator it = gaussianNeighborhood.Begin(); while (it != gaussianNeighborhood.End()) { typename GaussianFunctionType::InputType pt; pt[0] = gaussianNeighborhood.GetOffset(i)[direction] - offset[direction]; if (m_UseImageSpacing && (this->GetInputImage())) { if (this->GetInputImage()->GetSpacing()[direction] == 0.0) { itkExceptionMacro("Pixel spacing cannot be zero"); } else { pt[0] *= this->GetInputImage()->GetSpacing()[direction]; } } (*it) = m_GaussianFunction->Evaluate(pt); sum += (*it); ++i; ++it; } // Make the filter DC-Constant it = gaussianNeighborhood.Begin(); while (it != gaussianNeighborhood.End()) { (*it) /= sum; ++it; } m_ContinuousOperatorArray[direction] = gaussianNeighborhood; } } /** Evaluate the function at the specified point */ template TOutput GaussianBlurImageFunction::Evaluate(const PointType & point) const { const ContinuousIndexType cindex = this->m_InternalImage->template TransformPhysicalPointToContinuousIndex( point); return this->EvaluateAtContinuousIndex(cindex); } /** Evaluate the function at specified ContinuousIndex position.*/ template TOutput GaussianBlurImageFunction::EvaluateAtContinuousIndex(const ContinuousIndexType & cindex) const { IndexType index; index.CopyWithRound(cindex); double offset[Self::ImageDimension]; for (unsigned int i = 0; i < Self::ImageDimension; ++i) { offset[i] = cindex[i] - index[i]; } this->RecomputeContinuousGaussianKernel(offset); return this->EvaluateAtIndex(index, m_ContinuousOperatorArray); } } // end namespace itk #endif