mirror of https://github.com/JakubMelka/PDF4QT.git
Issue #107: Conversion algorithm
This commit is contained in:
parent
cf7d65dc82
commit
c5ddb521ed
|
@ -100,6 +100,8 @@ add_library(Pdf4QtLib SHARED
|
||||||
sources/pdfobjecteditorwidget_impl.h
|
sources/pdfobjecteditorwidget_impl.h
|
||||||
sources/pdfdocumentsanitizer.h
|
sources/pdfdocumentsanitizer.h
|
||||||
sources/pdfdocumentsanitizer.cpp
|
sources/pdfdocumentsanitizer.cpp
|
||||||
|
sources/pdfimageconversion.h
|
||||||
|
sources/pdfimageconversion.cpp
|
||||||
cmaps.qrc
|
cmaps.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
#include "pdfimageconversion.h"
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
PDFImageConversion::PDFImageConversion()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFImageConversion::setImage(QImage image)
|
||||||
|
{
|
||||||
|
m_image = std::move(image);
|
||||||
|
m_convertedImage = QImage();
|
||||||
|
m_automaticThreshold = DEFAULT_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFImageConversion::setConversionMethod(ConversionMethod method)
|
||||||
|
{
|
||||||
|
m_conversionMethod = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFImageConversion::setThreshold(int threshold)
|
||||||
|
{
|
||||||
|
m_manualThreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PDFImageConversion::convert()
|
||||||
|
{
|
||||||
|
if (m_image.isNull())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage bitonal(m_image.width(), m_image.height(), QImage::Format_Mono);
|
||||||
|
bitonal.fill(0);
|
||||||
|
|
||||||
|
// Thresholding
|
||||||
|
int threshold = DEFAULT_THRESHOLD;
|
||||||
|
|
||||||
|
switch (m_conversionMethod)
|
||||||
|
{
|
||||||
|
case pdf::PDFImageConversion::ConversionMethod::Automatic:
|
||||||
|
m_automaticThreshold = calculateOtsu1DThreshold();
|
||||||
|
threshold = m_automaticThreshold;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFImageConversion::ConversionMethod::Manual:
|
||||||
|
threshold = m_manualThreshold;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < m_image.height(); ++y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < m_image.width(); ++x)
|
||||||
|
{
|
||||||
|
QColor pixelColor = m_image.pixelColor(x, y);
|
||||||
|
int pixelValue = pixelColor.lightness();
|
||||||
|
bool bit = (pixelValue >= threshold);
|
||||||
|
bitonal.setPixel(x, y, bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_convertedImage = std::move(bitonal);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PDFImageConversion::getThreshold() const
|
||||||
|
{
|
||||||
|
switch (m_conversionMethod)
|
||||||
|
{
|
||||||
|
case pdf::PDFImageConversion::ConversionMethod::Automatic:
|
||||||
|
return m_automaticThreshold;
|
||||||
|
|
||||||
|
case pdf::PDFImageConversion::ConversionMethod::Manual:
|
||||||
|
return m_manualThreshold;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage PDFImageConversion::getConvertedImage() const
|
||||||
|
{
|
||||||
|
return m_convertedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PDFImageConversion::calculateOtsu1DThreshold() const
|
||||||
|
{
|
||||||
|
if (m_image.isNull())
|
||||||
|
{
|
||||||
|
return 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Histogram of lightness occurences
|
||||||
|
std::array<int, 256> histogram = { };
|
||||||
|
|
||||||
|
for (int x = 0; x < m_image.width(); ++x)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < m_image.height(); ++y)
|
||||||
|
{
|
||||||
|
int lightness = m_image.pixelColor(x, y).lightness();
|
||||||
|
Q_ASSERT(lightness >= 0 && lightness <= 255);
|
||||||
|
|
||||||
|
int clampedLightness = qBound(0, lightness, 255);
|
||||||
|
histogram[clampedLightness] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float factor = 1.0f / float(m_image.width() * m_image.height());
|
||||||
|
|
||||||
|
std::array<float, 256> normalizedHistogram = { };
|
||||||
|
std::array<float, 256> cumulativeProbabilities = { };
|
||||||
|
std::array<float, 256> interClassVariance = { };
|
||||||
|
|
||||||
|
// Compute probabilities
|
||||||
|
for (size_t i = 0; i < histogram.size(); ++i)
|
||||||
|
{
|
||||||
|
normalizedHistogram[i] = histogram[i] * factor;
|
||||||
|
cumulativeProbabilities[i] = normalizedHistogram[i];
|
||||||
|
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
cumulativeProbabilities[i] += cumulativeProbabilities[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the inter-class variance for each threshold. Variables
|
||||||
|
// with the subscript 0 denote the background, while those with
|
||||||
|
// subscript 1 denote the foreground.
|
||||||
|
for (size_t i = 0; i < histogram.size(); ++i)
|
||||||
|
{
|
||||||
|
const float w0 = cumulativeProbabilities[i] - normalizedHistogram[i];
|
||||||
|
const float w1 = 1.0f - w0;
|
||||||
|
|
||||||
|
float u0 = 0.0f;
|
||||||
|
float u1 = 0.0f;
|
||||||
|
|
||||||
|
// Calculate mean intensity value of the background.
|
||||||
|
if (!qFuzzyIsNull(w0))
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < i; ++j)
|
||||||
|
{
|
||||||
|
u0 += j * normalizedHistogram[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
u0 /= w0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate mean intensity value of the foreground.
|
||||||
|
if (!qFuzzyIsNull(w1))
|
||||||
|
{
|
||||||
|
for (size_t j = i; j < histogram.size(); ++j)
|
||||||
|
{
|
||||||
|
u1 += j * normalizedHistogram[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 /= w1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float variance = w0 * w1 * std::powf(u0 - u1, 2);
|
||||||
|
interClassVariance[i] = variance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find maximal value of the variance
|
||||||
|
size_t maxVarianceIndex = 0;
|
||||||
|
float maxVarianceValue = 0.0f;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < interClassVariance.size(); ++i)
|
||||||
|
{
|
||||||
|
if (interClassVariance[i] > maxVarianceValue)
|
||||||
|
{
|
||||||
|
maxVarianceValue = interClassVariance[i];
|
||||||
|
maxVarianceIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(maxVarianceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pdf
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright (C) 2023 Jakub Melka
|
||||||
|
//
|
||||||
|
// This file is part of PDF4QT.
|
||||||
|
//
|
||||||
|
// PDF4QT is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// with the written consent of the copyright owner, any later version.
|
||||||
|
//
|
||||||
|
// PDF4QT is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef PDFIMAGECONVERSION_H
|
||||||
|
#define PDFIMAGECONVERSION_H
|
||||||
|
|
||||||
|
#include "pdfglobal.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
/// This class facilitates various image conversions,
|
||||||
|
/// including transforming colored images into monochromatic (or bitonal) formats.
|
||||||
|
class PDF4QTLIBSHARED_EXPORT PDFImageConversion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PDFImageConversion();
|
||||||
|
|
||||||
|
enum class ConversionMethod
|
||||||
|
{
|
||||||
|
Automatic, ///< The threshold is determined automatically using an algorithm.
|
||||||
|
Manual ///< The threshold is manually provided by the user.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Sets the image to be converted using the specified conversion method.
|
||||||
|
/// This operation resets any previously converted image and the automatic threshold,
|
||||||
|
/// thereby erasing all prior image data.
|
||||||
|
/// \param image The image to be set for conversion.
|
||||||
|
void setImage(QImage image);
|
||||||
|
|
||||||
|
/// Sets the method for image conversion. Multiple methods are available
|
||||||
|
/// for selection. If the manual method is chosen, an appropriate threshold
|
||||||
|
/// must also be set by the user.
|
||||||
|
/// \param method The conversion method to be used.
|
||||||
|
void setConversionMethod(ConversionMethod method);
|
||||||
|
|
||||||
|
/// Sets the manual threshold value. When a non-manual (e.g., automatic) conversion
|
||||||
|
/// method is in use, this function will retain the manual threshold settings,
|
||||||
|
/// but the conversion will utilize an automatically calculated threshold for the image.
|
||||||
|
/// The manually set threshold is preserved and not overwritten. Therefore, if the
|
||||||
|
/// manual conversion method is later selected, the previously established manual
|
||||||
|
/// threshold will be applied.
|
||||||
|
/// \param threshold The manual threshold value to be set.
|
||||||
|
void setThreshold(int threshold);
|
||||||
|
|
||||||
|
/// This method converts the image into a bitonal (monochromatic) format. If
|
||||||
|
/// the automatic threshold calculation is enabled, it executes Otsu's 1D algorithm
|
||||||
|
/// to determine the threshold. When the manual conversion method is selected,
|
||||||
|
/// the automatic threshold calculation is bypassed, and the predefined manual threshold
|
||||||
|
/// value is utilized instead. This method returns true if the conversion is
|
||||||
|
/// successful, and false otherwise.
|
||||||
|
bool convert();
|
||||||
|
|
||||||
|
/// Returns the threshold used in image conversion. If the automatic conversion method is
|
||||||
|
/// selected, this function should be called only after executing the convert() method;
|
||||||
|
/// otherwise, it may return invalid data. The automatic threshold calculation is
|
||||||
|
/// performed within the convert() method.
|
||||||
|
/// \returns The threshold value used in image conversion.
|
||||||
|
int getThreshold() const;
|
||||||
|
|
||||||
|
/// Returns the converted image. This method should only be called after
|
||||||
|
/// the convert() method has been executed, and additionally, only if the
|
||||||
|
/// convert() method returns true. If these conditions are not met, the result
|
||||||
|
/// is undefined.
|
||||||
|
QImage getConvertedImage() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int calculateOtsu1DThreshold() const;
|
||||||
|
|
||||||
|
static constexpr int DEFAULT_THRESHOLD = 128;
|
||||||
|
|
||||||
|
QImage m_image;
|
||||||
|
QImage m_convertedImage;
|
||||||
|
ConversionMethod m_conversionMethod = ConversionMethod::Automatic;
|
||||||
|
int m_automaticThreshold = DEFAULT_THRESHOLD;
|
||||||
|
int m_manualThreshold = DEFAULT_THRESHOLD;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFIMAGECONVERSION_H
|
|
@ -24,6 +24,7 @@
|
||||||
#include "pdfdbgheap.h"
|
#include "pdfdbgheap.h"
|
||||||
#include "pdfexception.h"
|
#include "pdfexception.h"
|
||||||
#include "pdfwidgetutils.h"
|
#include "pdfwidgetutils.h"
|
||||||
|
#include "pdfimageconversion.h"
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
@ -39,6 +40,64 @@
|
||||||
namespace pdfviewer
|
namespace pdfviewer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PDFCreateBitonalDocumentPreviewWidget::PDFCreateBitonalDocumentPreviewWidget(QWidget* parent) :
|
||||||
|
QWidget(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFCreateBitonalDocumentPreviewWidget::~PDFCreateBitonalDocumentPreviewWidget()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCreateBitonalDocumentPreviewWidget::paintEvent(QPaintEvent* event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.fillRect(rect(), Qt::white);
|
||||||
|
|
||||||
|
// Caption rect
|
||||||
|
QRect captionRect = rect();
|
||||||
|
captionRect.setHeight(painter.fontMetrics().lineSpacing() * 2);
|
||||||
|
|
||||||
|
painter.fillRect(captionRect, QColor::fromRgb(0, 0, 128, 255));
|
||||||
|
|
||||||
|
if (!m_caption.isEmpty())
|
||||||
|
{
|
||||||
|
painter.setPen(Qt::white);
|
||||||
|
painter.drawText(captionRect, m_caption, QTextOption(Qt::AlignCenter));
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect imageRect = rect();
|
||||||
|
imageRect.setTop(captionRect.bottom());
|
||||||
|
imageRect = imageRect.adjusted(16, 16, -32, -32);
|
||||||
|
|
||||||
|
if (imageRect.isValid() && !m_image.isNull())
|
||||||
|
{
|
||||||
|
QRect imageDrawRect = imageRect;
|
||||||
|
imageDrawRect.setSize(m_image.size().scaled(imageRect.size(), Qt::KeepAspectRatio));
|
||||||
|
imageDrawRect.moveCenter(imageRect.center());
|
||||||
|
painter.drawImage(imageDrawRect, m_image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCreateBitonalDocumentPreviewWidget::setCaption(QString caption)
|
||||||
|
{
|
||||||
|
if (m_caption != caption)
|
||||||
|
{
|
||||||
|
m_caption = caption;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCreateBitonalDocumentPreviewWidget::setImage(QImage image)
|
||||||
|
{
|
||||||
|
m_image = std::move(image);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
class ImagePreviewDelegate : public QStyledItemDelegate
|
class ImagePreviewDelegate : public QStyledItemDelegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -148,10 +207,18 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
|
||||||
m_cms(cms),
|
m_cms(cms),
|
||||||
m_createBitonalDocumentButton(nullptr),
|
m_createBitonalDocumentButton(nullptr),
|
||||||
m_conversionInProgress(false),
|
m_conversionInProgress(false),
|
||||||
m_processed(false)
|
m_processed(false),
|
||||||
|
m_leftPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this)),
|
||||||
|
m_rightPreviewWidget(new PDFCreateBitonalDocumentPreviewWidget(this))
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
m_leftPreviewWidget->setCaption(tr("ORIGINAL"));
|
||||||
|
m_rightPreviewWidget->setCaption(tr("BITONIC"));
|
||||||
|
|
||||||
|
ui->mainGridLayout->addWidget(m_leftPreviewWidget, 1, 1);
|
||||||
|
ui->mainGridLayout->addWidget(m_rightPreviewWidget, 1, 2);
|
||||||
|
|
||||||
m_classifier.classify(document);
|
m_classifier.classify(document);
|
||||||
m_imageReferences = m_classifier.getObjectsByType(pdf::PDFObjectClassifier::Image);
|
m_imageReferences = m_classifier.getObjectsByType(pdf::PDFObjectClassifier::Image);
|
||||||
|
|
||||||
|
@ -159,6 +226,10 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
|
||||||
connect(m_createBitonalDocumentButton, &QPushButton::clicked, this, &PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked);
|
connect(m_createBitonalDocumentButton, &QPushButton::clicked, this, &PDFCreateBitonalDocumentDialog::onCreateBitonalDocumentButtonClicked);
|
||||||
connect(ui->automaticThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
|
connect(ui->automaticThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
|
||||||
connect(ui->manualThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
|
connect(ui->manualThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updateUi);
|
||||||
|
connect(ui->automaticThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updatePreview);
|
||||||
|
connect(ui->manualThresholdRadioButton, &QRadioButton::clicked, this, &PDFCreateBitonalDocumentDialog::updatePreview);
|
||||||
|
connect(ui->imageListWidget, &QListWidget::currentItemChanged, this, &PDFCreateBitonalDocumentDialog::updatePreview);
|
||||||
|
connect(ui->thresholdEditBox, &QSpinBox::editingFinished, this, &PDFCreateBitonalDocumentDialog::updatePreview);
|
||||||
|
|
||||||
pdf::PDFWidgetUtils::scaleWidget(this, QSize(640, 380));
|
pdf::PDFWidgetUtils::scaleWidget(this, QSize(640, 380));
|
||||||
updateUi();
|
updateUi();
|
||||||
|
@ -167,6 +238,7 @@ PDFCreateBitonalDocumentDialog::PDFCreateBitonalDocumentDialog(const pdf::PDFDoc
|
||||||
ui->imageListWidget->setItemDelegate( new ImagePreviewDelegate(&m_imagesToBeConverted, this));
|
ui->imageListWidget->setItemDelegate( new ImagePreviewDelegate(&m_imagesToBeConverted, this));
|
||||||
|
|
||||||
loadImages();
|
loadImages();
|
||||||
|
updatePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFCreateBitonalDocumentDialog::~PDFCreateBitonalDocumentDialog()
|
PDFCreateBitonalDocumentDialog::~PDFCreateBitonalDocumentDialog()
|
||||||
|
@ -243,6 +315,40 @@ void PDFCreateBitonalDocumentDialog::updateUi()
|
||||||
m_createBitonalDocumentButton->setEnabled(!m_conversionInProgress);
|
m_createBitonalDocumentButton->setEnabled(!m_conversionInProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFCreateBitonalDocumentDialog::updatePreview()
|
||||||
|
{
|
||||||
|
QModelIndex index = ui->imageListWidget->currentIndex();
|
||||||
|
|
||||||
|
m_previewImageLeft = QImage();
|
||||||
|
m_previewImageRight = QImage();
|
||||||
|
|
||||||
|
if (index.isValid())
|
||||||
|
{
|
||||||
|
const ImageConversionInfo& info = m_imagesToBeConverted.at(index.row());
|
||||||
|
|
||||||
|
std::optional<pdf::PDFImage> pdfImage = getImageFromReference(info.imageReference);
|
||||||
|
Q_ASSERT(pdfImage);
|
||||||
|
|
||||||
|
pdf::PDFCMSGeneric cmsGeneric;
|
||||||
|
pdf::PDFRenderErrorReporterDummy reporter;
|
||||||
|
QImage image = pdfImage->getImage(&cmsGeneric, &reporter, nullptr);
|
||||||
|
|
||||||
|
pdf::PDFImageConversion imageConversion;
|
||||||
|
imageConversion.setConversionMethod(ui->automaticThresholdRadioButton->isChecked() ? pdf::PDFImageConversion::ConversionMethod::Automatic : pdf::PDFImageConversion::ConversionMethod::Manual);
|
||||||
|
imageConversion.setThreshold(ui->thresholdEditBox->value());
|
||||||
|
imageConversion.setImage(image);
|
||||||
|
|
||||||
|
if (imageConversion.convert())
|
||||||
|
{
|
||||||
|
m_previewImageLeft = image;
|
||||||
|
m_previewImageRight = imageConversion.getConvertedImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_leftPreviewWidget->setImage(m_previewImageLeft);
|
||||||
|
m_rightPreviewWidget->setImage(m_previewImageRight);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<pdf::PDFImage> PDFCreateBitonalDocumentDialog::getImageFromReference(pdf::PDFObjectReference reference) const
|
std::optional<pdf::PDFImage> PDFCreateBitonalDocumentDialog::getImageFromReference(pdf::PDFObjectReference reference) const
|
||||||
{
|
{
|
||||||
std::optional<pdf::PDFImage> pdfImage;
|
std::optional<pdf::PDFImage> pdfImage;
|
||||||
|
@ -285,3 +391,5 @@ std::optional<pdf::PDFImage> PDFCreateBitonalDocumentDialog::getImageFromReferen
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace pdfviewer
|
} // namespace pdfviewer
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,24 @@ class PDFCreateBitonalDocumentDialog;
|
||||||
namespace pdfviewer
|
namespace pdfviewer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class PDFCreateBitonalDocumentPreviewWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PDFCreateBitonalDocumentPreviewWidget(QWidget* parent);
|
||||||
|
virtual ~PDFCreateBitonalDocumentPreviewWidget() override;
|
||||||
|
|
||||||
|
virtual void paintEvent(QPaintEvent* event) override;
|
||||||
|
|
||||||
|
void setCaption(QString caption);
|
||||||
|
void setImage(QImage image);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_caption;
|
||||||
|
QImage m_image;
|
||||||
|
};
|
||||||
|
|
||||||
class PDFCreateBitonalDocumentDialog : public QDialog
|
class PDFCreateBitonalDocumentDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -56,6 +74,7 @@ private:
|
||||||
void loadImages();
|
void loadImages();
|
||||||
|
|
||||||
void updateUi();
|
void updateUi();
|
||||||
|
void updatePreview();
|
||||||
|
|
||||||
std::optional<pdf::PDFImage> getImageFromReference(pdf::PDFObjectReference reference) const;
|
std::optional<pdf::PDFImage> getImageFromReference(pdf::PDFObjectReference reference) const;
|
||||||
|
|
||||||
|
@ -70,6 +89,12 @@ private:
|
||||||
pdf::PDFObjectClassifier m_classifier;
|
pdf::PDFObjectClassifier m_classifier;
|
||||||
std::vector<pdf::PDFObjectReference> m_imageReferences;
|
std::vector<pdf::PDFObjectReference> m_imageReferences;
|
||||||
std::vector<ImageConversionInfo> m_imagesToBeConverted;
|
std::vector<ImageConversionInfo> m_imagesToBeConverted;
|
||||||
|
|
||||||
|
QImage m_previewImageLeft;
|
||||||
|
QImage m_previewImageRight;
|
||||||
|
|
||||||
|
PDFCreateBitonalDocumentPreviewWidget* m_leftPreviewWidget;
|
||||||
|
PDFCreateBitonalDocumentPreviewWidget* m_rightPreviewWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdfviewer
|
} // namespace pdfviewer
|
||||||
|
|
|
@ -13,13 +13,23 @@
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Create Bitonal Document</string>
|
<string>Create Bitonal Document</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,1,0" columnstretch="2,1,1">
|
<layout class="QGridLayout" name="mainGridLayout" rowstretch="0,1,0" columnstretch="2,1,1">
|
||||||
|
<item row="2" column="0" colspan="3">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="1" colspan="2">
|
<item row="0" column="1" colspan="2">
|
||||||
<widget class="QGroupBox" name="createBitonalSettingsGroupBox">
|
<widget class="QGroupBox" name="createBitonalSettingsGroupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Color to Bitonal Conversion Options</string>
|
<string>Color to Bitonal Conversion Options</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="optionsGridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QRadioButton" name="automaticThresholdRadioButton">
|
<widget class="QRadioButton" name="automaticThresholdRadioButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -50,99 +60,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1" rowspan="2">
|
<item row="0" column="0" rowspan="2">
|
||||||
<widget class="QLabel" name="originalImageLabel">
|
|
||||||
<property name="palette">
|
|
||||||
<palette>
|
|
||||||
<active>
|
|
||||||
<colorrole role="Window">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="255">
|
|
||||||
<red>255</red>
|
|
||||||
<green>255</green>
|
|
||||||
<blue>255</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</active>
|
|
||||||
<inactive>
|
|
||||||
<colorrole role="Window">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="255">
|
|
||||||
<red>240</red>
|
|
||||||
<green>240</green>
|
|
||||||
<blue>240</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</inactive>
|
|
||||||
<disabled>
|
|
||||||
<colorrole role="Window">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="255">
|
|
||||||
<red>255</red>
|
|
||||||
<green>255</green>
|
|
||||||
<blue>255</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</disabled>
|
|
||||||
</palette>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2" rowspan="2">
|
|
||||||
<widget class="QLabel" name="convertedImageLabel">
|
|
||||||
<property name="palette">
|
|
||||||
<palette>
|
|
||||||
<active>
|
|
||||||
<colorrole role="Window">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="255">
|
|
||||||
<red>255</red>
|
|
||||||
<green>255</green>
|
|
||||||
<blue>255</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</active>
|
|
||||||
<inactive>
|
|
||||||
<colorrole role="Window">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="255">
|
|
||||||
<red>240</red>
|
|
||||||
<green>240</green>
|
|
||||||
<blue>240</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</inactive>
|
|
||||||
<disabled>
|
|
||||||
<colorrole role="Window">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="255">
|
|
||||||
<red>255</red>
|
|
||||||
<green>255</green>
|
|
||||||
<blue>255</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</disabled>
|
|
||||||
</palette>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="3">
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" rowspan="3">
|
|
||||||
<widget class="QListWidget" name="imageListWidget">
|
<widget class="QListWidget" name="imageListWidget">
|
||||||
<property name="resizeMode">
|
<property name="resizeMode">
|
||||||
<enum>QListView::Adjust</enum>
|
<enum>QListView::Adjust</enum>
|
||||||
|
|
Loading…
Reference in New Issue