Commit da3368f4 authored by Volker Krause's avatar Volker Krause
Browse files

Expand the barcode decoder to support 1D barcodes as well

Those are not automatically considered for generic extraction, as they
usually don't contain anything we can meaningfully match against, but
they can matter to custom extractors.
parent da45806c
......@@ -12,6 +12,8 @@
#include <QObject>
#include <QTest>
Q_DECLARE_METATYPE(KItinerary::BarcodeDecoder::BarcodeType)
using namespace KItinerary;
class BarcodeDecoderTest : public QObject
......@@ -98,8 +100,8 @@ private Q_SLOTS:
void testPlausibilityCheck()
{
QVERIFY(!BarcodeDecoder::maybeBarcode(10, 10));
QVERIFY(BarcodeDecoder::maybeBarcode(100, 100));
QVERIFY(!BarcodeDecoder::maybeBarcode(10, 10, BarcodeDecoder::Any));
QVERIFY(BarcodeDecoder::maybeBarcode(100, 100, BarcodeDecoder::Any));
QVERIFY(!BarcodeDecoder::maybeBarcode(100, 100, BarcodeDecoder::PDF417));
QVERIFY(BarcodeDecoder::maybeBarcode(100, 100, BarcodeDecoder::Aztec));
QVERIFY(BarcodeDecoder::maybeBarcode(100, 100, BarcodeDecoder::AnySquare));
......@@ -162,6 +164,33 @@ private Q_SLOTS:
const auto ba = decoder.decodeBinary(img, BarcodeDecoder::Any);
QCOMPARE(ba.size(), 351);
QVERIFY(ba.startsWith("OTI010080000020"));
#endif
}
void test1D_data()
{
QTest::addColumn<QString>("filename");
QTest::addColumn<BarcodeDecoder::BarcodeType>("type");
QTest::newRow("code39") << QStringLiteral("code39.png") << BarcodeDecoder::Code39;
QTest::newRow("code93") << QStringLiteral("code93.png") << BarcodeDecoder::Code93;
QTest::newRow("code128") << QStringLiteral("code128.png") << BarcodeDecoder::Code128;
}
void test1D()
{
QFETCH(QString, filename);
QFETCH(BarcodeDecoder::BarcodeType, type);
QImage img(QLatin1String(SOURCE_DIR "/barcodes/") + filename);
QVERIFY(!img.isNull());
BarcodeDecoder decoder;
#ifdef HAVE_ZXING
QCOMPARE(decoder.decodeString(img, BarcodeDecoder::Any2D), QString());
QCOMPARE(decoder.decodeString(img, type), QLatin1String("123456789"));
QCOMPARE(decoder.decodeString(img, BarcodeDecoder::Any1D), QLatin1String("123456789"));
QCOMPARE(decoder.decodeString(img, BarcodeDecoder::Any), QLatin1String("123456789"));
#endif
}
};
......
......@@ -35,6 +35,12 @@ enum {
MaxSourceImageWidth = 2000
};
static constexpr const auto SQUARE_MAX_ASPECT = 1.2f;
static constexpr const auto PDF417_MIN_ASPECT = 1.5f;
static constexpr const auto PDF417_MAX_ASPECT = 6.0f;
static constexpr const auto ANY1D_MIN_ASPECT = 6.0f;
BarcodeDecoder::BarcodeDecoder() = default;
BarcodeDecoder::~BarcodeDecoder() = default;
......@@ -104,12 +110,21 @@ bool BarcodeDecoder::isPlausibleAspectRatio(int width, int height, BarcodeDecode
const auto aspectRatio = (float)width / (float)height;
// almost square, assume Aztec or QR
if (aspectRatio < 1.2f && (hint & AnySquare)) {
if (aspectRatio < SQUARE_MAX_ASPECT && (hint & AnySquare)) {
return true;
}
// rectangular with medium aspect ratio, such as PDF 417
if (aspectRatio > PDF417_MIN_ASPECT && aspectRatio < PDF417_MAX_ASPECT && (hint & PDF417)) {
return true;
}
// rectangular with medium aspect ratio, assume PDF 417
return aspectRatio > 1.5 && aspectRatio < 6 && (hint & PDF417);
// 1D
if (aspectRatio > ANY1D_MIN_ASPECT && (hint & Any1D)) {
return true;
}
return false;
}
bool BarcodeDecoder::maybeBarcode(int width, int height, BarcodeDecoder::BarcodeTypes hint)
......@@ -127,11 +142,17 @@ struct {
{ BarcodeDecoder::QRCode, ZXing::BarcodeFormat::QRCode },
{ BarcodeDecoder::PDF417, ZXing::BarcodeFormat::PDF417 },
{ BarcodeDecoder::DataMatrix, ZXing::BarcodeFormat::DataMatrix },
{ BarcodeDecoder::Code39, ZXing::BarcodeFormat::Code39 },
{ BarcodeDecoder::Code93, ZXing::BarcodeFormat::Code93 },
{ BarcodeDecoder::Code128, ZXing::BarcodeFormat::Code128 },
#else
{ BarcodeDecoder::Aztec, ZXing::BarcodeFormat::AZTEC },
{ BarcodeDecoder::QRCode, ZXing::BarcodeFormat::QR_CODE },
{ BarcodeDecoder::PDF417, ZXing::BarcodeFormat::PDF_417 },
{ BarcodeDecoder::DataMatrix, ZXing::BarcodeFormat::DATA_MATRIX },
{ BarcodeDecoder::Code39, ZXing::BarcodeFormat::CODE_39 },
{ BarcodeDecoder::Code93, ZXing::BarcodeFormat::CODE_93 },
{ BarcodeDecoder::Code128, ZXing::BarcodeFormat::CODE_128 },
#endif
};
......@@ -255,11 +276,11 @@ void BarcodeDecoder::decodeIfNeeded(const QImage &img, BarcodeDecoder::BarcodeTy
(float)img.height() / (float)img.width() :
(float)img.width() / (float)img.height();
if (aspectRatio < 1.2f && (hint & AnySquare) && (result.negative & hint & AnySquare) != (hint & AnySquare)) {
if (aspectRatio < SQUARE_MAX_ASPECT && (hint & AnySquare) && (result.negative & hint & AnySquare) != (hint & AnySquare)) {
decodeZxing(img, hint & AnySquare, result);
}
if (aspectRatio > 1.5 && aspectRatio < 6 && (hint & PDF417) && (result.negative & hint & PDF417) != (hint & PDF417)) {
if (aspectRatio > PDF417_MIN_ASPECT && aspectRatio < PDF417_MAX_ASPECT && (hint & PDF417) && (result.negative & hint & PDF417) != (hint & PDF417)) {
auto normalizedImg = img;
// newer ZXing versions handle rotated/flipped codes themselves correctly
#ifndef ZXING_USE_READBARCODE
......@@ -280,4 +301,8 @@ void BarcodeDecoder::decodeIfNeeded(const QImage &img, BarcodeDecoder::BarcodeTy
decodeZxing(normalizedImg.transformed(QTransform{1, 0, 0, -1, 0, 0}), PDF417, result);
#endif
}
if (aspectRatio > ANY1D_MIN_ASPECT && (hint & Any1D) && (result.negative & hint & Any1D) != (hint & Any1D)) {
decodeZxing(img, hint & Any1D, result);
}
}
......@@ -38,20 +38,25 @@ public:
QRCode = 2,
PDF417 = 4,
DataMatrix = 8,
Any = 15,
AnySquare = 11,
Code39 = 16,
Code93 = 32,
Code128 = 64,
AnySquare = Aztec | QRCode | DataMatrix,
Any2D = AnySquare | PDF417,
Any1D = Code39 | Code93 | Code128,
Any = Any1D | Any2D,
None = 0
};
Q_DECLARE_FLAGS(BarcodeTypes, BarcodeType)
/** Checks if @p img contains a barcode of type @p hint. */
bool isBarcode(const QImage &img, BarcodeTypes hint = Any) const;
bool isBarcode(const QImage &img, BarcodeTypes hint = Any2D) const;
/** Decodes a binary payload barcode in @p img of type @p hint. */
QByteArray decodeBinary(const QImage &img, BarcodeTypes hint = Any) const;
QByteArray decodeBinary(const QImage &img, BarcodeTypes hint = Any2D) const;
/** Decodes a textual payload barcode in @p img of type @p hint. */
QString decodeString(const QImage &img, BarcodeTypes hint = Any) const;
QString decodeString(const QImage &img, BarcodeTypes hint = Any2D) const;
/** Clears the internal cache. */
void clearCache();
......@@ -66,10 +71,10 @@ public:
/** Checks if the given image dimensions are a barcode of type @p hint.
* See above.
*/
static bool isPlausibleAspectRatio(int width, int height, BarcodeTypes hint = Any);
static bool isPlausibleAspectRatio(int width, int height, BarcodeTypes hint);
/** The combination of the above. */
static bool maybeBarcode(int width, int height, BarcodeTypes hint = Any);
static bool maybeBarcode(int width, int height, BarcodeTypes hint);
private:
struct Result {
......
......@@ -17,7 +17,7 @@ class PdfImage;
namespace PdfBarcodeUtil
{
/** Quick pre-check without image decoding if @p img might be a barcode. */
bool maybeBarcode(const PdfImage &img, BarcodeDecoder::BarcodeTypes hint = BarcodeDecoder::Any);
bool maybeBarcode(const PdfImage &img, BarcodeDecoder::BarcodeTypes hint = BarcodeDecoder::Any2D);
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment