forked from scantailor/scantailor
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The re-design aims to achieve two goals: 1. Move dewarping to the Deskew stage. 2. Don't use the concept of DPI at all. This commit does achieve both goals though it breaks the command-line mode which is to be fixed later.
- Loading branch information
Showing
255 changed files
with
9,683 additions
and
10,320 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* | ||
Scan Tailor - Interactive post-processing tool for scanned pages. | ||
Copyright (C) 2015 Joseph Artsimovich <joseph.artsimovich@gmail.com> | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program 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 General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#ifndef ABSTRACT_IMAGE_TRANSFORM_H_ | ||
#define ABSTRACT_IMAGE_TRANSFORM_H_ | ||
|
||
#include <QtGlobal> | ||
#include <memory> | ||
#include <functional> | ||
|
||
class AffineImageTransform; | ||
class AffineTransformedImage; | ||
class QColor; | ||
class QImage; | ||
class QSize; | ||
class QRect; | ||
class QPointF; | ||
class QPolygonF; | ||
class QTransform; | ||
class QString; | ||
|
||
class AbstractImageTransform | ||
{ | ||
// Member-wise copying is OK. | ||
public: | ||
virtual ~AbstractImageTransform() {} | ||
|
||
virtual std::unique_ptr<AbstractImageTransform> clone() const = 0; | ||
|
||
virtual bool isAffine() const = 0; | ||
|
||
/** | ||
* @brief Produces a hash of the transform. | ||
* | ||
* The hash has to be resistent to loss of accuracy of floating point | ||
* values that happens when saving a project to an XML based format. | ||
* Implementations should use RoundingHasher to address this problem. | ||
*/ | ||
virtual QString fingerprint() const = 0; | ||
|
||
/** | ||
* @brief Dimensions of original image. | ||
*/ | ||
virtual QSize const& origSize() const = 0; | ||
|
||
/** | ||
* @brief The crop area in original image coordinates. | ||
*/ | ||
virtual QPolygonF const& origCropArea() const = 0; | ||
|
||
/** | ||
* @brief The crop area in transformed coordinates. | ||
*/ | ||
virtual QPolygonF transformedCropArea() const = 0; | ||
|
||
/** | ||
* @brief Applies additional scaling to transformed space. | ||
* | ||
* A point (x, y) in transformed space before this call becomes point | ||
* (x * xscale, y * yscale) after this call. For convenience, returns | ||
* a QTransform that maps points in transformed space before scaling | ||
* to corresponding points after scaling. | ||
*/ | ||
virtual QTransform scale(qreal xscale, qreal yscale) = 0; | ||
|
||
/** | ||
* If the concrete class implementing this interface is AffineImageTransform, | ||
* the image and the transform are wrapped into AffineTransformedImage | ||
* as they are. Otherwise, the transform is applied on the image provided, | ||
* resulting in a new image and a new affine transform. The new transform | ||
* will be a translation-only transform, to ensure that: | ||
* @code | ||
* transform.transformedCropArea() == transform.toAffine().transformedCropArea() | ||
* @endcode | ||
*/ | ||
virtual AffineTransformedImage toAffine( | ||
QImage const& image, QColor const& outside_color) const = 0; | ||
|
||
/** | ||
* This version of toAffine() can be viewed as a dry run for the full version. | ||
*/ | ||
virtual AffineImageTransform toAffine() const = 0; | ||
|
||
/** | ||
* Similar to a full version of toAffine(), except instead of producing some | ||
* intermediate image plus a follow-up affine transformation, this one | ||
* produces an image that represents the specified area of transformed space | ||
* exactly, without requiring a follow-up transformation. | ||
*/ | ||
virtual QImage materialize(QImage const& image, | ||
QRect const& target_rect, QColor const& outside_color) const = 0; | ||
|
||
/** | ||
* @brief Returns a function for mapping points from original image coordinates | ||
* to transformed space. | ||
*/ | ||
virtual std::function<QPointF(QPointF const&)> forwardMapper() const = 0; | ||
|
||
/** | ||
* @brief Returns a function for mapping points from transformed space | ||
* to original image coordinates. | ||
*/ | ||
virtual std::function<QPointF(QPointF const&)> backwardMapper() const = 0; | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* | ||
Scan Tailor - Interactive post-processing tool for scanned pages. | ||
Copyright (C) 2015 Joseph Artsimovich <joseph.artsimovich@gmail.com> | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program 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 General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "AffineImageTransform.h" | ||
#include "AffineTransformedImage.h" | ||
#include "RoundingHasher.h" | ||
#include "imageproc/Transform.h" | ||
#include <QSizeF> | ||
#include <QRectF> | ||
#include <QLineF> | ||
#include <QPointF> | ||
#include <QString> | ||
#include <QCryptographicHash> | ||
#include <assert.h> | ||
|
||
AffineImageTransform::AffineImageTransform(QSize const& orig_size) | ||
: m_origSize(orig_size) | ||
, m_origCropArea(QRectF(QPointF(0, 0), orig_size)) | ||
, m_transform() | ||
{ | ||
} | ||
|
||
AffineImageTransform::~AffineImageTransform() | ||
{ | ||
} | ||
|
||
QString | ||
AffineImageTransform::fingerprint() const | ||
{ | ||
RoundingHasher hash(QCryptographicHash::Sha1); | ||
hash << "AffineImageTransform"; | ||
hash << m_origSize << m_origCropArea; | ||
hash << m_transform.m11() << m_transform.m21() << m_transform.dx(); | ||
hash << m_transform.m12() << m_transform.m22() << m_transform.dy(); | ||
return QString::fromUtf8(hash.result().toHex()); | ||
} | ||
|
||
std::unique_ptr<AbstractImageTransform> | ||
AffineImageTransform::clone() const | ||
{ | ||
return std::unique_ptr<AbstractImageTransform>(new AffineImageTransform(*this)); | ||
} | ||
|
||
void | ||
AffineImageTransform::setTransform(QTransform const& transform) | ||
{ | ||
m_transform = transform; | ||
} | ||
|
||
QPolygonF | ||
AffineImageTransform::transformedCropArea() const | ||
{ | ||
return m_transform.map(m_origCropArea); | ||
} | ||
|
||
void | ||
AffineImageTransform::adjustForScaledOrigImage(QSize const& orig_size) | ||
{ | ||
double const old2new_xscale = double(orig_size.width()) / m_origSize.width(); | ||
double const old2new_yscale = double(orig_size.height()) / m_origSize.height(); | ||
|
||
m_transform.scale(1.0 / old2new_xscale, 1.0 / old2new_yscale); | ||
for (QPointF& pt : m_origCropArea) { | ||
pt.rx() *= old2new_xscale; | ||
pt.ry() *= old2new_yscale; | ||
} | ||
m_origSize = orig_size; | ||
} | ||
|
||
void | ||
AffineImageTransform::translateSoThatPointBecomes( | ||
QPointF const& transformed_pt, QPointF const& target_pos) | ||
{ | ||
QPointF const delta(target_pos - transformed_pt); | ||
QTransform translation; | ||
translation.translate(delta.x(), delta.y()); | ||
m_transform *= translation; | ||
} | ||
|
||
QTransform | ||
AffineImageTransform::scale(qreal xscale, qreal yscale) | ||
{ | ||
QTransform scaling_transform; | ||
scaling_transform.scale(xscale, yscale); | ||
|
||
m_transform *= scaling_transform; | ||
|
||
return scaling_transform; | ||
} | ||
|
||
void | ||
AffineImageTransform::scaleTo(QSizeF const& size, Qt::AspectRatioMode mode) | ||
{ | ||
assert(!size.isEmpty()); | ||
|
||
QSizeF const transformed_size(transformedCropArea().boundingRect().size()); | ||
QSizeF const desired_size(transformed_size.scaled(size, mode)); | ||
|
||
double const xscale = desired_size.width() / transformed_size.width(); | ||
double const yscale = desired_size.height() / transformed_size.height(); | ||
|
||
m_transform *= QTransform().scale(xscale, yscale); | ||
} | ||
|
||
void | ||
AffineImageTransform::rotate(qreal degrees) | ||
{ | ||
m_transform *= QTransform().rotate(degrees); | ||
} | ||
|
||
AffineTransformedImage | ||
AffineImageTransform::toAffine(QImage const& image, QColor const&) const | ||
{ | ||
assert(!image.isNull()); | ||
assert(image.size() == m_origSize); | ||
return AffineTransformedImage(image, *this); | ||
} | ||
|
||
AffineImageTransform | ||
AffineImageTransform::toAffine() const | ||
{ | ||
return *this; | ||
} | ||
|
||
QImage | ||
AffineImageTransform::materialize(QImage const& image, | ||
QRect const& target_rect, QColor const& outside_color) const | ||
{ | ||
assert(!image.isNull()); | ||
assert(!target_rect.isEmpty()); | ||
|
||
return imageproc::transform( | ||
image, m_transform, target_rect, | ||
imageproc::OutsidePixels::assumeColor(outside_color) | ||
); | ||
} | ||
|
||
std::function<QPointF(QPointF const&)> | ||
AffineImageTransform::forwardMapper() const | ||
{ | ||
QTransform transform(m_transform); | ||
return [=](QPointF const& pt) { | ||
return transform.map(pt); | ||
}; | ||
} | ||
|
||
std::function<QPointF(QPointF const&)> | ||
AffineImageTransform::backwardMapper() const | ||
{ | ||
QTransform inverted(m_transform.inverted()); | ||
return [=](QPointF const& pt) { | ||
return inverted.map(pt); | ||
}; | ||
} |
Oops, something went wrong.