diff --git a/src/app/DefaultParamsDialog.cpp b/src/app/DefaultParamsDialog.cpp index 29018ffc8..a011089ba 100644 --- a/src/app/DefaultParamsDialog.cpp +++ b/src/app/DefaultParamsDialog.cpp @@ -48,6 +48,9 @@ DefaultParamsDialog::DefaultParamsDialog(QWidget* parent) thresholdMethodBox->addItem(tr("Otsu"), OTSU); thresholdMethodBox->addItem(tr("Sauvola"), SAUVOLA); thresholdMethodBox->addItem(tr("Wolf"), WOLF); + thresholdMethodBox->addItem(tr("EdgePlus"), EDGEPLUS); + thresholdMethodBox->addItem(tr("BlurDiv"), BLURDIV); + thresholdMethodBox->addItem(tr("EdgeDiv"), EDGEDIV); pictureShapeSelector->addItem(tr("Off"), OFF_SHAPE); pictureShapeSelector->addItem(tr("Free"), FREE_SHAPE); @@ -662,7 +665,7 @@ std::unique_ptr DefaultParamsDialog::buildParams() const { blackWhiteOptions.setBinarizationMethod(binarizationMethod); blackWhiteOptions.setThresholdAdjustment(thresholdSlider->value()); blackWhiteOptions.setSauvolaCoef(sauvolaCoef->value()); - if (binarizationMethod == SAUVOLA) { + if (binarizationMethod == SAUVOLA || binarizationMethod == EDGEPLUS || binarizationMethod == BLURDIV || binarizationMethod == EDGEDIV) { blackWhiteOptions.setWindowSize(sauvolaWindowSize->value()); } else if (binarizationMethod == WOLF) { blackWhiteOptions.setWindowSize(wolfWindowSize->value()); diff --git a/src/app/MainWindow.cpp b/src/app/MainWindow.cpp index 2d5b755ab..dc7a3d4b4 100644 --- a/src/app/MainWindow.cpp +++ b/src/app/MainWindow.cpp @@ -2080,8 +2080,9 @@ void MainWindow::execGotoPageDialog() { bool ok; const PageSequence pageSequence = m_thumbSequence->toPageSequence(); const PageId& selectionLeader = m_thumbSequence->selectionLeader().id(); - int pageNumber = QInputDialog::getInt(this, tr("Go To Page"), tr("Enter the page number:"), - pageSequence.pageNo(selectionLeader) + 1, 1, pageSequence.numPages(), 1, &ok); + int pageNumber + = QInputDialog::getInt(this, tr("Go To Page"), tr("Enter the page number:"), + pageSequence.pageNo(selectionLeader) + 1, 1, (int) (pageSequence.numPages()), 1, &ok); if (ok) { const PageId& newSelectionLeader = pageSequence.pageAt(pageNumber - 1).id(); if (selectionLeader != newSelectionLeader) { diff --git a/src/app/RelinkablePathVisualization.cpp b/src/app/RelinkablePathVisualization.cpp index 9d753d1e6..93f2f4c0a 100644 --- a/src/app/RelinkablePathVisualization.cpp +++ b/src/app/RelinkablePathVisualization.cpp @@ -53,7 +53,7 @@ void RelinkablePathVisualization::clear() { void RelinkablePathVisualization::setPath(const RelinkablePath& path, bool clickable) { clear(); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto opt = QString::SkipEmptyParts; #else auto opt = Qt::SkipEmptyParts; diff --git a/src/core/Application.cpp b/src/core/Application.cpp index c3446018c..c6f5d21a0 100644 --- a/src/core/Application.cpp +++ b/src/core/Application.cpp @@ -58,7 +58,7 @@ std::list Application::getLanguagesList() const { } void Application::initTranslations() { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto opt = QString::SkipEmptyParts; #else auto opt = Qt::SkipEmptyParts; diff --git a/src/core/BackgroundTask.h b/src/core/BackgroundTask.h index 0b9ac8c51..a05fbfe51 100644 --- a/src/core/BackgroundTask.h +++ b/src/core/BackgroundTask.h @@ -27,7 +27,7 @@ class BackgroundTask : public AbstractCommand, public TaskStatu Type type() const { return m_type; } void cancel() override { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 m_cancelFlag.store(1); #else m_cancelFlag.storeRelaxed(1); @@ -35,7 +35,7 @@ class BackgroundTask : public AbstractCommand, public TaskStatu } bool isCancelled() const override { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 return m_cancelFlag.load() != 0; #else return m_cancelFlag.loadRelaxed() != 0; diff --git a/src/core/RelinkablePath.cpp b/src/core/RelinkablePath.cpp index 6950525d0..62f2a4e11 100644 --- a/src/core/RelinkablePath.cpp +++ b/src/core/RelinkablePath.cpp @@ -12,7 +12,7 @@ QString RelinkablePath::normalize(const QString& path) { frontSlashes.replace(QChar('\\'), QLatin1String("/")); QStringList newComponents; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto opt = QString::KeepEmptyParts; #else auto opt = Qt::KeepEmptyParts; diff --git a/src/core/filters/output/BlackWhiteOptions.cpp b/src/core/filters/output/BlackWhiteOptions.cpp index 4f81e9091..235789cfe 100644 --- a/src/core/filters/output/BlackWhiteOptions.cpp +++ b/src/core/filters/output/BlackWhiteOptions.cpp @@ -72,6 +72,12 @@ BinarizationMethod BlackWhiteOptions::parseBinarizationMethod(const QString& str return WOLF; } else if (str == "sauvola") { return SAUVOLA; + } else if (str == "edgeplus") { + return EDGEPLUS; + } else if (str == "blurdiv") { + return BLURDIV; + } else if (str == "edgediv") { + return EDGEDIV; } else { return OTSU; } @@ -89,6 +95,15 @@ QString BlackWhiteOptions::formatBinarizationMethod(BinarizationMethod type) { case WOLF: str = "wolf"; break; + case EDGEPLUS: + str = "edgeplus"; + break; + case BLURDIV: + str = "blurdiv"; + break; + case EDGEDIV: + str = "edgediv"; + break; } return str; } @@ -129,4 +144,4 @@ bool BlackWhiteOptions::ColorSegmenterOptions::operator==(const BlackWhiteOption bool BlackWhiteOptions::ColorSegmenterOptions::operator!=(const BlackWhiteOptions::ColorSegmenterOptions& other) const { return !(*this == other); } -} // namespace output \ No newline at end of file +} // namespace output diff --git a/src/core/filters/output/BlackWhiteOptions.h b/src/core/filters/output/BlackWhiteOptions.h index 90ba63883..2709b6428 100644 --- a/src/core/filters/output/BlackWhiteOptions.h +++ b/src/core/filters/output/BlackWhiteOptions.h @@ -9,7 +9,7 @@ class QDomDocument; class QDomElement; namespace output { -enum BinarizationMethod { OTSU, SAUVOLA, WOLF }; +enum BinarizationMethod { OTSU, SAUVOLA, WOLF, EDGEPLUS, BLURDIV, EDGEDIV }; class BlackWhiteOptions { public: diff --git a/src/core/filters/output/DewarpingView.cpp b/src/core/filters/output/DewarpingView.cpp index 842d85f15..22eaeaaf1 100644 --- a/src/core/filters/output/DewarpingView.cpp +++ b/src/core/filters/output/DewarpingView.cpp @@ -214,7 +214,7 @@ void DewarpingView::onPaint(QPainter& painter, const InteractionState& interacti const dewarping::Curve& bottomCurve = m_distortionModel.bottomCurve(); painter.drawLine(topCurve.polyline().front(), bottomCurve.polyline().front()); painter.drawLine(topCurve.polyline().back(), bottomCurve.polyline().back()); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 painter.drawPolyline(QVector::fromStdVector(topCurve.polyline())); painter.drawPolyline(QVector::fromStdVector(bottomCurve.polyline())); #else diff --git a/src/core/filters/output/OptionsWidget.cpp b/src/core/filters/output/OptionsWidget.cpp index 663f92ded..9111a7c28 100644 --- a/src/core/filters/output/OptionsWidget.cpp +++ b/src/core/filters/output/OptionsWidget.cpp @@ -42,6 +42,9 @@ OptionsWidget::OptionsWidget(std::shared_ptr settings, const PageSelec thresholdMethodBox->addItem(tr("Otsu"), OTSU); thresholdMethodBox->addItem(tr("Sauvola"), SAUVOLA); thresholdMethodBox->addItem(tr("Wolf"), WOLF); + thresholdMethodBox->addItem(tr("EdgePlus"), EDGEPLUS); + thresholdMethodBox->addItem(tr("BlurDiv"), BLURDIV); + thresholdMethodBox->addItem(tr("EdgeDiv"), EDGEDIV); fillingColorBox->addItem(tr("Background"), FILL_BACKGROUND); fillingColorBox->addItem(tr("White"), FILL_WHITE); @@ -51,6 +54,12 @@ OptionsWidget::OptionsWidget(std::shared_ptr settings, const PageSelec QPointer sauvolaBinarizationOptionsWidget = new SauvolaBinarizationOptionsWidget(m_settings); QPointer wolfBinarizationOptionsWidget = new WolfBinarizationOptionsWidget(m_settings); + QPointer edgeplusBinarizationOptionsWidget + = new SauvolaBinarizationOptionsWidget(m_settings); + QPointer blurdivBinarizationOptionsWidget + = new SauvolaBinarizationOptionsWidget(m_settings); + QPointer edgedivBinarizationOptionsWidget + = new SauvolaBinarizationOptionsWidget(m_settings); while (binarizationOptions->count() != 0) { binarizationOptions->removeWidget(binarizationOptions->widget(0)); @@ -58,6 +67,9 @@ OptionsWidget::OptionsWidget(std::shared_ptr settings, const PageSelec addBinarizationOptionsWidget(otsuBinarizationOptionsWidget); addBinarizationOptionsWidget(sauvolaBinarizationOptionsWidget); addBinarizationOptionsWidget(wolfBinarizationOptionsWidget); + addBinarizationOptionsWidget(edgeplusBinarizationOptionsWidget); + addBinarizationOptionsWidget(blurdivBinarizationOptionsWidget); + addBinarizationOptionsWidget(edgedivBinarizationOptionsWidget); updateBinarizationOptionsDisplay(binarizationOptions->currentIndex()); pictureShapeSelector->addItem(tr("Off"), OFF_SHAPE); @@ -983,4 +995,4 @@ void OptionsWidget::applyProcessingParamsConfirmed(const std::set& pages void OptionsWidget::updateProcessingDisplay() { blackOnWhiteCB->setChecked(m_settings->getParams(m_pageId).isBlackOnWhite()); } -} // namespace output \ No newline at end of file +} // namespace output diff --git a/src/core/filters/output/OutputGenerator.cpp b/src/core/filters/output/OutputGenerator.cpp index 840bb0b49..423027e77 100644 --- a/src/core/filters/output/OutputGenerator.cpp +++ b/src/core/filters/output/OutputGenerator.cpp @@ -2219,19 +2219,45 @@ BinaryImage OutputGenerator::Processor::binarize(const QImage& image) const { break; } case SAUVOLA: { + double thresholdDelta = blackWhiteOptions.thresholdAdjustment(); QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); - double sauvolaCoef = blackWhiteOptions.getSauvolaCoef(); + double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); - binarized = binarizeSauvola(image, windowsSize, sauvolaCoef); + binarized = binarizeSauvola(image, windowsSize, thresholdCoef, thresholdDelta); break; } case WOLF: { + double thresholdDelta = blackWhiteOptions.thresholdAdjustment(); QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); auto lowerBound = (unsigned char) blackWhiteOptions.getWolfLowerBound(); auto upperBound = (unsigned char) blackWhiteOptions.getWolfUpperBound(); - double wolfCoef = blackWhiteOptions.getWolfCoef(); + double thresholdCoef = blackWhiteOptions.getWolfCoef(); - binarized = binarizeWolf(image, windowsSize, lowerBound, upperBound, wolfCoef); + binarized = binarizeWolf(image, windowsSize, lowerBound, upperBound, thresholdCoef, thresholdDelta); + break; + } + case EDGEPLUS: { + double thresholdDelta = blackWhiteOptions.thresholdAdjustment(); + QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); + double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); + + binarized = binarizeEdgeDiv(image, windowsSize, thresholdCoef, 0.0, thresholdDelta); + break; + } + case BLURDIV: { + double thresholdDelta = blackWhiteOptions.thresholdAdjustment(); + QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); + double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); + + binarized = binarizeEdgeDiv(image, windowsSize, 0.0, thresholdCoef, thresholdDelta); + break; + } + case EDGEDIV: { + double thresholdDelta = blackWhiteOptions.thresholdAdjustment(); + QSize windowsSize = QSize(blackWhiteOptions.getWindowSize(), blackWhiteOptions.getWindowSize()); + double thresholdCoef = blackWhiteOptions.getSauvolaCoef(); + + binarized = binarizeEdgeDiv(image, windowsSize, thresholdCoef, thresholdCoef, thresholdDelta); break; } } diff --git a/src/core/filters/output/SauvolaBinarizationOptionsWidget.cpp b/src/core/filters/output/SauvolaBinarizationOptionsWidget.cpp index cbe8eba72..5c89c24c9 100644 --- a/src/core/filters/output/SauvolaBinarizationOptionsWidget.cpp +++ b/src/core/filters/output/SauvolaBinarizationOptionsWidget.cpp @@ -29,6 +29,15 @@ void SauvolaBinarizationOptionsWidget::updateUi(const PageId& pageId) { updateView(); } +void SauvolaBinarizationOptionsWidget::sauvolaDeltaChanged(double value) { + BlackWhiteOptions opt(m_colorParams.blackWhiteOptions()); + opt.setThresholdAdjustment(value); + m_colorParams.setBlackWhiteOptions(opt); + m_settings->setColorParams(m_pageId, m_colorParams); + + m_delayedStateChanger.start(750); +} + void SauvolaBinarizationOptionsWidget::windowSizeChanged(int value) { BlackWhiteOptions opt(m_colorParams.blackWhiteOptions()); opt.setWindowSize(value); @@ -49,6 +58,7 @@ void SauvolaBinarizationOptionsWidget::sauvolaCoefChanged(double value) { void SauvolaBinarizationOptionsWidget::updateView() { BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); + sauvolaDelta->setValue(blackWhiteOptions.thresholdAdjustment()); windowSize->setValue(blackWhiteOptions.getWindowSize()); sauvolaCoef->setValue(blackWhiteOptions.getSauvolaCoef()); } @@ -60,10 +70,11 @@ void SauvolaBinarizationOptionsWidget::sendStateChanged() { #define CONNECT(...) m_connectionManager.addConnection(connect(__VA_ARGS__)) void SauvolaBinarizationOptionsWidget::setupUiConnections() { + CONNECT(sauvolaDelta, SIGNAL(valueChanged(double)), this, SLOT(sauvolaDeltaChanged(double))); CONNECT(windowSize, SIGNAL(valueChanged(int)), this, SLOT(windowSizeChanged(int))); CONNECT(sauvolaCoef, SIGNAL(valueChanged(double)), this, SLOT(sauvolaCoefChanged(double))); CONNECT(&m_delayedStateChanger, SIGNAL(timeout()), this, SLOT(sendStateChanged())); } #undef CONNECT -} // namespace output \ No newline at end of file +} // namespace output diff --git a/src/core/filters/output/SauvolaBinarizationOptionsWidget.h b/src/core/filters/output/SauvolaBinarizationOptionsWidget.h index 0d7ca35d1..75eabaafe 100644 --- a/src/core/filters/output/SauvolaBinarizationOptionsWidget.h +++ b/src/core/filters/output/SauvolaBinarizationOptionsWidget.h @@ -28,6 +28,8 @@ class SauvolaBinarizationOptionsWidget : public BinarizationOptionsWidget, private slots: + void sauvolaDeltaChanged(double value); + void windowSizeChanged(int value); void sauvolaCoefChanged(double value); diff --git a/src/core/filters/output/SauvolaBinarizationOptionsWidget.ui b/src/core/filters/output/SauvolaBinarizationOptionsWidget.ui index 4dcecf0f9..0e509b68b 100644 --- a/src/core/filters/output/SauvolaBinarizationOptionsWidget.ui +++ b/src/core/filters/output/SauvolaBinarizationOptionsWidget.ui @@ -50,27 +50,57 @@ - - + + - Coef: + Delta: + + + Default value is 0. + + + -100.0 + + + 100.0 + + + 1.0 + + + + + + + Window size: + + + + The dimensions of a pixel neighborhood to consider. - 5 + 3 9999 - + + + + Coef: + + + + Default value is 0.34. @@ -86,13 +116,6 @@ - - - - Window size: - - - diff --git a/src/core/filters/output/WolfBinarizationOptionsWidget.cpp b/src/core/filters/output/WolfBinarizationOptionsWidget.cpp index 1faf46811..ec3e37c79 100644 --- a/src/core/filters/output/WolfBinarizationOptionsWidget.cpp +++ b/src/core/filters/output/WolfBinarizationOptionsWidget.cpp @@ -28,6 +28,15 @@ void WolfBinarizationOptionsWidget::updateUi(const PageId& pageId) { updateView(); } +void WolfBinarizationOptionsWidget::wolfDeltaChanged(double value) { + BlackWhiteOptions opt(m_colorParams.blackWhiteOptions()); + opt.setThresholdAdjustment(value); + m_colorParams.setBlackWhiteOptions(opt); + m_settings->setColorParams(m_pageId, m_colorParams); + + m_delayedStateChanger.start(750); +} + void WolfBinarizationOptionsWidget::windowSizeChanged(int value) { BlackWhiteOptions opt(m_colorParams.blackWhiteOptions()); opt.setWindowSize(value); @@ -66,6 +75,7 @@ void WolfBinarizationOptionsWidget::wolfCoefChanged(double value) { void WolfBinarizationOptionsWidget::updateView() { BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); + wolfDelta->setValue(blackWhiteOptions.thresholdAdjustment()); windowSize->setValue(blackWhiteOptions.getWindowSize()); lowerBound->setValue(blackWhiteOptions.getWolfLowerBound()); upperBound->setValue(blackWhiteOptions.getWolfUpperBound()); @@ -79,6 +89,7 @@ void WolfBinarizationOptionsWidget::sendStateChanged() { #define CONNECT(...) m_connectionManager.addConnection(connect(__VA_ARGS__)) void WolfBinarizationOptionsWidget::setupUiConnections() { + CONNECT(wolfDelta, SIGNAL(valueChanged(double)), this, SLOT(wolfDeltaChanged(double))); CONNECT(windowSize, SIGNAL(valueChanged(int)), this, SLOT(windowSizeChanged(int))); CONNECT(lowerBound, SIGNAL(valueChanged(int)), this, SLOT(lowerBoundChanged(int))); CONNECT(upperBound, SIGNAL(valueChanged(int)), this, SLOT(upperBoundChanged(int))); @@ -87,4 +98,4 @@ void WolfBinarizationOptionsWidget::setupUiConnections() { } #undef CONNECT -} // namespace output \ No newline at end of file +} // namespace output diff --git a/src/core/filters/output/WolfBinarizationOptionsWidget.h b/src/core/filters/output/WolfBinarizationOptionsWidget.h index c365a5407..5f1b9412e 100644 --- a/src/core/filters/output/WolfBinarizationOptionsWidget.h +++ b/src/core/filters/output/WolfBinarizationOptionsWidget.h @@ -27,6 +27,8 @@ class WolfBinarizationOptionsWidget : public BinarizationOptionsWidget, private private slots: + void wolfDeltaChanged(double value); + void windowSizeChanged(int value); void wolfCoefChanged(double value); diff --git a/src/core/filters/output/WolfBinarizationOptionsWidget.ui b/src/core/filters/output/WolfBinarizationOptionsWidget.ui index 369b94cdf..fda026d49 100644 --- a/src/core/filters/output/WolfBinarizationOptionsWidget.ui +++ b/src/core/filters/output/WolfBinarizationOptionsWidget.ui @@ -50,20 +50,30 @@ + + + + Delta: + + + - + - The dimensions of a pixel neighborhood to consider. + Default value is 0. - 5 + -100.0 - 9999 + 100.0 + + + 1.0 - + Window size: @@ -71,6 +81,26 @@ + + + The dimensions of a pixel neighborhood to consider. + + + 3 + + + 9999 + + + + + + + Lower bound: + + + + The minimum possible gray level that can be made white. @@ -83,21 +113,14 @@ - + Upper Bound: - - - - Lower bound: - - - - + The maximum possible gray level that can be made black. @@ -110,14 +133,14 @@ - + Coeff: - + Default value is 0.3. diff --git a/src/core/filters/page_split/ImageView.cpp b/src/core/filters/page_split/ImageView.cpp index 9ab1ea423..fdefe1897 100644 --- a/src/core/filters/page_split/ImageView.cpp +++ b/src/core/filters/page_split/ImageView.cpp @@ -209,7 +209,7 @@ QLineF ImageView::customInscribedCutterLine(const QLineF& line, const QRectF& re QPointF topPt; QPointF bottomPt; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 line.intersect(QLineF(rect.topLeft(), rect.topRight()), &topPt); line.intersect(QLineF(rect.bottomLeft(), rect.bottomRight()), &bottomPt); #else @@ -241,7 +241,7 @@ void ImageView::handleMoveRequest(int lineIdx, int handleIdx, const QPointF& pos QPointF pTopI; QPointF pBottomI; QLineF anotherLine = virtualToWidget().map(m_virtLayout.cutterLine(i)); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 anotherLine.intersect(QLineF(validArea.topLeft(), validArea.topRight()), &pTopI); anotherLine.intersect(QLineF(validArea.bottomLeft(), validArea.bottomRight()), &pBottomI); #else @@ -287,7 +287,7 @@ void ImageView::lineMoveRequest(int lineIdx, QLineF line) { QPointF pTop; QPointF pBottom; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 line.intersect(QLineF(validArea.topLeft(), validArea.topRight()), &pTop); line.intersect(QLineF(validArea.bottomLeft(), validArea.bottomRight()), &pBottom); #else @@ -306,7 +306,7 @@ void ImageView::lineMoveRequest(int lineIdx, QLineF line) { QPointF pTopI; QPointF pBottomI; QLineF anotherLine = virtualToWidget().map(m_virtLayout.cutterLine(i)); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 anotherLine.intersect(QLineF(validArea.topLeft(), validArea.topRight()), &pTopI); anotherLine.intersect(QLineF(validArea.bottomLeft(), validArea.bottomRight()), &pBottomI); #else diff --git a/src/core/filters/page_split/PageLayout.cpp b/src/core/filters/page_split/PageLayout.cpp index bbc72c99c..167bb5a94 100644 --- a/src/core/filters/page_split/PageLayout.cpp +++ b/src/core/filters/page_split/PageLayout.cpp @@ -114,7 +114,7 @@ QLineF PageLayout::inscribedCutterLine(int idx) const { for (int i = 0; i < 4; ++i) { const QLineF polySegment(m_uncutOutline[i], m_uncutOutline[(i + 1) & 3]); QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto is = polySegment.intersect(rawLine, &intersection); #else auto is = polySegment.intersects(rawLine, &intersection); @@ -351,7 +351,7 @@ void PageLayout::ensureSameDirection(const QLineF& line1, QLineF& line2) { */ void PageLayout::maybeAddIntersectionPoint(QPolygonF& poly, const QLineF& line1, const QLineF& line2) { QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto is = line1.intersect(line2, &intersection); #else auto is = line1.intersects(line2, &intersection); diff --git a/src/core/filters/page_split/PageLayoutAdapter.cpp b/src/core/filters/page_split/PageLayoutAdapter.cpp index 897308a30..39c847522 100644 --- a/src/core/filters/page_split/PageLayoutAdapter.cpp +++ b/src/core/filters/page_split/PageLayoutAdapter.cpp @@ -13,7 +13,7 @@ QLineF PageLayoutAdapter::adaptCutter(const QLineF& cutterLine, const QRectF& ne QLineF upperBorder(newRect.topLeft(), newRect.topRight()); QPointF upperIntersection; { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto is = upperBorder.intersect(cutterLine, &upperIntersection); #else auto is = upperBorder.intersects(cutterLine, &upperIntersection); @@ -32,7 +32,7 @@ QLineF PageLayoutAdapter::adaptCutter(const QLineF& cutterLine, const QRectF& ne QLineF lowerBorder(newRect.bottomLeft(), newRect.bottomRight()); QPointF lowerIntersection; { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto is = lowerBorder.intersect(cutterLine, &lowerIntersection); #else auto is = lowerBorder.intersects(cutterLine, &lowerIntersection); @@ -68,7 +68,7 @@ QVector PageLayoutAdapter::adaptCutters(const QVector& cuttersLi QPointF intersection; QLineF cutterLeft = adaptedCutters.at(i - 1); QLineF cutterRight = adaptedCutters.at(i); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto is = cutterLeft.intersect(cutterRight, &intersection); #else auto is = cutterLeft.intersects(cutterRight, &intersection); @@ -111,7 +111,7 @@ void PageLayoutAdapter::correctPageLayoutType(PageLayout* layout) { // if cutter lines match or intersect inside outline (not valid) QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 QLineF::IntersectType intersectType = cutterLine1.intersect(cutterLine2, &intersection); #else QLineF::IntersectType intersectType = cutterLine1.intersects(cutterLine2, &intersection); diff --git a/src/core/interaction/ZoomHandler.cpp b/src/core/interaction/ZoomHandler.cpp index 6c5b85fb5..6378895af 100644 --- a/src/core/interaction/ZoomHandler.cpp +++ b/src/core/interaction/ZoomHandler.cpp @@ -53,7 +53,7 @@ void ZoomHandler::onWheelEvent(QWheelEvent* event, InteractionState& interaction focusPoint = QRectF(m_imageView.rect()).center(); break; case CURSOR: -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 focusPoint = event->pos() + QPointF(0.5, 0.5); #else focusPoint = event->position() + QPointF(0.5, 0.5); diff --git a/src/dewarping/CylindricalSurfaceDewarper.cpp b/src/dewarping/CylindricalSurfaceDewarper.cpp index 0f0fc66b8..df7115d98 100644 --- a/src/dewarping/CylindricalSurfaceDewarper.cpp +++ b/src/dewarping/CylindricalSurfaceDewarper.cpp @@ -341,7 +341,7 @@ void CylindricalSurfaceDewarper::CoupledPolylinesIterator::next1(QPointF& imgPt1 const Vec2d plnPtx(plnPt1[0], plnPt1[1] + 1); const Vec2d imgPtx(m_pln2img(plnPtx)); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto intersect = QLineF(imgPt1, imgPtx).intersect(QLineF(m_nextImgPt2, m_prevImgPt2), &imgPt2); #else auto intersect = QLineF(imgPt1, imgPtx).intersects(QLineF(m_nextImgPt2, m_prevImgPt2), &imgPt2); @@ -364,7 +364,7 @@ void CylindricalSurfaceDewarper::CoupledPolylinesIterator::next2(QPointF& imgPt1 const Vec2d plnPtx(plnPt2[0], plnPt2[1] + 1); const Vec2d imgPtx(m_pln2img(plnPtx)); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto intersect = QLineF(imgPt2, imgPtx).intersect(QLineF(m_nextImgPt1, m_prevImgPt1), &imgPt1); #else auto intersect = QLineF(imgPt2, imgPtx).intersects(QLineF(m_nextImgPt1, m_prevImgPt1), &imgPt1); diff --git a/src/dewarping/DetectVertContentBounds.cpp b/src/dewarping/DetectVertContentBounds.cpp index a6fd4d409..c8ae66da3 100644 --- a/src/dewarping/DetectVertContentBounds.cpp +++ b/src/dewarping/DetectVertContentBounds.cpp @@ -8,7 +8,7 @@ #include #include -#if QT_VERSION_MAJOR > 5 or QT_VERSION_MINOR > 9 +#if QT_VERSION_MAJOR > 5 || QT_VERSION_MINOR > 9 #include #endif #include @@ -247,7 +247,7 @@ QLineF SequentialColumnProcessor::approximateWithLine(std::vector* dbgS // Run RANSAC on the segments. RansacAlgo ransac(segments); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR <= 9 qsrand(0); // Repeatablity is important. #else QRandomGenerator prng(0); // Repeatablity is important. @@ -265,7 +265,7 @@ QLineF SequentialColumnProcessor::approximateWithLine(std::vector* dbgS // Continue with random samples. const int ransacIterations = segments.empty() ? 0 : 200; for (int i = 0; i < ransacIterations; ++i) { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR <= 9 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR <= 9 auto r = qrand(); #else auto r = prng.generate(); @@ -402,7 +402,7 @@ QLineF extendLine(const QLineF& line, int height) { const QLineF topLine(QPointF(0, 0), QPointF(1, 0)); const QLineF bottomLine(QPointF(0, height), QPointF(1, height)); -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 line.intersect(topLine, &topIntersection); line.intersect(bottomLine, &bottomIntersection); #else diff --git a/src/dewarping/DistortionModelBuilder.cpp b/src/dewarping/DistortionModelBuilder.cpp index 8a7d7b451..410ce2f86 100644 --- a/src/dewarping/DistortionModelBuilder.cpp +++ b/src/dewarping/DistortionModelBuilder.cpp @@ -6,7 +6,7 @@ #include #include #include -#if QT_VERSION_MAJOR > 5 or QT_VERSION_MINOR > 9 +#if QT_VERSION_MAJOR > 5 || QT_VERSION_MINOR > 9 #include #endif #include @@ -159,14 +159,14 @@ DistortionModel DistortionModelBuilder::tryBuildModel(DebugImages* dbg, const QI } } // Continue by throwing in some random pairs of lines. -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR <= 9 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR <= 9 qsrand(0); // Repeatablity is important. #else QRandomGenerator prng(0); // Repeatablity is important. #endif int randomPairsRemaining = 10; while (randomPairsRemaining-- > 0) { -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR <= 9 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR <= 9 int i = qrand() % numCurves; int j = qrand() % numCurves; #else @@ -296,7 +296,7 @@ void DistortionModelBuilder::intersectFront(std::deque& polyline, const const QLineF frontSegment(polyline.front(), polyline[1]); QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto intersect = bound.intersect(frontSegment, &intersection); #else auto intersect = bound.intersects(frontSegment, &intersection); @@ -311,7 +311,7 @@ void DistortionModelBuilder::intersectBack(std::deque& polyline, const const QLineF backSegment(polyline[polyline.size() - 2], polyline.back()); QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto intersect = bound.intersect(backSegment, &intersection); #else auto intersect = bound.intersects(backSegment, &intersection); diff --git a/src/dewarping/TextLineTracer.cpp b/src/dewarping/TextLineTracer.cpp index 6316707ac..61c688c12 100644 --- a/src/dewarping/TextLineTracer.cpp +++ b/src/dewarping/TextLineTracer.cpp @@ -440,7 +440,7 @@ void TextLineTracer::findMidLineSeeds(const SEDM& sedm, QLineF midLine, std::vec QLineF TextLineTracer::calcMidLine(const QLineF& line1, const QLineF& line2) { QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto is = line1.intersect(line2, &intersection); #else auto is = line1.intersects(line2, &intersection); diff --git a/src/dewarping/TowardsLineTracer.cpp b/src/dewarping/TowardsLineTracer.cpp index a77f7a38f..f0506ea84 100644 --- a/src/dewarping/TowardsLineTracer.cpp +++ b/src/dewarping/TowardsLineTracer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "NumericTraits.h" #include "SidesOfLine.h" diff --git a/src/foundation/Proximity.cpp b/src/foundation/Proximity.cpp index 8afbad539..c45a597c5 100644 --- a/src/foundation/Proximity.cpp +++ b/src/foundation/Proximity.cpp @@ -29,7 +29,7 @@ Proximity Proximity::pointAndLineSegment(const QPointF& pt, const QLineF& segmen perpendicular.translate(pt); // Calculate intersection. QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 segment.intersect(perpendicular, &intersection); #else segment.intersects(perpendicular, &intersection); diff --git a/src/imageproc/Binarize.cpp b/src/imageproc/Binarize.cpp index 4a5ce679b..86b86fe6c 100644 --- a/src/imageproc/Binarize.cpp +++ b/src/imageproc/Binarize.cpp @@ -22,7 +22,7 @@ BinaryImage binarizeMokji(const QImage& src, const unsigned maxEdgeWidth, const return BinaryImage(src, threshold); } -BinaryImage binarizeSauvola(const QImage& src, const QSize windowSize, const double k) { +BinaryImage binarizeSauvola(const QImage& src, const QSize windowSize, const double k, const double delta) { if (windowSize.isEmpty()) { throw std::invalid_argument("binarizeSauvola: invalid windowSize"); } @@ -41,7 +41,7 @@ BinaryImage binarizeSauvola(const QImage& src, const QSize windowSize, const dou const uint8_t* grayLine = gray.bits(); const int grayBpl = gray.bytesPerLine(); - for (int y = 0; y < h; ++y, grayLine += grayBpl) { + for (int y = 0; y < h; ++y) { integralImage.beginRow(); integralSqimage.beginRow(); for (int x = 0; x < w; ++x) { @@ -49,6 +49,7 @@ BinaryImage binarizeSauvola(const QImage& src, const QSize windowSize, const dou integralImage.push(pixel); integralSqimage.push(pixel * pixel); } + grayLine += grayBpl; } const int windowLowerHalf = windowSize.height() >> 1; @@ -84,7 +85,7 @@ BinaryImage binarizeSauvola(const QImage& src, const QSize windowSize, const dou const uint32_t msb = uint32_t(1) << 31; const uint32_t mask = msb >> (x & 31); - if (int(grayLine[x]) < threshold) { + if (int(grayLine[x]) < (threshold + delta)) { // black bwLine[x >> 5] |= mask; } else { @@ -92,7 +93,6 @@ BinaryImage binarizeSauvola(const QImage& src, const QSize windowSize, const dou bwLine[x >> 5] &= ~mask; } } - grayLine += grayBpl; bwLine += bwWpl; } @@ -103,7 +103,8 @@ BinaryImage binarizeWolf(const QImage& src, const QSize windowSize, const unsigned char lowerBound, const unsigned char upperBound, - const double k) { + const double k, + const double delta) { if (windowSize.isEmpty()) { throw std::invalid_argument("binarizeWolf: invalid windowSize"); } @@ -124,7 +125,7 @@ BinaryImage binarizeWolf(const QImage& src, uint32_t minGrayLevel = 255; - for (int y = 0; y < h; ++y, grayLine += grayBpl) { + for (int y = 0; y < h; ++y) { integralImage.beginRow(); integralSqimage.beginRow(); for (int x = 0; x < w; ++x) { @@ -133,6 +134,7 @@ BinaryImage binarizeWolf(const QImage& src, integralSqimage.push(pixel * pixel); minGrayLevel = std::min(minGrayLevel, pixel); } + grayLine += grayBpl; } const int windowLowerHalf = windowSize.height() >> 1; @@ -176,7 +178,7 @@ BinaryImage binarizeWolf(const QImage& src, const int bwWpl = bwImg.wordsPerLine(); grayLine = gray.bits(); - for (int y = 0; y < h; ++y, grayLine += grayBpl, bwLine += bwWpl) { + for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const float mean = means[y * w + x]; const float deviation = deviations[y * w + x]; @@ -185,7 +187,7 @@ BinaryImage binarizeWolf(const QImage& src, const uint32_t msb = uint32_t(1) << 31; const uint32_t mask = msb >> (x & 31); - if ((grayLine[x] < lowerBound) || ((grayLine[x] <= upperBound) && (int(grayLine[x]) < threshold))) { + if ((grayLine[x] < lowerBound) || ((grayLine[x] <= upperBound) && (int(grayLine[x]) < (threshold + delta)))) { // black bwLine[x >> 5] |= mask; } else { @@ -193,10 +195,89 @@ BinaryImage binarizeWolf(const QImage& src, bwLine[x >> 5] &= ~mask; } } + grayLine += grayBpl; + bwLine += bwWpl; } return bwImg; } // binarizeWolf +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"); + } + + 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]; + 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; + } + grayLine += grayBpl; + } + return BinaryImage(gray, (BinaryThreshold::otsuThreshold(gray) + delta)); +} // binarizeBlurDiv + BinaryImage peakThreshold(const QImage& image) { return BinaryImage(image, BinaryThreshold::peakThreshold(image)); } diff --git a/src/imageproc/Binarize.h b/src/imageproc/Binarize.h index 4f918de6c..e3b470a21 100644 --- a/src/imageproc/Binarize.h +++ b/src/imageproc/Binarize.h @@ -40,7 +40,7 @@ BinaryImage binarizeMokji(const QImage& src, unsigned maxEdgeWidth = 3, unsigned * Sauvola, J. and M. Pietikainen. 2000. "Adaptive document image binarization". * http://www.mediateam.oulu.fi/publications/pdf/24.pdf */ -BinaryImage binarizeSauvola(const QImage& src, QSize windowSize, double k = 0.34); +BinaryImage binarizeSauvola(const QImage& src, QSize windowSize, double k = 0.34, double delta = 0.0); /** * \brief Image binarization using Wolf's local thresholding method. @@ -58,7 +58,15 @@ BinaryImage binarizeWolf(const QImage& src, QSize windowSize, unsigned char lowerBound = 1, unsigned char upperBound = 254, - double k = 0.3); + double k = 0.3, + 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 kep = 0.0, double kdb = 0.0, double delta = 0.0); BinaryImage peakThreshold(const QImage& image); } // namespace imageproc diff --git a/src/imageproc/Posterizer.cpp b/src/imageproc/Posterizer.cpp index bd737ed5c..a0b5a2a35 100644 --- a/src/imageproc/Posterizer.cpp +++ b/src/imageproc/Posterizer.cpp @@ -45,7 +45,7 @@ QVector paletteFromRgb(const QImage& image) { imgLine += imgStride; } - QVector palette(colorSet.size()); + QVector palette((int) (colorSet.size())); std::copy(colorSet.begin(), colorSet.end(), std::back_inserter(palette)); return palette; } @@ -179,7 +179,7 @@ void remapColorsInIndexedImage(QImage& image, const std::unordered_map newColorTable(colorToIndexMap.size()); + QVector newColorTable((int) (colorToIndexMap.size())); for (const auto& colorAndIndex : colorToIndexMap) { newColorTable[colorAndIndex.second] = colorAndIndex.first; } diff --git a/src/math/PolylineIntersector.cpp b/src/math/PolylineIntersector.cpp index 331e1c534..08b893c94 100644 --- a/src/math/PolylineIntersector.cpp +++ b/src/math/PolylineIntersector.cpp @@ -87,7 +87,7 @@ bool PolylineIntersector::intersectsSpan(const QLineF& normal, const QLineF& spa QPointF PolylineIntersector::intersectWithSegment(const QLineF& line, int segment) const { const QLineF segLine(m_polyline[segment], m_polyline[segment + 1]); QPointF intersection; -#if QT_VERSION_MAJOR == 5 and QT_VERSION_MINOR < 14 +#if QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR < 14 auto intersect = line.intersect(segLine, &intersection); #else auto intersect = line.intersects(segLine, &intersection);