Skip to content

Commit 750eea1

Browse files
committed
rewrite niBlackThreshold function
- no for-loops - support all thresholding types - support any input image depth - add Doxygen comments
1 parent ada87a9 commit 750eea1

File tree

2 files changed

+89
-48
lines changed

2 files changed

+89
-48
lines changed

modules/ximgproc/include/opencv2/ximgproc.hpp

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
@{
5353
@defgroup ximgproc_edge Structured forests for fast edge detection
5454
55-
This module contains implementations of modern structured edge detection algorithms, i.e. algorithms
56-
which somehow takes into account pixel affinities in natural images.
55+
This module contains implementations of modern structured edge detection algorithms,
56+
i.e. algorithms which somehow takes into account pixel affinities in natural images.
5757
5858
@defgroup ximgproc_filters Filters
5959
@@ -63,13 +63,47 @@ which somehow takes into account pixel affinities in natural images.
6363
@}
6464
*/
6565

66-
namespace cv {
67-
namespace ximgproc {
68-
CV_EXPORTS_W
69-
void niBlackThreshold( InputArray _src, OutputArray _dst, double maxValue,
70-
int type, int blockSize, double delta );
66+
namespace cv
67+
{
68+
namespace ximgproc
69+
{
7170

72-
} // namespace ximgproc
73-
} //namespace cv
71+
//! @addtogroup ximgproc
72+
//! @{
7473

75-
#endif
74+
/** @brief Applies Niblack thresholding to input image.
75+
76+
The function transforms a grayscale image to a binary image according to the formulae:
77+
- **THRESH_BINARY**
78+
\f[dst(x,y) = \fork{\texttt{maxValue}}{if \(src(x,y) > T(x,y)\)}{0}{otherwise}\f]
79+
- **THRESH_BINARY_INV**
80+
\f[dst(x,y) = \fork{0}{if \(src(x,y) > T(x,y)\)}{\texttt{maxValue}}{otherwise}\f]
81+
where \f$T(x,y)\f$ is a threshold calculated individually for each pixel.
82+
83+
The threshold value \f$T(x, y)\f$ is the mean minus \f$ delta \f$ times standard deviation
84+
of \f$\texttt{blockSize} \times\texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$.
85+
86+
The function can't process the image in-place.
87+
88+
@param _src Source 8-bit single-channel image.
89+
@param _dst Destination image of the same size and the same type as src.
90+
@param maxValue Non-zero value assigned to the pixels for which the condition is satisfied,
91+
used with the THRESH_BINARY and THRESH_BINARY_INV thresholding types.
92+
@param type Thresholding type, see cv::ThresholdTypes.
93+
@param blockSize Size of a pixel neighborhood that is used to calculate a threshold value
94+
for the pixel: 3, 5, 7, and so on.
95+
@param delta Constant multiplied with the standard deviation and subtracted from the mean.
96+
Normally, it is taken to be a real number between 0 and 1.
97+
98+
@sa threshold, adaptiveThreshold
99+
*/
100+
CV_EXPORTS_W void niBlackThreshold( InputArray _src, OutputArray _dst,
101+
double maxValue, int type,
102+
int blockSize, double delta );
103+
104+
//! @}
105+
106+
}
107+
}
108+
109+
#endif // __OPENCV_XIMGPROC_HPP__

modules/ximgproc/src/niblack_thresholding.cpp

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -49,52 +49,59 @@ namespace ximgproc {
4949
void 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_32F, Size(blockSize, blockSize),
74-
Point(-1,-1), true, BORDER_REPLICATE );
75-
sqrBoxFilter( src, sqmean, CV_32F, 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

Comments
 (0)