Skip to content

Commit a29ec8a

Browse files
committed
Merge pull request #542 from amroamroamro:fix_niblack
2 parents f162a93 + 6c7a806 commit a29ec8a

File tree

3 files changed

+110
-74
lines changed

3 files changed

+110
-74
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__
Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,53 @@
11
/*
2-
* Sample C++ to demonstrate Niblack thresholding.
3-
*
2+
* C++ sample to demonstrate Niblack thresholding.
43
*/
54

65
#include <iostream>
7-
#include <cstdio>
8-
9-
#include "opencv2/highgui.hpp"
106
#include "opencv2/core.hpp"
7+
#include "opencv2/highgui.hpp"
118
#include "opencv2/imgproc.hpp"
12-
139
#include "opencv2/ximgproc.hpp"
1410

1511
using namespace std;
1612
using namespace cv;
1713
using namespace cv::ximgproc;
1814

19-
Mat_<uchar> src, dst;
20-
21-
const int k_max_value = 10;
22-
int k_from_slider = 0;
23-
double k_actual = 0.0;
15+
Mat_<uchar> src;
16+
int k_ = 8;
17+
int blockSize_ = 11;
18+
int type_ = THRESH_BINARY;
2419

2520
void on_trackbar(int, void*);
2621

2722
int main(int argc, char** argv)
2823
{
29-
/*
30-
* Read filename from the command-line and load
31-
* corresponding gray-scale image.
32-
*/
24+
// read gray-scale image
3325
if(argc != 2)
3426
{
3527
cout << "Usage: ./niblack_thresholding [IMAGE]\n";
3628
return 1;
3729
}
3830
const char* filename = argv[1];
39-
src = imread(filename, 1);
40-
41-
namedWindow("k-slider", 1);
42-
string trackbar_name = "k";
43-
createTrackbar(trackbar_name, "k-slider", &k_from_slider, k_max_value, on_trackbar);
44-
on_trackbar(k_from_slider, 0);
45-
31+
src = imread(filename, IMREAD_GRAYSCALE);
4632
imshow("Source", src);
33+
34+
namedWindow("Niblack", WINDOW_AUTOSIZE);
35+
createTrackbar("k", "Niblack", &k_, 20, on_trackbar);
36+
createTrackbar("blockSize", "Niblack", &blockSize_, 30, on_trackbar);
37+
createTrackbar("threshType", "Niblack", &type_, 4, on_trackbar);
38+
on_trackbar(0, 0);
4739
waitKey(0);
4840

4941
return 0;
5042
}
5143

5244
void on_trackbar(int, void*)
5345
{
54-
k_actual = (double)k_from_slider/k_max_value;
55-
niBlackThreshold(src, dst, 255, THRESH_BINARY, 3, k_actual);
56-
57-
imshow("Destination", dst);
46+
double k = static_cast<double>(k_-10)/10; // [-1.0, 1.0]
47+
int blockSize = 2*(blockSize_ >= 1 ? blockSize_ : 1) + 1; // 3,5,7,...,61
48+
int type = type_; // THRESH_BINARY, THRESH_BINARY_INV,
49+
// THRESH_TRUNC, THRESH_TOZERO, THRESH_TOZERO_INV
50+
Mat dst;
51+
niBlackThreshold(src, dst, 255, type, blockSize, k);
52+
imshow("Niblack", dst);
5853
}

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_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

Comments
 (0)