Files
ANSLibs/QRCode/oned/ODRowReader.h

230 lines
7.3 KiB
C
Raw Normal View History

/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BitArray.h"
#include "Pattern.h"
#include "Barcode.h"
#include "ZXAlgorithms.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <iterator>
#include <limits>
#include <memory>
/*
Code39 : 1:2/3, 5+4+1 (0x3|2x1 wide) -> 12-15 mods, v1-? | ToNarrowWide(OMG 1) == *
Codabar: 1:2/3, 4+3+1 (1x1|1x2|3x0 wide) -> 9-13 mods, v1-? | ToNarrowWide(OMG 2) == ABCD
ITF : 1:2/3, 5+5 (2x2 wide) -> mods, v6-?| .5, .38 == * | qz:10
Code93 : 1-4, 3+3 -> 9 mods v1-? | round to 1-4 == *
Code128: 1-4, 3+3 -> 11 mods v1-? | .7, .25 == ABC | qz:10
UPC/EAN: 1-4, 2+2 -> 7 mods f | .7, .48 == *
UPC-A: 11d 95m = 3 + 6*4 + 5 + 6*4 + 3 = 59 | qz:3
EAN-13: 12d 95m
UPC-E: 6d, 3 + 6*4 + 6 = 33
EAN-8: 8d, 3 + 4*4 + 5 + 4*4 + 3 = 43
RSS14 : 1-8, finder: (15,2+3), symbol: (15/16,4+4) | .45, .2 (finder only), 14d
code = 2xguard + 2xfinder + 4xsymbol = (96,23), stacked = 2x50 mods
RSSExp.: v?-74d/?-41c
*/
namespace ZXing {
class ReaderOptions;
namespace OneD {
/**
* Encapsulates functionality and implementation that is common to all families
* of one-dimensional barcodes.
*/
class RowReader
{
protected:
const ReaderOptions& _opts;
public:
explicit RowReader(const ReaderOptions& opts) : _opts(opts) {}
explicit RowReader(ReaderOptions&&) = delete;
struct DecodingState
{
virtual ~DecodingState() = default;
};
virtual ~RowReader() {}
virtual Barcode decodePattern(int rowNumber, PatternView& next, std::unique_ptr<DecodingState>& state) const = 0;
/**
* Determines how closely a set of observed counts of runs of black/white values matches a given
* target pattern. This is reported as the ratio of the total variance from the expected pattern
* proportions across all pattern elements, to the length of the pattern.
*
* @param counters observed counters
* @param pattern expected pattern
* @param maxIndividualVariance The most any counter can differ before we give up
* @return ratio of total variance between counters and pattern compared to total pattern size
*/
template <typename CP, typename PP>
static float PatternMatchVariance(const CP* counters, const PP* pattern, size_t length, float maxIndividualVariance)
{
int total = Reduce(counters, counters + length, 0);
int patternLength = Reduce(pattern, pattern + length, 0);
if (total < patternLength) {
// If we don't even have one pixel per unit of bar width, assume this is too small
// to reliably match, so fail:
return std::numeric_limits<float>::max();
}
float unitBarWidth = (float)total / patternLength;
maxIndividualVariance *= unitBarWidth;
float totalVariance = 0.0f;
for (size_t x = 0; x < length; ++x) {
float variance = std::abs(counters[x] - pattern[x] * unitBarWidth);
if (variance > maxIndividualVariance) {
return std::numeric_limits<float>::max();
}
totalVariance += variance;
}
return totalVariance / total;
}
template <typename Counters, typename Pattern>
static float PatternMatchVariance(const Counters& counters, const Pattern& pattern, float maxIndividualVariance) {
assert(Size(counters) == Size(pattern));
return PatternMatchVariance(std::data(counters), std::data(pattern), std::size(counters), maxIndividualVariance);
}
/**
* Attempts to decode a sequence of black/white lines into single
* digit.
*
* @param counters the counts of runs of observed black/white/black/... values
* @param patterns the list of patterns to compare the contents of counters to
* @param requireUnambiguousMatch the 'best match' must be better than all other matches
* @return The decoded digit index, -1 if no pattern matched
*/
template <typename Counters, typename Patterns>
static int DecodeDigit(const Counters& counters, const Patterns& patterns, float maxAvgVariance,
float maxIndividualVariance, bool requireUnambiguousMatch = true)
{
float bestVariance = maxAvgVariance; // worst variance we'll accept
constexpr int INVALID_MATCH = -1;
int bestMatch = INVALID_MATCH;
for (int i = 0; i < Size(patterns); i++) {
float variance = PatternMatchVariance(counters, patterns[i], maxIndividualVariance);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = i;
} else if (requireUnambiguousMatch && variance == bestVariance) {
// if we find a second 'best match' with the same variance, we can not reliably report to have a suitable match
bestMatch = INVALID_MATCH;
}
}
return bestMatch;
}
/**
* @brief NarrowWideThreshold calculates width thresholds to separate narrow and wide bars and spaces.
*
* This is useful for codes like Codabar, Code39 and ITF which distinguish between narrow and wide
* bars/spaces. Where wide ones are between 2 and 3 times as wide as the narrow ones.
*
* @param view containing one character
* @return threshold value for bars and spaces
*/
static BarAndSpaceI NarrowWideThreshold(const PatternView& view)
{
BarAndSpaceI m = {view[0], view[1]};
BarAndSpaceI M = m;
for (int i = 2; i < view.size(); ++i)
UpdateMinMax(m[i], M[i], view[i]);
BarAndSpaceI res;
for (int i = 0; i < 2; ++i) {
// check that
// a) wide <= 4 * narrow
// b) bars and spaces are not more than a factor of 2 (or 3 for the max) apart from each other
if (M[i] > 4 * (m[i] + 1) || M[i] > 3 * M[i + 1] || m[i] > 2 * (m[i + 1] + 1))
return {};
// the threshold is the average of min and max but at least 1.5 * min
res[i] = std::max((m[i] + M[i]) / 2, m[i] * 3 / 2);
}
return res;
}
/**
* @brief ToNarrowWidePattern takes a PatternView, calculates a NarrowWideThreshold and returns int where a '0' bit
* means narrow and a '1' bit means 'wide'.
*/
static int NarrowWideBitPattern(const PatternView& view)
{
const auto threshold = NarrowWideThreshold(view);
if (!threshold.isValid())
return -1;
int pattern = 0;
for (int i = 0; i < view.size(); ++i) {
if (view[i] > threshold[i] * 2)
return -1;
AppendBit(pattern, view[i] > threshold[i]);
}
return pattern;
}
/**
* @brief each bar/space is 1-4 modules wide, we have N bars/spaces, they are SUM modules wide in total
*/
template <int LEN, int SUM>
static int OneToFourBitPattern(const PatternView& view)
{
// TODO: make sure none of the elements in the normalized pattern exceeds 4
return ToInt(NormalizedPattern<LEN, SUM>(view));
}
/**
* @brief Lookup the pattern in the table and return the character in alphabet at the same index.
* @returns 0 if pattern is not found. Used to be -1 but that fails on systems where char is unsigned.
*/
template<typename INDEX, typename ALPHABET>
static char LookupBitPattern(int pattern, const INDEX& table, const ALPHABET& alphabet)
{
int i = IndexOf(table, pattern);
return i == -1 ? 0 : alphabet[i];
}
template<typename INDEX, typename ALPHABET>
static char DecodeNarrowWidePattern(const PatternView& view, const INDEX& table, const ALPHABET& alphabet)
{
return LookupBitPattern(NarrowWideBitPattern(view), table, alphabet);
}
};
template<typename Range>
Barcode DecodeSingleRow(const RowReader& reader, const Range& range)
{
PatternRow row;
GetPatternRow(range, row);
PatternView view(row);
std::unique_ptr<RowReader::DecodingState> state;
return reader.decodePattern(0, view, state);
}
} // OneD
} // ZXing