/*========================================================================= * * 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 itkDiscreteLevelSetImage_hxx #define itkDiscreteLevelSetImage_hxx namespace itk { // ---------------------------------------------------------------------------- template auto DiscreteLevelSetImage::EvaluateGradient(const InputType & inputIndex) const -> GradientType { InputType inputIndexA = inputIndex; InputType inputIndexB = inputIndex; GradientType dx; for (unsigned int dim = 0; dim < Dimension; ++dim) { inputIndexA[dim] += 1; inputIndexB[dim] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim] = inputIndex[dim]; } if (!this->IsInsideDomain(inputIndexB)) { inputIndexB[dim] = inputIndex[dim]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const auto valueB = static_cast(this->Evaluate(inputIndexB)); // division by 0 only if image is a single pixel const OutputRealType scale = this->m_NeighborhoodScales[dim] / (inputIndexA[dim] - inputIndexB[dim]); dx[dim] = (valueA - valueB) * scale; inputIndexA[dim] = inputIndexB[dim] = inputIndex[dim]; } return dx; } // ---------------------------------------------------------------------------- template auto DiscreteLevelSetImage::EvaluateForwardGradient(const InputType & inputIndex) const -> GradientType { const auto centerValue = static_cast(this->Evaluate(inputIndex)); InputType inputIndexA = inputIndex; GradientType dx; for (unsigned int dim = 0; dim < Dimension; ++dim) { inputIndexA[dim] += 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim] = inputIndex[dim]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const OutputRealType scale = this->m_NeighborhoodScales[dim]; dx[dim] = (valueA - centerValue) * scale; inputIndexA[dim] = inputIndex[dim]; } return dx; } // ---------------------------------------------------------------------------- template auto DiscreteLevelSetImage::EvaluateBackwardGradient(const InputType & inputIndex) const -> GradientType { const auto centerValue = static_cast(this->Evaluate(inputIndex)); InputType inputIndexA = inputIndex; GradientType dx; for (unsigned int dim = 0; dim < Dimension; ++dim) { inputIndexA[dim] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim] = inputIndex[dim]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const OutputRealType scale = this->m_NeighborhoodScales[dim]; dx[dim] = (centerValue - valueA) * scale; inputIndexA[dim] = inputIndex[dim]; } return dx; } // ---------------------------------------------------------------------------- template auto DiscreteLevelSetImage::EvaluateHessian(const InputType & inputIndex) const -> HessianType { HessianType oHessian; const auto centerValue = static_cast(this->Evaluate(inputIndex)); InputType inputIndexA = inputIndex; InputType inputIndexB = inputIndex; InputType inputIndexAa; InputType inputIndexBa; InputType inputIndexCa; InputType inputIndexDa; for (unsigned int dim1 = 0; dim1 < Dimension; ++dim1) { inputIndexA[dim1] += 1; inputIndexB[dim1] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim1] = inputIndex[dim1]; } if (!this->IsInsideDomain(inputIndexB)) { inputIndexB[dim1] = inputIndex[dim1]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const auto valueB = static_cast(this->Evaluate(inputIndexB)); oHessian[dim1][dim1] = (valueA + valueB - 2.0 * centerValue) * itk::Math::sqr(this->m_NeighborhoodScales[dim1]); inputIndexAa = inputIndexB; inputIndexBa = inputIndexB; inputIndexCa = inputIndexA; inputIndexDa = inputIndexA; for (unsigned int dim2 = dim1 + 1; dim2 < Dimension; ++dim2) { inputIndexAa[dim2] -= 1; inputIndexBa[dim2] += 1; inputIndexCa[dim2] -= 1; inputIndexDa[dim2] += 1; if (!this->IsInsideDomain(inputIndexAa)) { inputIndexAa[dim2] = inputIndexB[dim2]; } if (!this->IsInsideDomain(inputIndexBa)) { inputIndexBa[dim2] = inputIndexB[dim2]; } if (!this->IsInsideDomain(inputIndexCa)) { inputIndexCa[dim2] = inputIndexA[dim2]; } if (!this->IsInsideDomain(inputIndexDa)) { inputIndexDa[dim2] = inputIndexA[dim2]; } const auto valueAa = static_cast(this->Evaluate(inputIndexAa)); const auto valueBa = static_cast(this->Evaluate(inputIndexBa)); const auto valueCa = static_cast(this->Evaluate(inputIndexCa)); const auto valueDa = static_cast(this->Evaluate(inputIndexDa)); oHessian[dim1][dim2] = oHessian[dim2][dim1] = 0.25 * (valueAa - valueBa - valueCa + valueDa) * this->m_NeighborhoodScales[dim1] * this->m_NeighborhoodScales[dim2]; inputIndexAa[dim2] = inputIndexB[dim2]; inputIndexBa[dim2] = inputIndexB[dim2]; inputIndexCa[dim2] = inputIndexA[dim2]; inputIndexDa[dim2] = inputIndexA[dim2]; } inputIndexA[dim1] = inputIndex[dim1]; inputIndexB[dim1] = inputIndex[dim1]; } return oHessian; } // ---------------------------------------------------------------------------- template auto DiscreteLevelSetImage::EvaluateLaplacian(const InputType & inputIndex) const -> OutputRealType { OutputRealType oLaplacian{}; const auto centerValue = static_cast(this->Evaluate(inputIndex)); InputType inputIndexA = inputIndex; InputType inputIndexB = inputIndex; for (unsigned int dim1 = 0; dim1 < Dimension; ++dim1) { inputIndexA[dim1] += 1; inputIndexB[dim1] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim1] = inputIndex[dim1]; } if (!this->IsInsideDomain(inputIndexB)) { inputIndexB[dim1] = inputIndex[dim1]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const auto valueB = static_cast(this->Evaluate(inputIndexB)); oLaplacian += (valueA + valueB - 2.0 * centerValue) * itk::Math::sqr(this->m_NeighborhoodScales[dim1]); inputIndexA[dim1] = inputIndex[dim1]; inputIndexB[dim1] = inputIndex[dim1]; } return oLaplacian; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::Evaluate(const InputType & inputIndex, LevelSetDataType & data) const { // If it has not already been computed before if (data.Value.m_Computed) { return; } data.Value.m_Value = this->Evaluate(inputIndex); data.Value.m_Computed = true; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::EvaluateGradient(const InputType & inputIndex, LevelSetDataType & data) const { if (data.Gradient.m_Computed) { return; } // If it has not already been computed before // compute the gradient InputType inputIndexA = inputIndex; InputType inputIndexB = inputIndex; for (unsigned int dim = 0; dim < Dimension; ++dim) { inputIndexA[dim] += 1; inputIndexB[dim] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim] = inputIndex[dim]; } if (!this->IsInsideDomain(inputIndexB)) { inputIndexB[dim] = inputIndex[dim]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const auto valueB = static_cast(this->Evaluate(inputIndexB)); const OutputRealType scale = this->m_NeighborhoodScales[dim] / (inputIndexA[dim] - inputIndexB[dim]); data.Gradient.m_Value[dim] = (valueA - valueB) * scale; inputIndexA[dim] = inputIndexB[dim] = inputIndex[dim]; } data.Gradient.m_Computed = true; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::EvaluateHessian(const InputType & inputIndex, LevelSetDataType & data) const { if (data.Hessian.m_Computed) { return; } if (!data.Value.m_Computed) { data.Value.m_Computed = true; data.Value.m_Value = this->Evaluate(inputIndex); } // compute the hessian auto centerValue = static_cast(data.Value.m_Value); InputType inputIndexA = inputIndex; InputType inputIndexB = inputIndex; InputType inputIndexAa; InputType inputIndexBa; InputType inputIndexCa; InputType inputIndexDa; bool backward = data.BackwardGradient.m_Computed; bool forward = data.ForwardGradient.m_Computed; for (unsigned int dim1 = 0; dim1 < Dimension; ++dim1) { inputIndexA[dim1] += 1; inputIndexB[dim1] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim1] = inputIndex[dim1]; } if (!this->IsInsideDomain(inputIndexB)) { inputIndexB[dim1] = inputIndex[dim1]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const auto valueB = static_cast(this->Evaluate(inputIndexB)); data.Hessian.m_Value[dim1][dim1] = (valueA + valueB - 2.0 * centerValue) * itk::Math::sqr(this->m_NeighborhoodScales[dim1]); if (!backward) { data.BackwardGradient.m_Computed = true; data.BackwardGradient.m_Value[dim1] = (centerValue - valueB) * this->m_NeighborhoodScales[dim1]; } if (!forward) { data.ForwardGradient.m_Computed = true; data.ForwardGradient.m_Value[dim1] = (valueA - centerValue) * this->m_NeighborhoodScales[dim1]; } inputIndexAa = inputIndexB; inputIndexBa = inputIndexB; inputIndexCa = inputIndexA; inputIndexDa = inputIndexA; for (unsigned int dim2 = dim1 + 1; dim2 < Dimension; ++dim2) { inputIndexAa[dim2] -= 1; inputIndexBa[dim2] += 1; inputIndexCa[dim2] -= 1; inputIndexDa[dim2] += 1; if (!this->IsInsideDomain(inputIndexAa)) { inputIndexAa[dim2] = inputIndexB[dim2]; } if (!this->IsInsideDomain(inputIndexBa)) { inputIndexBa[dim2] = inputIndexB[dim2]; } if (!this->IsInsideDomain(inputIndexCa)) { inputIndexCa[dim2] = inputIndexA[dim2]; } if (!this->IsInsideDomain(inputIndexDa)) { inputIndexDa[dim2] = inputIndexA[dim2]; } const auto valueAa = static_cast(this->Evaluate(inputIndexAa)); const auto valueBa = static_cast(this->Evaluate(inputIndexBa)); const auto valueCa = static_cast(this->Evaluate(inputIndexCa)); const auto valueDa = static_cast(this->Evaluate(inputIndexDa)); data.Hessian.m_Value[dim1][dim2] = data.Hessian.m_Value[dim2][dim1] = 0.25 * (valueAa - valueBa - valueCa + valueDa) * this->m_NeighborhoodScales[dim1] * this->m_NeighborhoodScales[dim2]; inputIndexAa[dim2] = inputIndexB[dim2]; inputIndexBa[dim2] = inputIndexB[dim2]; inputIndexCa[dim2] = inputIndexA[dim2]; inputIndexDa[dim2] = inputIndexA[dim2]; } inputIndexA[dim1] = inputIndex[dim1]; inputIndexB[dim1] = inputIndex[dim1]; } data.Hessian.m_Computed = true; } // ---------------------------------------------------------------------------- template auto DiscreteLevelSetImage::EvaluateMeanCurvature(const InputType & inputIndex) const -> OutputRealType { OutputRealType oValue{}; HessianType hessian = this->EvaluateHessian(inputIndex); GradientType grad = this->EvaluateGradient(inputIndex); for (unsigned int i = 0; i < Dimension; ++i) { for (unsigned int j = 0; j < Dimension; ++j) { if (j != i) { oValue -= grad[i] * grad[j] * hessian[i][j]; oValue += hessian[j][j] * grad[i] * grad[i]; } } } OutputRealType gradNorm = grad.GetNorm(); if (gradNorm > itk::Math::eps) { oValue /= (gradNorm * gradNorm * gradNorm); } else { oValue /= (NumericTraits::OneValue() + gradNorm); } return oValue; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::EvaluateLaplacian(const InputType & inputIndex, LevelSetDataType & data) const { if (data.Laplacian.m_Computed) { return; } if (!data.Value.m_Computed) { data.Value.m_Value = this->Evaluate(inputIndex); data.Value.m_Computed = true; } const auto centerValue = static_cast(data.Value.m_Value); InputType inputIndexA = inputIndex; InputType inputIndexB = inputIndex; for (unsigned int dim1 = 0; dim1 < Dimension; ++dim1) { inputIndexA[dim1] += 1; inputIndexB[dim1] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim1] = inputIndex[dim1]; } if (!this->IsInsideDomain(inputIndexB)) { inputIndexB[dim1] = inputIndex[dim1]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const auto valueB = static_cast(this->Evaluate(inputIndexB)); data.Laplacian.m_Value += (valueA + valueB - 2.0 * centerValue) * itk::Math::sqr(this->m_NeighborhoodScales[dim1]); inputIndexA[dim1] = inputIndex[dim1]; inputIndexB[dim1] = inputIndex[dim1]; } data.Laplacian.m_Computed = true; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::EvaluateMeanCurvature(const InputType & inputIndex, LevelSetDataType & data) const { if (!data.MeanCurvature.m_Computed) { if (!data.Hessian.m_Computed) { this->EvaluateHessian(inputIndex, data); } if (!data.Gradient.m_Computed) { this->EvaluateGradient(inputIndex, data); } if (!data.GradientNorm.m_Computed) { this->EvaluateGradientNorm(inputIndex, data); } data.MeanCurvature.m_Computed = true; data.MeanCurvature.m_Value = OutputRealType{}; for (unsigned int i = 0; i < Dimension; ++i) { for (unsigned int j = 0; j < Dimension; ++j) { if (j != i) { data.MeanCurvature.m_Value -= data.Gradient.m_Value[i] * data.Gradient.m_Value[j] * data.Hessian.m_Value[i][j]; data.MeanCurvature.m_Value += data.Hessian.m_Value[j][j] * data.Gradient.m_Value[i] * data.Gradient.m_Value[i]; } } } OutputRealType temp = data.GradientNorm.m_Value; if (temp > itk::Math::eps) { data.MeanCurvature.m_Value /= (temp * temp * temp); } else { data.MeanCurvature.m_Value /= (NumericTraits::OneValue() + temp); } } } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::EvaluateForwardGradient(const InputType & inputIndex, LevelSetDataType & data) const { if (data.ForwardGradient.m_Computed) { return; } // compute the gradient if (!data.Value.m_Computed) { data.Value.m_Computed = true; data.Value.m_Value = this->Evaluate(inputIndex); } const auto centerValue = static_cast(data.Value.m_Value); InputType inputIndexA = inputIndex; GradientType dx; for (unsigned int dim = 0; dim < Dimension; ++dim) { inputIndexA[dim] += 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim] = inputIndex[dim]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const OutputRealType scale = this->m_NeighborhoodScales[dim]; dx[dim] = (valueA - centerValue) * scale; inputIndexA[dim] = inputIndex[dim]; } data.ForwardGradient.m_Value = dx; data.ForwardGradient.m_Computed = true; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::EvaluateBackwardGradient(const InputType & inputIndex, LevelSetDataType & data) const { if (data.BackwardGradient.m_Computed) { return; } // compute the gradient if (!data.Value.m_Computed) { data.Value.m_Value = this->Evaluate(inputIndex); data.Value.m_Computed = true; } const auto centerValue = static_cast(data.Value.m_Value); InputType inputIndexA = inputIndex; GradientType dx; for (unsigned int dim = 0; dim < Dimension; ++dim) { inputIndexA[dim] -= 1; if (!this->IsInsideDomain(inputIndexA)) { inputIndexA[dim] = inputIndex[dim]; } const auto valueA = static_cast(this->Evaluate(inputIndexA)); const OutputRealType scale = this->m_NeighborhoodScales[dim]; dx[dim] = (centerValue - valueA) * scale; inputIndexA[dim] = inputIndex[dim]; } data.BackwardGradient.m_Value = dx; data.BackwardGradient.m_Computed = true; } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::Initialize() { Superclass::Initialize(); } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::CopyInformation(const DataObject * data) { Superclass::CopyInformation(data); const auto * LevelSet = dynamic_cast(data); if (!LevelSet) { // pointer could not be cast back down itkExceptionMacro("itk::DiscreteLevelSetImage::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self *).name()); } } // ---------------------------------------------------------------------------- template void DiscreteLevelSetImage::Graft(const DataObject * data) { Superclass::Graft(data); const auto * LevelSet = dynamic_cast(data); if (!LevelSet) { // pointer could not be cast back down itkExceptionMacro("itk::DiscreteLevelSetImage::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self *).name()); } this->m_NeighborhoodScales = LevelSet->m_NeighborhoodScales; } } // namespace itk #endif // itkDiscreteLevelSetImage_hxx