Merged master to feature/image-source
@@ -567,7 +567,7 @@ that node does not support web-workers out of the box. Therefore the config
|
|||||||
property `numOfWorkers` must be explicitly set to `0`.
|
property `numOfWorkers` must be explicitly set to `0`.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var Quagga = require('quagga');
|
var Quagga = require('quagga').default;
|
||||||
|
|
||||||
Quagga.decodeSingle({
|
Quagga.decodeSingle({
|
||||||
src: "image-abc-123.jpg",
|
src: "image-abc-123.jpg",
|
||||||
|
|||||||
@@ -51,7 +51,9 @@
|
|||||||
<option value="upc">UPC</option>
|
<option value="upc">UPC</option>
|
||||||
<option value="upc_e">UPC-E</option>
|
<option value="upc_e">UPC-E</option>
|
||||||
<option value="codabar">Codabar</option>
|
<option value="codabar">Codabar</option>
|
||||||
<option value="i2of5">ITF</option>
|
<option value="i2of5">Interleaved 2 of 5</option>
|
||||||
|
<option value="2of5">Standard 2 of 5</option>
|
||||||
|
<option value="code_93">Code 93</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
|
|||||||
@@ -43,7 +43,9 @@
|
|||||||
<option value="upc">UPC</option>
|
<option value="upc">UPC</option>
|
||||||
<option value="upc_e">UPC-E</option>
|
<option value="upc_e">UPC-E</option>
|
||||||
<option value="codabar">Codabar</option>
|
<option value="codabar">Codabar</option>
|
||||||
<option value="i2of5">ITF</option>
|
<option value="i2of5">Interleaved 2 of 5</option>
|
||||||
|
<option value="2of5">Standard 2 of 5</option>
|
||||||
|
<option value="code_93">Code 93</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
|
|||||||
@@ -1,4 +1,26 @@
|
|||||||
$(function() {
|
$(function() {
|
||||||
|
var resultCollector = Quagga.ResultCollector.create({
|
||||||
|
capture: true,
|
||||||
|
capacity: 20,
|
||||||
|
blacklist: [{
|
||||||
|
code: "WIWV8ETQZ1", format: "code_93"
|
||||||
|
}, {
|
||||||
|
code: "EH3C-%GU23RK3", format: "code_93"
|
||||||
|
}, {
|
||||||
|
code: "O308SIHQOXN5SA/PJ", format: "code_93"
|
||||||
|
}, {
|
||||||
|
code: "DG7Q$TV8JQ/EN", format: "code_93"
|
||||||
|
}, {
|
||||||
|
code: "VOFD1DB5A.1F6QU", format: "code_93"
|
||||||
|
}, {
|
||||||
|
code: "4SO64P4X8 U4YUU1T-", format: "code_93"
|
||||||
|
}],
|
||||||
|
filter: function(codeResult) {
|
||||||
|
// only store results which match this constraint
|
||||||
|
// e.g.: codeResult
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
var App = {
|
var App = {
|
||||||
init : function() {
|
init : function() {
|
||||||
this.overlay = document.querySelector('#interactive canvas.drawing');
|
this.overlay = document.querySelector('#interactive canvas.drawing');
|
||||||
@@ -183,7 +205,7 @@ $(function() {
|
|||||||
halfSample: true
|
halfSample: true
|
||||||
},
|
},
|
||||||
numOfWorkers: 2,
|
numOfWorkers: 2,
|
||||||
frequency: 2,
|
frequency: 5,
|
||||||
decoder: {
|
decoder: {
|
||||||
readers : [{
|
readers : [{
|
||||||
format: "code_128_reader",
|
format: "code_128_reader",
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
var Quagga = require('../lib/quagga').default;
|
||||||
|
|
||||||
|
var buffer = require('fs').readFileSync('../test/fixtures/code_128/image-001.jpg');
|
||||||
|
|
||||||
|
decode(buffer);
|
||||||
|
function decode(buff){
|
||||||
|
Quagga.decodeSingle({
|
||||||
|
src: buff,
|
||||||
|
numOfWorkers: 0,
|
||||||
|
inputStream: {
|
||||||
|
mime: "image/jpeg",
|
||||||
|
size: 800,
|
||||||
|
area: {
|
||||||
|
top: "10%",
|
||||||
|
right: "5%",
|
||||||
|
left: "5%",
|
||||||
|
bottom: "10%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, function(result) {
|
||||||
|
if (result.codeResult) {
|
||||||
|
console.log("result", result.codeResult.code);
|
||||||
|
} else {
|
||||||
|
console.log("not detected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -45,6 +45,9 @@
|
|||||||
<option value="upc_e">UPC-E</option>
|
<option value="upc_e">UPC-E</option>
|
||||||
<option value="codabar">Codabar</option>
|
<option value="codabar">Codabar</option>
|
||||||
<option value="i2of5">I2of5</option>
|
<option value="i2of5">I2of5</option>
|
||||||
|
<option value="i2of5">Interleaved 2 of 5</option>
|
||||||
|
<option value="2of5">Standard 2 of 5</option>
|
||||||
|
<option value="code_93">Code 93</option>
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ module.exports = function(config) {
|
|||||||
reporters: ['progress'],
|
reporters: ['progress'],
|
||||||
port: 9876,
|
port: 9876,
|
||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_INFO,
|
logLevel: config.LOG_INFO, // LOG_DEBUG
|
||||||
autoWatch: true,
|
autoWatch: true,
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome'],
|
||||||
singleRun: false
|
singleRun: false
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ InputStream.createImageStream = function() {
|
|||||||
|
|
||||||
function loadImages() {
|
function loadImages() {
|
||||||
loaded = false;
|
loaded = false;
|
||||||
GetPixels(baseUrl, function(err, pixels) {
|
GetPixels(baseUrl, _config.mime, function(err, pixels) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ export function grayAndHalfSampleFromCanvasData(canvasData, size, outArray) {
|
|||||||
|
|
||||||
while (bottomRowIdx < endIdx) {
|
while (bottomRowIdx < endIdx) {
|
||||||
for ( i = 0; i < outWidth; i++) {
|
for ( i = 0; i < outWidth; i++) {
|
||||||
outArray[outImgIdx] = Math.floor((
|
outArray[outImgIdx] = (
|
||||||
(0.299 * canvasData[topRowIdx * 4 + 0] +
|
(0.299 * canvasData[topRowIdx * 4 + 0] +
|
||||||
0.587 * canvasData[topRowIdx * 4 + 1] +
|
0.587 * canvasData[topRowIdx * 4 + 1] +
|
||||||
0.114 * canvasData[topRowIdx * 4 + 2]) +
|
0.114 * canvasData[topRowIdx * 4 + 2]) +
|
||||||
@@ -500,7 +500,7 @@ export function grayAndHalfSampleFromCanvasData(canvasData, size, outArray) {
|
|||||||
0.114 * canvasData[(bottomRowIdx) * 4 + 2]) +
|
0.114 * canvasData[(bottomRowIdx) * 4 + 2]) +
|
||||||
(0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] +
|
(0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] +
|
||||||
0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] +
|
0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] +
|
||||||
0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4);
|
0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4;
|
||||||
outImgIdx++;
|
outImgIdx++;
|
||||||
topRowIdx = topRowIdx + 2;
|
topRowIdx = topRowIdx + 2;
|
||||||
bottomRowIdx = bottomRowIdx + 2;
|
bottomRowIdx = bottomRowIdx + 2;
|
||||||
@@ -521,8 +521,8 @@ export function computeGray(imageData, outArray, config) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < l; i++) {
|
for (i = 0; i < l; i++) {
|
||||||
outArray[i] = Math.floor(
|
outArray[i] =
|
||||||
0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]);
|
0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import EAN2Reader from '../reader/ean_2_reader';
|
|||||||
import EAN5Reader from '../reader/ean_5_reader';
|
import EAN5Reader from '../reader/ean_5_reader';
|
||||||
import UPCEReader from '../reader/upc_e_reader';
|
import UPCEReader from '../reader/upc_e_reader';
|
||||||
import I2of5Reader from '../reader/i2of5_reader';
|
import I2of5Reader from '../reader/i2of5_reader';
|
||||||
|
import TwoOfFiveReader from '../reader/2of5_reader';
|
||||||
|
import Code93Reader from '../reader/code_93_reader';
|
||||||
|
|
||||||
const READERS = {
|
const READERS = {
|
||||||
code_128_reader: Code128Reader,
|
code_128_reader: Code128Reader,
|
||||||
@@ -23,7 +25,9 @@ const READERS = {
|
|||||||
codabar_reader: CodabarReader,
|
codabar_reader: CodabarReader,
|
||||||
upc_reader: UPCReader,
|
upc_reader: UPCReader,
|
||||||
upc_e_reader: UPCEReader,
|
upc_e_reader: UPCEReader,
|
||||||
i2of5_reader: I2of5Reader
|
i2of5_reader: I2of5Reader,
|
||||||
|
'2of5_reader': TwoOfFiveReader,
|
||||||
|
code_93_reader: Code93Reader
|
||||||
};
|
};
|
||||||
export default {
|
export default {
|
||||||
create: function(config) {
|
create: function(config) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function waitForVideo(video, stream) {
|
|||||||
|
|
||||||
function checkVideo() {
|
function checkVideo() {
|
||||||
if (attempts > 0) {
|
if (attempts > 0) {
|
||||||
if (video.videoWidth > 0 && video.videoHeight > 0) {
|
if (video.videoWidth > 10 && video.videoHeight > 10) {
|
||||||
if (ENV.development) {
|
if (ENV.development) {
|
||||||
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
|
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,257 @@
|
|||||||
|
import BarcodeReader from './barcode_reader';
|
||||||
|
|
||||||
|
function TwoOfFiveReader(opts) {
|
||||||
|
BarcodeReader.call(this, opts);
|
||||||
|
this.barSpaceRatio = [1, 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var N = 1,
|
||||||
|
W = 3,
|
||||||
|
properties = {
|
||||||
|
START_PATTERN: {value: [W, N, W, N, N, N]},
|
||||||
|
STOP_PATTERN: {value: [W, N, N, N, W]},
|
||||||
|
CODE_PATTERN: {value: [
|
||||||
|
[N, N, W, W, N],
|
||||||
|
[W, N, N, N, W],
|
||||||
|
[N, W, N, N, W],
|
||||||
|
[W, W, N, N, N],
|
||||||
|
[N, N, W, N, W],
|
||||||
|
[W, N, W, N, N],
|
||||||
|
[N, W, W, N, N],
|
||||||
|
[N, N, N, W, W],
|
||||||
|
[W, N, N, W, N],
|
||||||
|
[N, W, N, W, N]
|
||||||
|
]},
|
||||||
|
SINGLE_CODE_ERROR: {value: 0.78, writable: true},
|
||||||
|
AVG_CODE_ERROR: {value: 0.30, writable: true},
|
||||||
|
FORMAT: {value: "2of5"}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startPatternLength = properties.START_PATTERN.value.reduce((sum, val) => sum + val, 0);
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype = Object.create(BarcodeReader.prototype, properties);
|
||||||
|
TwoOfFiveReader.prototype.constructor = TwoOfFiveReader;
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) {
|
||||||
|
var counter = [],
|
||||||
|
self = this,
|
||||||
|
i,
|
||||||
|
counterPos = 0,
|
||||||
|
bestMatch = {
|
||||||
|
error: Number.MAX_VALUE,
|
||||||
|
code: -1,
|
||||||
|
start: 0,
|
||||||
|
end: 0
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
j,
|
||||||
|
sum,
|
||||||
|
epsilon = self.AVG_CODE_ERROR;
|
||||||
|
|
||||||
|
isWhite = isWhite || false;
|
||||||
|
tryHarder = tryHarder || false;
|
||||||
|
|
||||||
|
if (!offset) {
|
||||||
|
offset = self._nextSet(self._row);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( i = 0; i < pattern.length; i++) {
|
||||||
|
counter[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( i = offset; i < self._row.length; i++) {
|
||||||
|
if (self._row[i] ^ isWhite) {
|
||||||
|
counter[counterPos]++;
|
||||||
|
} else {
|
||||||
|
if (counterPos === counter.length - 1) {
|
||||||
|
sum = 0;
|
||||||
|
for ( j = 0; j < counter.length; j++) {
|
||||||
|
sum += counter[j];
|
||||||
|
}
|
||||||
|
error = self._matchPattern(counter, pattern);
|
||||||
|
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++) {
|
||||||
|
counter[j] = counter[j + 2];
|
||||||
|
}
|
||||||
|
counter[counter.length - 2] = 0;
|
||||||
|
counter[counter.length - 1] = 0;
|
||||||
|
counterPos--;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
counterPos++;
|
||||||
|
}
|
||||||
|
counter[counterPos] = 1;
|
||||||
|
isWhite = !isWhite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._findStart = function() {
|
||||||
|
var self = this,
|
||||||
|
leadingWhitespaceStart,
|
||||||
|
offset = self._nextSet(self._row),
|
||||||
|
startInfo,
|
||||||
|
narrowBarWidth = 1;
|
||||||
|
|
||||||
|
while (!startInfo) {
|
||||||
|
startInfo = self._findPattern(self.START_PATTERN, offset, false, true);
|
||||||
|
if (!startInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / startPatternLength);
|
||||||
|
leadingWhitespaceStart = startInfo.start - narrowBarWidth * 5;
|
||||||
|
if (leadingWhitespaceStart >= 0) {
|
||||||
|
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
|
||||||
|
return startInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset = startInfo.end;
|
||||||
|
startInfo = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._verifyTrailingWhitespace = function(endInfo) {
|
||||||
|
var self = this,
|
||||||
|
trailingWhitespaceEnd;
|
||||||
|
|
||||||
|
trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2);
|
||||||
|
if (trailingWhitespaceEnd < self._row.length) {
|
||||||
|
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
|
||||||
|
return endInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._findEnd = function() {
|
||||||
|
var self = this,
|
||||||
|
endInfo,
|
||||||
|
tmp,
|
||||||
|
offset;
|
||||||
|
|
||||||
|
self._row.reverse();
|
||||||
|
offset = self._nextSet(self._row);
|
||||||
|
endInfo = self._findPattern(self.STOP_PATTERN, offset, false, true);
|
||||||
|
self._row.reverse();
|
||||||
|
|
||||||
|
if (endInfo === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse numbers
|
||||||
|
tmp = endInfo.start;
|
||||||
|
endInfo.start = self._row.length - endInfo.end;
|
||||||
|
endInfo.end = self._row.length - tmp;
|
||||||
|
|
||||||
|
return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._decodeCode = function(counter) {
|
||||||
|
var j,
|
||||||
|
self = this,
|
||||||
|
sum = 0,
|
||||||
|
normalized,
|
||||||
|
error,
|
||||||
|
epsilon = self.AVG_CODE_ERROR,
|
||||||
|
code,
|
||||||
|
bestMatch = {
|
||||||
|
error: Number.MAX_VALUE,
|
||||||
|
code: -1,
|
||||||
|
start: 0,
|
||||||
|
end: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for ( j = 0; j < counter.length; j++) {
|
||||||
|
sum += counter[j];
|
||||||
|
}
|
||||||
|
for (code = 0; code < self.CODE_PATTERN.length; code++) {
|
||||||
|
error = self._matchPattern(counter, self.CODE_PATTERN[code]);
|
||||||
|
if (error < bestMatch.error) {
|
||||||
|
bestMatch.code = code;
|
||||||
|
bestMatch.error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestMatch.error < epsilon) {
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._decodePayload = function(counters, result, decodedCodes) {
|
||||||
|
var i,
|
||||||
|
self = this,
|
||||||
|
pos = 0,
|
||||||
|
counterLength = counters.length,
|
||||||
|
counter = [0, 0, 0, 0, 0],
|
||||||
|
code;
|
||||||
|
|
||||||
|
while (pos < counterLength) {
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
counter[i] = counters[pos] * this.barSpaceRatio[0];
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
code = self._decodeCode(counter);
|
||||||
|
if (!code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
result.push(code.code + "");
|
||||||
|
decodedCodes.push(code);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._verifyCounterLength = function(counters) {
|
||||||
|
return (counters.length % 10 === 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
TwoOfFiveReader.prototype._decode = function() {
|
||||||
|
var startInfo,
|
||||||
|
endInfo,
|
||||||
|
self = this,
|
||||||
|
code,
|
||||||
|
result = [],
|
||||||
|
decodedCodes = [],
|
||||||
|
counters;
|
||||||
|
|
||||||
|
startInfo = self._findStart();
|
||||||
|
if (!startInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
decodedCodes.push(startInfo);
|
||||||
|
|
||||||
|
endInfo = self._findEnd();
|
||||||
|
if (!endInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
counters = self._fillCounters(startInfo.end, endInfo.start, false);
|
||||||
|
if (!self._verifyCounterLength(counters)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
code = self._decodePayload(counters, result, decodedCodes);
|
||||||
|
if (!code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (result.length < 5) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedCodes.push(endInfo);
|
||||||
|
return {
|
||||||
|
code: result.join(""),
|
||||||
|
start: startInfo.start,
|
||||||
|
end: endInfo.end,
|
||||||
|
startInfo: startInfo,
|
||||||
|
decodedCodes: decodedCodes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TwoOfFiveReader;
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import ArrayHelper from '../common/array_helper';
|
||||||
|
|
||||||
function BarcodeReader(config, supplements) {
|
function BarcodeReader(config, supplements) {
|
||||||
this._row = [];
|
this._row = [];
|
||||||
this.config = config || {};
|
this.config = config || {};
|
||||||
@@ -195,6 +197,33 @@ BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) {
|
|||||||
return counters;
|
return counters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BarcodeReader.prototype._toCounters = function(start, counter) {
|
||||||
|
var self = this,
|
||||||
|
numCounters = counter.length,
|
||||||
|
end = self._row.length,
|
||||||
|
isWhite = !self._row[start],
|
||||||
|
i,
|
||||||
|
counterPos = 0;
|
||||||
|
|
||||||
|
ArrayHelper.init(counter, 0);
|
||||||
|
|
||||||
|
for ( i = start; i < end; i++) {
|
||||||
|
if (self._row[i] ^ isWhite) {
|
||||||
|
counter[counterPos]++;
|
||||||
|
} else {
|
||||||
|
counterPos++;
|
||||||
|
if (counterPos === numCounters) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
counter[counterPos] = 1;
|
||||||
|
isWhite = !isWhite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter;
|
||||||
|
};
|
||||||
|
|
||||||
Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
|
Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
|
||||||
value: 'unknown',
|
value: 'unknown',
|
||||||
writeable: false
|
writeable: false
|
||||||
|
|||||||
@@ -20,33 +20,6 @@ var properties = {
|
|||||||
Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties);
|
Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties);
|
||||||
Code39Reader.prototype.constructor = Code39Reader;
|
Code39Reader.prototype.constructor = Code39Reader;
|
||||||
|
|
||||||
Code39Reader.prototype._toCounters = function(start, counter) {
|
|
||||||
var self = this,
|
|
||||||
numCounters = counter.length,
|
|
||||||
end = self._row.length,
|
|
||||||
isWhite = !self._row[start],
|
|
||||||
i,
|
|
||||||
counterPos = 0;
|
|
||||||
|
|
||||||
ArrayHelper.init(counter, 0);
|
|
||||||
|
|
||||||
for ( i = start; i < end; i++) {
|
|
||||||
if (self._row[i] ^ isWhite) {
|
|
||||||
counter[counterPos]++;
|
|
||||||
} else {
|
|
||||||
counterPos++;
|
|
||||||
if (counterPos === numCounters) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
counter[counterPos] = 1;
|
|
||||||
isWhite = !isWhite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter;
|
|
||||||
};
|
|
||||||
|
|
||||||
Code39Reader.prototype._decode = function() {
|
Code39Reader.prototype._decode = function() {
|
||||||
var self = this,
|
var self = this,
|
||||||
counters = [0, 0, 0, 0, 0, 0, 0, 0, 0],
|
counters = [0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
import BarcodeReader from './barcode_reader';
|
||||||
|
import ArrayHelper from '../common/array_helper';
|
||||||
|
|
||||||
|
function Code93Reader() {
|
||||||
|
BarcodeReader.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALPHABETH_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
ALPHABETH_STRING: {value: ALPHABETH_STRING},
|
||||||
|
ALPHABET: {value: ALPHABETH_STRING.split('').map(char => char.charCodeAt(0))},
|
||||||
|
CHARACTER_ENCODINGS: {value: [
|
||||||
|
0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A,
|
||||||
|
0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134,
|
||||||
|
0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6,
|
||||||
|
0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, 0x12E, 0x1D4, 0x1D2, 0x1CA,
|
||||||
|
0x16E, 0x176, 0x1AE, 0x126, 0x1DA, 0x1D6, 0x132, 0x15E
|
||||||
|
]},
|
||||||
|
ASTERISK: {value: 0x15E},
|
||||||
|
FORMAT: {value: "code_93", writeable: false}
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype = Object.create(BarcodeReader.prototype, properties);
|
||||||
|
Code93Reader.prototype.constructor = Code93Reader;
|
||||||
|
|
||||||
|
Code93Reader.prototype._decode = function() {
|
||||||
|
var self = this,
|
||||||
|
counters = [0, 0, 0, 0, 0, 0],
|
||||||
|
result = [],
|
||||||
|
start = self._findStart(),
|
||||||
|
decodedChar,
|
||||||
|
lastStart,
|
||||||
|
pattern,
|
||||||
|
nextStart;
|
||||||
|
|
||||||
|
if (!start) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
nextStart = self._nextSet(self._row, start.end);
|
||||||
|
|
||||||
|
do {
|
||||||
|
counters = self._toCounters(nextStart, counters);
|
||||||
|
pattern = self._toPattern(counters);
|
||||||
|
if (pattern < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
decodedChar = self._patternToChar(pattern);
|
||||||
|
if (decodedChar < 0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
result.push(decodedChar);
|
||||||
|
lastStart = nextStart;
|
||||||
|
nextStart += ArrayHelper.sum(counters);
|
||||||
|
nextStart = self._nextSet(self._row, nextStart);
|
||||||
|
} while (decodedChar !== '*');
|
||||||
|
result.pop();
|
||||||
|
|
||||||
|
if (!result.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self._verifyEnd(lastStart, nextStart, counters)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self._verifyChecksums(result)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.slice(0, result.length - 2);
|
||||||
|
if ((result = self._decodeExtended(result)) === null) {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: result.join(""),
|
||||||
|
start: start.start,
|
||||||
|
end: nextStart,
|
||||||
|
startInfo: start,
|
||||||
|
decodedCodes: result
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._verifyEnd = function(lastStart, nextStart) {
|
||||||
|
if (lastStart === nextStart || !this._row[nextStart]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._patternToChar = function(pattern) {
|
||||||
|
var i,
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) {
|
||||||
|
if (self.CHARACTER_ENCODINGS[i] === pattern) {
|
||||||
|
return String.fromCharCode(self.ALPHABET[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._toPattern = function(counters) {
|
||||||
|
const numCounters = counters.length;
|
||||||
|
let pattern = 0;
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < numCounters; i++) {
|
||||||
|
sum += counters[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numCounters; i++) {
|
||||||
|
let normalized = Math.round(counters[i] * 9 / sum);
|
||||||
|
if (normalized < 1 || normalized > 4) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((i & 1) === 0) {
|
||||||
|
for (let j = 0; j < normalized; j++) {
|
||||||
|
pattern = (pattern << 1) | 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pattern <<= normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._findStart = function() {
|
||||||
|
var self = this,
|
||||||
|
offset = self._nextSet(self._row),
|
||||||
|
patternStart = offset,
|
||||||
|
counter = [0, 0, 0, 0, 0, 0],
|
||||||
|
counterPos = 0,
|
||||||
|
isWhite = false,
|
||||||
|
i,
|
||||||
|
j,
|
||||||
|
whiteSpaceMustStart;
|
||||||
|
|
||||||
|
for ( i = offset; i < self._row.length; i++) {
|
||||||
|
if (self._row[i] ^ isWhite) {
|
||||||
|
counter[counterPos]++;
|
||||||
|
} else {
|
||||||
|
if (counterPos === counter.length - 1) {
|
||||||
|
// find start pattern
|
||||||
|
if (self._toPattern(counter) === self.ASTERISK) {
|
||||||
|
whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4)));
|
||||||
|
if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) {
|
||||||
|
return {
|
||||||
|
start: patternStart,
|
||||||
|
end: i
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patternStart += counter[0] + counter[1];
|
||||||
|
for ( j = 0; j < 4; j++) {
|
||||||
|
counter[j] = counter[j + 2];
|
||||||
|
}
|
||||||
|
counter[4] = 0;
|
||||||
|
counter[5] = 0;
|
||||||
|
counterPos--;
|
||||||
|
} else {
|
||||||
|
counterPos++;
|
||||||
|
}
|
||||||
|
counter[counterPos] = 1;
|
||||||
|
isWhite = !isWhite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._decodeExtended = function(charArray) {
|
||||||
|
const length = charArray.length;
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const char = charArray[i];
|
||||||
|
if (char >= 'a' && char <= 'd') {
|
||||||
|
if (i > (length - 2)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const nextChar = charArray[++i];
|
||||||
|
const nextCharCode = nextChar.charCodeAt(0);
|
||||||
|
let decodedChar;
|
||||||
|
switch (char) {
|
||||||
|
case 'a':
|
||||||
|
if (nextChar >= 'A' && nextChar <= 'Z') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode - 64);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
if (nextChar >= 'A' && nextChar <= 'E') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode - 38);
|
||||||
|
} else if (nextChar >= 'F' && nextChar <= 'J') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode - 11);
|
||||||
|
} else if (nextChar >= 'K' && nextChar <= 'O') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode + 16);
|
||||||
|
} else if (nextChar >= 'P' && nextChar <= 'S') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode + 43);
|
||||||
|
} else if (nextChar >= 'T' && nextChar <= 'Z') {
|
||||||
|
decodedChar = String.fromCharCode(127);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
if (nextChar >= 'A' && nextChar <= 'O') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode - 32);
|
||||||
|
} else if (nextChar === 'Z') {
|
||||||
|
decodedChar = ':';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (nextChar >= 'A' && nextChar <= 'Z') {
|
||||||
|
decodedChar = String.fromCharCode(nextCharCode + 32);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.push(decodedChar);
|
||||||
|
} else {
|
||||||
|
result.push(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._verifyChecksums = function(charArray) {
|
||||||
|
return this._matchCheckChar(charArray, charArray.length - 2, 20)
|
||||||
|
&& this._matchCheckChar(charArray, charArray.length - 1, 15);
|
||||||
|
};
|
||||||
|
|
||||||
|
Code93Reader.prototype._matchCheckChar = function(charArray, index, maxWeight) {
|
||||||
|
const arrayToCheck = charArray.slice(0, index);
|
||||||
|
const length = arrayToCheck.length;
|
||||||
|
const weightedSums = arrayToCheck.reduce((sum, char, i) => {
|
||||||
|
const weight = (((i * -1) + (length - 1)) % maxWeight) + 1;
|
||||||
|
const value = this.ALPHABET.indexOf(char.charCodeAt(0));
|
||||||
|
return sum + (weight * value);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const checkChar = this.ALPHABET[(weightedSums % 47)];
|
||||||
|
return checkChar === charArray[index].charCodeAt(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Code93Reader;
|
||||||
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 140 KiB |
@@ -308,4 +308,78 @@ describe('decodeSingle', function () {
|
|||||||
});
|
});
|
||||||
_runTestSet(testSet, config);
|
_runTestSet(testSet, config);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("2of5", function() {
|
||||||
|
var config = config = {
|
||||||
|
inputStream: {
|
||||||
|
size: 800,
|
||||||
|
singleChannel: false
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
patchSize: "medium",
|
||||||
|
halfSample: true
|
||||||
|
},
|
||||||
|
numOfWorkers: 0,
|
||||||
|
decoder: {
|
||||||
|
readers: ["2of5_reader"]
|
||||||
|
},
|
||||||
|
locate: true,
|
||||||
|
src: null
|
||||||
|
},
|
||||||
|
testSet = [
|
||||||
|
{"name": "image-001.jpg", "result": "9577149002"},
|
||||||
|
{"name": "image-002.jpg", "result": "9577149002"},
|
||||||
|
{"name": "image-003.jpg", "result": "5776158811"},
|
||||||
|
{"name": "image-004.jpg", "result": "0463381455"},
|
||||||
|
{"name": "image-005.jpg", "result": "3261594101"},
|
||||||
|
{"name": "image-006.jpg", "result": "3261594101"},
|
||||||
|
{"name": "image-007.jpg", "result": "3261594101"},
|
||||||
|
{"name": "image-008.jpg", "result": "6730705801"},
|
||||||
|
{"name": "image-009.jpg", "result": "5776158811"},
|
||||||
|
{"name": "image-010.jpg", "result": "5776158811"}
|
||||||
|
];
|
||||||
|
|
||||||
|
testSet.forEach(function(sample) {
|
||||||
|
sample.format = "2of5";
|
||||||
|
});
|
||||||
|
|
||||||
|
_runTestSet(testSet, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("code_93", function() {
|
||||||
|
var config = config = {
|
||||||
|
inputStream: {
|
||||||
|
size: 800,
|
||||||
|
singleChannel: false
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
patchSize: "large",
|
||||||
|
halfSample: true
|
||||||
|
},
|
||||||
|
numOfWorkers: 0,
|
||||||
|
decoder: {
|
||||||
|
readers: ["code_93_reader"]
|
||||||
|
},
|
||||||
|
locate: true,
|
||||||
|
src: null
|
||||||
|
},
|
||||||
|
testSet = [
|
||||||
|
{"name": "image-001.jpg", "result": "WIWV8ETQZ1"},
|
||||||
|
{"name": "image-002.jpg", "result": "EH3C-%GU23RK3"},
|
||||||
|
{"name": "image-003.jpg", "result": "O308SIHQOXN5SA/PJ"},
|
||||||
|
{"name": "image-004.jpg", "result": "DG7Q$TV8JQ/EN"},
|
||||||
|
{"name": "image-005.jpg", "result": "DG7Q$TV8JQ/EN"},
|
||||||
|
{"name": "image-006.jpg", "result": "O308SIHQOXN5SA/PJ"},
|
||||||
|
{"name": "image-007.jpg", "result": "VOFD1DB5A.1F6QU"},
|
||||||
|
{"name": "image-008.jpg", "result": "WIWV8ETQZ1"},
|
||||||
|
{"name": "image-009.jpg", "result": "4SO64P4X8 U4YUU1T-"},
|
||||||
|
{"name": "image-010.jpg", "result": "4SO64P4X8 U4YUU1T-"}
|
||||||
|
];
|
||||||
|
|
||||||
|
testSet.forEach(function(sample) {
|
||||||
|
sample.format = "code_93";
|
||||||
|
});
|
||||||
|
|
||||||
|
_runTestSet(testSet, config);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||