@@ -49,52 +49,59 @@ namespace ximgproc {
4949void niBlackThreshold ( InputArray _src, OutputArray _dst, double maxValue,
5050 int type, int blockSize, double delta )
5151{
52+ // Input grayscale image
5253 Mat src = _src.getMat ();
53- CV_Assert ( src.type () == CV_8UC1 );
54- CV_Assert ( blockSize % 2 == 1 && blockSize > 1 );
55- Size size = src. size () ;
54+ CV_Assert (src.channels () == 1 );
55+ CV_Assert (blockSize % 2 == 1 && blockSize > 1 );
56+ type &= THRESH_MASK ;
5657
57- _dst. create ( size, src. type () );
58- Mat dst = _dst. getMat ();
59-
60- if ( maxValue < 0 )
58+ // Compute local threshold (T = mean + k * stddev)
59+ // using mean and standard deviation in the neighborhood of each pixel
60+ // (intermediate calculations are done with floating-point precision)
61+ Mat thresh;
6162 {
62- dst = Scalar (0 );
63- return ;
63+ // note that: Var[X] = E[X^2] - E[X]^2
64+ Mat mean, sqmean, stddev;
65+ boxFilter (src, mean, CV_32F, Size (blockSize, blockSize),
66+ Point (-1 ,-1 ), true , BORDER_REPLICATE);
67+ sqrBoxFilter (src, sqmean, CV_32F, Size (blockSize, blockSize),
68+ Point (-1 ,-1 ), true , BORDER_REPLICATE);
69+ sqrt (sqmean - mean.mul (mean), stddev);
70+ thresh = mean + stddev * static_cast <float >(delta);
71+ thresh.convertTo (thresh, src.depth ());
6472 }
6573
66- // Calculate and store the mean and mean of squares in the neighborhood
67- // of each pixel and store them in Mat mean and sqmean.
68- Mat_<float > mean (size), sqmean (size);
69-
70- if ( src.data != dst.data )
71- mean = dst;
72-
73- boxFilter ( src, mean, CV_64F, Size (blockSize, blockSize),
74- Point (-1 ,-1 ), true , BORDER_REPLICATE );
75- sqrBoxFilter ( src, sqmean, CV_64F, Size (blockSize, blockSize),
76- Point (-1 ,-1 ), true , BORDER_REPLICATE );
74+ // Prepare output image
75+ _dst.create (src.size (), src.type ());
76+ Mat dst = _dst.getMat ();
77+ CV_Assert (src.data != dst.data ); // no inplace processing
7778
78- // Compute (k * standard deviation) in the neighborhood of each pixel
79- // and store in Mat stddev. Also threshold the values in the src matrix to compute dst matrix.
80- Mat_<float > stddev (size);
81- int i, j, threshold;
82- uchar imaxval = saturate_cast<uchar>(maxValue);
83- for (i = 0 ; i < size.height ; ++i)
79+ // Apply thresholding: ( pixel > threshold ) ? foreground : background
80+ Mat mask;
81+ switch (type)
8482 {
85- for (j = 0 ; j < size.width ; ++j)
86- {
87- stddev.at <float >(i, j) = saturate_cast<float >(delta) * cvRound ( sqrt (sqmean.at <float >(i, j) -
88- mean.at <float >(i, j)*mean.at <float >(i, j)) );
89- threshold = cvRound (mean.at <float >(i, j) + stddev.at <float >(i, j));
90- if (src.at <uchar>(i, j) > threshold)
91- dst.at <uchar>(i, j) = (type == THRESH_BINARY) ? imaxval : 0 ;
92- else
93- dst.at <uchar>(i, j) = (type == THRESH_BINARY) ? 0 : imaxval;
94- }
83+ case THRESH_BINARY: // dst = (src > thresh) ? maxval : 0
84+ case THRESH_BINARY_INV: // dst = (src > thresh) ? 0 : maxval
85+ compare (src, thresh, mask, (type == THRESH_BINARY ? CMP_GT : CMP_LE));
86+ dst.setTo (0 );
87+ dst.setTo (maxValue, mask);
88+ break ;
89+ case THRESH_TRUNC: // dst = (src > thresh) ? thresh : src
90+ compare (src, thresh, mask, CMP_GT);
91+ src.copyTo (dst);
92+ thresh.copyTo (dst, mask);
93+ break ;
94+ case THRESH_TOZERO: // dst = (src > thresh) ? src : 0
95+ case THRESH_TOZERO_INV: // dst = (src > thresh) ? 0 : src
96+ compare (src, thresh, mask, (type == THRESH_TOZERO ? CMP_GT : CMP_LE));
97+ dst.setTo (0 );
98+ src.copyTo (dst, mask);
99+ break ;
100+ default :
101+ CV_Error ( CV_StsBadArg, " Unknown threshold type" );
102+ break ;
95103 }
96-
97104}
98105
99106} // namespace ximgproc
100- } // namespace cv
107+ } // namespace cv
0 commit comments