/*========================================================================= * * 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 itkMinMaxCurvatureFlowFunction_hxx #define itkMinMaxCurvatureFlowFunction_hxx #include "itkMath.h" #include "itkNeighborhoodInnerProduct.h" namespace itk { template MinMaxCurvatureFlowFunction::MinMaxCurvatureFlowFunction() { m_StencilRadius = 0; this->SetStencilRadius(2); } template void MinMaxCurvatureFlowFunction::SetStencilRadius(const RadiusValueType value) { if (m_StencilRadius == value) { return; } m_StencilRadius = (value > 1) ? value : 1; RadiusType radius; unsigned int j; for (j = 0; j < ImageDimension; ++j) { radius[j] = m_StencilRadius; } this->SetRadius(radius); this->InitializeStencilOperator(); } template void MinMaxCurvatureFlowFunction::InitializeStencilOperator() { // Fill stencil operator with a sphere of radius m_StencilRadius. m_StencilOperator.SetRadius(m_StencilRadius); RadiusValueType counter[ImageDimension]; unsigned int j; RadiusValueType span = 2 * m_StencilRadius + 1; RadiusValueType sqrRadius = m_StencilRadius * m_StencilRadius; for (j = 0; j < ImageDimension; ++j) { counter[j] = 0; } using Iterator = typename StencilOperatorType::Iterator; Iterator opIter; Iterator opEnd = m_StencilOperator.End(); SizeValueType numPixelsInSphere = 0; for (opIter = m_StencilOperator.Begin(); opIter < opEnd; ++opIter) { *opIter = PixelType{}; RadiusValueType length = 0; for (j = 0; j < ImageDimension; ++j) { length += static_cast( itk::Math::sqr(static_cast(counter[j]) - static_cast(m_StencilRadius))); } if (length <= sqrRadius) { *opIter = 1; ++numPixelsInSphere; } bool carryOver = true; for (j = 0; carryOver && j < ImageDimension; ++j) { counter[j] += 1; carryOver = false; if (counter[j] == span) { counter[j] = 0; carryOver = true; } } } // normalize the operator so that it sums to one if (numPixelsInSphere != 0) { for (opIter = m_StencilOperator.Begin(); opIter < opEnd; ++opIter) { *opIter = static_cast((double)*opIter / static_cast(numPixelsInSphere)); } } } template auto MinMaxCurvatureFlowFunction::ComputeThreshold(const DispatchBase &, const NeighborhoodType & it) const -> PixelType { PixelType threshold{}; // Compute gradient PixelType gradient[ImageDimension]; PixelType gradMagnitude; SizeValueType stride; SizeValueType center; unsigned int j; center = it.Size() / 2; gradMagnitude = PixelType{}; for (j = 0; j < ImageDimension; ++j) { stride = it.GetStride((SizeValueType)j); gradient[j] = 0.5 * (it.GetPixel(center + stride) - it.GetPixel(center - stride)); gradient[j] *= this->m_ScaleCoefficients[j]; gradMagnitude += itk::Math::sqr(static_cast(gradient[j])); } if (gradMagnitude == 0.0) { return threshold; } gradMagnitude = std::sqrt(static_cast(gradMagnitude)); // Search for all position in the neighborhood perpendicular to // the gradient and at a distance of StencilRadius from center. RadiusValueType counter[ImageDimension]; RadiusValueType span = 2 * m_StencilRadius + 1; for (j = 0; j < ImageDimension; ++j) { counter[j] = 0; } using Iterator = typename NeighborhoodType::ConstIterator; Iterator neighIter; Iterator neighEnd = it.End(); SizeValueType i = 0; SizeValueType numPixels = 0; for (neighIter = it.Begin(); neighIter < neighEnd; ++neighIter, ++i) { PixelType dotProduct{}; PixelType vectorMagnitude{}; for (j = 0; j < ImageDimension; ++j) { IndexValueType diff = static_cast(counter[j]) - static_cast(m_StencilRadius); dotProduct += static_cast(diff) * gradient[j]; vectorMagnitude += static_cast(itk::Math::sqr(diff)); } vectorMagnitude = std::sqrt(static_cast(vectorMagnitude)); if (vectorMagnitude != 0.0) { dotProduct /= gradMagnitude * vectorMagnitude; } if (vectorMagnitude >= m_StencilRadius && itk::Math::abs(dotProduct) < 0.262) { threshold += it.GetPixel(i); ++numPixels; } bool carryOver = true; for (j = 0; carryOver && j < ImageDimension; ++j) { counter[j] += 1; carryOver = false; if (counter[j] == span) { counter[j] = 0; carryOver = true; } } } if (numPixels > 0) { threshold /= static_cast(numPixels); } return threshold; } template auto MinMaxCurvatureFlowFunction::ComputeThreshold(const Dispatch<2> &, const NeighborhoodType & it) const -> PixelType { constexpr unsigned int imageDimension = 2; if (m_StencilRadius == 0) { return it.GetCenterPixel(); } PixelType threshold{}; // Compute gradient double gradient[imageDimension]; double gradMagnitude; SizeValueType stride; SizeValueType center; SizeValueType position[imageDimension]; center = it.Size() / 2; gradient[0] = 0.5 * (it.GetPixel(center + 1) - it.GetPixel(center - 1)); unsigned int k = 0; gradient[k] *= this->m_ScaleCoefficients[k]; gradMagnitude = Math::sqr(gradient[k]); ++k; stride = it.GetStride(1); gradient[k] = 0.5 * (it.GetPixel(center + stride) - it.GetPixel(center - stride)); gradient[k] *= this->m_ScaleCoefficients[k]; gradMagnitude += Math::sqr(gradient[k]); if (gradMagnitude == 0.0) { return threshold; } gradMagnitude = std::sqrt(static_cast(gradMagnitude)) / static_cast(m_StencilRadius); for (double & j : gradient) { j /= gradMagnitude; } // Compute first perpendicular point position[0] = Math::Round(static_cast(m_StencilRadius - gradient[1])); position[1] = Math::Round(static_cast(m_StencilRadius + gradient[0])); threshold = it.GetPixel(position[0] + stride * position[1]); // Compute second perpendicular point position[0] = Math::Round(static_cast(m_StencilRadius + gradient[1])); position[1] = Math::Round(static_cast(m_StencilRadius - gradient[0])); threshold += it.GetPixel(position[0] + stride * position[1]); threshold *= 0.5; return threshold; } template auto MinMaxCurvatureFlowFunction::ComputeThreshold(const Dispatch<3> &, const NeighborhoodType & it) const -> PixelType { constexpr unsigned int imageDimension = 3; if (m_StencilRadius == 0) { return it.GetCenterPixel(); } PixelType threshold{}; // Compute gradient double gradient[imageDimension]; double gradMagnitude; SizeValueType strideY; SizeValueType strideZ; SizeValueType center; SizeValueType position[imageDimension]; center = it.Size() / 2; strideY = it.GetStride(1); strideZ = it.GetStride(2); gradient[0] = 0.5 * (it.GetPixel(center + 1) - it.GetPixel(center - 1)); unsigned int k = 0; gradient[k] *= this->m_ScaleCoefficients[k]; gradMagnitude = itk::Math::sqr(gradient[k]); ++k; gradient[k] = 0.5 * (it.GetPixel(center + strideY) - it.GetPixel(center - strideY)); gradient[k] *= this->m_ScaleCoefficients[k]; gradMagnitude += itk::Math::sqr(gradient[k]); ++k; gradient[k] = 0.5 * (it.GetPixel(center + strideZ) - it.GetPixel(center - strideZ)); gradient[k] *= this->m_ScaleCoefficients[k]; gradMagnitude += Math::sqr(gradient[k]); if (gradMagnitude == 0.0) { return threshold; } gradMagnitude = std::sqrt(gradMagnitude) / static_cast(m_StencilRadius); for (double & j : gradient) { j /= gradMagnitude; } double theta; double phi; if (gradient[2] > 1.0) { gradient[2] = 1.0; } if (gradient[2] < -1.0) { gradient[2] = -1.0; } theta = std::acos(gradient[2]); if (Math::AlmostEquals(gradient[0], PixelType{})) { phi = Math::pi * 0.5; } else { phi = std::atan(gradient[1] / gradient[0]); } double cosTheta = std::cos(theta); double sinTheta = std::sin(theta); double cosPhi = std::cos(phi); double sinPhi = std::sin(phi); double rSinTheta = m_StencilRadius * sinTheta; double rCosThetaCosPhi = m_StencilRadius * cosTheta * cosPhi; double rCosThetaSinPhi = m_StencilRadius * cosTheta * sinPhi; double rSinPhi = m_StencilRadius * sinPhi; double rCosPhi = m_StencilRadius * cosPhi; // Point 1: angle = 0; position[0] = Math::Round(m_StencilRadius + rCosThetaCosPhi); position[1] = Math::Round(m_StencilRadius + rCosThetaSinPhi); position[2] = Math::Round(m_StencilRadius - rSinTheta); threshold += it.GetPixel(position[0] + strideY * position[1] + strideZ * position[2]); // Point 2: angle = 90; position[0] = Math::Round(m_StencilRadius - rSinPhi); position[1] = Math::Round(m_StencilRadius + rCosPhi); position[2] = m_StencilRadius; threshold += it.GetPixel(position[0] + strideY * position[1] + strideZ * position[2]); // Point 3: angle = 180; position[0] = Math::Round(m_StencilRadius - rCosThetaCosPhi); position[1] = Math::Round(m_StencilRadius - rCosThetaSinPhi); position[2] = Math::Round(m_StencilRadius + rSinTheta); threshold += it.GetPixel(position[0] + strideY * position[1] + strideZ * position[2]); // Point 4: angle = 270; position[0] = Math::Round(m_StencilRadius + rSinPhi); position[1] = Math::Round(m_StencilRadius - rCosPhi); position[2] = m_StencilRadius; threshold += it.GetPixel(position[0] + strideY * position[1] + strideZ * position[2]); threshold *= 0.25; return threshold; } template auto MinMaxCurvatureFlowFunction::ComputeUpdate(const NeighborhoodType & it, void * globalData, const FloatOffsetType & offset) -> PixelType { PixelType update = this->Superclass::ComputeUpdate(it, globalData, offset); if (update == 0.0) { return update; } PixelType threshold; threshold = this->ComputeThreshold(Dispatch(), it); NeighborhoodInnerProduct innerProduct; PixelType avgValue = innerProduct(it, m_StencilOperator); if (avgValue < threshold) { return (std::max(update, PixelType{})); } else { return (std::min(update, PixelType{})); } } } // end namespace itk #endif