mirror of
https://github.com/serratus/quaggaJS.git
synced 2026-08-13 05:31:37 +08:00
Improved code-matching
This commit is contained in:
@@ -254,7 +254,7 @@ export default {
|
|||||||
line = getLine(box);
|
line = getLine(box);
|
||||||
lineLength = getLineLength(line);
|
lineLength = getLineLength(line);
|
||||||
lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x);
|
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){
|
if (line === null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ function BarcodeReader(config, supplements) {
|
|||||||
this._row = [];
|
this._row = [];
|
||||||
this.config = config || {};
|
this.config = config || {};
|
||||||
this.supplements = supplements;
|
this.supplements = supplements;
|
||||||
|
this.minBarWidth = 1;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,15 +20,32 @@ BarcodeReader.prototype._nextUnset = function(line, start) {
|
|||||||
return line.length;
|
return line.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
BarcodeReader.prototype._matchPattern = function(counter, code) {
|
BarcodeReader.prototype._matchPattern = function(counter, code, maxSingleError) {
|
||||||
var i,
|
var i,
|
||||||
error = 0,
|
error = 0,
|
||||||
singleError = 0,
|
singleError = 0,
|
||||||
modulo = this.MODULO,
|
sum = 0,
|
||||||
maxSingleError = this.SINGLE_CODE_ERROR || 1;
|
modulo = 0,
|
||||||
|
barWidth,
|
||||||
|
count,
|
||||||
|
scaled;
|
||||||
|
|
||||||
|
maxSingleError = maxSingleError || this.SINGLE_CODE_ERROR || 1;
|
||||||
|
|
||||||
for (i = 0; i < counter.length; i++) {
|
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) {
|
if (singleError > maxSingleError) {
|
||||||
return Number.MAX_VALUE;
|
return Number.MAX_VALUE;
|
||||||
}
|
}
|
||||||
@@ -58,15 +76,18 @@ BarcodeReader.prototype._normalize = function(counter, correction) {
|
|||||||
norm = 0,
|
norm = 0,
|
||||||
modulo = self.MODULO;
|
modulo = self.MODULO;
|
||||||
|
|
||||||
if (correction) {
|
for (i = 0; i < normalized.length; i++) {
|
||||||
self._correct(counter, correction);
|
normalized[i] = counter[i] < this.minBarWidth ? counter[i] = this.minBarWidth : counter[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < counter.length; i++) {
|
if (correction) {
|
||||||
sum += counter[i];
|
self._correct(normalized, correction);
|
||||||
|
}
|
||||||
|
for (i = 0; i < normalized.length; i++) {
|
||||||
|
sum += normalized[i];
|
||||||
}
|
}
|
||||||
ratio = sum / (modulo - numOnes);
|
ratio = sum / (modulo - numOnes);
|
||||||
for (i = 0; i < counter.length; i++) {
|
for (i = 0; i < normalized.length; i++) {
|
||||||
norm = counter[i] === 1 ? counter[i] : counter[i] / ratio;
|
norm = normalized[i] === 1 ? normalized[i] : normalized[i] / ratio;
|
||||||
normalized[i] = norm;
|
normalized[i] = norm;
|
||||||
}
|
}
|
||||||
return normalized;
|
return normalized;
|
||||||
|
|||||||
@@ -19,13 +19,15 @@ EAN2Reader.prototype.decode = function(row, start) {
|
|||||||
offset = start,
|
offset = start,
|
||||||
end = this._row.length,
|
end = this._row.length,
|
||||||
code,
|
code,
|
||||||
result = [];
|
result = [],
|
||||||
|
decodedCodes = [];
|
||||||
|
|
||||||
for (i = 0; i < 2 && offset < end; i++) {
|
for (i = 0; i < 2 && offset < end; i++) {
|
||||||
code = this._decodeCode(offset);
|
code = this._decodeCode(offset);
|
||||||
if (!code) {
|
if (!code) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
decodedCodes.push(code);
|
||||||
result.push(code.code % 10);
|
result.push(code.code % 10);
|
||||||
if (code.code >= this.CODE_G_START) {
|
if (code.code >= this.CODE_G_START) {
|
||||||
codeFrequency |= 1 << (1 - i);
|
codeFrequency |= 1 << (1 - i);
|
||||||
@@ -39,8 +41,11 @@ EAN2Reader.prototype.decode = function(row, start) {
|
|||||||
if (result.length != 2 || (parseInt(result.join("")) % 4) !== codeFrequency) {
|
if (result.length != 2 || (parseInt(result.join("")) % 4) !== codeFrequency) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
console.log(result);
|
return {
|
||||||
return offset;
|
code: result.join(""),
|
||||||
|
decodedCodes,
|
||||||
|
end: code.end
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EAN2Reader;
|
export default EAN2Reader;
|
||||||
|
|||||||
@@ -21,13 +21,15 @@ EAN5Reader.prototype.decode = function(row, start) {
|
|||||||
offset = start,
|
offset = start,
|
||||||
end = this._row.length,
|
end = this._row.length,
|
||||||
code,
|
code,
|
||||||
result = [];
|
result = [],
|
||||||
|
decodedCodes = [];
|
||||||
|
|
||||||
for (i = 0; i < 5 && offset < end; i++) {
|
for (i = 0; i < 5 && offset < end; i++) {
|
||||||
code = this._decodeCode(offset);
|
code = this._decodeCode(offset);
|
||||||
if (!code) {
|
if (!code) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
decodedCodes.push(code);
|
||||||
result.push(code.code % 10);
|
result.push(code.code % 10);
|
||||||
if (code.code >= this.CODE_G_START) {
|
if (code.code >= this.CODE_G_START) {
|
||||||
codeFrequency |= 1 << (4 - i);
|
codeFrequency |= 1 << (4 - i);
|
||||||
@@ -45,8 +47,11 @@ EAN5Reader.prototype.decode = function(row, start) {
|
|||||||
if (extensionChecksum(result) !== determineCheckDigit(codeFrequency)) {
|
if (extensionChecksum(result) !== determineCheckDigit(codeFrequency)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
console.log(result);
|
return {
|
||||||
return offset;
|
code: result.join(""),
|
||||||
|
decodedCodes,
|
||||||
|
end: code.end
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function determineCheckDigit(codeFrequency) {
|
function determineCheckDigit(codeFrequency) {
|
||||||
|
|||||||
+53
-38
@@ -19,10 +19,10 @@ var properties = {
|
|||||||
CODE_L_START: {value: 0},
|
CODE_L_START: {value: 0},
|
||||||
MODULO: {value: 7},
|
MODULO: {value: 7},
|
||||||
CODE_G_START: {value: 10},
|
CODE_G_START: {value: 10},
|
||||||
START_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]},
|
START_PATTERN: {value: [1, 1, 1]},
|
||||||
STOP_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]},
|
STOP_PATTERN: {value: [1, 1, 1]},
|
||||||
MIDDLE_PATTERN: {value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7]},
|
MIDDLE_PATTERN: {value: [1, 1, 1, 1, 1]},
|
||||||
EXTENSION_START_PATTERN: {value: [1 / 4 * 7, 1 / 4 * 7, 2 / 4 * 7]},
|
EXTENSION_START_PATTERN: {value: [1, 1, 2]},
|
||||||
CODE_PATTERN: {value: [
|
CODE_PATTERN: {value: [
|
||||||
[3, 2, 1, 1],
|
[3, 2, 1, 1],
|
||||||
[2, 2, 2, 1],
|
[2, 2, 2, 1],
|
||||||
@@ -46,8 +46,8 @@ var properties = {
|
|||||||
[2, 1, 1, 3]
|
[2, 1, 1, 3]
|
||||||
]},
|
]},
|
||||||
CODE_FREQUENCY: {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]},
|
CODE_FREQUENCY: {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]},
|
||||||
SINGLE_CODE_ERROR: {value: 0.67},
|
SINGLE_CODE_ERROR: {value: 0.70},
|
||||||
AVG_CODE_ERROR: {value: 0.27},
|
AVG_CODE_ERROR: {value: 0.48},
|
||||||
FORMAT: {value: "ean_13", writeable: false}
|
FORMAT: {value: "ean_13", writeable: false}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,8 +68,7 @@ EANReader.prototype._decodeCode = function(start, coderange) {
|
|||||||
end: start
|
end: start
|
||||||
},
|
},
|
||||||
code,
|
code,
|
||||||
error,
|
error;
|
||||||
normalized;
|
|
||||||
|
|
||||||
if (!coderange) {
|
if (!coderange) {
|
||||||
coderange = self.CODE_PATTERN.length;
|
coderange = self.CODE_PATTERN.length;
|
||||||
@@ -80,21 +79,18 @@ EANReader.prototype._decodeCode = function(start, coderange) {
|
|||||||
counter[counterPos]++;
|
counter[counterPos]++;
|
||||||
} else {
|
} else {
|
||||||
if (counterPos === counter.length - 1) {
|
if (counterPos === counter.length - 1) {
|
||||||
normalized = self._normalize(counter);
|
for (code = 0; code < coderange; code++) {
|
||||||
if (normalized) {
|
error = self._matchPattern(counter, self.CODE_PATTERN[code]);
|
||||||
for (code = 0; code < coderange; code++) {
|
if (error < bestMatch.error) {
|
||||||
error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
|
bestMatch.code = code;
|
||||||
if (error < bestMatch.error) {
|
bestMatch.error = error;
|
||||||
bestMatch.code = code;
|
|
||||||
bestMatch.error = error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bestMatch.end = i;
|
|
||||||
if (bestMatch.error > self.AVG_CODE_ERROR) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return bestMatch;
|
|
||||||
}
|
}
|
||||||
|
bestMatch.end = i;
|
||||||
|
if (bestMatch.error > self.AVG_CODE_ERROR) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return bestMatch;
|
||||||
} else {
|
} else {
|
||||||
counterPos++;
|
counterPos++;
|
||||||
}
|
}
|
||||||
@@ -118,8 +114,7 @@ EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder,
|
|||||||
},
|
},
|
||||||
error,
|
error,
|
||||||
j,
|
j,
|
||||||
sum,
|
sum;
|
||||||
normalized;
|
|
||||||
|
|
||||||
if (!offset) {
|
if (!offset) {
|
||||||
offset = self._nextSet(self._row);
|
offset = self._nextSet(self._row);
|
||||||
@@ -150,16 +145,13 @@ EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder,
|
|||||||
for ( j = 0; j < counter.length; j++) {
|
for ( j = 0; j < counter.length; j++) {
|
||||||
sum += counter[j];
|
sum += counter[j];
|
||||||
}
|
}
|
||||||
normalized = self._normalize(counter);
|
error = self._matchPattern(counter, pattern);
|
||||||
if (normalized) {
|
|
||||||
error = self._matchPattern(normalized, pattern);
|
|
||||||
|
|
||||||
if (error < epsilon) {
|
if (error < epsilon) {
|
||||||
bestMatch.error = error;
|
bestMatch.error = error;
|
||||||
bestMatch.start = i - sum;
|
bestMatch.start = i - sum;
|
||||||
bestMatch.end = i;
|
bestMatch.end = i;
|
||||||
return bestMatch;
|
return bestMatch;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (tryHarder) {
|
if (tryHarder) {
|
||||||
for ( j = 0; j < counter.length - 2; j++) {
|
for ( j = 0; j < counter.length - 2; j++) {
|
||||||
@@ -285,12 +277,15 @@ EANReader.prototype._decode = function() {
|
|||||||
self = this,
|
self = this,
|
||||||
code,
|
code,
|
||||||
result = [],
|
result = [],
|
||||||
decodedCodes = [];
|
decodedCodes = [],
|
||||||
|
resultInfo = {};
|
||||||
|
|
||||||
|
this.minBarWidth = 1;
|
||||||
startInfo = self._findStart();
|
startInfo = self._findStart();
|
||||||
if (!startInfo) {
|
if (!startInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
this.minBarWidth = (startInfo.end - startInfo.start) / 3;
|
||||||
code = {
|
code = {
|
||||||
code: startInfo.code,
|
code: startInfo.code,
|
||||||
start: startInfo.start,
|
start: startInfo.start,
|
||||||
@@ -318,6 +313,18 @@ EANReader.prototype._decode = function() {
|
|||||||
if (!ext) {
|
if (!ext) {
|
||||||
return null;
|
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 {
|
return {
|
||||||
@@ -326,24 +333,32 @@ EANReader.prototype._decode = function() {
|
|||||||
end: code.end,
|
end: code.end,
|
||||||
codeset: "",
|
codeset: "",
|
||||||
startInfo: startInfo,
|
startInfo: startInfo,
|
||||||
decodedCodes: decodedCodes
|
decodedCodes: decodedCodes,
|
||||||
|
...resultInfo
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
EANReader.prototype._decodeExtensions = function(offset) {
|
EANReader.prototype._decodeExtensions = function(offset) {
|
||||||
var i,
|
var i,
|
||||||
start = this._nextSet(this._row, offset),
|
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;
|
result;
|
||||||
|
|
||||||
if (code === null) {
|
if (startInfo === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < this.supplements.length; i++) {
|
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) {
|
if (result !== null) {
|
||||||
return result;
|
return {
|
||||||
|
code: result.code,
|
||||||
|
start,
|
||||||
|
startInfo,
|
||||||
|
end: result.end,
|
||||||
|
codeset: "",
|
||||||
|
decodedCodes: result.decodedCodes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user