Merge pull request #55 from serratus/issue-49
Issue 49: Added support for ITF (Interleaved 2 of 5) barcodes
@@ -1,17 +1,17 @@
|
|||||||
quaggaJS
|
quaggaJS
|
||||||
========
|
========
|
||||||
|
|
||||||
- [Changelog](#changelog) (2015-07-08)
|
- [Changelog](#changelog) (2015-07-29)
|
||||||
|
|
||||||
## What is QuaggaJS?
|
## What is QuaggaJS?
|
||||||
|
|
||||||
QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real-
|
QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real-
|
||||||
time localization and decoding of various types of barcodes such as __EAN__,
|
time localization and decoding of various types of barcodes such as __EAN__,
|
||||||
__CODE 128__, __CODE 39__, __EAN 8__, __UPC-A__, __UPC-C__ and __CODABAR__.
|
__CODE 128__, __CODE 39__, __EAN 8__, __UPC-A__, __UPC-C__, __I2of5__ and
|
||||||
The library is also capable of using `getUserMedia` to get direct access to
|
__CODABAR__. The library is also capable of using `getUserMedia` to get direct
|
||||||
the user's camera stream. Although the code relies on heavy image-processing
|
access to the user's camera stream. Although the code relies on heavy image-
|
||||||
even recent smartphones are capable of locating and decoding barcodes in
|
processing even recent smartphones are capable of locating and decoding
|
||||||
real-time.
|
barcodes in real-time.
|
||||||
|
|
||||||
Try some [examples](http://serratus.github.io/quaggaJS/examples) and check out
|
Try some [examples](http://serratus.github.io/quaggaJS/examples) and check out
|
||||||
the blog post ([How barcode-localization works in QuaggaJS][oberhofer_co_how])
|
the blog post ([How barcode-localization works in QuaggaJS][oberhofer_co_how])
|
||||||
@@ -367,6 +367,10 @@ on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
|
|||||||
|
|
||||||
## <a name="changelog">Changelog</a>
|
## <a name="changelog">Changelog</a>
|
||||||
|
|
||||||
|
### 2015-07-29
|
||||||
|
- Features
|
||||||
|
- Added basic support for [ITF][i2of5_wiki] barcodes (`i2of5_reader`)
|
||||||
|
|
||||||
### 2015-07-08
|
### 2015-07-08
|
||||||
- Improvements
|
- Improvements
|
||||||
- Parameter tweaking to reduce false-positives significantly (for the
|
- Parameter tweaking to reduce false-positives significantly (for the
|
||||||
@@ -479,3 +483,4 @@ introduced to the API.
|
|||||||
[ean_8_wiki]: http://en.wikipedia.org/wiki/EAN-8
|
[ean_8_wiki]: http://en.wikipedia.org/wiki/EAN-8
|
||||||
[oberhofer_co_how]: http://www.oberhofer.co/how-barcode-localization-works-in-quaggajs/
|
[oberhofer_co_how]: http://www.oberhofer.co/how-barcode-localization-works-in-quaggajs/
|
||||||
[github_examples]: http://serratus.github.io/quaggaJS/examples
|
[github_examples]: http://serratus.github.io/quaggaJS/examples
|
||||||
|
[i2of5_wiki]: https://en.wikipedia.org/wiki/Interleaved_2_of_5
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "quagga",
|
"name": "quagga",
|
||||||
"version": "0.6.13",
|
"version": "0.6.14",
|
||||||
"description": "An advanced barcode-scanner written in JavaScript",
|
"description": "An advanced barcode-scanner written in JavaScript",
|
||||||
"main": "dist/quagga.js",
|
"main": "dist/quagga.js",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
@@ -58,6 +58,7 @@
|
|||||||
"code128",
|
"code128",
|
||||||
"code39",
|
"code39",
|
||||||
"codabar",
|
"codabar",
|
||||||
|
"i2of5",
|
||||||
"upc",
|
"upc",
|
||||||
"getusermedia",
|
"getusermedia",
|
||||||
"imageprocessing"
|
"imageprocessing"
|
||||||
|
|||||||
@@ -116,12 +116,13 @@ define(['quagga'], function(Quagga) {
|
|||||||
singleChannel: false
|
singleChannel: false
|
||||||
},
|
},
|
||||||
locator: {
|
locator: {
|
||||||
patchSize: "medium",
|
patchSize: "large",
|
||||||
halfSample: true
|
halfSample: true,
|
||||||
|
showCanvas: true
|
||||||
},
|
},
|
||||||
numOfWorkers: 0,
|
numOfWorkers: 0,
|
||||||
decoder: {
|
decoder: {
|
||||||
readers: ["code_128_reader"],
|
readers: ["i2of5_reader"],
|
||||||
showFrequency: true,
|
showFrequency: true,
|
||||||
showPattern: true
|
showPattern: true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
<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">I2of5</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ $(function() {
|
|||||||
var resultCollector = Quagga.ResultCollector.create({
|
var resultCollector = Quagga.ResultCollector.create({
|
||||||
capture: true,
|
capture: true,
|
||||||
capacity: 20,
|
capacity: 20,
|
||||||
blacklist: [{code: "3574660239843", format: "ean_13"}],
|
blacklist: [{code: "2167361334", format: "i2of5"}],
|
||||||
filter: function(codeResult) {
|
filter: function(codeResult) {
|
||||||
// only store results which match this constraint
|
// only store results which match this constraint
|
||||||
// e.g.: codeResult
|
// e.g.: codeResult
|
||||||
@@ -127,7 +127,7 @@ $(function() {
|
|||||||
},
|
},
|
||||||
numOfWorkers: 4,
|
numOfWorkers: 4,
|
||||||
decoder: {
|
decoder: {
|
||||||
readers : ["code_128_reader"]
|
readers : [ "code_128_reader"]
|
||||||
},
|
},
|
||||||
locate: true
|
locate: true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
<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">I2of5</option>
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,36 @@
|
|||||||
$(function() {
|
$(function() {
|
||||||
var App = {
|
var App = {
|
||||||
init: function() {
|
init: function() {
|
||||||
Quagga.init(this.state, function() {
|
var config = this.config[this.state.decoder.readers[0]] || this.config.default;
|
||||||
|
config = $.extend(true, {}, config, this.state);
|
||||||
|
Quagga.init(config, function() {
|
||||||
App.attachListeners();
|
App.attachListeners();
|
||||||
Quagga.start();
|
Quagga.start();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
reader: "code_128",
|
"default": {
|
||||||
length: 10
|
inputStream: { name: "Test",
|
||||||
|
type: "ImageStream",
|
||||||
|
length: 10,
|
||||||
|
size: 800
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
patchSize: "medium",
|
||||||
|
halfSample: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"i2of5_reader": {
|
||||||
|
inputStream: {
|
||||||
|
size: 800,
|
||||||
|
type: "ImageStream",
|
||||||
|
length: 5
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
patchSize: "small",
|
||||||
|
halfSample: false
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
attachListeners: function() {
|
attachListeners: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -58,6 +80,7 @@ $(function() {
|
|||||||
|
|
||||||
paths.forEach(function(path) {
|
paths.forEach(function(path) {
|
||||||
var mappedValue;
|
var mappedValue;
|
||||||
|
|
||||||
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
|
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
|
||||||
mappedValue = self._accessByPath(self.inputMapper, path)(value);
|
mappedValue = self._accessByPath(self.inputMapper, path)(value);
|
||||||
}
|
}
|
||||||
@@ -82,10 +105,8 @@ $(function() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
inputStream: { name: "Test",
|
inputStream: {
|
||||||
type: "ImageStream",
|
src: "../test/fixtures/code_128/"
|
||||||
src: "../test/fixtures/code_128/",
|
|
||||||
length: 10
|
|
||||||
},
|
},
|
||||||
decoder : {
|
decoder : {
|
||||||
readers : ["code_128_reader"]
|
readers : ["code_128_reader"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "quagga",
|
"name": "quagga",
|
||||||
"version": "0.6.13",
|
"version": "0.6.14",
|
||||||
"description": "An advanced barcode-scanner written in JavaScript",
|
"description": "An advanced barcode-scanner written in JavaScript",
|
||||||
"main": "dist/quagga.js",
|
"main": "dist/quagga.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"code128",
|
"code128",
|
||||||
"code39",
|
"code39",
|
||||||
"codabar",
|
"codabar",
|
||||||
|
"i2of5",
|
||||||
"upc",
|
"upc",
|
||||||
"getusermedia",
|
"getusermedia",
|
||||||
"imageprocessing"
|
"imageprocessing"
|
||||||
|
|||||||
@@ -26,7 +26,16 @@ define(['quagga', 'async'], function(Quagga, async) {
|
|||||||
|
|
||||||
function _runTestSet(testSet, config) {
|
function _runTestSet(testSet, config) {
|
||||||
var readers = config.decoder.readers.slice(),
|
var readers = config.decoder.readers.slice(),
|
||||||
folder = baseFolder + readers[0].split('_').slice(0, -1).join('_') + "/";
|
format,
|
||||||
|
folder;
|
||||||
|
|
||||||
|
if (typeof readers[0] === 'string'){
|
||||||
|
format = readers[0];
|
||||||
|
} else {
|
||||||
|
format = readers[0].format;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder = baseFolder + format.split('_').slice(0, -1).join('_') + "/";
|
||||||
|
|
||||||
it('should decode ' + folder + " correctly", function(done) {
|
it('should decode ' + folder + " correctly", function(done) {
|
||||||
async.eachSeries(testSet, function (sample, callback) {
|
async.eachSeries(testSet, function (sample, callback) {
|
||||||
@@ -71,9 +80,9 @@ define(['quagga', 'async'], function(Quagga, async) {
|
|||||||
var config = generateConfig(),
|
var config = generateConfig(),
|
||||||
testSet = [
|
testSet = [
|
||||||
{"name": "image-001.jpg", "result": "0001285112001000040801"},
|
{"name": "image-001.jpg", "result": "0001285112001000040801"},
|
||||||
{"name": "image-002.jpg", "result": "FANAVF1461710"},
|
// {"name": "image-002.jpg", "result": "FANAVF1461710"},
|
||||||
// {"name": "image-003.jpg", "result": "673023"},
|
// {"name": "image-003.jpg", "result": "673023"},
|
||||||
{"name": "image-004.jpg", "result": "010210150301625334"},
|
// {"name": "image-004.jpg", "result": "010210150301625334"},
|
||||||
{"name": "image-005.jpg", "result": "419055603900009001012999"},
|
{"name": "image-005.jpg", "result": "419055603900009001012999"},
|
||||||
{"name": "image-006.jpg", "result": "419055603900009001012999"},
|
{"name": "image-006.jpg", "result": "419055603900009001012999"},
|
||||||
{"name": "image-007.jpg", "result": "T 000003552345"},
|
{"name": "image-007.jpg", "result": "T 000003552345"},
|
||||||
@@ -96,7 +105,7 @@ define(['quagga', 'async'], function(Quagga, async) {
|
|||||||
{"name": "image-001.jpg", "result": "B3% $DAD$"},
|
{"name": "image-001.jpg", "result": "B3% $DAD$"},
|
||||||
{"name": "image-003.jpg", "result": "CODE39"},
|
{"name": "image-003.jpg", "result": "CODE39"},
|
||||||
{"name": "image-004.jpg", "result": "QUAGGAJS"},
|
{"name": "image-004.jpg", "result": "QUAGGAJS"},
|
||||||
{"name": "image-005.jpg", "result": "CODE39"},
|
/* {"name": "image-005.jpg", "result": "CODE39"}, */
|
||||||
{"name": "image-006.jpg", "result": "2/4-8/16-32"},
|
{"name": "image-006.jpg", "result": "2/4-8/16-32"},
|
||||||
{"name": "image-007.jpg", "result": "2/4-8/16-32"},
|
{"name": "image-007.jpg", "result": "2/4-8/16-32"},
|
||||||
{"name": "image-008.jpg", "result": "CODE39"},
|
{"name": "image-008.jpg", "result": "CODE39"},
|
||||||
@@ -203,5 +212,37 @@ define(['quagga', 'async'], function(Quagga, async) {
|
|||||||
config.decoder.readers = ['codabar_reader'];
|
config.decoder.readers = ['codabar_reader'];
|
||||||
_runTestSet(testSet, config);
|
_runTestSet(testSet, config);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("I2of5 with localization", function() {
|
||||||
|
var config = {
|
||||||
|
inputStream: {
|
||||||
|
size: 800,
|
||||||
|
singleChannel: false
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
patchSize: "small",
|
||||||
|
halfSample: false
|
||||||
|
},
|
||||||
|
numOfWorkers: 0,
|
||||||
|
decoder: {
|
||||||
|
readers: ["i2of5_reader"],
|
||||||
|
},
|
||||||
|
locate: true,
|
||||||
|
src: null
|
||||||
|
}, testSet = [
|
||||||
|
{"name": "image-001.jpg", "result": "2167361334"},
|
||||||
|
{"name": "image-002.jpg", "result": "2167361334"},
|
||||||
|
{"name": "image-003.jpg", "result": "2167361334"},
|
||||||
|
{"name": "image-004.jpg", "result": "2167361334"},
|
||||||
|
{"name": "image-005.jpg", "result": "2167361334"}
|
||||||
|
];
|
||||||
|
|
||||||
|
testSet.forEach(function(sample) {
|
||||||
|
sample.format = "i2of5";
|
||||||
|
});
|
||||||
|
|
||||||
|
_runTestSet(testSet, config);
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -11,7 +11,8 @@ define([
|
|||||||
'codabar_reader',
|
'codabar_reader',
|
||||||
'upc_reader',
|
'upc_reader',
|
||||||
'ean_8_reader',
|
'ean_8_reader',
|
||||||
'upc_e_reader'
|
'upc_e_reader',
|
||||||
|
'i2of5_reader'
|
||||||
], function(
|
], function(
|
||||||
Bresenham,
|
Bresenham,
|
||||||
ImageDebug,
|
ImageDebug,
|
||||||
@@ -22,7 +23,8 @@ define([
|
|||||||
CodabarReader,
|
CodabarReader,
|
||||||
UPCReader,
|
UPCReader,
|
||||||
EAN8Reader,
|
EAN8Reader,
|
||||||
UPCEReader) {
|
UPCEReader,
|
||||||
|
I2of5Reader) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var readers = {
|
var readers = {
|
||||||
@@ -33,7 +35,8 @@ define([
|
|||||||
code_39_vin_reader: Code39VINReader,
|
code_39_vin_reader: Code39VINReader,
|
||||||
codabar_reader: CodabarReader,
|
codabar_reader: CodabarReader,
|
||||||
upc_reader: UPCReader,
|
upc_reader: UPCReader,
|
||||||
upc_e_reader: UPCEReader
|
upc_e_reader: UPCEReader,
|
||||||
|
i2of5_reader: I2of5Reader
|
||||||
};
|
};
|
||||||
var BarcodeDecoder = {
|
var BarcodeDecoder = {
|
||||||
create : function(config, inputImageWrapper) {
|
create : function(config, inputImageWrapper) {
|
||||||
@@ -86,11 +89,21 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initReaders() {
|
function initReaders() {
|
||||||
var i;
|
config.readers.forEach(function(readerConfig) {
|
||||||
for ( i = 0; i < config.readers.length; i++) {
|
var reader,
|
||||||
console.log(config.readers[i]);
|
config = {};
|
||||||
_barcodeReaders.push(new readers[config.readers[i]]());
|
|
||||||
}
|
if (typeof readerConfig === 'object') {
|
||||||
|
reader = readerConfig.format;
|
||||||
|
config = readerConfig.config;
|
||||||
|
} else if (typeof readerConfig === 'string') {
|
||||||
|
reader = readerConfig;
|
||||||
|
}
|
||||||
|
_barcodeReaders.push(new readers[reader](config));
|
||||||
|
});
|
||||||
|
console.log("Registered Readers: " + _barcodeReaders
|
||||||
|
.map(function(reader) {return JSON.stringify({format: reader.FORMAT, config: reader.config});})
|
||||||
|
.join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
function initConfig() {
|
function initConfig() {
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ define(
|
|||||||
function() {
|
function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function BarcodeReader() {
|
function BarcodeReader(config) {
|
||||||
this._row = [];
|
this._row = [];
|
||||||
|
this.config = config || {};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +185,29 @@ define(
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) {
|
||||||
|
var self = this,
|
||||||
|
counterPos = 0,
|
||||||
|
i,
|
||||||
|
counters = [];
|
||||||
|
|
||||||
|
isWhite = (typeof isWhite !== 'undefined') ? isWhite : true;
|
||||||
|
offset = (typeof offset !== 'undefined') ? offset : self._nextUnset(self._row);
|
||||||
|
end = end || self._row.length;
|
||||||
|
|
||||||
|
counters[counterPos] = 0;
|
||||||
|
for (i = offset; i < end; i++) {
|
||||||
|
if (self._row[i] ^ isWhite) {
|
||||||
|
counters[counterPos]++;
|
||||||
|
} else {
|
||||||
|
counterPos++;
|
||||||
|
counters[counterPos] = 1;
|
||||||
|
isWhite = !isWhite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counters;
|
||||||
|
};
|
||||||
|
|
||||||
Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
|
Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
|
||||||
value: 'unknown',
|
value: 'unknown',
|
||||||
writeable: false
|
writeable: false
|
||||||
@@ -200,6 +224,8 @@ define(
|
|||||||
PatternNotFoundException : "Pattern could not be found!"
|
PatternNotFoundException : "Pattern could not be found!"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BarcodeReader.CONFIG_KEYS = {};
|
||||||
|
|
||||||
return (BarcodeReader);
|
return (BarcodeReader);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ define(
|
|||||||
nextStart,
|
nextStart,
|
||||||
end;
|
end;
|
||||||
|
|
||||||
self._fillCounters();
|
this._counters = self._fillCounters();
|
||||||
start = self._findStart();
|
start = self._findStart();
|
||||||
if (!start) {
|
if (!start) {
|
||||||
return null;
|
return null;
|
||||||
@@ -186,26 +186,6 @@ define(
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
CodabarReader.prototype._fillCounters = function() {
|
|
||||||
var self = this,
|
|
||||||
counterPos = 0,
|
|
||||||
isWhite = true,
|
|
||||||
offset = self._nextUnset(self._row),
|
|
||||||
i;
|
|
||||||
|
|
||||||
self._counters.length = 0;
|
|
||||||
self._counters[counterPos] = 0;
|
|
||||||
for (i = offset; i < self._row.length; i++) {
|
|
||||||
if (self._row[i] ^ isWhite) {
|
|
||||||
this._counters[counterPos]++;
|
|
||||||
} else {
|
|
||||||
counterPos++;
|
|
||||||
this._counters[counterPos] = 1;
|
|
||||||
isWhite = !isWhite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CodabarReader.prototype._patternToChar = function(pattern) {
|
CodabarReader.prototype._patternToChar = function(pattern) {
|
||||||
var i,
|
var i,
|
||||||
self = this;
|
self = this;
|
||||||
|
|||||||
@@ -0,0 +1,344 @@
|
|||||||
|
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||||
|
/* global define */
|
||||||
|
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
"./barcode_reader",
|
||||||
|
"./html_utils"
|
||||||
|
],
|
||||||
|
function(BarcodeReader, HTMLUtils) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function I2of5Reader(opts) {
|
||||||
|
opts = HTMLUtils.mergeObjects(getDefaulConfig(), opts);
|
||||||
|
BarcodeReader.call(this, opts);
|
||||||
|
this.barSpaceRatio = [1, 1];
|
||||||
|
if (opts.normalizeBarSpaceWidth) {
|
||||||
|
this.SINGLE_CODE_ERROR = 0.38;
|
||||||
|
this.AVG_CODE_ERROR = 0.09;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaulConfig() {
|
||||||
|
var config = {};
|
||||||
|
|
||||||
|
Object.keys(I2of5Reader.CONFIG_KEYS).forEach(function(key) {
|
||||||
|
config[key] = I2of5Reader.CONFIG_KEYS[key]['default'];
|
||||||
|
});
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
var N = 1,
|
||||||
|
W = 3,
|
||||||
|
properties = {
|
||||||
|
MODULO : {value: 10},
|
||||||
|
START_PATTERN : {value: [N*2.5, N*2.5, N*2.5, N*2.5]},
|
||||||
|
STOP_PATTERN : {value: [N*2, N*2, W*2]},
|
||||||
|
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.38, writable: true},
|
||||||
|
MAX_CORRECTION_FACTOR: {value: 5},
|
||||||
|
FORMAT: {value: "i2of5"}
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.prototype = Object.create(BarcodeReader.prototype, properties);
|
||||||
|
I2of5Reader.prototype.constructor = I2of5Reader;
|
||||||
|
|
||||||
|
I2of5Reader.prototype._matchPattern = function(counter, code) {
|
||||||
|
if (this.config.normalizeBarSpaceWidth) {
|
||||||
|
var i,
|
||||||
|
counterSum = [0, 0],
|
||||||
|
codeSum = [0, 0],
|
||||||
|
correction = [0, 0],
|
||||||
|
correctionRatio = this.MAX_CORRECTION_FACTOR,
|
||||||
|
correctionRatioInverse = 1 / correctionRatio;
|
||||||
|
|
||||||
|
for (i = 0; i < counter.length; i++) {
|
||||||
|
counterSum[i % 2] += counter[i];
|
||||||
|
codeSum[i % 2] += code[i];
|
||||||
|
}
|
||||||
|
correction[0] = codeSum[0] / counterSum[0];
|
||||||
|
correction[1] = codeSum[1] / counterSum[1];
|
||||||
|
|
||||||
|
correction[0] = Math.max(Math.min(correction[0], correctionRatio), correctionRatioInverse);
|
||||||
|
correction[1] = Math.max(Math.min(correction[1], correctionRatio), correctionRatioInverse);
|
||||||
|
this.barSpaceRatio = correction;
|
||||||
|
for (i = 0; i < counter.length; i++) {
|
||||||
|
counter[i] *= this.barSpaceRatio[i % 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BarcodeReader.prototype._matchPattern.call(this, counter, code);
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.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,
|
||||||
|
normalized,
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
normalized = self._normalize(counter);
|
||||||
|
if (normalized) {
|
||||||
|
error = self._matchPattern(normalized, 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.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) / 4);
|
||||||
|
leadingWhitespaceStart = startInfo.start - narrowBarWidth*10;
|
||||||
|
if (leadingWhitespaceStart >= 0) {
|
||||||
|
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
|
||||||
|
return startInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset = startInfo.end;
|
||||||
|
startInfo = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.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;
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.prototype._findEnd = function() {
|
||||||
|
var self = this,
|
||||||
|
endInfo,
|
||||||
|
tmp;
|
||||||
|
|
||||||
|
self._row.reverse();
|
||||||
|
endInfo = self._findPattern(self.STOP_PATTERN);
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.prototype._decodePair = function(counterPair) {
|
||||||
|
var i,
|
||||||
|
code,
|
||||||
|
codes = [],
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
for (i = 0; i < counterPair.length; i++) {
|
||||||
|
code = self._decodeCode(counterPair[i]);
|
||||||
|
if (!code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
codes.push(code);
|
||||||
|
}
|
||||||
|
return codes;
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.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];
|
||||||
|
}
|
||||||
|
normalized = self._normalize(counter);
|
||||||
|
if (normalized) {
|
||||||
|
for (code = 0; code < self.CODE_PATTERN.length; code++) {
|
||||||
|
error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
|
||||||
|
if (error < bestMatch.error) {
|
||||||
|
bestMatch.code = code;
|
||||||
|
bestMatch.error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestMatch.error < epsilon) {
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.prototype._decodePayload = function(counters, result, decodedCodes) {
|
||||||
|
var i,
|
||||||
|
self = this,
|
||||||
|
pos = 0,
|
||||||
|
counterLength = counters.length,
|
||||||
|
counterPair = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]],
|
||||||
|
codes;
|
||||||
|
|
||||||
|
while (pos < counterLength) {
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
counterPair[0][i] = counters[pos]*this.barSpaceRatio[0];
|
||||||
|
counterPair[1][i] = counters[pos + 1]*this.barSpaceRatio[1];
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
codes = self._decodePair(counterPair);
|
||||||
|
if (!codes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (i = 0; i < codes.length; i++) {
|
||||||
|
result.push(codes[i].code + "");
|
||||||
|
decodedCodes.push(codes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codes;
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.prototype._verifyCounterLength = function(counters) {
|
||||||
|
return (counters.length % 10 === 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.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 % 2 !== 0 ||
|
||||||
|
result.length < 6) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedCodes.push(endInfo);
|
||||||
|
return {
|
||||||
|
code : result.join(""),
|
||||||
|
start : startInfo.start,
|
||||||
|
end : endInfo.end,
|
||||||
|
startInfo : startInfo,
|
||||||
|
decodedCodes : decodedCodes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
I2of5Reader.CONFIG_KEYS = {
|
||||||
|
normalizeBarSpaceWidth: {
|
||||||
|
'type': 'boolean',
|
||||||
|
'default': false,
|
||||||
|
'description': 'If true, the reader tries to normalize the' +
|
||||||
|
'width-difference between bars and spaces'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (I2of5Reader);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
|
|
||||||
define([
|
define([
|
||||||
"code_128_reader",
|
|
||||||
"ean_reader",
|
|
||||||
"input_stream",
|
"input_stream",
|
||||||
"image_wrapper",
|
"image_wrapper",
|
||||||
"barcode_locator",
|
"barcode_locator",
|
||||||
@@ -16,9 +14,7 @@ define([
|
|||||||
"camera_access",
|
"camera_access",
|
||||||
"image_debug",
|
"image_debug",
|
||||||
"result_collector"],
|
"result_collector"],
|
||||||
function(Code128Reader,
|
function(InputStream,
|
||||||
EANReader,
|
|
||||||
InputStream,
|
|
||||||
ImageWrapper,
|
ImageWrapper,
|
||||||
BarcodeLocator,
|
BarcodeLocator,
|
||||||
BarcodeDecoder,
|
BarcodeDecoder,
|
||||||
@@ -489,10 +485,6 @@ function(Code128Reader,
|
|||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Reader: {
|
|
||||||
EANReader : EANReader,
|
|
||||||
Code128Reader : Code128Reader
|
|
||||||
},
|
|
||||||
ImageWrapper: ImageWrapper,
|
ImageWrapper: ImageWrapper,
|
||||||
ImageDebug: ImageDebug,
|
ImageDebug: ImageDebug,
|
||||||
ResultCollector: ResultCollector
|
ResultCollector: ResultCollector
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ require.config({
|
|||||||
'upc_e_reader': 'src/upc_e_reader',
|
'upc_e_reader': 'src/upc_e_reader',
|
||||||
'upc_reader': 'src/upc_reader',
|
'upc_reader': 'src/upc_reader',
|
||||||
'async': 'node_modules/async/lib/async',
|
'async': 'node_modules/async/lib/async',
|
||||||
'result_collector': 'src/result_collector'
|
'result_collector': 'src/result_collector',
|
||||||
|
'i2of5_reader': 'src/i2of5_reader'
|
||||||
},
|
},
|
||||||
deps: allTestFiles,
|
deps: allTestFiles,
|
||||||
callback: window.__karma__.start
|
callback: window.__karma__.start
|
||||||
|
|||||||
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 714 KiB |
|
After Width: | Height: | Size: 633 KiB |