@@ -52,7 +52,17 @@ BinaryImage binarizeMokji(
5252 return BinaryImage (src, threshold);
5353}
5454
55- BinaryImage binarizeSauvola (QImage const & src, QSize const window_size, double const k, int const delta)
55+ /*
56+ * sauvola = mean * (1.0 - k * (1.0 - stderr / 128.0)), k = 0.34
57+ * modification by zvezdochiot:
58+ * sauvola = base * (1.0 - k * (1.0 - (frac_s + frac_d))), k = 0.34, delta = 0
59+ * base = mean, frac_s = stderr / range, frac_d = delta / range, range = 128.0
60+ */
61+ BinaryImage binarizeSauvola (
62+ QImage const & src,
63+ QSize const window_size,
64+ double const k,
65+ int const delta)
5666{
5767 if (window_size.isEmpty ()) {
5868 throw std::invalid_argument (" binarizeSauvola: invalid window_size" );
@@ -102,6 +112,8 @@ BinaryImage binarizeSauvola(QImage const& src, QSize const window_size, double c
102112 uint32_t * bw_line = bw_img.data ();
103113 int const bw_wpl = bw_img.wordsPerLine ();
104114
115+ double const range = 128.0 ;
116+ double const frac_d = (double ) delta / range;
105117 uint32_t const msb = uint32_t (1 ) << 31 ;
106118 gray_line = gray.bits ();
107119 for (int y = 0 ; y < h; ++y) {
@@ -124,12 +136,13 @@ BinaryImage binarizeSauvola(QImage const& src, QSize const window_size, double c
124136
125137 long double const variance = sqmean - mean * mean;
126138 long double const deviation = sqrt (fabs (variance));
139+ long double const frac_s = deviation / range;
127140
128- long double const threshold = mean * (1.0 + k * (deviation / 128 .0 - 1.0 ));
141+ long double const threshold = mean * (1.0 - k * (1 .0 - (frac_s + frac_d) ));
129142
130143 uint32_t const mask = msb >> (x & 31 );
131144 int const origin = gray_line[x];
132- if (origin < ( threshold + delta) )
145+ if (origin < threshold)
133146 {
134147 // black
135148 bw_line[x >> 5 ] |= mask;
@@ -147,10 +160,19 @@ BinaryImage binarizeSauvola(QImage const& src, QSize const window_size, double c
147160 return bw_img;
148161}
149162
163+ /*
164+ * wolf = mean - k * (mean - min_v) * (1.0 - stderr / stdmax), k = 0.3
165+ * modification by zvezdochiot:
166+ * wolf = base * (1.0 - k * (1.0 - (frac_sn + frac_d))) + min_v, k = 0.3, delta = 0
167+ * base = mean - min_v, frac_sn = stderr / stdmax, frac_d = delta / range, range = 128.0
168+ */
150169BinaryImage binarizeWolf (
151- QImage const & src, QSize const window_size,
152- unsigned char const lower_bound, unsigned char const upper_bound,
153- double const k, int const delta)
170+ QImage const & src,
171+ QSize const window_size,
172+ unsigned char const lower_bound,
173+ unsigned char const upper_bound,
174+ double const k,
175+ int const delta)
154176{
155177 if (window_size.isEmpty ())
156178 {
@@ -199,7 +221,7 @@ BinaryImage binarizeWolf(
199221 std::vector<float > means (w * h, 0 );
200222 std::vector<float > deviations (w * h, 0 );
201223
202- long double max_deviation = 0 ;
224+ long double max_deviation = 1. 0 ;
203225
204226 for (int y = 0 ; y < h; y++)
205227 {
@@ -239,6 +261,8 @@ BinaryImage binarizeWolf(
239261 uint32_t * bw_line = bw_img.data ();
240262 int const bw_wpl = bw_img.wordsPerLine ();
241263
264+ double const range = 128.0 ;
265+ double const frac_d = (double ) delta / range;
242266 uint32_t const msb = uint32_t (1 ) << 31 ;
243267 gray_line = gray.bits ();
244268 for (int y = 0 ; y < h; y++)
@@ -247,12 +271,171 @@ BinaryImage binarizeWolf(
247271 {
248272 float const mean = means[y * w + x];
249273 float const deviation = deviations[y * w + x];
250- long double const a = 1.0 - deviation / max_deviation;
251- long double const threshold = mean - k * a * (mean - min_gray_level);
274+
275+ long double const base = mean - min_gray_level;
276+ long double const frac_sn = deviation / max_deviation;
277+ long double const threshold = base * (1.0 - k * (1.0 - (frac_sn + frac_d))) + min_gray_level;
278+
279+ uint32_t const mask = msb >> (x & 31 );
280+ unsigned char const origin = gray_line[x];
281+ if (origin < lower_bound || (origin <= upper_bound && (int ) origin < threshold))
282+ {
283+ // black
284+ bw_line[x >> 5 ] |= mask;
285+ } else {
286+ // white
287+ bw_line[x >> 5 ] &= ~mask;
288+ }
289+ }
290+ gray_line += gray_bpl;
291+ bw_line += bw_wpl;
292+ }
293+
294+ return bw_img;
295+ }
296+
297+ /*
298+ * window = mean * (1 - k * md / kd), k = 1.0
299+ * where:
300+ * md = (mean + 1) / (meanFull + deviation + 1)
301+ * kd = 1 + kdm * kds
302+ * kdm = (2 * meanFull + 1) / (deviation + 1)
303+ * deviationD = deviationMax - deviationMin
304+ * kds = (deviation - deviationMin) / deviationD if deviationD > 0, 1 if other
305+ * modification by zvezdochiot:
306+ * md = (mean + 1 - delta) / (meanFull + deviation + 1), delta = 0
307+ */
308+ BinaryImage binarizeWindow (
309+ QImage const & src,
310+ QSize const window_size,
311+ unsigned char const lower_bound,
312+ unsigned char const upper_bound,
313+ double const k,
314+ int const delta)
315+ {
316+ if (window_size.isEmpty ())
317+ {
318+ throw std::invalid_argument (" binarizeWolf: invalid window_size" );
319+ }
320+
321+ if (src.isNull ())
322+ {
323+ return BinaryImage ();
324+ }
325+ int const w = src.width ();
326+ int const h = src.height ();
327+
328+ QImage gray (toGrayscale (src));
329+ if (gray.isNull ())
330+ {
331+ return BinaryImage ();
332+ }
333+ uint8_t const * gray_line = gray.bits ();
334+ int const gray_bpl = gray.bytesPerLine ();
335+
336+ IntegralImage<uint32_t > integral_image (w, h);
337+ IntegralImage<uint64_t > integral_sqimage (w, h);
338+
339+ uint32_t min_gray_level = 255 ;
340+
341+ for (int y = 0 ; y < h; y++)
342+ {
343+ integral_image.beginRow ();
344+ integral_sqimage.beginRow ();
345+ for (int x = 0 ; x < w; x++)
346+ {
347+ uint32_t const pixel = gray_line[x];
348+ integral_image.push (pixel);
349+ integral_sqimage.push (pixel * pixel);
350+ min_gray_level = std::min (min_gray_level, pixel);
351+ }
352+ gray_line += gray_bpl;
353+ }
354+
355+ int const window_lower_half = window_size.height () >> 1 ;
356+ int const window_upper_half = window_size.height () - window_lower_half;
357+ int const window_left_half = window_size.width () >> 1 ;
358+ int const window_right_half = window_size.width () - window_left_half;
359+
360+ int const areaFull = w * h;
361+ assert (areaFull > 0 ); // because w > 0 and h > 0
362+ long double const meanFull = integral_image.sum (QRect (0 , 0 , w, h)) / areaFull;
363+ long double deviationMax = 0.0 ;
364+ long double deviationMin = 256.0 ;
365+
366+ for (int y = 0 ; y < h; y++)
367+ {
368+ int const top = (y > window_lower_half) ? (y -window_lower_half) : 0 ;;
369+ int const bottom = ((y + window_upper_half) < h) ? (y + window_upper_half) : h;
370+
371+ for (int x = 0 ; x < w; x++)
372+ {
373+ int const left = (x > window_left_half) ? (x - window_left_half) : 0 ;;
374+ int const right = ((x + window_right_half) < w) ? (x + window_right_half) : w;
375+ int const area = (bottom - top) * (right - left);
376+ assert (area > 0 ); // because window_size > 0 and w > 0 and h > 0
252377
378+ QRect const rect (left, top, right - left, bottom - top);
379+ long double const window_sum = integral_image.sum (rect);
380+ long double const window_sqsum = integral_sqimage.sum (rect);
381+
382+ long double const r_area = 1.0 / area;
383+ long double const mean = window_sum * r_area;
384+ long double const sqmean = window_sqsum * r_area;
385+
386+ long double const variance = sqmean - mean * mean;
387+ long double const deviation = sqrt (fabs (variance));
388+
389+ deviationMax = (deviation > deviationMax) ? deviation : deviationMax;
390+ deviationMin = (deviation < deviationMin) ? deviation : deviationMin;
391+ }
392+ }
393+
394+ long double deviationD = (deviationMax > deviationMin) ? (deviationMax - deviationMin) : 1.0 ;
395+
396+ BinaryImage bw_img (w, h);
397+ if (bw_img.isNull ())
398+ {
399+ return BinaryImage ();
400+ }
401+ uint32_t * bw_line = bw_img.data ();
402+ int const bw_wpl = bw_img.wordsPerLine ();
403+
404+ uint32_t const msb = uint32_t (1 ) << 31 ;
405+ gray_line = gray.bits ();
406+ for (int y = 0 ; y < h; y++)
407+ {
408+ int const top = (y > window_lower_half) ? (y -window_lower_half) : 0 ;;
409+ int const bottom = ((y + window_upper_half) < h) ? (y + window_upper_half) : h;
410+
411+ for (int x = 0 ; x < w; x++)
412+ {
413+ int const left = (x > window_left_half) ? (x - window_left_half) : 0 ;;
414+ int const right = ((x + window_right_half) < w) ? (x + window_right_half) : w;
415+ int const area = (bottom - top) * (right - left);
416+ assert (area > 0 ); // because window_size > 0 and w > 0 and h > 0
417+
418+ QRect const rect (left, top, right - left, bottom - top);
419+ long double const window_sum = integral_image.sum (rect);
420+ long double const window_sqsum = integral_sqimage.sum (rect);
421+
422+ long double const r_area = 1.0 / area;
423+ long double const mean = window_sum * r_area;
424+ long double const sqmean = window_sqsum * r_area;
425+
426+ long double const variance = sqmean - mean * mean;
427+ long double const deviation = sqrt (fabs (variance));
428+
429+ long double const md = (mean + 1.0 - delta) / (meanFull + deviation + 1.0 );
430+ long double const kdm = (meanFull + meanFull + 1.0 ) / (deviation + 1.0 );
431+ long double const kds = (deviation - deviationMin) / deviationD;
432+ long double const kd = 1.0 + kdm * kds;
433+
434+ long double const threshold = mean * (1.0 - k * 3.0 * md / kd);
435+
253436 uint32_t const mask = msb >> (x & 31 );
254437 unsigned char const origin = gray_line[x];
255- if (origin < lower_bound || (origin <= upper_bound && (int ) origin < ( threshold + delta) ))
438+ if (origin < lower_bound || (origin <= upper_bound && (int ) origin < threshold))
256439 {
257440 // black
258441 bw_line[x >> 5 ] |= mask;
@@ -354,6 +537,11 @@ BinaryImage binarizeBradley(
354537 return bw_img;
355538} // binarizeBradley
356539
540+ /*
541+ * grad = mean * k + meanG * (1.0 - k), meanG = mean(I * G) / mean(G), G = |I - mean|, k = 0.75
542+ * modification by zvezdochiot:
543+ * mean = mean + delta, delta = 0
544+ */
357545BinaryImage binarizeGrad (
358546 QImage const & src, QSize const window_size,
359547 double const k, int const delta)
@@ -418,7 +606,7 @@ BinaryImage binarizeGrad(
418606 double const window_sum = integral_image.sum (rect);
419607
420608 double const r_area = 1.0 / area;
421- double const mean = window_sum * r_area + 0.5 ;
609+ double const mean = window_sum * r_area + 0.5 + delta ;
422610 int const imean = (int ) ((mean < 0.0 ) ? 0.0 : (mean < 255.0 ) ? mean : 255.0 );
423611 gmean_line[x] = imean;
424612 }
@@ -471,7 +659,7 @@ BinaryImage binarizeGrad(
471659 double const mean = gmean_line[x];
472660 double const threshold = mean_grad + mean * k;
473661 uint32_t const mask = msb >> (x & 31 );
474- if (origin < ( threshold + delta) )
662+ if (origin < threshold)
475663 {
476664 // black
477665 bw_line[x >> 5 ] |= mask;
@@ -489,6 +677,9 @@ BinaryImage binarizeGrad(
489677 return bw_img;
490678} // binarizeGrad
491679
680+ /*
681+ * edgediv == EdgeDiv image prefilter before the Otsu threshold
682+ */
492683BinaryImage binarizeEdgeDiv (
493684 QImage const & src, QSize const window_size,
494685 double const kep, double const kbd, int const delta)
0 commit comments