diff --git a/src/core/filters/output/OutputGenerator.cpp b/src/core/filters/output/OutputGenerator.cpp index f2f9848e3..423027e77 100644 --- a/src/core/filters/output/OutputGenerator.cpp +++ b/src/core/filters/output/OutputGenerator.cpp @@ -2241,7 +2241,7 @@ BinaryImage OutputGenerator::Processor::binarize(const QImage& image) const { QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); - binarized = binarizeEdgePlus(image, windowsSize, thresholdCoef, thresholdDelta); + binarized = binarizeEdgeDiv(image, windowsSize, thresholdCoef, 0.0, thresholdDelta); break; } case BLURDIV: { @@ -2249,7 +2249,7 @@ BinaryImage OutputGenerator::Processor::binarize(const QImage& image) const { QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); - binarized = binarizeBlurDiv(image, windowsSize, thresholdCoef, thresholdDelta); + binarized = binarizeEdgeDiv(image, windowsSize, 0.0, thresholdCoef, thresholdDelta); break; } case EDGEDIV: { @@ -2257,7 +2257,7 @@ BinaryImage OutputGenerator::Processor::binarize(const QImage& image) const { QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); - binarized = binarizeEdgeDiv(image, windowsSize, thresholdCoef, thresholdDelta); + binarized = binarizeEdgeDiv(image, windowsSize, thresholdCoef, thresholdCoef, thresholdDelta); break; } } diff --git a/src/imageproc/Binarize.cpp b/src/imageproc/Binarize.cpp index 08806c9f6..86b86fe6c 100644 --- a/src/imageproc/Binarize.cpp +++ b/src/imageproc/Binarize.cpp @@ -201,131 +201,7 @@ BinaryImage binarizeWolf(const QImage& src, return bwImg; } // binarizeWolf -BinaryImage binarizeEdgePlus(const QImage& src, const QSize windowSize, const double k, const double delta) { - if (windowSize.isEmpty()) { - throw std::invalid_argument("binarizeEdgePlus: invalid windowSize"); - } - - if (src.isNull()) { - return BinaryImage(); - } - - QImage gray(toGrayscale(src)); - const int w = gray.width(); - const int h = gray.height(); - - IntegralImage integralImage(w, h); - - uint8_t* grayLine = gray.bits(); - const int grayBpl = gray.bytesPerLine(); - - for (int y = 0; y < h; ++y) { - integralImage.beginRow(); - for (int x = 0; x < w; ++x) { - const uint32_t pixel = grayLine[x]; - integralImage.push(pixel); - } - grayLine += grayBpl; - } - - const int windowLowerHalf = windowSize.height() >> 1; - const int windowUpperHalf = windowSize.height() - windowLowerHalf; - const int windowLeftHalf = windowSize.width() >> 1; - const int windowRightHalf = windowSize.width() - windowLeftHalf; - - grayLine = gray.bits(); - for (int y = 0; y < h; ++y) { - const int top = std::max(0, y - windowLowerHalf); - const int bottom = std::min(h, y + windowUpperHalf); // exclusive - for (int x = 0; x < w; ++x) { - const int left = std::max(0, x - windowLeftHalf); - const int right = std::min(w, x + windowRightHalf); // exclusive - const int area = (bottom - top) * (right - left); - assert(area > 0); // because windowSize > 0 and w > 0 and h > 0 - const QRect rect(left, top, right - left, bottom - top); - const double windowSum = integralImage.sum(rect); - - const double rArea = 1.0 / area; - const double mean = windowSum * rArea; - const double origin = grayLine[x]; - // edge = I / blur (shift = -0.5) {0.0 .. >1.0}, mean value = 0.5 - const double edge = (origin + 1) / (mean + 1) - 0.5; - // edgeplus = I * edge, mean value = 0.5 * mean(I) - const double edgeplus = origin * edge; - // return k * edgeplus + (1 - k) * I - double retval = k * edgeplus + (1.0 - k) * origin; - // trim value {0..255} - retval = (retval < 0.0) ? 0.0 : (retval < 255.0) ? retval : 255.0; - grayLine[x] = (int) retval; - } - grayLine += grayBpl; - } - return BinaryImage(gray, (BinaryThreshold::otsuThreshold(gray) + delta)); -} // binarizeEdgePlus - -BinaryImage binarizeBlurDiv(const QImage& src, const QSize windowSize, const double k, const double delta) { - if (windowSize.isEmpty()) { - throw std::invalid_argument("binarizeBlurDiv: invalid windowSize"); - } - - if (src.isNull()) { - return BinaryImage(); - } - - QImage gray(toGrayscale(src)); - const int w = gray.width(); - const int h = gray.height(); - - IntegralImage integralImage(w, h); - - uint8_t* grayLine = gray.bits(); - const int grayBpl = gray.bytesPerLine(); - - for (int y = 0; y < h; ++y) { - integralImage.beginRow(); - for (int x = 0; x < w; ++x) { - const uint32_t pixel = grayLine[x]; - integralImage.push(pixel); - } - grayLine += grayBpl; - } - - const int windowLowerHalf = windowSize.height() >> 1; - const int windowUpperHalf = windowSize.height() - windowLowerHalf; - const int windowLeftHalf = windowSize.width() >> 1; - const int windowRightHalf = windowSize.width() - windowLeftHalf; - - grayLine = gray.bits(); - for (int y = 0; y < h; ++y) { - const int top = std::max(0, y - windowLowerHalf); - const int bottom = std::min(h, y + windowUpperHalf); // exclusive - for (int x = 0; x < w; ++x) { - const int left = std::max(0, x - windowLeftHalf); - const int right = std::min(w, x + windowRightHalf); // exclusive - const int area = (bottom - top) * (right - left); - assert(area > 0); // because windowSize > 0 and w > 0 and h > 0 - const QRect rect(left, top, right - left, bottom - top); - const double windowSum = integralImage.sum(rect); - - const double rArea = 1.0 / area; - const double mean = windowSum * rArea; - const double origin = grayLine[x]; - // edge = blur / I (shift = -0.5) {0.0 .. >1.0}, mean value = 0.5 - const double edge = (mean + 1) / (origin + 1) - 0.5; - // edgenorm = edge * k + max * (1 - k), mean value = {0.5 .. 1.0} * mean(I) - const double edgenorm = k * edge + (1.0 - k); - // return I / edgenorm - double retval = (edgenorm > 0.0) ? (origin / edgenorm) : origin; - // trim value {0..255} - retval = (retval < 0.0) ? 0.0 : (retval < 255.0) ? retval : 255.0; - grayLine[x] = (int) retval; - } - grayLine += grayBpl; - } - return BinaryImage(gray, (BinaryThreshold::otsuThreshold(gray) + delta)); -} // binarizeBlurDiv - -BinaryImage binarizeEdgeDiv(const QImage& src, const QSize windowSize, const double k, const double delta) { +BinaryImage binarizeEdgeDiv(const QImage& src, const QSize windowSize, const double kep, const double kbd, const double delta) { if (windowSize.isEmpty()) { throw std::invalid_argument("binarizeBlurDiv: invalid windowSize"); } @@ -372,20 +248,27 @@ BinaryImage binarizeEdgeDiv(const QImage& src, const QSize windowSize, const dou const double rArea = 1.0 / area; const double mean = windowSum * rArea; const double origin = grayLine[x]; - // EdgePlus - // edge = I / blur (shift = -0.5) {0.0 .. >1.0}, mean value = 0.5 - const double edge = (origin + 1) / (mean + 1) - 0.5; - // edgeplus = I * edge, mean value = 0.5 * mean(I) - const double edgeplus = origin * edge; - // return k * edgeplus + (1 - k) * I - double retval = k * edgeplus + (1.0 - k) * origin; - // BlurDiv - // edge = blur / I (shift = -0.5) {0.0 .. >1.0}, mean value = 0.5 - const double edgeinv = (mean + 1) / (retval + 1) - 0.5; - // edgenorm = edge * k + max * (1 - k), mean value = {0.5 .. 1.0} * mean(I) - const double edgenorm = k * edgeinv + (1.0 - k); - // return I / edgenorm - retval = (edgenorm > 0.0) ? (origin / edgenorm) : origin; + double retval = origin; + if (kep > 0.0) + { + // EdgePlus + // edge = I / blur (shift = -0.5) {0.0 .. >1.0}, mean value = 0.5 + const double edge = (retval + 1) / (mean + 1) - 0.5; + // edgeplus = I * edge, mean value = 0.5 * mean(I) + const double edgeplus = origin * edge; + // return k * edgeplus + (1 - k) * I + retval = kep * edgeplus + (1.0 - kep) * origin; + } + if (kbd > 0.0) + { + // BlurDiv + // edge = blur / I (shift = -0.5) {0.0 .. >1.0}, mean value = 0.5 + const double edgeinv = (mean + 1) / (retval + 1) - 0.5; + // edgenorm = edge * k + max * (1 - k), mean value = {0.5 .. 1.0} * mean(I) + const double edgenorm = kbd * edgeinv + (1.0 - kbd); + // return I / edgenorm + retval = (edgenorm > 0.0) ? (origin / edgenorm) : origin; + } // trim value {0..255} retval = (retval < 0.0) ? 0.0 : (retval < 255.0) ? retval : 255.0; grayLine[x] = (int) retval; diff --git a/src/imageproc/Binarize.h b/src/imageproc/Binarize.h index 33d7d0a5b..e3b470a21 100644 --- a/src/imageproc/Binarize.h +++ b/src/imageproc/Binarize.h @@ -61,26 +61,12 @@ BinaryImage binarizeWolf(const QImage& src, double k = 0.3, double delta = 0.0); -/** - * \brief Image binarization using EdgePlus local/global thresholding method. - * - * EdgePlus, zvezdochiot 2023. "Adaptive/global document image binarization". - */ -BinaryImage binarizeEdgePlus(const QImage& src, QSize windowSize, double k = 0.34, double delta = 0.0); - -/** - * \brief Image binarization using BlurDiv local/global thresholding method. - * - * BlurDiv, zvezdochiot 2023. "Adaptive/global document image binarization". - */ -BinaryImage binarizeBlurDiv(const QImage& src, QSize windowSize, double k = 0.34, double delta = 0.0); - /** * \brief Image binarization using EdgeDiv (EdgePlus & BlurDiv) local/global thresholding method. * * EdgeDiv, zvezdochiot 2023. "Adaptive/global document image binarization". */ -BinaryImage binarizeEdgeDiv(const QImage& src, QSize windowSize, double k = 0.34, double delta = 0.0); +BinaryImage binarizeEdgeDiv(const QImage& src, QSize windowSize, double kep = 0.0, double kdb = 0.0, double delta = 0.0); BinaryImage peakThreshold(const QImage& image); } // namespace imageproc