Commit 750eea14 authored by Amro's avatar Amro

rewrite niBlackThreshold function

- no for-loops
- support all thresholding types
- support any input image depth
- add Doxygen comments
parent ada87a99
...@@ -52,8 +52,8 @@ ...@@ -52,8 +52,8 @@
@{ @{
@defgroup ximgproc_edge Structured forests for fast edge detection @defgroup ximgproc_edge Structured forests for fast edge detection
This module contains implementations of modern structured edge detection algorithms, i.e. algorithms This module contains implementations of modern structured edge detection algorithms,
which somehow takes into account pixel affinities in natural images. i.e. algorithms which somehow takes into account pixel affinities in natural images.
@defgroup ximgproc_filters Filters @defgroup ximgproc_filters Filters
...@@ -63,13 +63,47 @@ which somehow takes into account pixel affinities in natural images. ...@@ -63,13 +63,47 @@ which somehow takes into account pixel affinities in natural images.
@} @}
*/ */
namespace cv { namespace cv
namespace ximgproc { {
CV_EXPORTS_W namespace ximgproc
void niBlackThreshold( InputArray _src, OutputArray _dst, double maxValue, {
int type, int blockSize, double delta );
} // namespace ximgproc //! @addtogroup ximgproc
} //namespace cv //! @{
#endif /** @brief Applies Niblack thresholding to input image.
The function transforms a grayscale image to a binary image according to the formulae:
- **THRESH_BINARY**
\f[dst(x,y) = \fork{\texttt{maxValue}}{if \(src(x,y) > T(x,y)\)}{0}{otherwise}\f]
- **THRESH_BINARY_INV**
\f[dst(x,y) = \fork{0}{if \(src(x,y) > T(x,y)\)}{\texttt{maxValue}}{otherwise}\f]
where \f$T(x,y)\f$ is a threshold calculated individually for each pixel.
The threshold value \f$T(x, y)\f$ is the mean minus \f$ delta \f$ times standard deviation
of \f$\texttt{blockSize} \times\texttt{blockSize}\f$ neighborhood of \f$(x, y)\f$.
The function can't process the image in-place.
@param _src Source 8-bit single-channel image.
@param _dst Destination image of the same size and the same type as src.
@param maxValue Non-zero value assigned to the pixels for which the condition is satisfied,
used with the THRESH_BINARY and THRESH_BINARY_INV thresholding types.
@param type Thresholding type, see cv::ThresholdTypes.
@param blockSize Size of a pixel neighborhood that is used to calculate a threshold value
for the pixel: 3, 5, 7, and so on.
@param delta Constant multiplied with the standard deviation and subtracted from the mean.
Normally, it is taken to be a real number between 0 and 1.
@sa threshold, adaptiveThreshold
*/
CV_EXPORTS_W void niBlackThreshold( InputArray _src, OutputArray _dst,
double maxValue, int type,
int blockSize, double delta );
//! @}
}
}
#endif // __OPENCV_XIMGPROC_HPP__
...@@ -49,52 +49,59 @@ namespace ximgproc { ...@@ -49,52 +49,59 @@ namespace ximgproc {
void niBlackThreshold( InputArray _src, OutputArray _dst, double maxValue, void niBlackThreshold( InputArray _src, OutputArray _dst, double maxValue,
int type, int blockSize, double delta ) int type, int blockSize, double delta )
{ {
// Input grayscale image
Mat src = _src.getMat(); Mat src = _src.getMat();
CV_Assert( src.type() == CV_8UC1 ); CV_Assert(src.channels() == 1);
CV_Assert( blockSize % 2 == 1 && blockSize > 1 ); CV_Assert(blockSize % 2 == 1 && blockSize > 1);
Size size = src.size(); type &= THRESH_MASK;
_dst.create( size, src.type() ); // Compute local threshold (T = mean + k * stddev)
Mat dst = _dst.getMat(); // using mean and standard deviation in the neighborhood of each pixel
// (intermediate calculations are done with floating-point precision)
if( maxValue < 0 ) Mat thresh;
{ {
dst = Scalar(0); // note that: Var[X] = E[X^2] - E[X]^2
return; Mat mean, sqmean, stddev;
boxFilter(src, mean, CV_32F, Size(blockSize, blockSize),
Point(-1,-1), true, BORDER_REPLICATE);
sqrBoxFilter(src, sqmean, CV_32F, Size(blockSize, blockSize),
Point(-1,-1), true, BORDER_REPLICATE);
sqrt(sqmean - mean.mul(mean), stddev);
thresh = mean + stddev * static_cast<float>(delta);
thresh.convertTo(thresh, src.depth());
} }
// Calculate and store the mean and mean of squares in the neighborhood // Prepare output image
// of each pixel and store them in Mat mean and sqmean. _dst.create(src.size(), src.type());
Mat_<float> mean(size), sqmean(size); Mat dst = _dst.getMat();
CV_Assert(src.data != dst.data); // no inplace processing
if( src.data != dst.data )
mean = dst;
boxFilter( src, mean, CV_32F, Size(blockSize, blockSize),
Point(-1,-1), true, BORDER_REPLICATE );
sqrBoxFilter( src, sqmean, CV_32F, Size(blockSize, blockSize),
Point(-1,-1), true, BORDER_REPLICATE );
// Compute (k * standard deviation) in the neighborhood of each pixel // Apply thresholding: ( pixel > threshold ) ? foreground : background
// and store in Mat stddev. Also threshold the values in the src matrix to compute dst matrix. Mat mask;
Mat_<float> stddev(size); switch (type)
int i, j, threshold;
uchar imaxval = saturate_cast<uchar>(maxValue);
for(i = 0; i < size.height; ++i)
{ {
for(j = 0; j < size.width; ++j) case THRESH_BINARY: // dst = (src > thresh) ? maxval : 0
{ case THRESH_BINARY_INV: // dst = (src > thresh) ? 0 : maxval
stddev.at<float>(i, j) = saturate_cast<float>(delta) * cvRound( sqrt(sqmean.at<float>(i, j) - compare(src, thresh, mask, (type == THRESH_BINARY ? CMP_GT : CMP_LE));
mean.at<float>(i, j)*mean.at<float>(i, j)) ); dst.setTo(0);
threshold = cvRound(mean.at<float>(i, j) + stddev.at<float>(i, j)); dst.setTo(maxValue, mask);
if(src.at<uchar>(i, j) > threshold) break;
dst.at<uchar>(i, j) = (type == THRESH_BINARY) ? imaxval : 0; case THRESH_TRUNC: // dst = (src > thresh) ? thresh : src
else compare(src, thresh, mask, CMP_GT);
dst.at<uchar>(i, j) = (type == THRESH_BINARY) ? 0 : imaxval; src.copyTo(dst);
} thresh.copyTo(dst, mask);
break;
case THRESH_TOZERO: // dst = (src > thresh) ? src : 0
case THRESH_TOZERO_INV: // dst = (src > thresh) ? 0 : src
compare(src, thresh, mask, (type == THRESH_TOZERO ? CMP_GT : CMP_LE));
dst.setTo(0);
src.copyTo(dst, mask);
break;
default:
CV_Error( CV_StsBadArg, "Unknown threshold type" );
break;
} }
} }
} // namespace ximgproc } // namespace ximgproc
} //namespace cv } // namespace cv
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment