From dadb8dbc9f1add2b0a1065f41eb724bfeb0065fe Mon Sep 17 00:00:00 2001 From: Christoph Oberhofer Date: Sun, 24 Apr 2016 17:26:17 +0200 Subject: [PATCH] Improved code-matching --- src/decoder/barcode_decoder.js | 2 +- src/reader/barcode_reader.js | 39 +++++++++++---- src/reader/ean_2_reader.js | 11 ++-- src/reader/ean_5_reader.js | 11 ++-- src/reader/ean_reader.js | 91 ++++++++++++++++++++-------------- 5 files changed, 100 insertions(+), 54 deletions(-) diff --git a/src/decoder/barcode_decoder.js b/src/decoder/barcode_decoder.js index 4ff70a6..4c68ee6 100644 --- a/src/decoder/barcode_decoder.js +++ b/src/decoder/barcode_decoder.js @@ -254,7 +254,7 @@ export default { line = getLine(box); lineLength = getLineLength(line); lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); - line = getExtendedLine(line, lineAngle, Math.floor(lineLength * 0.2)); + line = getExtendedLine(line, lineAngle, Math.floor(lineLength * 0.1)); if (line === null){ return null; } diff --git a/src/reader/barcode_reader.js b/src/reader/barcode_reader.js index e346d00..3eeadb1 100644 --- a/src/reader/barcode_reader.js +++ b/src/reader/barcode_reader.js @@ -2,6 +2,7 @@ function BarcodeReader(config, supplements) { this._row = []; this.config = config || {}; this.supplements = supplements; + this.minBarWidth = 1; return this; } @@ -19,15 +20,32 @@ BarcodeReader.prototype._nextUnset = function(line, start) { return line.length; }; -BarcodeReader.prototype._matchPattern = function(counter, code) { +BarcodeReader.prototype._matchPattern = function(counter, code, maxSingleError) { var i, error = 0, singleError = 0, - modulo = this.MODULO, - maxSingleError = this.SINGLE_CODE_ERROR || 1; + sum = 0, + modulo = 0, + barWidth, + count, + scaled; + + maxSingleError = maxSingleError || this.SINGLE_CODE_ERROR || 1; for (i = 0; i < counter.length; i++) { - singleError = Math.abs(code[i] - counter[i]); + sum += counter[i]; + modulo += code[i]; + } + if (sum < modulo) { + return Number.MAX_VALUE; + } + barWidth = sum / modulo; + maxSingleError *= barWidth; + + for (i = 0; i < counter.length; i++) { + count = counter[i]; + scaled = code[i] * barWidth; + singleError = Math.abs(count - scaled) / scaled; if (singleError > maxSingleError) { return Number.MAX_VALUE; } @@ -58,15 +76,18 @@ BarcodeReader.prototype._normalize = function(counter, correction) { norm = 0, modulo = self.MODULO; + for (i = 0; i < normalized.length; i++) { + normalized[i] = counter[i] < this.minBarWidth ? counter[i] = this.minBarWidth : counter[i]; + } if (correction) { - self._correct(counter, correction); + self._correct(normalized, correction); } - for (i = 0; i < counter.length; i++) { - sum += counter[i]; + for (i = 0; i < normalized.length; i++) { + sum += normalized[i]; } ratio = sum / (modulo - numOnes); - for (i = 0; i < counter.length; i++) { - norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; + for (i = 0; i < normalized.length; i++) { + norm = normalized[i] === 1 ? normalized[i] : normalized[i] / ratio; normalized[i] = norm; } return normalized; diff --git a/src/reader/ean_2_reader.js b/src/reader/ean_2_reader.js index 74bf25b..cbfdceb 100644 --- a/src/reader/ean_2_reader.js +++ b/src/reader/ean_2_reader.js @@ -19,13 +19,15 @@ EAN2Reader.prototype.decode = function(row, start) { offset = start, end = this._row.length, code, - result = []; + result = [], + decodedCodes = []; for (i = 0; i < 2 && offset < end; i++) { code = this._decodeCode(offset); if (!code) { return null; } + decodedCodes.push(code); result.push(code.code % 10); if (code.code >= this.CODE_G_START) { codeFrequency |= 1 << (1 - i); @@ -39,8 +41,11 @@ EAN2Reader.prototype.decode = function(row, start) { if (result.length != 2 || (parseInt(result.join("")) % 4) !== codeFrequency) { return null; } - console.log(result); - return offset; + return { + code: result.join(""), + decodedCodes, + end: code.end + }; }; export default EAN2Reader; diff --git a/src/reader/ean_5_reader.js b/src/reader/ean_5_reader.js index 012e8e4..6d660c9 100644 --- a/src/reader/ean_5_reader.js +++ b/src/reader/ean_5_reader.js @@ -21,13 +21,15 @@ EAN5Reader.prototype.decode = function(row, start) { offset = start, end = this._row.length, code, - result = []; + result = [], + decodedCodes = []; for (i = 0; i < 5 && offset < end; i++) { code = this._decodeCode(offset); if (!code) { return null; } + decodedCodes.push(code); result.push(code.code % 10); if (code.code >= this.CODE_G_START) { codeFrequency |= 1 << (4 - i); @@ -45,8 +47,11 @@ EAN5Reader.prototype.decode = function(row, start) { if (extensionChecksum(result) !== determineCheckDigit(codeFrequency)) { return null; } - console.log(result); - return offset; + return { + code: result.join(""), + decodedCodes, + end: code.end + }; }; function determineCheckDigit(codeFrequency) { diff --git a/src/reader/ean_reader.js b/src/reader/ean_reader.js index be7a804..9845859 100644 --- a/src/reader/ean_reader.js +++ b/src/reader/ean_reader.js @@ -19,10 +19,10 @@ var properties = { CODE_L_START: {value: 0}, MODULO: {value: 7}, CODE_G_START: {value: 10}, - START_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, - STOP_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, - MIDDLE_PATTERN: {value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7]}, - EXTENSION_START_PATTERN: {value: [1 / 4 * 7, 1 / 4 * 7, 2 / 4 * 7]}, + START_PATTERN: {value: [1, 1, 1]}, + STOP_PATTERN: {value: [1, 1, 1]}, + MIDDLE_PATTERN: {value: [1, 1, 1, 1, 1]}, + EXTENSION_START_PATTERN: {value: [1, 1, 2]}, CODE_PATTERN: {value: [ [3, 2, 1, 1], [2, 2, 2, 1], @@ -46,8 +46,8 @@ var properties = { [2, 1, 1, 3] ]}, CODE_FREQUENCY: {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]}, - SINGLE_CODE_ERROR: {value: 0.67}, - AVG_CODE_ERROR: {value: 0.27}, + SINGLE_CODE_ERROR: {value: 0.70}, + AVG_CODE_ERROR: {value: 0.48}, FORMAT: {value: "ean_13", writeable: false} }; @@ -68,8 +68,7 @@ EANReader.prototype._decodeCode = function(start, coderange) { end: start }, code, - error, - normalized; + error; if (!coderange) { coderange = self.CODE_PATTERN.length; @@ -80,21 +79,18 @@ EANReader.prototype._decodeCode = function(start, coderange) { counter[counterPos]++; } else { if (counterPos === counter.length - 1) { - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < coderange; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - bestMatch.end = i; - if (bestMatch.error > self.AVG_CODE_ERROR) { - return null; + for (code = 0; code < coderange; code++) { + error = self._matchPattern(counter, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; } - return bestMatch; } + bestMatch.end = i; + if (bestMatch.error > self.AVG_CODE_ERROR) { + return null; + } + return bestMatch; } else { counterPos++; } @@ -118,8 +114,7 @@ EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, }, error, j, - sum, - normalized; + sum; if (!offset) { offset = self._nextSet(self._row); @@ -150,16 +145,13 @@ EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, for ( j = 0; j < counter.length; j++) { sum += counter[j]; } - normalized = self._normalize(counter); - if (normalized) { - error = self._matchPattern(normalized, pattern); + error = self._matchPattern(counter, pattern); - if (error < epsilon) { - bestMatch.error = error; - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; } if (tryHarder) { for ( j = 0; j < counter.length - 2; j++) { @@ -285,12 +277,15 @@ EANReader.prototype._decode = function() { self = this, code, result = [], - decodedCodes = []; + decodedCodes = [], + resultInfo = {}; + this.minBarWidth = 1; startInfo = self._findStart(); if (!startInfo) { return null; } + this.minBarWidth = (startInfo.end - startInfo.start) / 3; code = { code: startInfo.code, start: startInfo.start, @@ -318,6 +313,18 @@ EANReader.prototype._decode = function() { if (!ext) { return null; } + let lastCode = ext.decodedCodes[ext.decodedCodes.length-1], + endInfo = { + start: lastCode.start + (((lastCode.end - lastCode.start) / 2) | 0), + end: lastCode.end + }; + if(!self._verifyTrailingWhitespace(endInfo)) { + return null; + } + resultInfo = { + supplement: ext, + code: result.join("") + ext.code + } } return { @@ -326,24 +333,32 @@ EANReader.prototype._decode = function() { end: code.end, codeset: "", startInfo: startInfo, - decodedCodes: decodedCodes + decodedCodes: decodedCodes, + ...resultInfo }; }; EANReader.prototype._decodeExtensions = function(offset) { var i, start = this._nextSet(this._row, offset), - code = this._findPattern(this.EXTENSION_START_PATTERN, start, false, false), + startInfo = this._findPattern(this.EXTENSION_START_PATTERN, start, false, false), result; - if (code === null) { + if (startInfo === null) { return null; } for (i = 0; i < this.supplements.length; i++) { - result = this.supplements[i].decode(this._row, code.end); + result = this.supplements[i].decode(this._row, startInfo.end); if (result !== null) { - return result; + return { + code: result.code, + start, + startInfo, + end: result.end, + codeset: "", + decodedCodes: result.decodedCodes + } } } return null;