@@ -153,6 +153,7 @@ StructurePreservingColorNormalizationFilter<TImage>::BeforeThreadedGenerateData(
153153 if (this ->ImageToNMF (inIt, m_InputH, m_InputUnstainedPixel) != 0 )
154154 {
155155 // we failed
156+ m_Input = nullptr ;
156157 itkAssertOrThrowMacro (
157158 m_Input != nullptr ,
158159 " The image to be normalized could not be processed; does it have white, blue, and pink pixels?" );
@@ -184,7 +185,7 @@ StructurePreservingColorNormalizationFilter<TImage>::BeforeThreadedGenerateData(
184185 m_Reference = referenceImage;
185186 m_ReferenceTimeStamp = referenceImage->GetTimeStamp ();
186187
187- if ((m_InputH * m_ReferenceH.transpose ()).determinant () < CalcElementType ( 0 ) )
188+ if ((m_InputH * m_ReferenceH.transpose ()).determinant () < Zero )
188189 {
189190 // Somehow the hematoxylin and eosin rows got swapped in one of
190191 // the input image or reference image. Flip them in referenceH to get
@@ -194,7 +195,7 @@ StructurePreservingColorNormalizationFilter<TImage>::BeforeThreadedGenerateData(
194195 m_ReferenceH.row (0 ) = referenceHOriginal.row (1 );
195196 m_ReferenceH.row (1 ) = referenceHOriginal.row (0 );
196197 }
197- itkAssertOrThrowMacro ((m_InputH * m_ReferenceH.transpose ()).determinant () > CalcElementType ( 0 ) ,
198+ itkAssertOrThrowMacro ((m_InputH * m_ReferenceH.transpose ()).determinant () > Zero ,
198199 " Hematoxylin and Eosin are getting mixed up; failed" );
199200}
200201
@@ -245,8 +246,6 @@ StructurePreservingColorNormalizationFilter<TImage>::ImageToNMF(RegionConstItera
245246 }
246247
247248 // Improve matrixH using Virtanen's algorithm
248- // { std::ostringstream mesg; mesg << "matrixH before VirtanenNMFEuclidean = " << std::endl << matrixH << std::endl;
249- // std::cout << mesg.str() << std::flush; }
250249 {
251250 CalcMatrixType matrixW; // Could end up large.
252251 this ->VirtanenNMFEuclidean (matrixBrightV, matrixW, matrixH);
@@ -255,11 +254,7 @@ StructurePreservingColorNormalizationFilter<TImage>::ImageToNMF(RegionConstItera
255254 // Rescale each row of matrixH so that the
256255 // (100-VeryDarkPercentileLevel) value of each column of matrixW is
257256 // 1.0.
258- // { std::ostringstream mesg; mesg << "matrixH before NormalizeMatrixH = " << std::endl << matrixH << std::endl;
259- // std::cout << mesg.str() << std::flush; }
260257 this ->NormalizeMatrixH (matrixDarkV, unstainedPixel, matrixH);
261- // { std::ostringstream mesg; mesg << "matrixH at end = " << std::endl << matrixH << std::endl; std::cout <<
262- // mesg.str() << std::flush; }
263258
264259 return 0 ;
265260}
@@ -289,7 +284,7 @@ StructurePreservingColorNormalizationFilter<TImage>::ImageToMatrix(RegionConstIt
289284 PixelType pixelValue = iter.Get ();
290285 for (Eigen::Index color = 0 ; color < m_NumberOfColors; ++color)
291286 {
292- matrixV (numberOfRows, color) = pixelValue[color] + CalcElementType ( 1.0 ) ;
287+ matrixV (numberOfRows, color) = pixelValue[color] + One ;
293288 }
294289 }
295290 }
@@ -466,7 +461,7 @@ StructurePreservingColorNormalizationFilter<TImage>::SecondPassDistinguishers(
466461 const CalcColVectorType dotProducts{ normV * normV.row (firstPassDistinguisherIndices[distinguisher]).transpose () };
467462 const CalcElementType threshold{ *std::max_element (Self::cbegin (dotProducts), Self::cend (dotProducts)) *
468463 SecondPassDistinguishersThreshold };
469- CalcRowVectorType cumulative{ CalcRowVectorType::Constant (1 , normVStart.cols (), 0.0 ) };
464+ CalcRowVectorType cumulative{ CalcRowVectorType::Constant (1 , normVStart.cols (), Zero ) };
470465 SizeValueType numberOfContributions{ 0 };
471466 for (Eigen::Index row = 0 ; row < dotProducts.size (); ++row)
472467 {
@@ -542,8 +537,6 @@ StructurePreservingColorNormalizationFilter<TImage>::DistinguishersToNMFSeeds(co
542537 return 1 ; // we failed
543538 }
544539
545- // { std::ostringstream mesg; mesg << "distinguishers = " << std::endl << distinguishers << std::endl; std::cout <<
546- // mesg.str() << std::flush; }
547540 unstainedPixel = distinguishers.row (unstainedIndex);
548541 const CalcRowVectorType logUnstained{ unstainedPixel.unaryExpr (CalcUnaryFunctionPointer (std::log)) };
549542 const CalcRowVectorType logHematoxylin{
@@ -556,7 +549,7 @@ StructurePreservingColorNormalizationFilter<TImage>::DistinguishersToNMFSeeds(co
556549 matrixH.row (1 ) = logEosin;
557550
558551 // If somehow an element of matrixH is negative, set it to zero.
559- const auto clip = [](const CalcElementType & x) { return std::max (CalcElementType ( 0.0 ) , x); };
552+ const auto clip = [](const CalcElementType & x) { return std::max (Zero , x); };
560553 matrixH = matrixH.unaryExpr (clip);
561554
562555 return 0 ;
@@ -582,12 +575,26 @@ StructurePreservingColorNormalizationFilter<TImage>::DistinguishersToColors(Calc
582575 // m_ColorIndexSuppressedByHematoxylin, and the index of the color
583576 // suppressed by eosin is indicated by
584577 // m_ColorIndexSuppressedByEosin.
585- const CalcColVectorType redValues{ distinguishers.col (m_ColorIndexSuppressedByHematoxylin) };
586- const CalcElementType * const hematoxylinIt{ std::min_element (Self::cbegin (redValues), Self::cend (redValues)) };
587- hematoxylinIndex = std::distance (Self::cbegin (redValues), hematoxylinIt);
588- const CalcColVectorType greenValues{ distinguishers.col (m_ColorIndexSuppressedByEosin) };
589- const CalcElementType * const eosinIt{ std::min_element (Self::cbegin (greenValues), Self::cend (greenValues)) };
590- eosinIndex = std::distance (Self::cbegin (greenValues), eosinIt);
578+ std::vector<int > suppressedIndex;
579+ std::vector<CalcRowVectorType> suppressedPixel;
580+ for (int fromRow = 0 ; fromRow < distinguishers.rows (); ++fromRow)
581+ {
582+ if (fromRow == unstainedIndex)
583+ continue ;
584+ suppressedIndex.push_back (fromRow);
585+ suppressedPixel.push_back (distinguishers.row (unstainedIndex) - distinguishers.row (fromRow));
586+ }
587+ if (suppressedPixel[0 ](m_ColorIndexSuppressedByHematoxylin) * suppressedPixel[1 ](m_ColorIndexSuppressedByEosin) >
588+ suppressedPixel[0 ](m_ColorIndexSuppressedByEosin) * suppressedPixel[1 ](m_ColorIndexSuppressedByHematoxylin))
589+ {
590+ hematoxylinIndex = suppressedIndex[0 ];
591+ eosinIndex = suppressedIndex[1 ];
592+ }
593+ else
594+ {
595+ hematoxylinIndex = suppressedIndex[1 ];
596+ eosinIndex = suppressedIndex[0 ];
597+ }
591598}
592599
593600
@@ -597,7 +604,7 @@ StructurePreservingColorNormalizationFilter<TImage>::NormalizeMatrixH(const Calc
597604 const CalcRowVectorType & unstainedPixel,
598605 CalcMatrixType & matrixH) const
599606{
600- const CalcColVectorType firstOnes{ CalcColVectorType::Constant (matrixDarkVIn.rows (), 1 , 1.0 ) };
607+ const CalcColVectorType firstOnes{ CalcColVectorType::Constant (matrixDarkVIn.rows (), 1 , One ) };
601608
602609 // Compute the VeryDarkPercentileLevel percentile of a stain's
603610 // negative(matrixW) column. This a dark value due to its being the
@@ -606,7 +613,7 @@ StructurePreservingColorNormalizationFilter<TImage>::NormalizeMatrixH(const Calc
606613 CalcMatrixType matrixDarkV{ matrixDarkVIn };
607614 matrixDarkV = (firstOnes * logUnstainedCalcPixel) - matrixDarkV.unaryExpr (CalcUnaryFunctionPointer (std::log));
608615
609- const auto clip = [](const CalcElementType & x) { return std::max (CalcElementType ( 0.0 ) , x); };
616+ const auto clip = [](const CalcElementType & x) { return std::max (Zero , x); };
610617 CalcMatrixType negativeMatrixW = -(((matrixDarkV * matrixH.transpose ()).array () - lambda).unaryExpr (clip).matrix () *
611618 (matrixH * matrixH.transpose ()).inverse ())
612619 .unaryExpr (clip);
@@ -629,7 +636,7 @@ StructurePreservingColorNormalizationFilter<TImage>::VirtanenNMFEuclidean(const
629636 CalcMatrixType & matrixW,
630637 CalcMatrixType & matrixH)
631638{
632- const auto clip = [](const CalcElementType & x) { return std::max (CalcElementType ( 0.0 ) , x); };
639+ const auto clip = [](const CalcElementType & x) { return std::max (Zero , x); };
633640 matrixW = ((((matrixV * matrixH.transpose ()).array () - lambda).unaryExpr (clip) + epsilon2).matrix () *
634641 (matrixH * matrixH.transpose ()).inverse ())
635642 .unaryExpr (clip);
@@ -664,8 +671,6 @@ StructurePreservingColorNormalizationFilter<TImage>::VirtanenNMFEuclidean(const
664671 previousMatrixW = matrixW;
665672 }
666673 }
667- // { std::ostringstream mesg; mesg << "Executed = " << loopIter << " iterations of VirtanenNMFEuclidean" << std::endl;
668- // std::cout << mesg.str() << std::flush; }
669674
670675 // Round off values in the response, so that numbers are quite small
671676 // are set to zero.
@@ -685,15 +690,15 @@ StructurePreservingColorNormalizationFilter<TImage>::VirtanenNMFKLDivergence(con
685690 // the Lasso penalty lambda for matrixW and incorporate the Lagrange
686691 // multipliers to make each row of matrixH have magnitude 1.0.
687692
688- const auto clip = [](const CalcElementType & x) { return std::max (CalcElementType ( 0.0 ) , x); };
693+ const auto clip = [](const CalcElementType & x) { return std::max (Zero , x); };
689694 matrixW = ((((matrixV * matrixH.transpose ()).array () - lambda).unaryExpr (clip) + epsilon2).matrix () *
690695 (matrixH * matrixH.transpose ()).inverse ())
691696 .unaryExpr (clip);
692697
693698 // Apply Virtanen's algorithm to iteratively improve matrixW and
694699 // matrixH.
695- const CalcRowVectorType firstOnes{ CalcRowVectorType::Constant (1 , matrixV.rows (), 1.0 ) };
696- const CalcColVectorType lastOnes{ CalcColVectorType::Constant (matrixV.cols (), 1 , 1.0 ) };
700+ const CalcRowVectorType firstOnes{ CalcRowVectorType::Constant (1 , matrixV.rows (), One ) };
701+ const CalcColVectorType lastOnes{ CalcColVectorType::Constant (matrixV.cols (), 1 , One ) };
697702 CalcMatrixType previousMatrixW{ matrixW };
698703 SizeValueType loopIter{ 0 };
699704 for (; loopIter < maxNumberOfIterations; ++loopIter)
@@ -726,8 +731,6 @@ StructurePreservingColorNormalizationFilter<TImage>::VirtanenNMFKLDivergence(con
726731 previousMatrixW = matrixW;
727732 }
728733 }
729- // { std::ostringstream mesg; mesg << "Executed = " << loopIter << " iterations of VirtanenNMFKLDivergence" <<
730- // std::endl; std::cout << mesg.str() << std::flush; }
731734
732735 // Round off values in the response, so that numbers are quite small
733736 // are set to zero.
@@ -770,12 +773,12 @@ StructurePreservingColorNormalizationFilter<TImage>::NMFsToImage(const CalcMatri
770773 // logarithm.
771774 CalcRowVectorType logInputUnstained = inputUnstained.unaryExpr (CalcUnaryFunctionPointer (std::log));
772775 CalcRowVectorType logReferenceUnstained = referenceUnstained.unaryExpr (CalcUnaryFunctionPointer (std::log));
773- const CalcColVectorType firstOnes{ CalcColVectorType::Constant (numberOfPixels, 1 , 1.0 ) };
776+ const CalcColVectorType firstOnes{ CalcColVectorType::Constant (numberOfPixels, 1 , One ) };
774777 matrixV = (firstOnes * logInputUnstained) - matrixV.unaryExpr (CalcUnaryFunctionPointer (std::log));
775778
776779 // Switch from inputH to referenceH.
777780 {
778- const auto clip = [](const CalcElementType & x) { return std::max (CalcElementType ( 0.0 ) , x); };
781+ const auto clip = [](const CalcElementType & x) { return std::max (Zero , x); };
779782 CalcMatrixType matrixW = (((matrixV * inputH.transpose ()).array () - lambda).unaryExpr (clip).matrix () *
780783 (inputH * inputH.transpose ()).inverse ())
781784 .unaryExpr (clip);
@@ -797,7 +800,7 @@ StructurePreservingColorNormalizationFilter<TImage>::NMFsToImage(const CalcMatri
797800 }
798801 for (Eigen::Index color = 0 ; color < m_NumberOfColors; ++color)
799802 {
800- pixelValue[color] = std::max (std::min (matrixV (pixelIndex, color) - CalcElementType ( 1.0 ) , upperbound), lowerbound);
803+ pixelValue[color] = std::max (std::min (matrixV (pixelIndex, color) - One , upperbound), lowerbound);
801804 }
802805 const PixelType inputPixel = inIt.Get ();
803806 for (Eigen::Index dim = m_NumberOfColors; dim < m_NumberOfDimensions; ++dim)
@@ -907,6 +910,14 @@ template <typename TImage>
907910constexpr typename StructurePreservingColorNormalizationFilter<TImage>::SizeValueType
908911 StructurePreservingColorNormalizationFilter<TImage>::maxNumberOfRows;
909912
913+ template <typename TImage>
914+ constexpr typename StructurePreservingColorNormalizationFilter<TImage>::CalcElementType
915+ StructurePreservingColorNormalizationFilter<TImage>::Zero;
916+
917+ template <typename TImage>
918+ constexpr typename StructurePreservingColorNormalizationFilter<TImage>::CalcElementType
919+ StructurePreservingColorNormalizationFilter<TImage>::One;
920+
910921template <typename TImage>
911922constexpr typename StructurePreservingColorNormalizationFilter<TImage>::CalcElementType
912923 StructurePreservingColorNormalizationFilter<TImage>::SecondPassDistinguishersThreshold;
0 commit comments