Commit a29ec8a1 authored by Maksim Shabunin's avatar Maksim Shabunin

Merge pull request #542 from amroamroamro:fix_niblack

parents f162a935 6c7a806a
...@@ -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__
/* /*
* Sample C++ to demonstrate Niblack thresholding. * C++ sample to demonstrate Niblack thresholding.
*
*/ */
#include <iostream> #include <iostream>
#include <cstdio>
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp" #include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp" #include "opencv2/imgproc.hpp"
#include "opencv2/ximgproc.hpp" #include "opencv2/ximgproc.hpp"
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace cv::ximgproc; using namespace cv::ximgproc;
Mat_<uchar> src, dst; Mat_<uchar> src;
int k_ = 8;
const int k_max_value = 10; int blockSize_ = 11;
int k_from_slider = 0; int type_ = THRESH_BINARY;
double k_actual = 0.0;
void on_trackbar(int, void*); void on_trackbar(int, void*);
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
/* // read gray-scale image
* Read filename from the command-line and load
* corresponding gray-scale image.
*/
if(argc != 2) if(argc != 2)
{ {
cout << "Usage: ./niblack_thresholding [IMAGE]\n"; cout << "Usage: ./niblack_thresholding [IMAGE]\n";
return 1; return 1;
} }
const char* filename = argv[1]; const char* filename = argv[1];
src = imread(filename, 1); src = imread(filename, IMREAD_GRAYSCALE);
namedWindow("k-slider", 1);
string trackbar_name = "k";
createTrackbar(trackbar_name, "k-slider", &k_from_slider, k_max_value, on_trackbar);
on_trackbar(k_from_slider, 0);
imshow("Source", src); imshow("Source", src);
namedWindow("Niblack", WINDOW_AUTOSIZE);
createTrackbar("k", "Niblack", &k_, 20, on_trackbar);
createTrackbar("blockSize", "Niblack", &blockSize_, 30, on_trackbar);
createTrackbar("threshType", "Niblack", &type_, 4, on_trackbar);
on_trackbar(0, 0);
waitKey(0); waitKey(0);
return 0; return 0;
...@@ -51,8 +43,11 @@ int main(int argc, char** argv) ...@@ -51,8 +43,11 @@ int main(int argc, char** argv)
void on_trackbar(int, void*) void on_trackbar(int, void*)
{ {
k_actual = (double)k_from_slider/k_max_value; double k = static_cast<double>(k_-10)/10; // [-1.0, 1.0]
niBlackThreshold(src, dst, 255, THRESH_BINARY, 3, k_actual); int blockSize = 2*(blockSize_ >= 1 ? blockSize_ : 1) + 1; // 3,5,7,...,61
int type = type_; // THRESH_BINARY, THRESH_BINARY_INV,
imshow("Destination", dst); // THRESH_TRUNC, THRESH_TOZERO, THRESH_TOZERO_INV
Mat dst;
niBlackThreshold(src, dst, 255, type, blockSize, k);
imshow("Niblack", dst);
} }
...@@ -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_64F, Size(blockSize, blockSize),
Point(-1,-1), true, BORDER_REPLICATE );
sqrBoxFilter( src, sqmean, CV_64F, 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