/*========================================================================= * * 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 itkRenyiEntropyThresholdCalculator_hxx #define itkRenyiEntropyThresholdCalculator_hxx #include "itkProgressReporter.h" #include "itkMath.h" namespace itk { template void RenyiEntropyThresholdCalculator::GenerateData() { const HistogramType * histogram = this->GetInput(); TotalAbsoluteFrequencyType total = histogram->GetTotalFrequency(); if (total == TotalAbsoluteFrequencyType{}) { itkExceptionMacro("Histogram is empty"); } m_Size = histogram->GetSize(0); ProgressReporter progress(this, 0, m_Size); if (m_Size == 1) { this->GetOutput()->Set(static_cast(histogram->GetMeasurement(0, 0))); return; } const double tolerance = itk::Math::eps; InstanceIdentifier ih; std::vector norm_histo(m_Size); /* normalized histogram */ std::vector P1(m_Size); /* cumulative normalized histogram */ std::vector P2(m_Size); for (ih = 0; ih < m_Size; ++ih) { norm_histo[ih] = static_cast(histogram->GetFrequency(ih, 0)) / static_cast(total); } P1[0] = norm_histo[0]; P2[0] = 1.0 - P1[0]; for (ih = 1; ih < m_Size; ++ih) { P1[ih] = P1[ih - 1] + norm_histo[ih]; P2[ih] = 1.0 - P1[ih]; } // Determine the first non-zero bin m_FirstBin = 0; for (ih = 0; ih < m_Size; ++ih) { if (!(itk::Math::abs(P1[ih]) < tolerance)) { m_FirstBin = ih; break; } } // Determine the last non-zero bin m_LastBin = static_cast(m_Size - 1); for (ih = m_Size - 1; ih >= m_FirstBin; ih--) { if (!(itk::Math::abs(P2[ih]) < tolerance)) { m_LastBin = ih; break; } } InstanceIdentifier t_star2 = this->MaxEntropyThresholding(histogram, norm_histo, P1, P2); InstanceIdentifier t_star1 = this->MaxEntropyThresholding2(histogram, norm_histo, P1, P2); InstanceIdentifier t_star3 = this->MaxEntropyThresholding3(histogram, norm_histo, P1, P2); InstanceIdentifier tmp_var; // Sort t_star values if (t_star2 < t_star1) { tmp_var = t_star1; t_star1 = t_star2; t_star2 = tmp_var; } if (t_star3 < t_star2) { tmp_var = t_star2; t_star2 = t_star3; t_star3 = tmp_var; } if (t_star2 < t_star1) { tmp_var = t_star1; t_star1 = t_star2; t_star2 = tmp_var; } double beta1 = 0.; double beta2 = 0.; double beta3 = 0.; // Adjust beta values. // Note that t_star1, t_star2, t_star3 are unsigned int. if (itk::Math::abs(static_cast(t_star1) - static_cast(t_star2)) <= 5.) { if (itk::Math::abs(static_cast(t_star2) - static_cast(t_star3)) <= 5.) { beta1 = 1.; beta2 = 2.; beta3 = 1.; } else { beta1 = 0.; beta2 = 1.; beta3 = 3.; } } else { if (itk::Math::abs(static_cast(t_star2) - static_cast(t_star3)) <= 5.) { beta1 = 3.; beta2 = 1.; beta3 = 0.; } else { beta1 = 1.; beta2 = 2.; beta3 = 1.; } } itkAssertInDebugAndIgnoreInReleaseMacro(t_star1 < m_Size); itkAssertInDebugAndIgnoreInReleaseMacro(t_star2 < m_Size); itkAssertInDebugAndIgnoreInReleaseMacro(t_star3 < m_Size); double omega = P1[t_star3] - P1[t_star1]; // Determine the optimal threshold value double realOptThreshold = static_cast(t_star1) * (P1[t_star1] + 0.25 * omega * beta1) + static_cast(t_star2) * 0.25 * omega * beta2 + static_cast(t_star3) * (P2[t_star3] + 0.25 * omega * beta3); auto opt_threshold = static_cast(realOptThreshold); this->GetOutput()->Set(static_cast(histogram->GetMeasurement(opt_threshold, 0))); } template auto RenyiEntropyThresholdCalculator::MaxEntropyThresholding(const HistogramType * histogram, const std::vector & normHisto, const std::vector & P1, const std::vector & P2) -> InstanceIdentifier { // Calculate the total entropy each gray-level and fin the threshold that // maximizes it InstanceIdentifier threshold = 0; // was MIN_INT in original code, but if an empty image is processed it gives an error later on. double max_ent = NumericTraits::min(); // max entropy for (InstanceIdentifier it = m_FirstBin; it <= m_LastBin; ++it) { // Entropy of the background pixels double ent_back = 0.0; for (InstanceIdentifier ih = 0; ih <= it; ++ih) { if (histogram->GetFrequency(ih, 0) != AbsoluteFrequencyType{}) { double x = (normHisto[ih] / P1[it]); ent_back -= x * std::log(x); } } // Entropy of the object pixels double ent_obj = 0.0; for (InstanceIdentifier ih = it + 1; ih < m_Size; ++ih) { if (histogram->GetFrequency(ih, 0) != AbsoluteFrequencyType{}) { double x = (normHisto[ih] / P2[it]); ent_obj -= x * std::log(x); } } // Total entropy double tot_ent = ent_back + ent_obj; // IJ.log(""+max_ent+" "+tot_ent); if (max_ent < tot_ent) { max_ent = tot_ent; threshold = it; } } return threshold; } template auto RenyiEntropyThresholdCalculator::MaxEntropyThresholding2( const HistogramType * itkNotUsed(histogram), const std::vector & normHisto, const std::vector & P1, const std::vector & P2) -> InstanceIdentifier { InstanceIdentifier threshold = 0; // was MIN_INT in original code, but if an empty image is processed it gives an error later on. double max_ent = NumericTraits::min(); double alpha = 0.5; double term = 1.0 / (1.0 - alpha); for (InstanceIdentifier it = m_FirstBin; it <= m_LastBin; ++it) { // Entropy of the background pixels double ent_back = 0.0; for (InstanceIdentifier ih = 0; ih <= it; ++ih) { ent_back += std::sqrt(normHisto[ih] / P1[it]); } // Entropy of the object pixel double ent_obj = 0.0; for (InstanceIdentifier ih = it + 1; ih < m_Size; ++ih) { ent_obj += std::sqrt(normHisto[ih] / P2[it]); } // Total entropy double product = ent_back * ent_obj; double tot_ent = 0.; if (product > 0.0) { tot_ent = term * std::log(ent_back * ent_obj); } if (tot_ent > max_ent) { max_ent = tot_ent; threshold = it; } } return threshold; } template auto RenyiEntropyThresholdCalculator::MaxEntropyThresholding3( const HistogramType * itkNotUsed(histogram), const std::vector & normHisto, const std::vector & P1, const std::vector & P2) -> InstanceIdentifier { InstanceIdentifier threshold = 0; // was MIN_INT in original code, but if an empty image is processed it gives an error later on. double max_ent = 0.0; double alpha = 2.0; double term = 1.0 / (1.0 - alpha); for (InstanceIdentifier it = m_FirstBin; it <= m_LastBin; ++it) { // Entropy of the background pixels double ent_back = 0.0; for (InstanceIdentifier ih = 0; ih <= it; ++ih) { double x = normHisto[ih] / P1[it]; ent_back += x * x; } // Entropy of the object pixels double ent_obj = 0.0; for (InstanceIdentifier ih = it + 1; ih < m_Size; ++ih) { double x = normHisto[ih] / P2[it]; ent_obj += x * x; } // Total entropy double tot_ent = 0.0; double product = ent_back * ent_obj; if (product > 0.0) { tot_ent = term * std::log(product); } if (tot_ent > max_ent) { max_ent = tot_ent; threshold = it; } } return threshold; } template void RenyiEntropyThresholdCalculator::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "FirstBin: " << static_cast::PrintType>(m_FirstBin) << std::endl; os << indent << "LastBin: " << static_cast::PrintType>(m_LastBin) << std::endl; os << indent << "Size: " << static_cast::PrintType>(m_Size) << std::endl; } } // end namespace itk #endif