Merge pull request #72 from serratus/feature/webpack

From RequireJS to Webpack
This commit is contained in:
Christoph Oberhofer
2015-10-14 19:49:56 -04:00
69 changed files with 28043 additions and 14731 deletions
+80
View File
@@ -0,0 +1,80 @@
{
"settings" : {
"ecmascript": 6,
"jsx": true
},
"env": {
"browser": true,
"node": true
},
"ecmaFeatures": {
"blockBindings": true,
"forOf": true,
"blockBindings": true,
"defaultParams": true,
"globalReturn": false,
"modules": true,
"objectLiteralShorthandMethods": true,
"objectLiteralShorthandProperties": true,
"templateStrings": true,
"spread": true,
"jsx": true,
"arrowFunctions": true,
"classes": true,
"destructuring": true,
"objectLiteralComputedProperties": true
},
"globals": {},
"rules": {
"no-unused-expressions": 1,
"no-extra-boolean-cast": 1,
"no-multi-spaces": 2,
"no-underscore-dangle": 0,
"comma-dangle": 2,
"camelcase": 0,
"curly": 2,
"eqeqeq": 2,
"guard-for-in": 2,
"wrap-iife": 0,
"no-use-before-define": [1, "nofunc"],
"new-cap": 2,
"quotes": 0,
"strict": 0,
"no-caller": 2,
"no-empty": 1,
"no-new": 2,
"no-plusplus": 0,
"no-unused-vars": 1,
"no-trailing-spaces": 2,
// STYLE
"max-params": [2, 7],
"key-spacing": [1, {
beforeColon: false,
afterColon: true
}],
"indent": [2, 4],
"brace-style": [2, "1tbs"],
"comma-spacing": [2, {before: false, after: true}],
"comma-style": [2, "last"],
"consistent-this": [1, "self"],
"eol-last": 0,
"new-cap": 0,
"new-parens": 2,
"no-array-constructor": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": 2,
"semi-spacing": 2,
"dot-notation": 2,
"no-spaced-func": 1,
"no-shadow": 2,
"no-undef": 2,
"padded-blocks": [2, "never"],
"semi": [2, "always"],
"space-after-keywords": [2, "always"],
"space-infix-ops": 2,
"max-len" : [1, 120],
"consistent-return": 2,
"yoda": 2
}
}
+2 -56
View File
@@ -1,8 +1,7 @@
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
pkg: grunt.file.readJSON('package.json'),
karma: {
unit: {
configFile: 'karma.conf.js'
@@ -10,67 +9,14 @@ module.exports = function(grunt) {
integration: {
configFile: 'karma-integration.conf.js'
}
},
uglify : {
options : {
banner : '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
preserveComments: 'some'
},
build : {
src : 'dist/<%= pkg.name %>.js',
dest : 'dist/<%= pkg.name %>.min.js'
}
},
jshint : {
all : ['Gruntfile.js', 'src/*.js']
},
requirejs : {
compile : {
options : {
almond : true,
wrap : {
startFile : 'build/start.frag',
endFile : 'build/end.frag'
},
"baseUrl" : "src",
"name" : "quagga",
"useStrict": true,
"out" : "dist/quagga.js",
"include" : ['quagga'],
"optimize" : "none",
"findNestedDependencies" : true,
"skipSemiColonInsertion" : true,
"shim" : {
"typedefs" : {
"deps" : [],
"exports" : "typedefs"
}
},
"paths" : {
"typedefs" : "typedefs",
"gl-matrix": "../node_modules/gl-matrix/dist/gl-matrix-min"
}
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-requirejs');
grunt.loadNpmTasks('grunt-karma');
grunt.loadTasks('tasks');
grunt.registerTask('build', ['check', 'requirejs']);
grunt.registerTask('check', ['jshint']);
grunt.registerTask('dist', ['build', 'uglify', 'uglyasm']);
grunt.registerTask('test', ['karma']);
grunt.registerTask('integrationtest', ['karma:integration']);
grunt.registerTask('default', ['build']);
};
};
+17 -6
View File
@@ -1,7 +1,7 @@
quaggaJS
========
- [Changelog](#changelog) (2015-09-15)
- [Changelog](#changelog) (2015-10-13)
## What is QuaggaJS?
@@ -34,9 +34,9 @@ be aligned with the viewport.
In order to take full advantage of quaggaJS, the browser needs to support the
`getUserMedia` API which is already implemented in recent versions of Firefox,
Chrome, IE (Edge) and Opera. The API is also available on their mobile
Chrome, IE (Edge) and Opera. The API is also available on their mobile
counterparts installed on Android (except IE). Safari does not allow the access
to the camera yet, neither on desktop, nor on mobile. You can check
to the camera yet, neither on desktop, nor on mobile. You can check
[caniuse][caniuse_getusermedia] for updates.
In cases where real-time decoding is not needed, or the platform does not
@@ -90,11 +90,15 @@ You can build the library yourself by simply cloning the repo and typing:
```console
> npm install
> grunt dist
> npm run build
```
This grunt task builds a non optimized version `quagga.js` and a minified
This npm script builds a non optimized version `quagga.js` and a minified
version `quagga.min.js` and places both files in the `dist` folder.
Additionally, a `quagga.map` source-map is placed alongside these files. This
file is only valid for the non-uglified version `quagga.js` because the
minified version is altered after compression and does not align with the map
file any more.
## API
@@ -360,7 +364,7 @@ automatically generated in the coverage/ folder.
```console
> npm install
> grunt test
> npm run test
```
## Image Debugging
@@ -430,6 +434,13 @@ on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
## <a name="changelog">Changelog</a>
### 2015-10-13
Take a look at the release-notes ([0.8.0]
(https://github.com/serratus/quaggaJS/releases/tag/v0.8.0))
- Improvements
- Replaced RequireJS with webpack
### 2015-09-15
Take a look at the release-notes ([0.7.0]
(https://github.com/serratus/quaggaJS/releases/tag/v0.7.0))
+12894 -6975
View File
File diff suppressed because one or more lines are too long
+3 -10
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -101,8 +101,8 @@
&copy; Copyright by Christoph Oberhofer
</p>
</footer>
<script src="../src/vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
<script src="vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
<script src="../dist/quagga.js" type="text/javascript"></script>
<script src="file_input.js" type="text/javascript"></script>
</body>
</html>
</html>
-101
View File
@@ -1,101 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<title>index</title>
<meta name="description" content=""/>
<meta name="author" content="Christoph Oberhofer"/>
<meta name="viewport" content="width=device-width; initial-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="css/styles.css"/>
</head>
<body>
<header>
<div class="headline">
<h1>QuaggaJS</h1>
<h2>An advanced barcode-scanner written in JavaScript</h2>
</div>
</header>
<section id="container" class="container">
<h3>Working with file-input</h3>
<p>This example let's you select an image from your local filesystem.
QuaggaJS then tries to decode the barcode using
the preferred method (<strong>Code128</strong> or <strong>EAN</strong>).
There is no server interaction needed as the
file is simply accessed through the <a
href="http://www.w3.org/TR/file-upload/">File API</a>.</p>
<p>This also works great on a wide range of mobile-phones where the camera
access through <code>getUserMedia</code> is still very limited.</p>
<div class="controls">
<fieldset class="input-group">
<input type="file" capture/>
<button>Rerun</button>
</fieldset>
<fieldset class="reader-config-group">
<label>
<span>Barcode-Type</span>
<select name="decoder_readers">
<option value="code_128" selected="selected">Code 128</option>
<option value="code_39">Code 39</option>
<option value="code_39_vin">Code 39 VIN</option>
<option value="ean">EAN</option>
<option value="ean_8">EAN-8</option>
<option value="upc">UPC</option>
<option value="upc_e">UPC-E</option>
<option value="codabar">Codabar</option>
</select>
</label>
<label>
<span>Resolution (long side)</span>
<select name="input-stream_size">
<option value="320">320px</option>
<option selected="selected" value="640">640px</option>
<option value="800">800px</option>
<option value="1280">1280px</option>
<option value="1600">1600px</option>
<option value="1920">1920px</option>
</select>
</label>
<label>
<span>Patch-Size</span>
<select name="locator_patch-size">
<option value="x-small">x-small</option>
<option value="small">small</option>
<option selected="selected" value="medium">medium</option>
<option value="large">large</option>
<option value="x-large">x-large</option>
</select>
</label>
<label>
<span>Half-Sample</span>
<input type="checkbox" checked="checked"
name="locator_half-sample" />
</label>
<label>
<span>Single Channel</span>
<input type="checkbox" name="input-stream_single-channel" />
</label>
</fieldset>
</div>
<div id="result_strip">
<ul class="thumbnails"></ul>
</div>
<div id="interactive" class="viewport"></div>
<div id="debug" class="detection"></div>
</section>
<footer>
<p>
&copy; Copyright by Christoph Oberhofer
</p>
</footer>
<script src="../src/vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
<script data-main="file_input_require.js" src="../node_modules/requirejs/require.js"></script>
</body>
</html>
-161
View File
@@ -1,161 +0,0 @@
requirejs.config({
"baseUrl" : "../src",
"shim" : {
"typedefs" : {
"deps" : [],
"exports" : "typedefs"
}
},
"paths" : {
"typedefs" : "typedefs",
"gl-matrix": "../node_modules/gl-matrix/dist/gl-matrix-min"
}
});
requirejs(['quagga'], function(Quagga) {
var App = {
init: function() {
App.attachListeners();
},
attachListeners: function() {
var self = this;
$(".controls input[type=file]").on("change", function(e) {
if (e.target.files && e.target.files.length) {
App.decode(URL.createObjectURL(e.target.files[0]));
}
});
$(".controls button").on("click", function(e) {
var input = document.querySelector(".controls input[type=file]");
if (input.files && input.files.length) {
App.decode(URL.createObjectURL(input.files[0]));
}
});
$(".controls .reader-config-group").on("change", "input, select", function(e) {
e.preventDefault();
var $target = $(e.target),
value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(),
name = $target.attr("name"),
state = self._convertNameToState(name);
console.log("Value of "+ state + " changed to " + value);
self.setState(state, value);
});
},
_accessByPath: function(obj, path, val) {
var parts = path.split('.'),
depth = parts.length,
setter = (typeof val !== "undefined") ? true : false;
return parts.reduce(function(o, key, i) {
if (setter && (i + 1) === depth) {
o[key] = val;
}
return key in o ? o[key] : {};
}, obj);
},
_convertNameToState: function(name) {
return name.replace("_", ".").split("-").reduce(function(result, value) {
return result + value.charAt(0).toUpperCase() + value.substring(1);
});
},
detachListeners: function() {
$(".controls input[type=file]").off("change");
$(".controls .reader-config-group").off("change", "input, select");
$(".controls button").off("click");
},
decode: function(src) {
var self = this,
config = $.extend({}, self.state, {src: src});
Quagga.decodeSingle(config, function(result) {});
},
setState: function(path, value) {
var self = this;
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
value = self._accessByPath(self.inputMapper, path)(value);
}
self._accessByPath(self.state, path, value);
console.log(JSON.stringify(self.state));
App.detachListeners();
App.init();
},
inputMapper: {
inputStream: {
size: function(value){
return parseInt(value);
}
},
numOfWorkers: function(value) {
return parseInt(value);
},
decoder: {
readers: function(value) {
return [value + "_reader"];
}
}
},
state: {
inputStream: {
size: 640,
singleChannel: false
},
locator: {
patchSize: "medium",
halfSample: true,
showCanvas: true
},
numOfWorkers: 0,
decoder: {
readers: ["code_128_reader"],
showFrequency: true,
showPattern: true
},
locate: true,
src: null
}
};
App.init();
Quagga.onProcessed(function(result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2});
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
}
if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
}
}
});
Quagga.onDetected(function(result) {
var code = result.codeResult.code,
$node,
canvas = Quagga.canvas.dom.image;
$node = $('<li><div class="thumbnail"><div class="imgWrapper"><img /></div><div class="caption"><h4 class="code"></h4></div></div></li>');
$node.find("img").attr("src", canvas.toDataURL());
$node.find("h4.code").html(code + " (" + result.codeResult.format + ")");
$("#result_strip ul.thumbnails").prepend($node);
});
});
+50 -30
View File
@@ -1,32 +1,52 @@
var path = require('path');
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['mocha', 'requirejs', 'chai', 'sinon', 'sinon-chai'],
files: [
'test-main.js',
'src/typedefs.js',
{pattern: 'node_modules/async/lib/async.js', included: false},
{pattern: 'node_modules/gl-matrix/dist/gl-matrix-min.js', included: false},
{pattern: 'src/*.js', included: false},
{pattern: 'spec/**/*integration.spec.js', included: false},
{pattern: 'test/**/*.*', included: false}
],
exclude: [
],
plugins: [
'karma-chrome-launcher',
'karma-mocha',
'karma-requirejs',
'karma-chai',
'karma-sinon',
'karma-sinon-chai'
],
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
config.set({
basePath: '',
frameworks: ['mocha', 'chai', 'sinon', 'sinon-chai'],
files: [
'test/test-main-integration.js',
{pattern: 'test/integration/**/*.js', included: false},
{pattern: 'test/fixtures/**/*.*', included: false}
],
preprocessors: {
'test/test-main-integration.js': ['webpack']
},
webpack: {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: [
/node_modules/
],
loader: 'babel'
}
]
},
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
'input_stream$': path.resolve(__dirname, 'src/input_stream'),
'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber')
}
}
},
plugins: [
'karma-chrome-launcher',
'karma-mocha',
'karma-requirejs',
'karma-chai',
'karma-sinon',
'karma-sinon-chai',
require('karma-webpack')
],
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
+62 -40
View File
@@ -1,42 +1,64 @@
var path = require('path');
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['mocha', 'requirejs', 'chai', 'sinon', 'sinon-chai'],
files: [
'test-main.js',
'src/typedefs.js',
{pattern: 'node_modules/async/lib/async.js', included: false},
{pattern: 'node_modules/gl-matrix/dist/gl-matrix-min.js', included: false},
{pattern: 'src/*.js', included: false},
{pattern: 'spec/**/*.js', included: false},
{pattern: 'test/**/*.*', included: false}
],
exclude: [
'spec/**/*integration.spec.js'
],
preprocessors: {
'src/*.js': ['coverage']
},
plugins: [
'karma-chrome-launcher',
'karma-coverage',
'karma-mocha',
'karma-requirejs',
'karma-chai',
'karma-sinon',
'karma-sinon-chai',
'karma-phantomjs-launcher'
],
reporters: ['progress', 'coverage'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
coverageReporter: {
type : 'html',
dir : 'coverage/'
}
});
config.set({
basePath: '',
frameworks: ['source-map-support', 'mocha', 'chai', 'sinon', 'sinon-chai'],
files: [
'test/test-main.js',
{pattern: 'test/spec/**/*.js', included: false}
],
preprocessors: {
'test/test-main.js': ['webpack']
},
webpack: {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: [
/node_modules/
],
loader: 'babel'
},
{
test: /\.js$/,
include: [
path.resolve('src')
],
exclude: /node_modules/,
loader: 'isparta'
}
]
},
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
'input_stream$': path.resolve(__dirname, 'src/input_stream'),
'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber')
}
},
},
plugins: [
'karma-chrome-launcher',
'karma-coverage',
'karma-mocha',
'karma-chai',
'karma-sinon',
'karma-sinon-chai',
'karma-source-map-support',
require('karma-webpack')
],
reporters: ['progress', 'coverage'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
coverageReporter: {
type: 'html',
dir: 'coverage/'
}
});
};
+89 -95
View File
@@ -1,101 +1,95 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
const CVUtils = require('../src/cv_utils'),
Ndarray = require("ndarray"),
Interp2D = require("ndarray-linear-interpolate").d2;
define(["cv_utils"], function(CVUtils) {
"use strict";
var FrameGrabber = {};
var Ndarray = require("ndarray"),
Interp2D = require("ndarray-linear-interpolate").d2;
FrameGrabber.create = function(inputStream) {
var _that = {},
_streamConfig = inputStream.getConfig(),
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()),
_canvasSize = inputStream.getCanvasSize(),
_size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()),
_topRight = inputStream.getTopRight(),
_data = new Uint8Array(_size.x * _size.y),
_grayData = new Uint8Array(_video_size.x * _video_size.y),
_canvasData = new Uint8Array(_canvasSize.x * _canvasSize.y),
_grayImageArray = Ndarray(_grayData, [_video_size.y, _video_size.x]).transpose(1, 0),
_canvasImageArray = Ndarray(_canvasData, [_canvasSize.y, _canvasSize.x]).transpose(1, 0),
_targetImageArray = _canvasImageArray.hi(_topRight.x + _size.x, _topRight.y + _size.y).lo(_topRight.x, _topRight.y),
_stepSizeX = _video_size.x/_canvasSize.x,
_stepSizeY = _video_size.y/_canvasSize.y;
var FrameGrabber = {};
console.log("FrameGrabber", JSON.stringify({
videoSize: _grayImageArray.shape,
canvasSize: _canvasImageArray.shape,
stepSize: [_stepSizeX, _stepSizeY],
size: _targetImageArray.shape,
topRight: _topRight
}));
FrameGrabber.create = function(inputStream) {
var _that = {},
_streamConfig = inputStream.getConfig(),
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()),
_canvasSize = inputStream.getCanvasSize(),
_size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()),
_topRight = inputStream.getTopRight(),
_data = new Uint8Array(_size.x * _size.y),
_grayData = new Uint8Array(_video_size.x * _video_size.y),
_canvasData = new Uint8Array(_canvasSize.x * _canvasSize.y),
_grayImageArray = Ndarray(_grayData, [_video_size.y, _video_size.x]).transpose(1, 0),
_canvasImageArray = Ndarray(_canvasData, [_canvasSize.y, _canvasSize.x]).transpose(1, 0),
_targetImageArray = _canvasImageArray.hi(_topRight.x + _size.x, _topRight.y + _size.y).lo(_topRight.x, _topRight.y),
_stepSizeX = _video_size.x/_canvasSize.x,
_stepSizeY = _video_size.y/_canvasSize.y;
console.log("FrameGrabber", JSON.stringify({
videoSize: _grayImageArray.shape,
canvasSize: _canvasImageArray.shape,
stepSize: [_stepSizeX, _stepSizeY],
size: _targetImageArray.shape,
topRight: _topRight
}));
/**
* Uses the given array as frame-buffer
*/
_that.attachData = function(data) {
_data = data;
};
/**
* Returns the used frame-buffer
*/
_that.getData = function() {
return _data;
};
/**
* Fetches a frame from the input-stream and puts into the frame-buffer.
* The image-data is converted to gray-scale and then half-sampled if configured.
*/
_that.grab = function() {
var frame = inputStream.getFrame();
if (frame) {
this.scaleAndCrop(frame);
return true;
} else {
return false;
}
};
_that.scaleAndCrop = function(frame) {
var x,
y;
// 1. compute full-sized gray image
CVUtils.computeGray(frame.data, _grayData);
// 2. interpolate
for (y = 0; y < _canvasSize.y; y++) {
for (x = 0; x < _canvasSize.x; x++) {
_canvasImageArray.set(x, y, (Interp2D(_grayImageArray, x * _stepSizeX, y * _stepSizeY)) | 0);
}
}
// targetImageArray must be equal to targetSize
if (_targetImageArray.shape[0] !== _size.x ||
_targetImageArray.shape[1] !== _size.y) {
throw new Error("Shapes do not match!");
}
// 3. crop
for (y = 0; y < _size.y; y++) {
for (x = 0; x < _size.x; x++) {
_data[y * _size.x + x] = _targetImageArray.get(x, y);
}
}
},
_that.getSize = function() {
return _size;
};
return _that;
/**
* Uses the given array as frame-buffer
*/
_that.attachData = function(data) {
_data = data;
};
return (FrameGrabber);
});
/**
* Returns the used frame-buffer
*/
_that.getData = function() {
return _data;
};
/**
* Fetches a frame from the input-stream and puts into the frame-buffer.
* The image-data is converted to gray-scale and then half-sampled if configured.
*/
_that.grab = function() {
var frame = inputStream.getFrame();
if (frame) {
this.scaleAndCrop(frame);
return true;
} else {
return false;
}
};
_that.scaleAndCrop = function(frame) {
var x,
y;
// 1. compute full-sized gray image
CVUtils.computeGray(frame.data, _grayData);
// 2. interpolate
for (y = 0; y < _canvasSize.y; y++) {
for (x = 0; x < _canvasSize.x; x++) {
_canvasImageArray.set(x, y, (Interp2D(_grayImageArray, x * _stepSizeX, y * _stepSizeY)) | 0);
}
}
// targetImageArray must be equal to targetSize
if (_targetImageArray.shape[0] !== _size.x ||
_targetImageArray.shape[1] !== _size.y) {
throw new Error("Shapes do not match!");
}
// 3. crop
for (y = 0; y < _size.y; y++) {
for (x = 0; x < _size.x; x++) {
_data[y * _size.x + x] = _targetImageArray.get(x, y);
}
}
},
_that.getSize = function() {
return _size;
};
return _that;
};
module.exports = FrameGrabber;
+138 -145
View File
@@ -1,160 +1,153 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
const GetPixels = require("get-pixels");
define(function() {
"use strict";
var InputStream = {};
var GetPixels = require("get-pixels");
InputStream.createImageStream = function() {
var that = {};
var _config = null;
var InputStream = {};
var width = 0,
height = 0,
frameIdx = 0,
paused = true,
loaded = false,
frame = null,
baseUrl,
ended = false,
size,
calculatedWidth,
calculatedHeight,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
InputStream.createImageStream = function() {
var that = {};
var _config = null;
function loadImages() {
loaded = false;
GetPixels(baseUrl, function(err, pixels) {
if (err) {
console.log(err);
exit(1);
}
loaded = true;
console.log(pixels.shape);
frame = pixels;
width = pixels.shape[0];
height = pixels.shape[1];
calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width;
calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height;
var width = 0,
height = 0,
frameIdx = 0,
paused = true,
loaded = false,
frame = null,
baseUrl,
ended = false,
size,
calculatedWidth,
calculatedHeight,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
_canvasSize.x = calculatedWidth;
_canvasSize.y = calculatedHeight;
function loadImages() {
loaded = false;
GetPixels(baseUrl, function(err, pixels) {
if (err) {
console.log(err);
exit(1);
}
loaded = true;
console.log(pixels.shape);
frame = pixels;
width = pixels.shape[0];
height = pixels.shape[1];
calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width;
calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height;
setTimeout(function() {
publishEvent("canrecord", []);
}, 0);
});
}
_canvasSize.x = calculatedWidth;
_canvasSize.y = calculatedHeight;
function publishEvent(eventName, args) {
var j,
handlers = _eventHandlers[eventName];
setTimeout(function() {
publishEvent("canrecord", []);
}, 0);
});
}
function publishEvent(eventName, args) {
var j,
handlers = _eventHandlers[eventName];
if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
}
}
that.trigger = publishEvent;
that.trigger = publishEvent;
that.getWidth = function() {
return calculatedWidth;
};
that.getHeight = function() {
return calculatedHeight;
};
that.setWidth = function(width) {
calculatedWidth = width;
};
that.setHeight = function(height) {
calculatedHeight = height;
};
that.getRealWidth = function() {
return width;
};
that.getRealHeight = function() {
return height;
};
that.setInputStream = function(stream) {
_config = stream;
baseUrl = _config.src;
size = 1;
loadImages();
};
that.ended = function() {
return ended;
};
that.setAttribute = function() {};
that.getConfig = function() {
return _config;
};
that.pause = function() {
paused = true;
};
that.play = function() {
paused = false;
};
that.setCurrentTime = function(time) {
frameIdx = time;
};
that.addEventListener = function(event, f) {
if (_eventNames.indexOf(event) !== -1) {
if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
}
_eventHandlers[event].push(f);
}
};
that.setTopRight = function(topRight) {
_topRight.x = topRight.x;
_topRight.y = topRight.y;
};
that.getTopRight = function() {
return _topRight;
};
that.setCanvasSize = function(size) {
_canvasSize.x = size.x;
_canvasSize.y = size.y;
};
that.getCanvasSize = function() {
return _canvasSize;
};
that.getFrame = function() {
if (!loaded){
return null;
}
return frame;
};
return that;
that.getWidth = function() {
return calculatedWidth;
};
return (InputStream);
});
that.getHeight = function() {
return calculatedHeight;
};
that.setWidth = function(width) {
calculatedWidth = width;
};
that.setHeight = function(height) {
calculatedHeight = height;
};
that.getRealWidth = function() {
return width;
};
that.getRealHeight = function() {
return height;
};
that.setInputStream = function(stream) {
_config = stream;
baseUrl = _config.src;
size = 1;
loadImages();
};
that.ended = function() {
return ended;
};
that.setAttribute = function() {};
that.getConfig = function() {
return _config;
};
that.pause = function() {
paused = true;
};
that.play = function() {
paused = false;
};
that.setCurrentTime = function(time) {
frameIdx = time;
};
that.addEventListener = function(event, f) {
if (_eventNames.indexOf(event) !== -1) {
if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
}
_eventHandlers[event].push(f);
}
};
that.setTopRight = function(topRight) {
_topRight.x = topRight.x;
_topRight.y = topRight.y;
};
that.getTopRight = function() {
return _topRight;
};
that.setCanvasSize = function(size) {
_canvasSize.x = size.x;
_canvasSize.y = size.y;
};
that.getCanvasSize = function() {
return _canvasSize;
};
that.getFrame = function() {
if (!loaded){
return null;
}
return frame;
};
return that;
};
module.exports = InputStream;
+7799 -12
View File
File diff suppressed because it is too large Load Diff
+17 -6
View File
@@ -1,18 +1,23 @@
{
"name": "quagga",
"version": "0.7.0",
"version": "0.8.0",
"description": "An advanced barcode-scanner written in JavaScript",
"main": "lib/quagga.js",
"browser": "dist/quagga.js",
"browser": "dist/quagga.min.js",
"devDependencies": {
"async": "^1.4.2",
"babel-core": "^5.8.25",
"babel-eslint": "^4.1.3",
"babel-loader": "^5.3.2",
"chai": "^3.2.0",
"core-js": "^1.2.1",
"grunt": "^0.4.5",
"grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-nodeunit": "^0.4.1",
"grunt-contrib-uglify": "^0.9.2",
"grunt-karma": "^0.12.1",
"grunt-requirejs": "^0.4.2",
"isparta-loader": "^1.0.0",
"karma": "^0.13.9",
"karma-chai": "0.1.0",
"karma-chrome-launcher": "^0.2.0",
@@ -22,14 +27,20 @@
"karma-requirejs": "~0.2.2",
"karma-sinon": "^1.0.4",
"karma-sinon-chai": "~0.2.0",
"karma-source-map-support": "^1.1.0",
"karma-webpack": "^1.7.0",
"mocha": "^2.3.2",
"sinon": "^1.16.1"
"sinon": "^1.16.1",
"webpack": "^1.12.2"
},
"directories": {
"doc": "doc"
},
"scripts": {
"test": "karma start"
"test": "grunt test",
"integrationtest": "grunt integrationtest",
"build": "webpack && webpack --config webpack.config.min.js && grunt uglyasm && webpack --config webpack.node.config.js",
"watch": "webpack --watch"
},
"repository": {
"type": "git",
@@ -56,8 +67,8 @@
"dependencies": {
"get-pixels": "^3.2.3",
"gl-matrix": "^2.3.1",
"lodash": "^3.10.1",
"ndarray": "^1.0.18",
"ndarray-linear-interpolate": "^1.0.0",
"requirejs": "^2.1.20"
"ndarray-linear-interpolate": "^1.0.0"
}
}
+33
View File
@@ -0,0 +1,33 @@
var ConcatSource = require("webpack-core/lib/ConcatSource");
var OriginalSource = require("webpack-core/lib/OriginalSource");
function MyUmdPlugin(options) {
this.name = options.library;
}
module.exports = MyUmdPlugin;
MyUmdPlugin.prototype.apply = function(compiler) {
compiler.plugin("this-compilation", function(compilation) {
var mainTemplate = compilation.mainTemplate;
compilation.templatesPlugin("render-with-entry", function(source, chunk, hash) {
var amdFactory = "factory";
return new ConcatSource(new OriginalSource(
"(function webpackUniversalModuleDefinition(root, factory) {\n" +
" if(typeof exports === 'object' && typeof module === 'object')\n" +
" module.exports = factory(factory.toString());\n" +
" else if(typeof exports === 'object')\n" +
" exports[\"" + this.name + "\"] = factory(factory.toString());\n" +
" else\n" +
" root[\"" + this.name + "\"] = factory(factory.toString());\n" +
"})(this, function(__factorySource__) {\nreturn ", "webpack/myModuleDefinition"), source, "\n});\n");
}.bind(this));
mainTemplate.plugin("global-hash-paths", function(paths) {
if(this.name) paths = paths.concat(this.name);
return paths;
}.bind(this));
mainTemplate.plugin("hash", function(hash) {
hash.update("umd");
hash.update(this.name + "");
}.bind(this));
}.bind(this));
};
-53
View File
@@ -1,53 +0,0 @@
define(['array_helper'], function(ArrayHelper){
describe('init', function() {
it('initializes an array with the given value', function() {
var input = [0, 0, 0];
ArrayHelper.init(input, 5);
expect(input).to.deep.equal([5, 5, 5]);
});
});
describe('shuffle', function() {
before(function() {
sinon.stub(Math, 'random').returns(0.5);
});
after(function() {
sinon.restore(Math);
});
it('shuffles the content of an array', function() {
var input = [1, 2, 3];
expect(ArrayHelper.shuffle(input)).to.deep.equal([3, 1, 2]);
});
});
describe('toPointList', function() {
it('converts an Array to a List of poitns', function() {
var input = [[1, 2], [2, 2], [3, 2]];
expect(ArrayHelper.toPointList(input)).to.equal("[[1,2],\r\n[2,2],\r\n[3,2]]");
});
});
describe('threshold', function() {
it('returns all elements above the given threshold', function() {
var input = [1, 2, 3];
expect(ArrayHelper.threshold(input, 2, function(score) {
return score * 1.5;
})).to.deep.equal([2, 3]);
});
});
describe('maxIndex', function() {
it('gets the index of the biggest element in the array', function() {
var input = [1, 2, 3];
expect(ArrayHelper.maxIndex(input)).to.equal(2);
});
});
describe('max', function() {
it('gets the biggest element in the array', function() {
var input = [1, 3, 2];
expect(ArrayHelper.max(input)).to.equal(3);
});
});
});
-131
View File
@@ -1,131 +0,0 @@
define(['barcode_locator', 'config', 'html_utils'],
function(BarcodeLocator, Config, HtmlUtils){
describe('checkImageConstraints', function() {
var config,
inputStream,
imageSize,
streamConfig = {};
beforeEach(function() {
imageSize = {
x: 640, y: 480
};
config = HtmlUtils.mergeObjects({}, Config);
inputStream = {
getWidth: function() {
return imageSize.x;
},
getHeight: function() {
return imageSize.y;
},
setWidth: function() {},
setHeight: function() {},
setTopRight: function() {},
setCanvasSize: function() {},
getConfig: function() {
return streamConfig;
}
};
sinon.stub(inputStream, "setWidth", function(width) {
imageSize.x = width;
});
sinon.stub(inputStream, "setHeight", function(height) {
imageSize.y = height;
});
sinon.stub(inputStream, "setTopRight");
sinon.stub(inputStream, "setCanvasSize");
});
afterEach(function() {
inputStream.setWidth.restore();
inputStream.setHeight.restore();
});
it('should not adjust the image-size if not needed', function() {
var expected = {x: imageSize.x, y: imageSize.y};
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getWidth()).to.be.equal(expected.x);
expect(inputStream.getHeight()).to.be.equal(expected.y);
});
it('should adjust the image-size', function() {
var expected = {x: imageSize.x, y: imageSize.y};
config.locator.halfSample = true;
imageSize.y += 1;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getWidth()).to.be.equal(expected.x);
expect(inputStream.getHeight()).to.be.equal(expected.y);
});
it('should adjust the image-size', function() {
var expected = {x: imageSize.x, y: imageSize.y};
imageSize.y += 1;
config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expected.y);
expect(inputStream.getWidth()).to.be.equal(expected.x);
});
it("should take the defined area into account", function() {
var expectedSize = {
x: 420,
y: 315
},
expectedTopRight = {
x: 115,
y: 52
},
expectedCanvasSize = {
x: 640,
y: 480
};
streamConfig.area = {
top: "11%",
right: "15%",
bottom: "20%",
left: "18%"
};
config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expectedSize.y);
expect(inputStream.getWidth()).to.be.equal(expectedSize.x);
expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight);
expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize);
});
it("should return the original size if set to full image", function() {
var expectedSize = {
x: 640,
y: 480
},
expectedTopRight = {
x: 0,
y: 0
},
expectedCanvasSize = {
x: 640,
y: 480
};
streamConfig.area = {
top: "0%",
right: "0%",
bottom: "0%",
left: "0%"
};
config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expectedSize.y);
expect(inputStream.getWidth()).to.be.equal(expectedSize.x);
expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight);
expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize);
});
});
});
-123
View File
@@ -1,123 +0,0 @@
define(['camera_access'], function(CameraAccess){
var originalURL,
originalMediaStreamTrack,
video,
stream;
beforeEach(function() {
var tracks = [{
stop: function() {}
}];
originalURL = window.URL;
originalMediaStreamTrack = window.MediaStreamTrack;
window.MediaStreamTrack = {};
window.URL = null;
stream = {
getVideoTracks: function() {
return tracks;
}
};
sinon.spy(tracks[0], "stop");
video = {
src: null,
addEventListener: function() {
},
removeEventListener: function() {
},
play: function() {
},
videoWidth: 320,
videoHeight: 480
};
sinon.stub(video, "addEventListener", function(event, cb) {
cb();
});
sinon.stub(video, "play");
});
afterEach(function() {
window.URL = originalURL;
window.MediaStreamTrack = originalMediaStreamTrack;
});
describe('success', function() {
beforeEach(function() {
sinon.stub(navigator, "getUserMedia", function(constraints, success) {
success(stream);
});
});
afterEach(function() {
navigator.getUserMedia.restore();
});
describe('request', function () {
it('should request the camera', function (done) {
CameraAccess.request(video, {}, function () {
expect(navigator.getUserMedia.calledOnce).to.equal(true);
expect(video.src).to.deep.equal(stream);
done();
});
});
});
describe('release', function () {
it('should release the camera', function (done) {
CameraAccess.request(video, {}, function () {
expect(video.src).to.deep.equal(stream);
CameraAccess.release();
expect(video.src.getVideoTracks()).to.have.length(1);
expect(video.src.getVideoTracks()[0].stop.calledOnce).to.equal(true);
done();
});
});
});
});
describe('failure', function() {
describe("permission denied", function(){
before(function() {
sinon.stub(navigator, "getUserMedia", function(constraints, success, failure) {
failure(new Error());
});
});
after(function() {
navigator.getUserMedia.restore();
});
it('should throw if getUserMedia not available', function(done) {
CameraAccess.request(video, {}, function(err) {
expect(err).to.be.defined;
done();
});
});
});
describe("not available", function(){
var originalGetUserMedia;
before(function() {
originalGetUserMedia = navigator.getUserMedia;
navigator.getUserMedia = undefined;
});
after(function() {
navigator.getUserMedia = originalGetUserMedia;
});
it('should throw if getUserMedia not available', function(done) {
CameraAccess.request(video, {}, function(err) {
expect(err).to.be.defined;
done();
});
});
});
});
});
-145
View File
@@ -1,145 +0,0 @@
define(['cv_utils'], function(CVUtils){
describe('imageRef', function() {
it('gets the image Reference for a coordinate', function() {
var res = CVUtils.imageRef(1, 2);
expect(res.x).to.equal(1);
expect(res.y).to.equal(2);
expect(res.toVec2()[0]).to.equal(1);
});
});
describe('calculatePatchSize', function() {
it('should not throw an error in case of valid image size', function() {
var expected = {x: 32, y: 32},
patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480});
expect(patchSize).to.be.deep.equal(expected);
});
it('should thow an error if image size it not valid', function() {
var expected = {x: 32, y: 32},
patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480});
expect(patchSize).to.be.deep.equal(expected);
});
});
describe('_parseCSSDimensionValues', function() {
it("should convert a percentual value correctly", function() {
var expected = {
value: 10,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("10%");
expect(result).to.be.deep.equal(expected);
});
it("should convert a 0% value correctly", function() {
var expected = {
value: 100,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("100%");
expect(result).to.be.deep.equal(expected);
});
it("should convert a 100% value correctly", function() {
var expected = {
value: 0,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("0%");
expect(result).to.be.deep.equal(expected);
});
it("should convert a pixel value to percentage", function() {
var expected = {
value: 26.3,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("26.3px");
console.log(result);
expect(result).to.be.deep.equal(expected);
});
});
describe("_dimensionsConverters", function(){
var context;
beforeEach(function() {
context = {
width: 640,
height: 480
};
});
it("should convert a top-value correclty", function() {
var expected = 48,
result = CVUtils._dimensionsConverters.top({value: 10, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
it("should convert a right-value correclty", function() {
var expected = 640 - 128,
result = CVUtils._dimensionsConverters.right({value: 20, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
it("should convert a bottom-value correclty", function() {
var expected = 480 - 77,
result = CVUtils._dimensionsConverters.bottom({value: 16, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
it("should convert a left-value correclty", function() {
var expected = 57,
result = CVUtils._dimensionsConverters.left({value: 9, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
});
describe("computeImageArea", function() {
it("should calculate an image-area", function() {
var expected = {
sx: 115,
sy: 48,
sw: 429,
sh: 336
},
result = CVUtils.computeImageArea(640, 480, {
top: "10%",
right: "15%",
bottom: "20%",
left: "18%"
});
expect(result).to.be.deep.equal(expected);
});
it("should calculate full image-area", function() {
var expected = {
sx: 0,
sy: 0,
sw: 640,
sh: 480
},
result = CVUtils.computeImageArea(640, 480, {
top: "0%",
right: "0%",
bottom: "0%",
left: "0%"
});
expect(result).to.be.deep.equal(expected);
});
});
});
-114
View File
@@ -1,114 +0,0 @@
define(['events'], function(Events){
beforeEach(function() {
Events.unsubscribe();
});
describe("subscribe", function() {
it("should call one callback for a single event", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub();
Events.subscribe("test", callbackA);
Events.subscribe("test", callbackB);
Events.publish("test");
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
});
it("should call one callback twice if published twice", function() {
var callback = sinon.stub();
Events.subscribe("test", callback);
Events.publish("test");
Events.publish("test");
expect(callback.calledTwice).to.be.equal(true);
});
it("should call the callback asynchronuously", function(done) {
var test = {
callback: function() {
}
};
sinon.stub(test, "callback", function() {
expect(test.callback.calledOnce).to.be.true;
done();
});
Events.subscribe("test", test.callback, true);
Events.publish("test");
expect(test.callback.called).to.be.false;
});
});
describe("once", function() {
it("should call the callback once, even when published twice", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub();
Events.once("test", callbackA);
Events.subscribe("test", callbackB);
Events.publish("test");
Events.publish("test");
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledTwice).to.be.equal(true);
});
});
describe("unsubscribe", function() {
it("should unsubscribe all callbacks from a single event", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub(),
callbackC = sinon.stub();
Events.subscribe("test", callbackA);
Events.subscribe("test", callbackB);
Events.subscribe("testC", callbackC);
Events.publish("test");
expect(callbackC.called).to.be.equal(false);
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
Events.publish("testC");
expect(callbackC.calledOnce).to.be.equal(true);
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
Events.unsubscribe("test");
Events.publish("test");
expect(callbackC.calledOnce).to.be.equal(true);
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
});
it("should unsubscribe a single callback from a single event", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub();
Events.subscribe("test", callbackA);
Events.subscribe("test", callbackB);
Events.publish("test");
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
Events.unsubscribe("test", callbackB);
Events.publish("test");
expect(callbackA.calledTwice).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
});
});
});
-248
View File
@@ -1,248 +0,0 @@
define(['quagga', 'async'], function(Quagga, async) {
describe('decodeSingle', function () {
var baseFolder = "base/test/fixtures/";
function generateConfig() {
return {
inputStream: {
size: 640
},
locator: {
patchSize: "medium",
halfSample: true
},
numOfWorkers: 0,
decoder: {
readers: ["ean_reader"]
},
locate: true,
src: null
};
}
this.timeout(10000);
function _runTestSet(testSet, config) {
var readers = config.decoder.readers.slice(),
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) {
async.eachSeries(testSet, function (sample, callback) {
config.src = folder + sample.name;
config.readers = readers;
Quagga.decodeSingle(config, function(result) {
console.log(sample.name);
expect(result.codeResult.code).to.equal(sample.result);
expect(result.codeResult.format).to.equal(sample.format);
callback();
});
}, function() {
done();
});
});
}
describe("EAN", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "3574660239843"},
{"name": "image-002.jpg", "result": "8032754490297"},
{"name": "image-003.jpg", "result": "4006209700068"},
/* {"name": "image-004.jpg", "result": "9002233139084"}, */
/* {"name": "image-005.jpg", "result": "8004030044005"}, */
{"name": "image-006.jpg", "result": "4003626011159"},
{"name": "image-007.jpg", "result": "2111220009686"},
{"name": "image-008.jpg", "result": "9000275609022"},
{"name": "image-009.jpg", "result": "9004593978587"},
{"name": "image-010.jpg", "result": "9002244845578"}
];
testSet.forEach(function(sample) {
sample.format = "ean_13";
});
config.decoder.readers = ['ean_reader'];
_runTestSet(testSet, config);
});
describe("Code128", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "0001285112001000040801"},
// {"name": "image-002.jpg", "result": "FANAVF1461710"},
// {"name": "image-003.jpg", "result": "673023"},
// {"name": "image-004.jpg", "result": "010210150301625334"},
{"name": "image-005.jpg", "result": "419055603900009001012999"},
{"name": "image-006.jpg", "result": "419055603900009001012999"},
{"name": "image-007.jpg", "result": "T 000003552345"},
{"name": "image-008.jpg", "result": "FANAVF1461710"},
{"name": "image-009.jpg", "result": "0001285112001000040801"},
{"name": "image-010.jpg", "result": "673023"}
];
testSet.forEach(function(sample) {
sample.format = "code_128";
});
config.decoder.readers = ['code_128_reader'];
_runTestSet(testSet, config);
});
describe("Code39", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "B3% $DAD$"},
{"name": "image-003.jpg", "result": "CODE39"},
{"name": "image-004.jpg", "result": "QUAGGAJS"},
/* {"name": "image-005.jpg", "result": "CODE39"}, */
{"name": "image-006.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-009.jpg", "result": "2/4-8/16-32"},
{"name": "image-010.jpg", "result": "CODE39"}
];
testSet.forEach(function(sample) {
sample.format = "code_39";
});
config.decoder.readers = ['code_39_reader'];
_runTestSet(testSet, config);
});
describe("EAN-8", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "42191605"},
{"name": "image-002.jpg", "result": "42191605"},
{"name": "image-003.jpg", "result": "90311208"},
{"name": "image-004.jpg", "result": "24057257"},
{"name": "image-005.jpg", "result": "90162602"},
{"name": "image-006.jpg", "result": "24036153"},
{"name": "image-007.jpg", "result": "42176817"},
{"name": "image-008.jpg", "result": "42191605"},
{"name": "image-009.jpg", "result": "42242215"},
{"name": "image-010.jpg", "result": "42184799"}
];
testSet.forEach(function(sample) {
sample.format = "ean_8";
});
config.decoder.readers = ['ean_8_reader'];
_runTestSet(testSet, config);
});
describe("UPC", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "882428015268"},
{"name": "image-002.jpg", "result": "882428015268"},
{"name": "image-003.jpg", "result": "882428015084"},
{"name": "image-004.jpg", "result": "882428015343"},
{"name": "image-005.jpg", "result": "882428015343"},
/* {"name": "image-006.jpg", "result": "882428015046"}, */
{"name": "image-007.jpg", "result": "882428015084"},
{"name": "image-008.jpg", "result": "882428015046"},
{"name": "image-009.jpg", "result": "039047013551"},
{"name": "image-010.jpg", "result": "039047013551"}
];
testSet.forEach(function(sample) {
sample.format = "upc_a";
});
config.decoder.readers = ['upc_reader'];
_runTestSet(testSet, config);
});
describe("UPC-E", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "04965802"},
{"name": "image-002.jpg", "result": "04965802"},
{"name": "image-003.jpg", "result": "03897425"},
{"name": "image-004.jpg", "result": "05096893"},
{"name": "image-005.jpg", "result": "05096893"},
{"name": "image-006.jpg", "result": "05096893"},
{"name": "image-007.jpg", "result": "03897425"},
{"name": "image-008.jpg", "result": "01264904"},
/*{"name": "image-009.jpg", "result": "01264904"},*/
{"name": "image-010.jpg", "result": "01264904"}
];
testSet.forEach(function(sample) {
sample.format = "upc_e";
});
config.decoder.readers = ['upc_e_reader'];
_runTestSet(testSet, config);
});
describe("Codabar", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "A10/53+17-70D"},
{"name": "image-002.jpg", "result": "B546745735B"},
{"name": "image-003.jpg", "result": "C$399.95A"},
{"name": "image-004.jpg", "result": "B546745735B"},
{"name": "image-005.jpg", "result": "C$399.95A"},
{"name": "image-006.jpg", "result": "B546745735B"},
{"name": "image-007.jpg", "result": "C$399.95A"},
{"name": "image-008.jpg", "result": "A16:9/4:3/3:2D"},
{"name": "image-009.jpg", "result": "C$399.95A"},
{"name": "image-010.jpg", "result": "C$399.95A"}
];
testSet.forEach(function(sample) {
sample.format = "codabar";
});
config.decoder.readers = ['codabar_reader'];
_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);
});
});
});
-103
View File
@@ -1,103 +0,0 @@
define(['result_collector', 'image_debug'], function(ResultCollector, ImageDebug) {
var canvasMock,
imageSize,
config;
beforeEach(function() {
imageSize = {x: 320, y: 240};
config = {
capture: true,
capacity: 20,
blacklist: [{code: "3574660239843", format: "ean_13"}],
filter: function(codeResult) {
return true;
}
};
canvasMock = {
getContext: function() {
return {};
},
toDataURL: sinon.spy(),
width: 0,
height: 0
};
sinon.stub(document, "createElement", function(type) {
if (type === "canvas") {
return canvasMock;
}
});
});
afterEach(function() {
document.createElement.restore();
});
describe('create', function () {
it("should return a new collector", function() {
ResultCollector.create(config);
expect(document.createElement.calledOnce).to.be.equal(true);
expect(document.createElement.getCall(0).args[0]).to.equal("canvas");
});
});
describe('addResult', function() {
beforeEach(function() {
sinon.stub(ImageDebug, "drawImage", function() {});
});
afterEach(function() {
ImageDebug.drawImage.restore();
});
it("should not add result if capacity is full", function(){
config.capacity = 1;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {});
expect(collector.getResults()).to.have.length(1);
});
it("should only add results which match constraints", function(){
var collector = ResultCollector.create(config),
results;
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"});
results = collector.getResults();
expect(results).to.have.length(2);
results.forEach(function(result) {
expect(result).not.to.deep.equal(config.blacklist[0]);
});
});
it("should add result if no filter is set", function() {
delete config.filter;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(1);
});
it("should not add results if filter returns false", function() {
config.filter = function(){ return false };
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(0);
});
it("should add result if no blacklist is set", function() {
delete config.blacklist;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
expect(collector.getResults()).to.have.length(1);
});
});
});
+78 -85
View File
@@ -1,86 +1,79 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define(function() {
"use strict";
return {
init : function(arr, val) {
var l = arr.length;
while (l--) {
arr[l] = val;
}
},
/**
* Shuffles the content of an array
* @return {Array} the array itself shuffled
*/
shuffle : function(arr) {
var i = arr.length - 1, j, x;
for (i; i >= 0; i--) {
j = Math.floor(Math.random() * i);
x = arr[i];
arr[i] = arr[j];
arr[j] = x;
}
return arr;
},
toPointList : function(arr) {
var i, j, row = [], rows = [];
for ( i = 0; i < arr.length; i++) {
row = [];
for ( j = 0; j < arr[i].length; j++) {
row[j] = arr[i][j];
}
rows[i] = "[" + row.join(",") + "]";
}
return "[" + rows.join(",\r\n") + "]";
},
/**
* returns the elements which's score is bigger than the threshold
* @return {Array} the reduced array
*/
threshold : function(arr, threshold, scoreFunc) {
var i, queue = [];
for ( i = 0; i < arr.length; i++) {
if (scoreFunc.apply(arr, [arr[i]]) >= threshold) {
queue.push(arr[i]);
}
}
return queue;
},
maxIndex : function(arr) {
var i, max = 0;
for ( i = 0; i < arr.length; i++) {
if (arr[i] > arr[max]) {
max = i;
}
}
return max;
},
max : function(arr) {
var i, max = 0;
for ( i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
},
sum: function(arr) {
var length = arr.length,
sum = 0;
while(length--) {
sum += arr[length];
}
return sum;
export default {
init: function(arr, val) {
var l = arr.length;
while (l--) {
arr[l] = val;
}
};
});
},
/**
* Shuffles the content of an array
* @return {Array} the array itself shuffled
*/
shuffle: function(arr) {
var i = arr.length - 1, j, x;
for (i; i >= 0; i--) {
j = Math.floor(Math.random() * i);
x = arr[i];
arr[i] = arr[j];
arr[j] = x;
}
return arr;
},
toPointList: function(arr) {
var i, j, row = [], rows = [];
for ( i = 0; i < arr.length; i++) {
row = [];
for ( j = 0; j < arr[i].length; j++) {
row[j] = arr[i][j];
}
rows[i] = "[" + row.join(",") + "]";
}
return "[" + rows.join(",\r\n") + "]";
},
/**
* returns the elements which's score is bigger than the threshold
* @return {Array} the reduced array
*/
threshold: function(arr, threshold, scoreFunc) {
var i, queue = [];
for ( i = 0; i < arr.length; i++) {
if (scoreFunc.apply(arr, [arr[i]]) >= threshold) {
queue.push(arr[i]);
}
}
return queue;
},
maxIndex: function(arr) {
var i, max = 0;
for ( i = 0; i < arr.length; i++) {
if (arr[i] > arr[max]) {
max = i;
}
}
return max;
},
max: function(arr) {
var i, max = 0;
for ( i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
},
sum: function(arr) {
var length = arr.length,
sum = 0;
while (length--) {
sum += arr[length];
}
return sum;
}
};
+252 -271
View File
@@ -1,305 +1,286 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import Bresenham from './bresenham';
import ImageDebug from './image_debug';
import Code128Reader from './code_128_reader';
import EANReader from './ean_reader';
import Code39Reader from './code_39_reader';
import Code39VINReader from './code_39_vin_reader';
import CodabarReader from './codabar_reader';
import UPCReader from './upc_reader';
import EAN8Reader from './ean_8_reader';
import UPCEReader from './upc_e_reader';
import I2of5Reader from './i2of5_reader';
define([
"bresenham",
"image_debug",
'code_128_reader',
'ean_reader',
'code_39_reader',
'code_39_vin_reader',
'codabar_reader',
'upc_reader',
'ean_8_reader',
'upc_e_reader',
'i2of5_reader'
], function(
Bresenham,
ImageDebug,
Code128Reader,
EANReader,
Code39Reader,
Code39VINReader,
CodabarReader,
UPCReader,
EAN8Reader,
UPCEReader,
I2of5Reader) {
"use strict";
var readers = {
code_128_reader: Code128Reader,
ean_reader: EANReader,
ean_8_reader: EAN8Reader,
code_39_reader: Code39Reader,
code_39_vin_reader: Code39VINReader,
codabar_reader: CodabarReader,
upc_reader: UPCReader,
upc_e_reader: UPCEReader,
i2of5_reader: I2of5Reader
};
var BarcodeDecoder = {
create : function(config, inputImageWrapper) {
var _canvas = {
ctx : {
frequency : null,
pattern : null,
overlay : null
},
dom : {
frequency : null,
pattern : null,
overlay : null
}
const READERS = {
code_128_reader: Code128Reader,
ean_reader: EANReader,
ean_8_reader: EAN8Reader,
code_39_reader: Code39Reader,
code_39_vin_reader: Code39VINReader,
codabar_reader: CodabarReader,
upc_reader: UPCReader,
upc_e_reader: UPCEReader,
i2of5_reader: I2of5Reader
};
export default {
create: function(config, inputImageWrapper) {
var _canvas = {
ctx: {
frequency: null,
pattern: null,
overlay: null
},
_barcodeReaders = [];
dom: {
frequency: null,
pattern: null,
overlay: null
}
},
_barcodeReaders = [];
initCanvas();
initReaders();
initConfig();
initCanvas();
initReaders();
initConfig();
function initCanvas() {
if (typeof document !== 'undefined') {
var $debug = document.querySelector("#debug.detection");
_canvas.dom.frequency = document.querySelector("canvas.frequency");
if (!_canvas.dom.frequency) {
_canvas.dom.frequency = document.createElement("canvas");
_canvas.dom.frequency.className = "frequency";
if($debug) {
$debug.appendChild(_canvas.dom.frequency);
}
function initCanvas() {
if (typeof document !== 'undefined') {
var $debug = document.querySelector("#debug.detection");
_canvas.dom.frequency = document.querySelector("canvas.frequency");
if (!_canvas.dom.frequency) {
_canvas.dom.frequency = document.createElement("canvas");
_canvas.dom.frequency.className = "frequency";
if ($debug) {
$debug.appendChild(_canvas.dom.frequency);
}
_canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d");
}
_canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d");
_canvas.dom.pattern = document.querySelector("canvas.patternBuffer");
if (!_canvas.dom.pattern) {
_canvas.dom.pattern = document.createElement("canvas");
_canvas.dom.pattern.className = "patternBuffer";
if($debug) {
$debug.appendChild(_canvas.dom.pattern);
}
_canvas.dom.pattern = document.querySelector("canvas.patternBuffer");
if (!_canvas.dom.pattern) {
_canvas.dom.pattern = document.createElement("canvas");
_canvas.dom.pattern.className = "patternBuffer";
if ($debug) {
$debug.appendChild(_canvas.dom.pattern);
}
_canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d");
}
_canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d");
_canvas.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (_canvas.dom.overlay) {
_canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d");
_canvas.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (_canvas.dom.overlay) {
_canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d");
}
}
}
function initReaders() {
config.readers.forEach(function(readerConfig) {
var reader,
configuration = {};
if (typeof readerConfig === 'object') {
reader = readerConfig.format;
configuration = readerConfig.config;
} else if (typeof readerConfig === 'string') {
reader = readerConfig;
}
console.log("Before registering reader: ", reader);
_barcodeReaders.push(new READERS[reader](configuration));
});
console.log("Registered Readers: " + _barcodeReaders
.map((reader) => JSON.stringify({format: reader.FORMAT, config: reader.config}))
.join(', '));
}
function initConfig() {
if (typeof document !== 'undefined') {
var i,
vis = [{
node: _canvas.dom.frequency,
prop: config.showFrequency
}, {
node: _canvas.dom.pattern,
prop: config.showPattern
}];
for (i = 0; i < vis.length; i++) {
if (vis[i].prop === true) {
vis[i].node.style.display = "block";
} else {
vis[i].node.style.display = "none";
}
}
}
}
function initReaders() {
config.readers.forEach(function(readerConfig) {
var reader,
config = {};
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() {
if (typeof document !== 'undefined') {
var i,
vis = [{
node : _canvas.dom.frequency,
prop : config.showFrequency
}, {
node : _canvas.dom.pattern,
prop : config.showPattern
}];
for (i = 0; i < vis.length; i++) {
if (vis[i].prop === true) {
vis[i].node.style.display = "block";
} else {
vis[i].node.style.display = "none";
}
}
}
}
/**
* extend the line on both ends
* @param {Array} line
* @param {Number} angle
*/
function getExtendedLine(line, angle, ext) {
function extendLine(amount) {
var extension = {
y : amount * Math.sin(angle),
x : amount * Math.cos(angle)
};
line[0].y -= extension.y;
line[0].x -= extension.x;
line[1].y += extension.y;
line[1].x += extension.x;
}
// check if inside image
extendLine(ext);
while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0))) {
ext -= Math.ceil(ext/2);
extendLine(-ext);
}
return line;
}
function getLine(box) {
return [{
x : (box[1][0] - box[0][0]) / 2 + box[0][0],
y : (box[1][1] - box[0][1]) / 2 + box[0][1]
}, {
x : (box[3][0] - box[2][0]) / 2 + box[2][0],
y : (box[3][1] - box[2][1]) / 2 + box[2][1]
}];
}
function tryDecode(line) {
var result = null,
i,
barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, line[0], line[1]);
if (config.showFrequency) {
ImageDebug.drawPath(line, {x: 'x', y: 'y'}, _canvas.ctx.overlay, {color: 'red', lineWidth: 3});
Bresenham.debug.printFrequency(barcodeLine.line, _canvas.dom.frequency);
}
Bresenham.toBinaryLine(barcodeLine);
if (config.showPattern) {
Bresenham.debug.printPattern(barcodeLine.line, _canvas.dom.pattern);
}
for ( i = 0; i < _barcodeReaders.length && result === null; i++) {
result = _barcodeReaders[i].decodePattern(barcodeLine.line);
}
if(result === null){
return null;
}
return {
codeResult: result,
barcodeLine: barcodeLine
/**
* extend the line on both ends
* @param {Array} line
* @param {Number} angle
*/
function getExtendedLine(line, angle, ext) {
function extendLine(amount) {
var extension = {
y: amount * Math.sin(angle),
x: amount * Math.cos(angle)
};
line[0].y -= extension.y;
line[0].x -= extension.x;
line[1].y += extension.y;
line[1].x += extension.x;
}
/**
* This method slices the given area apart and tries to detect a barcode-pattern
* for each slice. It returns the decoded barcode, or null if nothing was found
* @param {Array} box
* @param {Array} line
* @param {Number} lineAngle
*/
function tryDecodeBruteForce(box, line, lineAngle) {
var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)),
i,
slices = 16,
result = null,
dir,
extension,
xdir = Math.sin(lineAngle),
ydir = Math.cos(lineAngle);
// check if inside image
extendLine(ext);
while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0)
|| !inputImageWrapper.inImageWithBorder(line[1], 0))) {
ext -= Math.ceil(ext / 2);
extendLine(-ext);
}
return line;
}
for ( i = 1; i < slices && result === null; i++) {
// move line perpendicular to angle
dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1);
extension = {
y : dir * xdir,
x : dir * ydir
};
line[0].y += extension.x;
line[0].x -= extension.y;
line[1].y += extension.x;
line[1].x -= extension.y;
function getLine(box) {
return [{
x: (box[1][0] - box[0][0]) / 2 + box[0][0],
y: (box[1][1] - box[0][1]) / 2 + box[0][1]
}, {
x: (box[3][0] - box[2][0]) / 2 + box[2][0],
y: (box[3][1] - box[2][1]) / 2 + box[2][1]
}];
}
result = tryDecode(line);
}
return result;
function tryDecode(line) {
var result = null,
i,
barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, line[0], line[1]);
if (config.showFrequency) {
ImageDebug.drawPath(line, {x: 'x', y: 'y'}, _canvas.ctx.overlay, {color: 'red', lineWidth: 3});
Bresenham.debug.printFrequency(barcodeLine.line, _canvas.dom.frequency);
}
Bresenham.toBinaryLine(barcodeLine);
if (config.showPattern) {
Bresenham.debug.printPattern(barcodeLine.line, _canvas.dom.pattern);
}
function getLineLength(line) {
return Math.sqrt(
Math.pow(Math.abs(line[1].y - line[0].y), 2) +
Math.pow(Math.abs(line[1].x - line[0].x), 2));
for ( i = 0; i < _barcodeReaders.length && result === null; i++) {
result = _barcodeReaders[i].decodePattern(barcodeLine.line);
}
if (result === null){
return null;
}
return {
codeResult: result,
barcodeLine: barcodeLine
};
}
/**
* With the help of the configured readers (Code128 or EAN) this function tries to detect a
* valid barcode pattern within the given area.
* @param {Object} box The area to search in
* @returns {Object} the result {codeResult, line, angle, pattern, threshold}
*/
function decodeFromBoundingBox(box) {
var line,
lineAngle,
ctx = _canvas.ctx.overlay,
result,
lineLength;
/**
* This method slices the given area apart and tries to detect a barcode-pattern
* for each slice. It returns the decoded barcode, or null if nothing was found
* @param {Array} box
* @param {Array} line
* @param {Number} lineAngle
*/
function tryDecodeBruteForce(box, line, lineAngle) {
var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)),
i,
slices = 16,
result = null,
dir,
extension,
xdir = Math.sin(lineAngle),
ydir = Math.cos(lineAngle);
if (config.drawBoundingBox && ctx) {
ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2});
}
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.1));
if(line === null){
return null;
}
for ( i = 1; i < slices && result === null; i++) {
// move line perpendicular to angle
dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1);
extension = {
y: dir * xdir,
x: dir * ydir
};
line[0].y += extension.x;
line[0].x -= extension.y;
line[1].y += extension.x;
line[1].x -= extension.y;
result = tryDecode(line);
if(result === null) {
result = tryDecodeBruteForce(box, line, lineAngle);
}
}
return result;
}
if(result === null) {
return null;
}
function getLineLength(line) {
return Math.sqrt(
Math.pow(Math.abs(line[1].y - line[0].y), 2) +
Math.pow(Math.abs(line[1].x - line[0].x), 2));
}
if (result && config.drawScanline && ctx) {
ImageDebug.drawPath(line, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 3});
}
/**
* With the help of the configured readers (Code128 or EAN) this function tries to detect a
* valid barcode pattern within the given area.
* @param {Object} box The area to search in
* @returns {Object} the result {codeResult, line, angle, pattern, threshold}
*/
function decodeFromBoundingBox(box) {
var line,
lineAngle,
ctx = _canvas.ctx.overlay,
result,
lineLength;
return {
codeResult : result.codeResult,
line : line,
angle : lineAngle,
pattern : result.barcodeLine.line,
threshold : result.barcodeLine.threshold
};
if (config.drawBoundingBox && ctx) {
ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2});
}
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.1));
if (line === null){
return null;
}
result = tryDecode(line);
if (result === null) {
result = tryDecodeBruteForce(box, line, lineAngle);
}
if (result === null) {
return null;
}
if (result && config.drawScanline && ctx) {
ImageDebug.drawPath(line, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 3});
}
return {
decodeFromBoundingBox : function(box) {
return decodeFromBoundingBox(box);
},
decodeFromBoundingBoxes : function(boxes) {
var i, result;
for ( i = 0; i < boxes.length; i++) {
result = decodeFromBoundingBox(boxes[i]);
if (result && result.codeResult) {
result.box = boxes[i];
return result;
}
}
},
setReaders: function(readers) {
config.readers = readers;
_barcodeReaders.length = 0;
initReaders();
}
codeResult: result.codeResult,
line: line,
angle: lineAngle,
pattern: result.barcodeLine.line,
threshold: result.barcodeLine.threshold
};
}
};
return (BarcodeDecoder);
});
return {
decodeFromBoundingBox: function(box) {
return decodeFromBoundingBox(box);
},
decodeFromBoundingBoxes: function(boxes) {
var i, result;
for ( i = 0; i < boxes.length; i++) {
result = decodeFromBoundingBox(boxes[i]);
if (result && result.codeResult) {
result.box = boxes[i];
return result;
}
}
},
setReaders: function(readers) {
config.readers = readers;
_barcodeReaders.length = 0;
initReaders();
}
};
}
};
+549 -522
View File
File diff suppressed because it is too large Load Diff
+208 -217
View File
@@ -1,231 +1,222 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
function BarcodeReader(config) {
this._row = [];
this.config = config || {};
return this;
}
define(
function() {
"use strict";
function BarcodeReader(config) {
this._row = [];
this.config = config || {};
return this;
BarcodeReader.prototype._nextUnset = function(line, start) {
var i;
if (start === undefined) {
start = 0;
}
for (i = start; i < line.length; i++) {
if (!line[i]) {
return i;
}
BarcodeReader.prototype._nextUnset = function(line, start) {
var i;
if (start === undefined) {
start = 0;
}
for (i = start; i < line.length; i++) {
if (!line[i]) {
return i;
}
}
return line.length;
};
BarcodeReader.prototype._matchPattern = function(counter, code) {
var i,
error = 0,
singleError = 0,
modulo = this.MODULO,
maxSingleError = this.SINGLE_CODE_ERROR || 1;
for (i = 0; i < counter.length; i++) {
singleError = Math.abs(code[i] - counter[i]);
if (singleError > maxSingleError) {
return Number.MAX_VALUE;
}
error += singleError;
}
return error/modulo;
};
}
return line.length;
};
BarcodeReader.prototype._nextSet = function(line, offset) {
var i;
BarcodeReader.prototype._matchPattern = function(counter, code) {
var i,
error = 0,
singleError = 0,
modulo = this.MODULO,
maxSingleError = this.SINGLE_CODE_ERROR || 1;
offset = offset || 0;
for (i = offset; i < line.length; i++) {
if (line[i]) {
return i;
}
}
return line.length;
};
for (i = 0; i < counter.length; i++) {
singleError = Math.abs(code[i] - counter[i]);
if (singleError > maxSingleError) {
return Number.MAX_VALUE;
}
error += singleError;
}
return error / modulo;
};
BarcodeReader.prototype._normalize = function(counter, modulo) {
var i,
self = this,
sum = 0,
ratio,
numOnes = 0,
normalized = [],
norm = 0;
if (!modulo) {
modulo = self.MODULO;
}
for (i = 0; i < counter.length; i++) {
if (counter[i] === 1) {
numOnes++;
} else {
sum += counter[i];
}
}
ratio = sum / (modulo - numOnes);
if (ratio > 1.0) {
for (i = 0; i < counter.length; i++) {
norm = counter[i] === 1 ? counter[i] : counter[i] / ratio;
normalized.push(norm);
}
BarcodeReader.prototype._nextSet = function(line, offset) {
var i;
offset = offset || 0;
for (i = offset; i < line.length; i++) {
if (line[i]) {
return i;
}
}
return line.length;
};
BarcodeReader.prototype._normalize = function(counter, modulo) {
var i,
self = this,
sum = 0,
ratio,
numOnes = 0,
normalized = [],
norm = 0;
if (!modulo) {
modulo = self.MODULO;
}
for (i = 0; i < counter.length; i++) {
if (counter[i] === 1) {
numOnes++;
} else {
sum += counter[i];
}
}
ratio = sum / (modulo - numOnes);
if (ratio > 1.0) {
for (i = 0; i < counter.length; i++) {
norm = counter[i] === 1 ? counter[i] : counter[i] / ratio;
normalized.push(norm);
}
} else {
ratio = (sum + numOnes) / modulo;
for (i = 0; i < counter.length; i++) {
norm = counter[i] / ratio;
normalized.push(norm);
}
}
return normalized;
};
BarcodeReader.prototype._matchTrace = function(cmpCounter, epsilon) {
var counter = [],
i,
self = this,
offset = self._nextSet(self._row),
isWhite = !self._row[offset],
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: 0
},
error;
if (cmpCounter) {
for ( i = 0; i < cmpCounter.length; i++) {
counter.push(0);
}
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
ratio = (sum + numOnes)/modulo;
for (i = 0; i < counter.length; i++) {
norm = counter[i] / ratio;
normalized.push(norm);
}
}
return normalized;
};
if (counterPos === counter.length - 1) {
error = self._matchPattern(counter, cmpCounter);
BarcodeReader.prototype._matchTrace = function(cmpCounter, epsilon) {
var counter = [],
i,
self = this,
offset = self._nextSet(self._row),
isWhite = !self._row[offset],
counterPos = 0,
bestMatch = {
error : Number.MAX_VALUE,
code : -1,
start : 0
},
error;
if (cmpCounter) {
for ( i = 0; i < cmpCounter.length; i++) {
counter.push(0);
}
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
if (error < epsilon) {
bestMatch.start = i - offset;
bestMatch.end = i;
bestMatch.counter = counter;
return bestMatch;
} else {
if (counterPos === counter.length - 1) {
error = self._matchPattern(counter, cmpCounter);
if (error < epsilon) {
bestMatch.start = i - offset;
bestMatch.end = i;
bestMatch.counter = counter;
return bestMatch;
} else {
return null;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
return null;
}
}
} else {
counter.push(0);
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
counterPos++;
counter.push(0);
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
}
// if cmpCounter was not given
bestMatch.start = offset;
bestMatch.end = self._row.length - 1;
bestMatch.counter = counter;
return bestMatch;
};
BarcodeReader.prototype.decodePattern = function(pattern) {
var self = this,
result;
self._row = pattern;
result = self._decode();
if (result === null) {
self._row.reverse();
result = self._decode();
if (result) {
result.direction = BarcodeReader.DIRECTION.REVERSE;
result.start = self._row.length - result.start;
result.end = self._row.length - result.end;
}
} else {
result.direction = BarcodeReader.DIRECTION.FORWARD;
}
if (result) {
result.format = self.FORMAT;
}
return result;
};
BarcodeReader.prototype._matchRange = function(start, end, value) {
var i;
start = start < 0 ? 0 : start;
for (i = start; i < end; i++) {
if (this._row[i] !== value) {
return false;
}
}
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;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
return counters;
};
Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
value: 'unknown',
writeable: false
});
BarcodeReader.DIRECTION = {
FORWARD : 1,
REVERSE : -1
};
BarcodeReader.Exception = {
StartNotFoundException : "Start-Info was not found!",
CodeNotFoundException : "Code could not be found!",
PatternNotFoundException : "Pattern could not be found!"
};
BarcodeReader.CONFIG_KEYS = {};
return (BarcodeReader);
}
} else {
counter.push(0);
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
counterPos++;
counter.push(0);
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
}
);
// if cmpCounter was not given
bestMatch.start = offset;
bestMatch.end = self._row.length - 1;
bestMatch.counter = counter;
return bestMatch;
};
BarcodeReader.prototype.decodePattern = function(pattern) {
var self = this,
result;
self._row = pattern;
result = self._decode();
if (result === null) {
self._row.reverse();
result = self._decode();
if (result) {
result.direction = BarcodeReader.DIRECTION.REVERSE;
result.start = self._row.length - result.start;
result.end = self._row.length - result.end;
}
} else {
result.direction = BarcodeReader.DIRECTION.FORWARD;
}
if (result) {
result.format = self.FORMAT;
}
return result;
};
BarcodeReader.prototype._matchRange = function(start, end, value) {
var i;
start = start < 0 ? 0 : start;
for (i = start; i < end; i++) {
if (this._row[i] !== value) {
return false;
}
}
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", {
value: 'unknown',
writeable: false
});
BarcodeReader.DIRECTION = {
FORWARD: 1,
REVERSE: -1
};
BarcodeReader.Exception = {
StartNotFoundException: "Start-Info was not found!",
CodeNotFoundException: "Code could not be found!",
PatternNotFoundException: "Pattern could not be found!"
};
BarcodeReader.CONFIG_KEYS = {};
export default BarcodeReader;
+195 -199
View File
@@ -1,217 +1,213 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import CVUtils from './cv_utils';
import ImageWrapper from './image_wrapper';
define(["cv_utils", "image_wrapper"], function(CVUtils, ImageWrapper) {
"use strict";
var Bresenham = {};
var Bresenham = {};
var Slope = {
DIR : {
UP : 1,
DOWN : -1
var Slope = {
DIR: {
UP: 1,
DOWN: -1
}
};
/**
* Scans a line of the given image from point p1 to p2 and returns a result object containing
* gray-scale values (0-255) of the underlying pixels in addition to the min
* and max values.
* @param {Object} imageWrapper
* @param {Object} p1 The start point {x,y}
* @param {Object} p2 The end point {x,y}
* @returns {line, min, max}
*/
Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) {
var x0 = p1.x | 0,
y0 = p1.y | 0,
x1 = p2.x | 0,
y1 = p2.y | 0,
steep = Math.abs(y1 - y0) > Math.abs(x1 - x0),
deltax,
deltay,
error,
ystep,
y,
tmp,
x,
line = [],
imageData = imageWrapper.data,
width = imageWrapper.size.x,
sum = 0,
val,
min = 255,
max = 0;
function read(a, b) {
val = imageData[b * width + a];
sum += val;
min = val < min ? val : min;
max = val > max ? val : max;
line.push(val);
}
if (steep) {
tmp = x0;
x0 = y0;
y0 = tmp;
tmp = x1;
x1 = y1;
y1 = tmp;
}
if (x0 > x1) {
tmp = x0;
x0 = x1;
x1 = tmp;
tmp = y0;
y0 = y1;
y1 = tmp;
}
deltax = x1 - x0;
deltay = Math.abs(y1 - y0);
error = (deltax / 2) | 0;
y = y0;
ystep = y0 < y1 ? 1 : -1;
for ( x = x0; x < x1; x++) {
if (steep){
read(y, x);
} else {
read(x, y);
}
error = error - deltay;
if (error < 0) {
y = y + ystep;
error = error + deltax;
}
}
return {
line: line,
min: min,
max: max
};
/**
* Scans a line of the given image from point p1 to p2 and returns a result object containing
* gray-scale values (0-255) of the underlying pixels in addition to the min
* and max values.
* @param {Object} imageWrapper
* @param {Object} p1 The start point {x,y}
* @param {Object} p2 The end point {x,y}
* @returns {line, min, max}
*/
Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) {
var x0 = p1.x | 0,
y0 = p1.y | 0,
x1 = p2.x | 0,
y1 = p2.y | 0,
steep = Math.abs(y1 - y0) > Math.abs(x1 - x0),
deltax,
deltay,
error,
ystep,
y,
tmp,
x,
line = [],
imageData = imageWrapper.data,
width = imageWrapper.size.x,
sum = 0,
val,
min = 255,
max = 0;
};
function read(a, b) {
val = imageData[b * width + a];
sum += val;
min = val < min ? val : min;
max = val > max ? val : max;
line.push(val);
}
Bresenham.toOtsuBinaryLine = function(result) {
var line = result.line,
image = new ImageWrapper({x: line.length - 1, y: 1}, line),
threshold = CVUtils.determineOtsuThreshold(image, 5);
if (steep) {
tmp = x0;
x0 = y0;
y0 = tmp;
line = CVUtils.sharpenLine(line);
CVUtils.thresholdImage(image, threshold);
tmp = x1;
x1 = y1;
y1 = tmp;
}
if (x0 > x1) {
tmp = x0;
x0 = x1;
x1 = tmp;
tmp = y0;
y0 = y1;
y1 = tmp;
}
deltax = x1 - x0;
deltay = Math.abs(y1 - y0);
error = (deltax / 2) | 0;
y = y0;
ystep = y0 < y1 ? 1 : -1;
for ( x = x0; x < x1; x++) {
if(steep){
read(y, x);
} else {
read(x, y);
}
error = error - deltay;
if (error < 0) {
y = y + ystep;
error = error + deltax;
}
}
return {
line : line,
min : min,
max : max
};
return {
line: line,
threshold: threshold
};
};
Bresenham.toOtsuBinaryLine = function(result) {
var line = result.line,
image = new ImageWrapper({x: line.length - 1, y: 1}, line),
threshold = CVUtils.determineOtsuThreshold(image, 5);
/**
* Converts the result from getBarcodeLine into a binary representation
* also considering the frequency and slope of the signal for more robust results
* @param {Object} result {line, min, max}
*/
Bresenham.toBinaryLine = function(result) {
var min = result.min,
max = result.max,
line = result.line,
slope,
slope2,
center = min + (max - min) / 2,
extrema = [],
currentDir,
dir,
threshold = (max - min) / 12,
rThreshold = -threshold,
i,
j;
line = CVUtils.sharpenLine(line);
CVUtils.thresholdImage(image, threshold);
// 1. find extrema
currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN;
extrema.push({
pos: 0,
val: line[0]
});
for ( i = 0; i < line.length - 2; i++) {
slope = (line[i + 1] - line[i]);
slope2 = (line[i + 2] - line[i + 1]);
if ((slope + slope2) < rThreshold && line[i + 1] < (center * 1.5)) {
dir = Slope.DIR.DOWN;
} else if ((slope + slope2) > threshold && line[i + 1] > (center * 0.5)) {
dir = Slope.DIR.UP;
} else {
dir = currentDir;
}
return {
line: line,
threshold: threshold
};
if (currentDir !== dir) {
extrema.push({
pos: i,
val: line[i]
});
currentDir = dir;
}
}
extrema.push({
pos: line.length,
val: line[line.length - 1]
});
for ( j = extrema[0].pos; j < extrema[1].pos; j++) {
line[j] = line[j] > center ? 0 : 1;
}
// iterate over extrema and convert to binary based on avg between minmax
for ( i = 1; i < extrema.length - 1; i++) {
if (extrema[i + 1].val > extrema[i].val) {
threshold = (extrema[i].val + ((extrema[i + 1].val - extrema[i].val) / 3) * 2) | 0;
} else {
threshold = (extrema[i + 1].val + ((extrema[i].val - extrema[i + 1].val) / 3)) | 0;
}
for ( j = extrema[i].pos; j < extrema[i + 1].pos; j++) {
line[j] = line[j] > threshold ? 0 : 1;
}
}
return {
line: line,
threshold: threshold
};
/**
* Converts the result from getBarcodeLine into a binary representation
* also considering the frequency and slope of the signal for more robust results
* @param {Object} result {line, min, max}
*/
Bresenham.toBinaryLine = function(result) {
};
var min = result.min,
max = result.max,
line = result.line,
slope,
slope2,
center = min + (max - min) / 2,
extrema = [],
currentDir,
dir,
threshold = (max - min) / 12,
rThreshold = -threshold,
i,
j;
/**
* Used for development only
*/
Bresenham.debug = {
printFrequency: function(line, canvas) {
var i,
ctx = canvas.getContext("2d");
canvas.width = line.length;
canvas.height = 256;
// 1. find extrema
currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN;
extrema.push({
pos : 0,
val : line[0]
});
for ( i = 0; i < line.length - 2; i++) {
slope = (line[i + 1] - line[i]);
slope2 = (line[i + 2] - line[i + 1]);
if ((slope + slope2) < rThreshold && line[i + 1] < (center*1.5)) {
dir = Slope.DIR.DOWN;
} else if ((slope + slope2) > threshold && line[i + 1] > (center*0.5)) {
dir = Slope.DIR.UP;
} else {
dir = currentDir;
}
ctx.beginPath();
ctx.strokeStyle = "blue";
for ( i = 0; i < line.length; i++) {
ctx.moveTo(i, 255);
ctx.lineTo(i, 255 - line[i]);
}
ctx.stroke();
ctx.closePath();
},
if (currentDir !== dir) {
extrema.push({
pos : i,
val : line[i]
});
currentDir = dir;
printPattern: function(line, canvas) {
var ctx = canvas.getContext("2d"), i;
canvas.width = line.length;
ctx.fillColor = "black";
for ( i = 0; i < line.length; i++) {
if (line[i] === 1) {
ctx.fillRect(i, 0, 1, 100);
}
}
extrema.push({
pos : line.length,
val : line[line.length - 1]
});
}
};
for ( j = extrema[0].pos; j < extrema[1].pos; j++) {
line[j] = line[j] > center ? 0 : 1;
}
// iterate over extrema and convert to binary based on avg between minmax
for ( i = 1; i < extrema.length - 1; i++) {
if (extrema[i + 1].val > extrema[i].val) {
threshold = (extrema[i].val + ((extrema[i + 1].val - extrema[i].val) / 3) * 2) | 0;
} else {
threshold = (extrema[i + 1].val + ((extrema[i].val - extrema[i + 1].val) / 3)) | 0;
}
for ( j = extrema[i].pos; j < extrema[i + 1].pos; j++) {
line[j] = line[j] > threshold ? 0 : 1;
}
}
return {
line : line,
threshold : threshold
};
};
/**
* Used for development only
*/
Bresenham.debug = {
printFrequency: function(line, canvas) {
var i,
ctx = canvas.getContext("2d");
canvas.width = line.length;
canvas.height = 256;
ctx.beginPath();
ctx.strokeStyle = "blue";
for ( i = 0; i < line.length; i++) {
ctx.moveTo(i, 255);
ctx.lineTo(i, 255 - line[i]);
}
ctx.stroke();
ctx.closePath();
},
printPattern: function(line, canvas) {
var ctx = canvas.getContext("2d"), i;
canvas.width = line.length;
ctx.fillColor = "black";
for ( i = 0; i < line.length; i++) {
if (line[i] === 1) {
ctx.fillRect(i, 0, 1, 100);
}
}
}
};
return (Bresenham);
});
export default Bresenham;
+124 -128
View File
@@ -1,143 +1,139 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define, MediaStreamTrack */
const merge = require('lodash/object/merge');
define(["html_utils"], function(HtmlUtils) {
"use strict";
var streamRef,
loadedDataHandler;
var streamRef,
loadedDataHandler;
/**
* Wraps browser-specific getUserMedia
* @param {Object} constraints
* @param {Object} success Callback
* @param {Object} failure Callback
*/
function getUserMedia(constraints, success, failure) {
if (typeof navigator.getUserMedia !== 'undefined') {
navigator.getUserMedia(constraints, function (stream) {
streamRef = stream;
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
success.apply(null, [videoSrc]);
}, failure);
} else {
failure(new TypeError("getUserMedia not available"));
}
/**
* Wraps browser-specific getUserMedia
* @param {Object} constraints
* @param {Object} success Callback
* @param {Object} failure Callback
*/
function getUserMedia(constraints, success, failure) {
if (typeof navigator.getUserMedia !== 'undefined') {
navigator.getUserMedia(constraints, function (stream) {
streamRef = stream;
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
success.apply(null, [videoSrc]);
}, failure);
} else {
failure(new TypeError("getUserMedia not available"));
}
}
function loadedData(video, callback) {
var attempts = 10;
function loadedData(video, callback) {
var attempts = 10;
function checkVideo() {
if (attempts > 0) {
if (video.videoWidth > 0 && video.videoHeight > 0) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
callback();
} else {
window.setTimeout(checkVideo, 500);
}
function checkVideo() {
if (attempts > 0) {
if (video.videoWidth > 0 && video.videoHeight > 0) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
callback();
} else {
callback('Unable to play video stream. Is webcam working?');
window.setTimeout(checkVideo, 500);
}
attempts--;
}
checkVideo();
}
/**
* Tries to attach the camera-stream to a given video-element
* and calls the callback function when the content is ready
* @param {Object} constraints
* @param {Object} video
* @param {Object} callback
*/
function initCamera(constraints, video, callback) {
getUserMedia(constraints, function(src) {
video.src = src;
if (loadedDataHandler) {
video.removeEventListener("loadeddata", loadedDataHandler, false);
}
loadedDataHandler = loadedData.bind(null, video, callback);
video.addEventListener('loadeddata', loadedDataHandler, false);
video.play();
}, function(e) {
callback(e);
});
}
/**
* Normalizes the incoming constraints to satisfy the current browser
* @param config
* @param cb Callback which is called whenever constraints are created
* @returns {*}
*/
function normalizeConstraints(config, cb) {
var constraints = {
audio: false,
video: true
},
videoConstraints = HtmlUtils.mergeObjects({
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment"
}, config);
if ( typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') {
MediaStreamTrack.getSources(function(sourceInfos) {
var videoSourceId;
for (var i = 0; i != sourceInfos.length; ++i) {
var sourceInfo = sourceInfos[i];
if (sourceInfo.kind == "video" && sourceInfo.facing == videoConstraints.facing) {
videoSourceId = sourceInfo.id;
}
}
constraints.video = {
mandatory: {
minWidth: videoConstraints.width,
minHeight: videoConstraints.height,
minAspectRatio: videoConstraints.minAspectRatio,
maxAspectRatio: videoConstraints.maxAspectRatio
},
optional: [{
sourceId: videoSourceId
}]
};
return cb(constraints);
});
} else {
callback('Unable to play video stream. Is webcam working?');
}
attempts--;
}
checkVideo();
}
/**
* Tries to attach the camera-stream to a given video-element
* and calls the callback function when the content is ready
* @param {Object} constraints
* @param {Object} video
* @param {Object} callback
*/
function initCamera(constraints, video, callback) {
getUserMedia(constraints, function(src) {
video.src = src;
if (loadedDataHandler) {
video.removeEventListener("loadeddata", loadedDataHandler, false);
}
loadedDataHandler = loadedData.bind(null, video, callback);
video.addEventListener('loadeddata', loadedDataHandler, false);
video.play();
}, function(e) {
callback(e);
});
}
/**
* Normalizes the incoming constraints to satisfy the current browser
* @param config
* @param cb Callback which is called whenever constraints are created
* @returns {*}
*/
function normalizeConstraints(config, cb) {
var constraints = {
audio: false,
video: true
},
videoConstraints = merge({
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment"
}, config);
if ( typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') {
MediaStreamTrack.getSources(function(sourceInfos) {
var videoSourceId;
for (var i = 0; i < sourceInfos.length; ++i) {
var sourceInfo = sourceInfos[i];
if (sourceInfo.kind === "video" && sourceInfo.facing === videoConstraints.facing) {
videoSourceId = sourceInfo.id;
}
}
constraints.video = {
mediaSource: "camera",
width: { min: videoConstraints.width, max: videoConstraints.width },
height: { min: videoConstraints.height, max: videoConstraints.height },
require: ["width", "height"]
mandatory: {
minWidth: videoConstraints.width,
minHeight: videoConstraints.height,
minAspectRatio: videoConstraints.minAspectRatio,
maxAspectRatio: videoConstraints.maxAspectRatio
},
optional: [{
sourceId: videoSourceId
}]
};
return cb(constraints);
}
}
/**
* Requests the back-facing camera of the user. The callback is called
* whenever the stream is ready to be consumed, or if an error occures.
* @param {Object} video
* @param {Object} callback
*/
function request(video, videoConstraints, callback) {
normalizeConstraints(videoConstraints, function(constraints) {
initCamera(constraints, video, callback);
});
} else {
constraints.video = {
mediaSource: "camera",
width: { min: videoConstraints.width, max: videoConstraints.width },
height: { min: videoConstraints.height, max: videoConstraints.height },
require: ["width", "height"]
};
return cb(constraints);
}
}
return {
request : function(video, constraints, callback) {
request(video, constraints, callback);
},
release : function() {
var tracks = streamRef && streamRef.getVideoTracks();
if (tracks.length) {
tracks[0].stop();
}
streamRef = null;
/**
* Requests the back-facing camera of the user. The callback is called
* whenever the stream is ready to be consumed, or if an error occures.
* @param {Object} video
* @param {Object} callback
*/
function request(video, videoConstraints, callback) {
normalizeConstraints(videoConstraints, function(constraints) {
initCamera(constraints, video, callback);
});
}
export default {
request: function(video, constraints, callback) {
request(video, constraints, callback);
},
release: function() {
var tracks = streamRef && streamRef.getVideoTracks();
if (tracks.length) {
tracks[0].stop();
}
};
});
streamRef = null;
}
};
+60 -67
View File
@@ -1,72 +1,65 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define(["gl-matrix"], function(glMatrix) {
"use strict";
var vec2 = glMatrix.vec2;
import {vec2} from 'gl-matrix';
/**
* Creates a cluster for grouping similar orientations of datapoints
* Creates a cluster for grouping similar orientations of datapoints
*/
var Cluster = {
create : function(point, threshold) {
var points = [], center = {
rad : 0,
vec : vec2.clone([0, 0])
}, pointMap = {};
export default {
create: function(point, threshold) {
var points = [],
center = {
rad: 0,
vec: vec2.clone([0, 0])
},
pointMap = {};
function init() {
add(point);
updateCenter();
}
function add(point) {
pointMap[point.id] = point;
points.push(point);
}
function updateCenter() {
var i, sum = 0;
for ( i = 0; i < points.length; i++) {
sum += points[i].rad;
}
center.rad = sum / points.length;
center.vec = vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]);
}
init();
return {
add : function(point) {
if (!pointMap[point.id]) {
add(point);
updateCenter();
}
},
fits : function(point) {
// check cosine similarity to center-angle
var similarity = Math.abs(vec2.dot(point.point.vec, center.vec));
if (similarity > threshold) {
return true;
}
return false;
},
getPoints : function() {
return points;
},
getCenter : function() {
return center;
}
};
},
createPoint : function(point, id, property) {
return {
rad : point[property],
point : point,
id : id
};
function init() {
add(point);
updateCenter();
}
};
return (Cluster);
});
function add(pointToAdd) {
pointMap[pointToAdd.id] = pointToAdd;
points.push(pointToAdd);
}
function updateCenter() {
var i, sum = 0;
for ( i = 0; i < points.length; i++) {
sum += points[i].rad;
}
center.rad = sum / points.length;
center.vec = vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]);
}
init();
return {
add: function(pointToAdd) {
if (!pointMap[pointToAdd.id]) {
add(pointToAdd);
updateCenter();
}
},
fits: function(otherPoint) {
// check cosine similarity to center-angle
var similarity = Math.abs(vec2.dot(otherPoint.point.vec, center.vec));
if (similarity > threshold) {
return true;
}
return false;
},
getPoints: function() {
return points;
},
getCenter: function() {
return center;
}
};
},
createPoint: function(newPoint, id, property) {
return {
rad: newPoint[property],
point: newPoint,
id: id
};
}
};
+282 -288
View File
@@ -1,294 +1,288 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import BarcodeReader from './barcode_reader';
define(
[
"./barcode_reader"
],
function(BarcodeReader) {
"use strict";
function CodabarReader() {
BarcodeReader.call(this);
this._counters = [];
}
function CodabarReader() {
BarcodeReader.call(this);
this._counters = [];
}
var properties = {
ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"},
ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68]},
CHARACTER_ENCODINGS: {value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018,
0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E]},
START_END: {value: [0x01A, 0x029, 0x00B, 0x00E]},
MIN_ENCODED_CHARS: {value: 4},
MAX_ACCEPTABLE: {value: 2.0},
PADDING: {value: 1.5},
FORMAT: {value: "codabar", writeable: false}
};
var properties = {
ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"},
ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68]},
CHARACTER_ENCODINGS: {value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E]},
START_END: {value: [0x01A, 0x029, 0x00B, 0x00E]},
MIN_ENCODED_CHARS: {value: 4},
MAX_ACCEPTABLE: {value: 2.0},
PADDING: {value: 1.5},
FORMAT: {value: "codabar", writeable: false}
};
CodabarReader.prototype = Object.create(BarcodeReader.prototype, properties);
CodabarReader.prototype.constructor = CodabarReader;
CodabarReader.prototype = Object.create(BarcodeReader.prototype, properties);
CodabarReader.prototype.constructor = CodabarReader;
CodabarReader.prototype._decode = function() {
var self = this,
result = [],
start,
decodedChar,
pattern,
nextStart,
end;
CodabarReader.prototype._decode = function() {
var self = this,
result = [],
start,
decodedChar,
pattern,
nextStart,
end;
this._counters = self._fillCounters();
start = self._findStart();
if (!start) {
return null;
}
nextStart = start.startCounter;
do {
pattern = self._toPattern(nextStart);
if (pattern < 0) {
return null;
}
decodedChar = self._patternToChar(pattern);
if (decodedChar < 0){
return null;
}
result.push(decodedChar);
nextStart += 8;
if (result.length > 1 && self._isStartEnd(pattern)) {
break;
}
} while(nextStart < self._counters.length);
// verify end
if ((result.length - 2) < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) {
return null;
}
// verify end white space
if (!self._verifyWhitespace(start.startCounter, nextStart - 8)){
return null;
}
if (!self._validateResult(result, start.startCounter)){
return null;
}
nextStart = nextStart > self._counters.length ? self._counters.length : nextStart;
end = start.start + self._sumCounters(start.startCounter, nextStart - 8);
return {
code : result.join(""),
start : start.start,
end : end,
startInfo : start,
decodedCodes : result
};
};
CodabarReader.prototype._verifyWhitespace = function(startCounter, endCounter) {
if ((startCounter - 1 <= 0) || this._counters[startCounter-1] >= (this._calculatePatternLength(startCounter) / 2.0)) {
if ((endCounter + 8 >= this._counters.length) || this._counters[endCounter+7] >= (this._calculatePatternLength(endCounter) / 2.0)) {
return true;
}
}
return false;
};
CodabarReader.prototype._calculatePatternLength = function(offset) {
var i,
sum = 0;
for (i = offset; i < offset + 7; i++) {
sum += this._counters[i];
}
return sum;
};
CodabarReader.prototype._thresholdResultPattern = function(result, startCounter){
var self = this,
categorization = {
space: {
narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE},
wide: {size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}
},
bar: {
narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE},
wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}
}
},
kind,
cat,
i,
j,
pos = startCounter,
pattern;
for (i = 0; i < result.length; i++){
pattern = self._charToPattern(result[i]);
for (j = 6; j >= 0; j--) {
kind = (j & 1) === 2 ? categorization.bar : categorization.space;
cat = (pattern & 1) === 1 ? kind.wide : kind.narrow;
cat.size += self._counters[pos + j];
cat.counts++;
pattern >>= 1;
}
pos += 8;
}
["space", "bar"].forEach(function(key) {
var kind = categorization[key];
kind.wide.min = Math.floor((kind.narrow.size/kind.narrow.counts + kind.wide.size / kind.wide.counts) / 2);
kind.narrow.max = Math.ceil(kind.wide.min);
kind.wide.max = Math.ceil((kind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / kind.wide.counts);
});
return categorization;
};
CodabarReader.prototype._charToPattern = function(char) {
var self = this,
charCode = char.charCodeAt(0),
i;
for (i = 0; i < self.ALPHABET.length; i++) {
if (self.ALPHABET[i] === charCode){
return self.CHARACTER_ENCODINGS[i];
}
}
return 0x0;
};
CodabarReader.prototype._validateResult = function(result, startCounter) {
var self = this,
thresholds = self._thresholdResultPattern(result, startCounter),
i,
j,
kind,
cat,
size,
pos = startCounter,
pattern;
for (i = 0; i < result.length; i++) {
pattern = self._charToPattern(result[i]);
for (j = 6; j >= 0; j--) {
kind = (j & 1) === 0 ? thresholds.bar : thresholds.space;
cat = (pattern & 1) === 1 ? kind.wide : kind.narrow;
size = self._counters[pos + j];
if (size < cat.min || size > cat.max) {
return false;
}
pattern >>= 1;
}
pos += 8;
}
return true;
};
CodabarReader.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;
};
CodabarReader.prototype._computeAlternatingThreshold = function(offset, end) {
var i,
min = Number.MAX_VALUE,
max = 0,
counter;
for (i = offset; i < end; i += 2){
counter = this._counters[i];
if (counter > max) {
max = counter;
}
if (counter < min) {
min = counter;
}
}
return ((min + max) / 2.0) | 0;
};
CodabarReader.prototype._toPattern = function(offset) {
var numCounters = 7,
end = offset + numCounters,
barThreshold,
spaceThreshold,
bitmask = 1 << (numCounters - 1),
pattern = 0,
i,
threshold;
if (end > this._counters.length) {
return -1;
}
barThreshold = this._computeAlternatingThreshold(offset, end);
spaceThreshold = this._computeAlternatingThreshold(offset + 1, end);
for (i = 0; i < numCounters; i++){
threshold = (i & 1) === 0 ? barThreshold : spaceThreshold;
if (this._counters[offset + i] > threshold) {
pattern |= bitmask;
}
bitmask >>= 1;
}
return pattern;
};
CodabarReader.prototype._isStartEnd = function(pattern) {
var i;
for (i = 0; i < this.START_END.length; i++) {
if (this.START_END[i] === pattern) {
return true;
}
}
return false;
};
CodabarReader.prototype._sumCounters = function(start, end) {
var i,
sum = 0;
for (i = start; i < end; i++) {
sum += this._counters[i];
}
return sum;
};
CodabarReader.prototype._findStart = function() {
var self = this,
i,
pattern,
start = self._nextUnset(self._row),
end;
for (i = 1; i < this._counters.length; i++) {
pattern = self._toPattern(i);
if (pattern !== -1 && self._isStartEnd(pattern)) {
// TODO: Look for whitespace ahead
start += self._sumCounters(0, i);
end = start + self._sumCounters(i, i + 8);
return {
start: start,
end: end,
startCounter: i,
endCounter: i + 8
};
}
}
};
return (CodabarReader);
this._counters = self._fillCounters();
start = self._findStart();
if (!start) {
return null;
}
);
nextStart = start.startCounter;
do {
pattern = self._toPattern(nextStart);
if (pattern < 0) {
return null;
}
decodedChar = self._patternToChar(pattern);
if (decodedChar < 0){
return null;
}
result.push(decodedChar);
nextStart += 8;
if (result.length > 1 && self._isStartEnd(pattern)) {
break;
}
} while (nextStart < self._counters.length);
// verify end
if ((result.length - 2) < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) {
return null;
}
// verify end white space
if (!self._verifyWhitespace(start.startCounter, nextStart - 8)){
return null;
}
if (!self._validateResult(result, start.startCounter)){
return null;
}
nextStart = nextStart > self._counters.length ? self._counters.length : nextStart;
end = start.start + self._sumCounters(start.startCounter, nextStart - 8);
return {
code: result.join(""),
start: start.start,
end: end,
startInfo: start,
decodedCodes: result
};
};
CodabarReader.prototype._verifyWhitespace = function(startCounter, endCounter) {
if ((startCounter - 1 <= 0)
|| this._counters[startCounter - 1] >= (this._calculatePatternLength(startCounter) / 2.0)) {
if ((endCounter + 8 >= this._counters.length)
|| this._counters[endCounter + 7] >= (this._calculatePatternLength(endCounter) / 2.0)) {
return true;
}
}
return false;
};
CodabarReader.prototype._calculatePatternLength = function(offset) {
var i,
sum = 0;
for (i = offset; i < offset + 7; i++) {
sum += this._counters[i];
}
return sum;
};
CodabarReader.prototype._thresholdResultPattern = function(result, startCounter){
var self = this,
categorization = {
space: {
narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE},
wide: {size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}
},
bar: {
narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE},
wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}
}
},
kind,
cat,
i,
j,
pos = startCounter,
pattern;
for (i = 0; i < result.length; i++){
pattern = self._charToPattern(result[i]);
for (j = 6; j >= 0; j--) {
kind = (j & 1) === 2 ? categorization.bar : categorization.space;
cat = (pattern & 1) === 1 ? kind.wide : kind.narrow;
cat.size += self._counters[pos + j];
cat.counts++;
pattern >>= 1;
}
pos += 8;
}
["space", "bar"].forEach(function(key) {
var newkind = categorization[key];
newkind.wide.min =
Math.floor((newkind.narrow.size / newkind.narrow.counts + newkind.wide.size / newkind.wide.counts) / 2);
newkind.narrow.max = Math.ceil(newkind.wide.min);
newkind.wide.max = Math.ceil((newkind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / newkind.wide.counts);
});
return categorization;
};
CodabarReader.prototype._charToPattern = function(char) {
var self = this,
charCode = char.charCodeAt(0),
i;
for (i = 0; i < self.ALPHABET.length; i++) {
if (self.ALPHABET[i] === charCode){
return self.CHARACTER_ENCODINGS[i];
}
}
return 0x0;
};
CodabarReader.prototype._validateResult = function(result, startCounter) {
var self = this,
thresholds = self._thresholdResultPattern(result, startCounter),
i,
j,
kind,
cat,
size,
pos = startCounter,
pattern;
for (i = 0; i < result.length; i++) {
pattern = self._charToPattern(result[i]);
for (j = 6; j >= 0; j--) {
kind = (j & 1) === 0 ? thresholds.bar : thresholds.space;
cat = (pattern & 1) === 1 ? kind.wide : kind.narrow;
size = self._counters[pos + j];
if (size < cat.min || size > cat.max) {
return false;
}
pattern >>= 1;
}
pos += 8;
}
return true;
};
CodabarReader.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;
};
CodabarReader.prototype._computeAlternatingThreshold = function(offset, end) {
var i,
min = Number.MAX_VALUE,
max = 0,
counter;
for (i = offset; i < end; i += 2){
counter = this._counters[i];
if (counter > max) {
max = counter;
}
if (counter < min) {
min = counter;
}
}
return ((min + max) / 2.0) | 0;
};
CodabarReader.prototype._toPattern = function(offset) {
var numCounters = 7,
end = offset + numCounters,
barThreshold,
spaceThreshold,
bitmask = 1 << (numCounters - 1),
pattern = 0,
i,
threshold;
if (end > this._counters.length) {
return -1;
}
barThreshold = this._computeAlternatingThreshold(offset, end);
spaceThreshold = this._computeAlternatingThreshold(offset + 1, end);
for (i = 0; i < numCounters; i++){
threshold = (i & 1) === 0 ? barThreshold : spaceThreshold;
if (this._counters[offset + i] > threshold) {
pattern |= bitmask;
}
bitmask >>= 1;
}
return pattern;
};
CodabarReader.prototype._isStartEnd = function(pattern) {
var i;
for (i = 0; i < this.START_END.length; i++) {
if (this.START_END[i] === pattern) {
return true;
}
}
return false;
};
CodabarReader.prototype._sumCounters = function(start, end) {
var i,
sum = 0;
for (i = start; i < end; i++) {
sum += this._counters[i];
}
return sum;
};
CodabarReader.prototype._findStart = function() {
var self = this,
i,
pattern,
start = self._nextUnset(self._row),
end;
for (i = 1; i < this._counters.length; i++) {
pattern = self._toPattern(i);
if (pattern !== -1 && self._isStartEnd(pattern)) {
// TODO: Look for whitespace ahead
start += self._sumCounters(0, i);
end = start + self._sumCounters(i, i + 8);
return {
start: start,
end: end,
startCounter: i,
endCounter: i + 8
};
}
}
};
export default CodabarReader;
+385 -401
View File
@@ -1,423 +1,407 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import BarcodeReader from './barcode_reader';
define(
[
"./barcode_reader"
],
function(BarcodeReader) {
"use strict";
function Code128Reader() {
BarcodeReader.call(this);
function Code128Reader() {
BarcodeReader.call(this);
}
var properties = {
CODE_SHIFT: {value: 98},
CODE_C: {value: 99},
CODE_B: {value: 100},
CODE_A: {value: 101},
START_CODE_A: {value: 103},
START_CODE_B: {value: 104},
START_CODE_C: {value: 105},
STOP_CODE: {value: 106},
MODULO: {value: 11},
CODE_PATTERN: {value: [
[2, 1, 2, 2, 2, 2],
[2, 2, 2, 1, 2, 2],
[2, 2, 2, 2, 2, 1],
[1, 2, 1, 2, 2, 3],
[1, 2, 1, 3, 2, 2],
[1, 3, 1, 2, 2, 2],
[1, 2, 2, 2, 1, 3],
[1, 2, 2, 3, 1, 2],
[1, 3, 2, 2, 1, 2],
[2, 2, 1, 2, 1, 3],
[2, 2, 1, 3, 1, 2],
[2, 3, 1, 2, 1, 2],
[1, 1, 2, 2, 3, 2],
[1, 2, 2, 1, 3, 2],
[1, 2, 2, 2, 3, 1],
[1, 1, 3, 2, 2, 2],
[1, 2, 3, 1, 2, 2],
[1, 2, 3, 2, 2, 1],
[2, 2, 3, 2, 1, 1],
[2, 2, 1, 1, 3, 2],
[2, 2, 1, 2, 3, 1],
[2, 1, 3, 2, 1, 2],
[2, 2, 3, 1, 1, 2],
[3, 1, 2, 1, 3, 1],
[3, 1, 1, 2, 2, 2],
[3, 2, 1, 1, 2, 2],
[3, 2, 1, 2, 2, 1],
[3, 1, 2, 2, 1, 2],
[3, 2, 2, 1, 1, 2],
[3, 2, 2, 2, 1, 1],
[2, 1, 2, 1, 2, 3],
[2, 1, 2, 3, 2, 1],
[2, 3, 2, 1, 2, 1],
[1, 1, 1, 3, 2, 3],
[1, 3, 1, 1, 2, 3],
[1, 3, 1, 3, 2, 1],
[1, 1, 2, 3, 1, 3],
[1, 3, 2, 1, 1, 3],
[1, 3, 2, 3, 1, 1],
[2, 1, 1, 3, 1, 3],
[2, 3, 1, 1, 1, 3],
[2, 3, 1, 3, 1, 1],
[1, 1, 2, 1, 3, 3],
[1, 1, 2, 3, 3, 1],
[1, 3, 2, 1, 3, 1],
[1, 1, 3, 1, 2, 3],
[1, 1, 3, 3, 2, 1],
[1, 3, 3, 1, 2, 1],
[3, 1, 3, 1, 2, 1],
[2, 1, 1, 3, 3, 1],
[2, 3, 1, 1, 3, 1],
[2, 1, 3, 1, 1, 3],
[2, 1, 3, 3, 1, 1],
[2, 1, 3, 1, 3, 1],
[3, 1, 1, 1, 2, 3],
[3, 1, 1, 3, 2, 1],
[3, 3, 1, 1, 2, 1],
[3, 1, 2, 1, 1, 3],
[3, 1, 2, 3, 1, 1],
[3, 3, 2, 1, 1, 1],
[3, 1, 4, 1, 1, 1],
[2, 2, 1, 4, 1, 1],
[4, 3, 1, 1, 1, 1],
[1, 1, 1, 2, 2, 4],
[1, 1, 1, 4, 2, 2],
[1, 2, 1, 1, 2, 4],
[1, 2, 1, 4, 2, 1],
[1, 4, 1, 1, 2, 2],
[1, 4, 1, 2, 2, 1],
[1, 1, 2, 2, 1, 4],
[1, 1, 2, 4, 1, 2],
[1, 2, 2, 1, 1, 4],
[1, 2, 2, 4, 1, 1],
[1, 4, 2, 1, 1, 2],
[1, 4, 2, 2, 1, 1],
[2, 4, 1, 2, 1, 1],
[2, 2, 1, 1, 1, 4],
[4, 1, 3, 1, 1, 1],
[2, 4, 1, 1, 1, 2],
[1, 3, 4, 1, 1, 1],
[1, 1, 1, 2, 4, 2],
[1, 2, 1, 1, 4, 2],
[1, 2, 1, 2, 4, 1],
[1, 1, 4, 2, 1, 2],
[1, 2, 4, 1, 1, 2],
[1, 2, 4, 2, 1, 1],
[4, 1, 1, 2, 1, 2],
[4, 2, 1, 1, 1, 2],
[4, 2, 1, 2, 1, 1],
[2, 1, 2, 1, 4, 1],
[2, 1, 4, 1, 2, 1],
[4, 1, 2, 1, 2, 1],
[1, 1, 1, 1, 4, 3],
[1, 1, 1, 3, 4, 1],
[1, 3, 1, 1, 4, 1],
[1, 1, 4, 1, 1, 3],
[1, 1, 4, 3, 1, 1],
[4, 1, 1, 1, 1, 3],
[4, 1, 1, 3, 1, 1],
[1, 1, 3, 1, 4, 1],
[1, 1, 4, 1, 3, 1],
[3, 1, 1, 1, 4, 1],
[4, 1, 1, 1, 3, 1],
[2, 1, 1, 4, 1, 2],
[2, 1, 1, 2, 1, 4],
[2, 1, 1, 2, 3, 2],
[2, 3, 3, 1, 1, 1, 2]
]},
SINGLE_CODE_ERROR: {value: 1},
AVG_CODE_ERROR: {value: 0.5},
FORMAT: {value: "code_128", writeable: false}
};
Code128Reader.prototype = Object.create(BarcodeReader.prototype, properties);
Code128Reader.prototype.constructor = Code128Reader;
Code128Reader.prototype._decodeCode = function(start) {
var counter = [0, 0, 0, 0, 0, 0],
i,
self = this,
offset = start,
isWhite = !self._row[offset],
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: start,
end: start
},
code,
error,
normalized;
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
if (counterPos === counter.length - 1) {
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;
}
}
bestMatch.end = i;
return bestMatch;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
var properties = {
CODE_SHIFT : {value: 98},
CODE_C : {value: 99},
CODE_B : {value: 100},
CODE_A : {value: 101},
START_CODE_A : {value: 103},
START_CODE_B : {value: 104},
START_CODE_C : {value: 105},
STOP_CODE : {value: 106},
MODULO : {value: 11},
CODE_PATTERN : {value: [
[2, 1, 2, 2, 2, 2],
[2, 2, 2, 1, 2, 2],
[2, 2, 2, 2, 2, 1],
[1, 2, 1, 2, 2, 3],
[1, 2, 1, 3, 2, 2],
[1, 3, 1, 2, 2, 2],
[1, 2, 2, 2, 1, 3],
[1, 2, 2, 3, 1, 2],
[1, 3, 2, 2, 1, 2],
[2, 2, 1, 2, 1, 3],
[2, 2, 1, 3, 1, 2],
[2, 3, 1, 2, 1, 2],
[1, 1, 2, 2, 3, 2],
[1, 2, 2, 1, 3, 2],
[1, 2, 2, 2, 3, 1],
[1, 1, 3, 2, 2, 2],
[1, 2, 3, 1, 2, 2],
[1, 2, 3, 2, 2, 1],
[2, 2, 3, 2, 1, 1],
[2, 2, 1, 1, 3, 2],
[2, 2, 1, 2, 3, 1],
[2, 1, 3, 2, 1, 2],
[2, 2, 3, 1, 1, 2],
[3, 1, 2, 1, 3, 1],
[3, 1, 1, 2, 2, 2],
[3, 2, 1, 1, 2, 2],
[3, 2, 1, 2, 2, 1],
[3, 1, 2, 2, 1, 2],
[3, 2, 2, 1, 1, 2],
[3, 2, 2, 2, 1, 1],
[2, 1, 2, 1, 2, 3],
[2, 1, 2, 3, 2, 1],
[2, 3, 2, 1, 2, 1],
[1, 1, 1, 3, 2, 3],
[1, 3, 1, 1, 2, 3],
[1, 3, 1, 3, 2, 1],
[1, 1, 2, 3, 1, 3],
[1, 3, 2, 1, 1, 3],
[1, 3, 2, 3, 1, 1],
[2, 1, 1, 3, 1, 3],
[2, 3, 1, 1, 1, 3],
[2, 3, 1, 3, 1, 1],
[1, 1, 2, 1, 3, 3],
[1, 1, 2, 3, 3, 1],
[1, 3, 2, 1, 3, 1],
[1, 1, 3, 1, 2, 3],
[1, 1, 3, 3, 2, 1],
[1, 3, 3, 1, 2, 1],
[3, 1, 3, 1, 2, 1],
[2, 1, 1, 3, 3, 1],
[2, 3, 1, 1, 3, 1],
[2, 1, 3, 1, 1, 3],
[2, 1, 3, 3, 1, 1],
[2, 1, 3, 1, 3, 1],
[3, 1, 1, 1, 2, 3],
[3, 1, 1, 3, 2, 1],
[3, 3, 1, 1, 2, 1],
[3, 1, 2, 1, 1, 3],
[3, 1, 2, 3, 1, 1],
[3, 3, 2, 1, 1, 1],
[3, 1, 4, 1, 1, 1],
[2, 2, 1, 4, 1, 1],
[4, 3, 1, 1, 1, 1],
[1, 1, 1, 2, 2, 4],
[1, 1, 1, 4, 2, 2],
[1, 2, 1, 1, 2, 4],
[1, 2, 1, 4, 2, 1],
[1, 4, 1, 1, 2, 2],
[1, 4, 1, 2, 2, 1],
[1, 1, 2, 2, 1, 4],
[1, 1, 2, 4, 1, 2],
[1, 2, 2, 1, 1, 4],
[1, 2, 2, 4, 1, 1],
[1, 4, 2, 1, 1, 2],
[1, 4, 2, 2, 1, 1],
[2, 4, 1, 2, 1, 1],
[2, 2, 1, 1, 1, 4],
[4, 1, 3, 1, 1, 1],
[2, 4, 1, 1, 1, 2],
[1, 3, 4, 1, 1, 1],
[1, 1, 1, 2, 4, 2],
[1, 2, 1, 1, 4, 2],
[1, 2, 1, 2, 4, 1],
[1, 1, 4, 2, 1, 2],
[1, 2, 4, 1, 1, 2],
[1, 2, 4, 2, 1, 1],
[4, 1, 1, 2, 1, 2],
[4, 2, 1, 1, 1, 2],
[4, 2, 1, 2, 1, 1],
[2, 1, 2, 1, 4, 1],
[2, 1, 4, 1, 2, 1],
[4, 1, 2, 1, 2, 1],
[1, 1, 1, 1, 4, 3],
[1, 1, 1, 3, 4, 1],
[1, 3, 1, 1, 4, 1],
[1, 1, 4, 1, 1, 3],
[1, 1, 4, 3, 1, 1],
[4, 1, 1, 1, 1, 3],
[4, 1, 1, 3, 1, 1],
[1, 1, 3, 1, 4, 1],
[1, 1, 4, 1, 3, 1],
[3, 1, 1, 1, 4, 1],
[4, 1, 1, 1, 3, 1],
[2, 1, 1, 4, 1, 2],
[2, 1, 1, 2, 1, 4],
[2, 1, 1, 2, 3, 2],
[2, 3, 3, 1, 1, 1, 2]
]},
SINGLE_CODE_ERROR: {value: 1},
AVG_CODE_ERROR: {value: 0.5},
FORMAT: {value: "code_128", writeable: false}
};
Code128Reader.prototype = Object.create(BarcodeReader.prototype, properties);
Code128Reader.prototype.constructor = Code128Reader;
Code128Reader.prototype._decodeCode = function(start) {
var counter = [0, 0, 0, 0, 0, 0],
i,
self = this,
offset = start,
isWhite = !self._row[offset],
counterPos = 0,
bestMatch = {
error : Number.MAX_VALUE,
code : -1,
start : start,
end : start
},
code,
error,
normalized;
}
return null;
};
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
if (counterPos === counter.length - 1) {
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;
}
}
bestMatch.end = i;
return bestMatch;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
Code128Reader.prototype._findStart = function() {
var counter = [0, 0, 0, 0, 0, 0],
i,
self = this,
offset = self._nextSet(self._row),
isWhite = false,
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: 0,
end: 0
},
code,
error,
j,
sum,
normalized;
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];
}
}
return null;
};
Code128Reader.prototype._findStart = function() {
var counter = [0, 0, 0, 0, 0, 0],
i,
self = this,
offset = self._nextSet(self._row),
isWhite = false,
counterPos = 0,
bestMatch = {
error : Number.MAX_VALUE,
code : -1,
start : 0,
end : 0
},
code,
error,
j,
sum,
normalized;
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) {
for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) {
error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
if (error < bestMatch.error) {
bestMatch.code = code;
bestMatch.error = error;
}
normalized = self._normalize(counter);
if (normalized) {
for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) {
error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
if (error < bestMatch.error) {
bestMatch.code = code;
bestMatch.error = error;
}
}
if (bestMatch.error < self.AVG_CODE_ERROR) {
bestMatch.start = i - sum;
bestMatch.end = i;
return bestMatch;
}
}
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;
if (bestMatch.error < self.AVG_CODE_ERROR) {
bestMatch.start = i - sum;
bestMatch.end = i;
return bestMatch;
}
}
}
return null;
};
Code128Reader.prototype._decode = function() {
var self = this,
startInfo = self._findStart(),
code = null,
done = false,
result = [],
multiplier = 0,
checksum = 0,
codeset,
rawResult = [],
decodedCodes = [],
shiftNext = false,
unshift,
lastCharacterWasPrintable;
if (startInfo === null) {
return null;
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;
};
Code128Reader.prototype._decode = function() {
var self = this,
startInfo = self._findStart(),
code = null,
done = false,
result = [],
multiplier = 0,
checksum = 0,
codeset,
rawResult = [],
decodedCodes = [],
shiftNext = false,
unshift;
if (startInfo === null) {
return null;
}
code = {
code: startInfo.code,
start: startInfo.start,
end: startInfo.end
};
decodedCodes.push(code);
checksum = code.code;
switch (code.code) {
case self.START_CODE_A:
codeset = self.CODE_A;
break;
case self.START_CODE_B:
codeset = self.CODE_B;
break;
case self.START_CODE_C:
codeset = self.CODE_C;
break;
default:
return null;
}
while (!done) {
unshift = shiftNext;
shiftNext = false;
code = self._decodeCode(code.end);
if (code !== null) {
if (code.code !== self.STOP_CODE) {
rawResult.push(code.code);
multiplier++;
checksum += multiplier * code.code;
}
code = {
code : startInfo.code,
start : startInfo.start,
end : startInfo.end
};
decodedCodes.push(code);
checksum = code.code;
switch(code.code) {
case self.START_CODE_A:
codeset = self.CODE_A;
break;
case self.START_CODE_B:
codeset = self.CODE_B;
break;
case self.START_CODE_C:
codeset = self.CODE_C;
break;
default:
return null;
}
while (!done) {
unshift = shiftNext;
shiftNext = false;
code = self._decodeCode(code.end);
if (code !== null) {
if (code.code !== self.STOP_CODE) {
rawResult.push(code.code);
multiplier++;
checksum += multiplier * code.code;
}
decodedCodes.push(code);
switch(codeset) {
case self.CODE_A:
if (code.code < 64) {
result.push(String.fromCharCode(32 + code.code));
} else if (code.code < 96) {
result.push(String.fromCharCode(code.code - 64));
} else {
switch (code.code) {
case self.CODE_SHIFT:
shiftNext = true;
codeset = self.CODE_B;
break;
case self.CODE_B:
codeset = self.CODE_B;
break;
case self.CODE_C:
codeset = self.CODE_C;
break;
case self.STOP_CODE:
done = true;
break;
}
}
switch (codeset) {
case self.CODE_A:
if (code.code < 64) {
result.push(String.fromCharCode(32 + code.code));
} else if (code.code < 96) {
result.push(String.fromCharCode(code.code - 64));
} else {
switch (code.code) {
case self.CODE_SHIFT:
shiftNext = true;
codeset = self.CODE_B;
break;
case self.CODE_B:
if (code.code < 96) {
result.push(String.fromCharCode(32 + code.code));
} else {
if (code.code != self.STOP_CODE) {
lastCharacterWasPrintable = false;
}
switch (code.code) {
case self.CODE_SHIFT:
shiftNext = true;
codeset = self.CODE_A;
break;
case self.CODE_A:
codeset = self.CODE_A;
break;
case self.CODE_C:
codeset = self.CODE_C;
break;
case self.STOP_CODE:
done = true;
break;
}
}
codeset = self.CODE_B;
break;
case self.CODE_C:
if (code.code < 100) {
result.push(code.code < 10 ? "0" + code.code : code.code);
}
switch (code.code) {
case self.CODE_A:
codeset = self.CODE_A;
break;
case self.CODE_B:
codeset = self.CODE_B;
break;
case self.STOP_CODE:
done = true;
break;
}
codeset = self.CODE_C;
break;
case self.STOP_CODE:
done = true;
break;
}
}
break;
case self.CODE_B:
if (code.code < 96) {
result.push(String.fromCharCode(32 + code.code));
} else {
switch (code.code) {
case self.CODE_SHIFT:
shiftNext = true;
codeset = self.CODE_A;
break;
case self.CODE_A:
codeset = self.CODE_A;
break;
case self.CODE_C:
codeset = self.CODE_C;
break;
case self.STOP_CODE:
done = true;
break;
}
}
break;
case self.CODE_C:
if (code.code < 100) {
result.push(code.code < 10 ? "0" + code.code : code.code);
}
switch (code.code) {
case self.CODE_A:
codeset = self.CODE_A;
break;
case self.CODE_B:
codeset = self.CODE_B;
break;
case self.STOP_CODE:
done = true;
break;
}
if (unshift) {
codeset = codeset == self.CODE_A ? self.CODE_B : self.CODE_A;
}
break;
}
if (code === null) {
return null;
}
// find end bar
code.end = self._nextUnset(self._row, code.end);
if(!self._verifyTrailingWhitespace(code)){
return null;
}
// checksum
// Does not work correctly yet!!! startcode - endcode?
checksum -= multiplier * rawResult[rawResult.length - 1];
if (checksum % 103 != rawResult[rawResult.length - 1]) {
return null;
}
if (!result.length) {
return null;
}
// remove last code from result (checksum)
result.splice(result.length - 1, 1);
return {
code : result.join(""),
start : startInfo.start,
end : code.end,
codeset : codeset,
startInfo : startInfo,
decodedCodes : decodedCodes,
endInfo : code
};
};
BarcodeReader.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;
};
return (Code128Reader);
} else {
done = true;
}
if (unshift) {
codeset = codeset === self.CODE_A ? self.CODE_B : self.CODE_A;
}
}
);
if (code === null) {
return null;
}
// find end bar
code.end = self._nextUnset(self._row, code.end);
if (!self._verifyTrailingWhitespace(code)){
return null;
}
// checksum
// Does not work correctly yet!!! startcode - endcode?
checksum -= multiplier * rawResult[rawResult.length - 1];
if (checksum % 103 !== rawResult[rawResult.length - 1]) {
return null;
}
if (!result.length) {
return null;
}
// remove last code from result (checksum)
result.splice(result.length - 1, 1);
return {
code: result.join(""),
start: startInfo.start,
end: code.end,
codeset: codeset,
startInfo: startInfo,
decodedCodes: decodedCodes,
endInfo: code
};
};
BarcodeReader.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;
};
export default Code128Reader;
+206 -213
View File
@@ -1,221 +1,214 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import BarcodeReader from './barcode_reader';
import ArrayHelper from './array_helper';
define(
[
"./barcode_reader",
"./array_helper"
],
function(BarcodeReader, ArrayHelper) {
"use strict";
function Code39Reader() {
BarcodeReader.call(this);
}
function Code39Reader() {
BarcodeReader.call(this);
var properties = {
ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"},
ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]},
CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049,
0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106,
0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A
]},
ASTERISK: {value: 0x094},
FORMAT: {value: "code_39", writeable: false}
};
Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties);
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() {
var self = this,
counters = [0, 0, 0, 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._verifyTrailingWhitespace(lastStart, nextStart, counters)) {
return null;
}
return {
code: result.join(""),
start: start.start,
end: nextStart,
startInfo: start,
decodedCodes: result
};
};
Code39Reader.prototype._verifyTrailingWhitespace = function(lastStart, nextStart, counters) {
var trailingWhitespaceEnd,
patternSize = ArrayHelper.sum(counters);
trailingWhitespaceEnd = nextStart - lastStart - patternSize;
if ((trailingWhitespaceEnd * 3) >= patternSize) {
return true;
}
return false;
};
Code39Reader.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]);
}
}
};
Code39Reader.prototype._findNextWidth = function(counters, current) {
var i,
minWidth = Number.MAX_VALUE;
for (i = 0; i < counters.length; i++) {
if (counters[i] < minWidth && counters[i] > current) {
minWidth = counters[i];
}
}
return minWidth;
};
Code39Reader.prototype._toPattern = function(counters) {
var numCounters = counters.length,
maxNarrowWidth = 0,
numWideBars = numCounters,
wideBarWidth = 0,
self = this,
pattern,
i;
while (numWideBars > 3) {
maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth);
numWideBars = 0;
pattern = 0;
for (i = 0; i < numCounters; i++) {
if (counters[i] > maxNarrowWidth) {
pattern |= 1 << (numCounters - 1 - i);
numWideBars++;
wideBarWidth += counters[i];
}
}
var properties = {
ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"},
ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]},
CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A]},
ASTERISK: {value: 0x094},
FORMAT: {value: "code_39", writeable: false}
};
Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties);
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;
if (numWideBars === 3) {
for (i = 0; i < numCounters && numWideBars > 0; i++) {
if (counters[i] > maxNarrowWidth) {
numWideBars--;
if ((counters[i] * 2) >= wideBarWidth) {
return -1;
}
}
}
return counter;
};
Code39Reader.prototype._decode = function() {
var self = this,
counters = [0,0,0,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._verifyTrailingWhitespace(lastStart, nextStart, counters)) {
return null;
}
return {
code : result.join(""),
start : start.start,
end : nextStart,
startInfo : start,
decodedCodes : result
};
};
Code39Reader.prototype._verifyTrailingWhitespace = function(lastStart, nextStart, counters) {
var trailingWhitespaceEnd,
patternSize = ArrayHelper.sum(counters);
trailingWhitespaceEnd = nextStart - lastStart - patternSize;
if ((trailingWhitespaceEnd * 3) >= patternSize) {
return true;
}
return false;
};
Code39Reader.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]);
}
}
};
Code39Reader.prototype._findNextWidth = function(counters, current) {
var i,
minWidth = Number.MAX_VALUE;
for (i = 0; i < counters.length; i++) {
if (counters[i] < minWidth && counters[i] > current) {
minWidth = counters[i];
}
}
return minWidth;
};
Code39Reader.prototype._toPattern = function(counters) {
var numCounters = counters.length,
maxNarrowWidth = 0,
numWideBars = numCounters,
wideBarWidth = 0,
self = this,
pattern,
i;
while(numWideBars > 3) {
maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth);
numWideBars = 0;
pattern = 0;
for (i = 0; i < numCounters; i++) {
if (counters[i] > maxNarrowWidth) {
pattern |= 1 << (numCounters - 1 - i);
numWideBars++;
wideBarWidth += counters[i];
}
}
if (numWideBars === 3) {
for (i = 0; i < numCounters && numWideBars > 0; i++) {
if (counters[i] > maxNarrowWidth) {
numWideBars--;
if ((counters[i] * 2) >= wideBarWidth) {
return -1;
}
}
}
return pattern;
}
}
return -1;
};
Code39Reader.prototype._findStart = function() {
var self = this,
offset = self._nextSet(self._row),
patternStart = offset,
counter = [0,0,0,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 < 7; j++) {
counter[j] = counter[j + 2];
}
counter[7] = 0;
counter[8] = 0;
counterPos--;
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};
return (Code39Reader);
return pattern;
}
}
);
return -1;
};
Code39Reader.prototype._findStart = function() {
var self = this,
offset = self._nextSet(self._row),
patternStart = offset,
counter = [0, 0, 0, 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 < 7; j++) {
counter[j] = counter[j + 2];
}
counter[7] = 0;
counter[8] = 0;
counterPos--;
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};
export default Code39Reader;
+44 -54
View File
@@ -1,59 +1,49 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import Code39Reader from './code_39_reader';
define(
[
"./code_39_reader"
],
function(Code39Reader) {
"use strict";
function Code39VINReader() {
Code39Reader.call(this);
}
function Code39VINReader() {
Code39Reader.call(this);
}
var patterns = {
IOQ: /[IOQ]/g,
AZ09: /[A-Z0-9]{17}/
};
var patterns = {
IOQ: /[IOQ]/g,
AZ09: /[A-Z0-9]{17}/
};
Code39VINReader.prototype = Object.create(Code39Reader.prototype);
Code39VINReader.prototype.constructor = Code39VINReader;
Code39VINReader.prototype = Object.create(Code39Reader.prototype);
Code39VINReader.prototype.constructor = Code39VINReader;
// Cribbed from:
// https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java
Code39VINReader.prototype._decode = function() {
var result = Code39Reader.prototype._decode.apply(this);
if (!result) {
return null;
}
var code = result.code;
if (!code) {
return;
}
code = code.replace(patterns.IOQ, '');
if (!code.match(patterns.AZ09)) {
console.log('Failed AZ09 pattern code:', code);
return null;
}
if (!this._checkChecksum(code)) {
return null;
}
result.code = code;
return result;
};
Code39VINReader.prototype._checkChecksum = function(code) {
// TODO
return !!code;
};
return (Code39VINReader);
// Cribbed from:
// https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java
Code39VINReader.prototype._decode = function() {
var result = Code39Reader.prototype._decode.apply(this);
if (!result) {
return null;
}
);
var code = result.code;
if (!code) {
return null;
}
code = code.replace(patterns.IOQ, '');
if (!code.match(patterns.AZ09)) {
console.log('Failed AZ09 pattern code:', code);
return null;
}
if (!this._checkChecksum(code)) {
return null;
}
result.code = code;
return result;
};
Code39VINReader.prototype._checkChecksum = function(code) {
// TODO
return !!code;
};
export default Code39VINReader;
+35 -42
View File
@@ -1,44 +1,40 @@
/**
* The basic configuration
*/
define(function(){
var config = {
inputStream: { name: "Live",
type: "LiveStream",
constraints: {
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment" // or user
},
area: {
top: "0%",
right: "0%",
left: "0%",
bottom: "0%"
},
singleChannel: false // true: only the red color-channel is read
},
tracking: false,
debug: false,
controls: false,
locate: true,
numOfWorkers: 4,
visual: {
export default {
inputStream: {
name: "Live",
type: "LiveStream",
constraints: {
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment" // or user
},
area: {
top: "0%",
right: "0%",
left: "0%",
bottom: "0%"
},
singleChannel: false // true: only the red color-channel is read
},
tracking: false,
debug: false,
controls: false,
locate: true,
numOfWorkers: 4,
visual: {
show: true
},
decoder:{
},
decoder: {
drawBoundingBox: false,
showFrequency: false,
drawScanline: false,
showPattern: false,
readers: [
'code_128_reader'
'code_128_reader'
]
},
locator: {
},
locator: {
halfSample: true,
patchSize: "medium", // x-small, small, medium, large, x-large
showCanvas: false,
@@ -49,12 +45,9 @@ define(function(){
showPatchLabels: false,
showRemainingPatchLabels: false,
boxFromPatches: {
showTransformed: false,
showTransformedBox: false,
showBB: false
showTransformed: false,
showTransformedBox: false,
showBB: false
}
}
};
return config;
});
}
};
+703 -672
View File
File diff suppressed because it is too large Load Diff
+41 -51
View File
@@ -1,55 +1,45 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import EANReader from './ean_reader';
define(
[
"./ean_reader"
],
function(EANReader) {
"use strict";
function EAN8Reader() {
EANReader.call(this);
}
function EAN8Reader() {
EANReader.call(this);
var properties = {
FORMAT: {value: "ean_8", writeable: false}
};
EAN8Reader.prototype = Object.create(EANReader.prototype, properties);
EAN8Reader.prototype.constructor = EAN8Reader;
EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) {
var i,
self = this;
for ( i = 0; i < 4; i++) {
code = self._decodeCode(code.end, self.CODE_G_START);
if (!code) {
return null;
}
var properties = {
FORMAT: {value: "ean_8", writeable: false}
};
EAN8Reader.prototype = Object.create(EANReader.prototype, properties);
EAN8Reader.prototype.constructor = EAN8Reader;
EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) {
var i,
self = this;
for ( i = 0; i < 4; i++) {
code = self._decodeCode(code.end, self.CODE_G_START);
if (!code) {
return null;
}
result.push(code.code);
decodedCodes.push(code);
}
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false);
if (code === null) {
return null;
}
decodedCodes.push(code);
for ( i = 0; i < 4; i++) {
code = self._decodeCode(code.end, self.CODE_G_START);
if (!code) {
return null;
}
decodedCodes.push(code);
result.push(code.code);
}
return code;
};
return (EAN8Reader);
result.push(code.code);
decodedCodes.push(code);
}
);
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false);
if (code === null) {
return null;
}
decodedCodes.push(code);
for ( i = 0; i < 4; i++) {
code = self._decodeCode(code.end, self.CODE_G_START);
if (!code) {
return null;
}
decodedCodes.push(code);
result.push(code.code);
}
return code;
};
export default EAN8Reader;
+321 -331
View File
@@ -1,337 +1,327 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import BarcodeReader from './barcode_reader';
define(
[
"./barcode_reader"
],
function(BarcodeReader) {
"use strict";
function EANReader(opts) {
BarcodeReader.call(this, opts);
}
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]},
CODE_PATTERN : {value: [
[3, 2, 1, 1],
[2, 2, 2, 1],
[2, 1, 2, 2],
[1, 4, 1, 1],
[1, 1, 3, 2],
[1, 2, 3, 1],
[1, 1, 1, 4],
[1, 3, 1, 2],
[1, 2, 1, 3],
[3, 1, 1, 2],
[1, 1, 2, 3],
[1, 2, 2, 2],
[2, 2, 1, 2],
[1, 1, 4, 1],
[2, 3, 1, 1],
[1, 3, 2, 1],
[4, 1, 1, 1],
[2, 1, 3, 1],
[3, 1, 2, 1],
[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},
FORMAT: {value: "ean_13", writeable: false}
};
EANReader.prototype = Object.create(BarcodeReader.prototype, properties);
EANReader.prototype.constructor = EANReader;
EANReader.prototype._decodeCode = function(start, coderange) {
var counter = [0, 0, 0, 0],
i,
self = this,
offset = start,
isWhite = !self._row[offset],
counterPos = 0,
bestMatch = {
error : Number.MAX_VALUE,
code : -1,
start : start,
end : start
},
code,
error,
normalized;
function EANReader(opts) {
BarcodeReader.call(this, opts);
}
if (!coderange) {
coderange = self.CODE_PATTERN.length;
}
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]},
CODE_PATTERN: {value: [
[3, 2, 1, 1],
[2, 2, 2, 1],
[2, 1, 2, 2],
[1, 4, 1, 1],
[1, 1, 3, 2],
[1, 2, 3, 1],
[1, 1, 1, 4],
[1, 3, 1, 2],
[1, 2, 1, 3],
[3, 1, 1, 2],
[1, 1, 2, 3],
[1, 2, 2, 2],
[2, 2, 1, 2],
[1, 1, 4, 1],
[2, 3, 1, 1],
[1, 3, 2, 1],
[4, 1, 1, 1],
[2, 1, 3, 1],
[3, 1, 2, 1],
[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},
FORMAT: {value: "ean_13", writeable: false}
};
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
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;
}
return bestMatch;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};
EANReader.prototype = Object.create(BarcodeReader.prototype, properties);
EANReader.prototype.constructor = EANReader;
EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, epsilon) {
var counter = [],
self = this,
i,
counterPos = 0,
bestMatch = {
error : Number.MAX_VALUE,
code : -1,
start : 0,
end : 0
},
error,
j,
sum,
normalized;
EANReader.prototype._decodeCode = function(start, coderange) {
var counter = [0, 0, 0, 0],
i,
self = this,
offset = start,
isWhite = !self._row[offset],
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: start,
end: start
},
code,
error,
normalized;
if (!offset) {
offset = self._nextSet(self._row);
}
if (isWhite === undefined) {
isWhite = false;
}
if (tryHarder === undefined) {
tryHarder = true;
}
if ( epsilon === undefined) {
epsilon = self.AVG_CODE_ERROR;
}
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;
};
EANReader.prototype._findStart = function() {
var self = this,
leadingWhitespaceStart,
offset = self._nextSet(self._row),
startInfo;
while(!startInfo) {
startInfo = self._findPattern(self.START_PATTERN, offset);
if (!startInfo) {
return null;
}
leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start);
if (leadingWhitespaceStart >= 0) {
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
return startInfo;
}
}
offset = startInfo.end;
startInfo = null;
}
};
EANReader.prototype._verifyTrailingWhitespace = function(endInfo) {
var self = this,
trailingWhitespaceEnd;
trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start);
if (trailingWhitespaceEnd < self._row.length) {
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
return endInfo;
}
}
return null;
};
EANReader.prototype._findEnd = function(offset, isWhite) {
var self = this,
endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false);
return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null;
};
EANReader.prototype._calculateFirstDigit = function(codeFrequency) {
var i,
self = this;
for ( i = 0; i < self.CODE_FREQUENCY.length; i++) {
if (codeFrequency === self.CODE_FREQUENCY[i]) {
return i;
}
}
return null;
};
EANReader.prototype._decodePayload = function(code, result, decodedCodes) {
var i,
self = this,
codeFrequency = 0x0,
firstDigit;
for ( i = 0; i < 6; i++) {
code = self._decodeCode(code.end);
if (!code) {
return null;
}
if (code.code >= self.CODE_G_START) {
code.code = code.code - self.CODE_G_START;
codeFrequency |= 1 << (5 - i);
} else {
codeFrequency |= 0 << (5 - i);
}
result.push(code.code);
decodedCodes.push(code);
}
firstDigit = self._calculateFirstDigit(codeFrequency);
if (firstDigit === null) {
return null;
}
result.unshift(firstDigit);
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false);
if (code === null) {
return null;
}
decodedCodes.push(code);
for ( i = 0; i < 6; i++) {
code = self._decodeCode(code.end, self.CODE_G_START);
if (!code) {
return null;
}
decodedCodes.push(code);
result.push(code.code);
}
return code;
};
EANReader.prototype._decode = function() {
var startInfo,
self = this,
code,
result = [],
decodedCodes = [];
startInfo = self._findStart();
if (!startInfo) {
return null;
}
code = {
code : startInfo.code,
start : startInfo.start,
end : startInfo.end
};
decodedCodes.push(code);
code = self._decodePayload(code, result, decodedCodes);
if (!code) {
return null;
}
code = self._findEnd(code.end, false);
if (!code){
return null;
}
decodedCodes.push(code);
// Checksum
if (!self._checksum(result)) {
return null;
}
return {
code : result.join(""),
start : startInfo.start,
end : code.end,
codeset : "",
startInfo : startInfo,
decodedCodes : decodedCodes
};
};
EANReader.prototype._checksum = function(result) {
var sum = 0, i;
for ( i = result.length - 2; i >= 0; i -= 2) {
sum += result[i];
}
sum *= 3;
for ( i = result.length - 1; i >= 0; i -= 2) {
sum += result[i];
}
return sum % 10 === 0;
};
return (EANReader);
if (!coderange) {
coderange = self.CODE_PATTERN.length;
}
);
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
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;
}
return bestMatch;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};
EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, epsilon) {
var counter = [],
self = this,
i,
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: 0,
end: 0
},
error,
j,
sum,
normalized;
if (!offset) {
offset = self._nextSet(self._row);
}
if (isWhite === undefined) {
isWhite = false;
}
if (tryHarder === undefined) {
tryHarder = true;
}
if ( epsilon === undefined) {
epsilon = self.AVG_CODE_ERROR;
}
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;
};
EANReader.prototype._findStart = function() {
var self = this,
leadingWhitespaceStart,
offset = self._nextSet(self._row),
startInfo;
while (!startInfo) {
startInfo = self._findPattern(self.START_PATTERN, offset);
if (!startInfo) {
return null;
}
leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start);
if (leadingWhitespaceStart >= 0) {
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
return startInfo;
}
}
offset = startInfo.end;
startInfo = null;
}
};
EANReader.prototype._verifyTrailingWhitespace = function(endInfo) {
var self = this,
trailingWhitespaceEnd;
trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start);
if (trailingWhitespaceEnd < self._row.length) {
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
return endInfo;
}
}
return null;
};
EANReader.prototype._findEnd = function(offset, isWhite) {
var self = this,
endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false);
return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null;
};
EANReader.prototype._calculateFirstDigit = function(codeFrequency) {
var i,
self = this;
for ( i = 0; i < self.CODE_FREQUENCY.length; i++) {
if (codeFrequency === self.CODE_FREQUENCY[i]) {
return i;
}
}
return null;
};
EANReader.prototype._decodePayload = function(code, result, decodedCodes) {
var i,
self = this,
codeFrequency = 0x0,
firstDigit;
for ( i = 0; i < 6; i++) {
code = self._decodeCode(code.end);
if (!code) {
return null;
}
if (code.code >= self.CODE_G_START) {
code.code = code.code - self.CODE_G_START;
codeFrequency |= 1 << (5 - i);
} else {
codeFrequency |= 0 << (5 - i);
}
result.push(code.code);
decodedCodes.push(code);
}
firstDigit = self._calculateFirstDigit(codeFrequency);
if (firstDigit === null) {
return null;
}
result.unshift(firstDigit);
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false);
if (code === null) {
return null;
}
decodedCodes.push(code);
for ( i = 0; i < 6; i++) {
code = self._decodeCode(code.end, self.CODE_G_START);
if (!code) {
return null;
}
decodedCodes.push(code);
result.push(code.code);
}
return code;
};
EANReader.prototype._decode = function() {
var startInfo,
self = this,
code,
result = [],
decodedCodes = [];
startInfo = self._findStart();
if (!startInfo) {
return null;
}
code = {
code: startInfo.code,
start: startInfo.start,
end: startInfo.end
};
decodedCodes.push(code);
code = self._decodePayload(code, result, decodedCodes);
if (!code) {
return null;
}
code = self._findEnd(code.end, false);
if (!code){
return null;
}
decodedCodes.push(code);
// Checksum
if (!self._checksum(result)) {
return null;
}
return {
code: result.join(""),
start: startInfo.start,
end: code.end,
codeset: "",
startInfo: startInfo,
decodedCodes: decodedCodes
};
};
EANReader.prototype._checksum = function(result) {
var sum = 0, i;
for ( i = result.length - 2; i >= 0; i -= 2) {
sum += result[i];
}
sum *= 3;
for ( i = result.length - 1; i >= 0; i -= 2) {
sum += result[i];
}
return sum % 10 === 0;
};
export default (EANReader);
+70 -79
View File
@@ -1,91 +1,82 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
export default (function() {
var events = {};
define(function() {
"use strict";
var _events = function() {
var events = {};
function getEvent(eventName) {
if (!events[eventName]) {
events[eventName] = {
subscribers : []
};
}
return events[eventName];
}
function clearEvents(){
events = {};
function getEvent(eventName) {
if (!events[eventName]) {
events[eventName] = {
subscribers: []
};
}
return events[eventName];
}
function publishSubscription(subscription, data) {
if (subscription.async) {
setTimeout(function() {
subscription.callback(data);
}, 4);
} else {
function clearEvents(){
events = {};
}
function publishSubscription(subscription, data) {
if (subscription.async) {
setTimeout(function() {
subscription.callback(data);
}, 4);
} else {
subscription.callback(data);
}
}
function subscribe(event, callback, async) {
var subscription;
if ( typeof callback === "function") {
subscription = {
callback: callback,
async: async
};
} else {
subscription = callback;
if (!subscription.callback) {
throw "Callback was not specified on options";
}
}
function subscribe(event, callback, async) {
var subscription;
if ( typeof callback === "function") {
subscription = {
callback : callback,
async : async
};
} else {
subscription = callback;
if (!subscription.callback) {
throw "Callback was not specified on options";
}
}
getEvent(event).subscribers.push(subscription);
}
getEvent(event).subscribers.push(subscription);
}
return {
subscribe: function(event, callback, async) {
return subscribe(event, callback, async);
},
publish: function(eventName, data) {
var event = getEvent(eventName),
subscribers = event.subscribers;
return {
subscribe : function(event, callback, async) {
return subscribe(event, callback, async);
},
publish : function(eventName, data) {
var event = getEvent(eventName),
subscribers = event.subscribers;
event.subscribers = subscribers.filter(function(subscriber) {
publishSubscription(subscriber, data);
return !subscriber.once;
});
},
once: function(event, callback, async) {
subscribe(event, {
callback: callback,
async: async,
once: true
});
},
unsubscribe: function(eventName, callback) {
var event;
if (eventName) {
event = getEvent(eventName);
if (event && callback) {
event.subscribers = event.subscribers.filter(function(subscriber){
return subscriber.callback !== callback;
});
} else {
event.subscribers = [];
}
event.subscribers = subscribers.filter(function(subscriber) {
publishSubscription(subscriber, data);
return !subscriber.once;
});
},
once: function(event, callback, async) {
subscribe(event, {
callback: callback,
async: async,
once: true
});
},
unsubscribe: function(eventName, callback) {
var event;
if (eventName) {
event = getEvent(eventName);
if (event && callback) {
event.subscribers = event.subscribers.filter(function(subscriber){
return subscriber.callback !== callback;
});
} else {
clearEvents();
event.subscribers = [];
}
} else {
clearEvents();
}
};
}();
return _events;
});
}
};
})();
+67 -72
View File
@@ -1,78 +1,73 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import CVUtils from './cv_utils';
define(["cv_utils"], function(CVUtils) {
"use strict";
var FrameGrabber = {};
var FrameGrabber = {};
FrameGrabber.create = function(inputStream, canvas) {
var _that = {},
_streamConfig = inputStream.getConfig(),
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()),
_canvasSize = inputStream.getCanvasSize(),
_size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()),
topRight = inputStream.getTopRight(),
_sx = topRight.x,
_sy = topRight.y,
_canvas,
_ctx = null,
_data = null;
FrameGrabber.create = function(inputStream, canvas) {
var _that = {},
_streamConfig = inputStream.getConfig(),
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()),
_canvasSize = inputStream.getCanvasSize(),
_size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()),
topRight = inputStream.getTopRight(),
_sx = topRight.x,
_sy = topRight.y,
_canvas,
_ctx = null,
_data = null;
_canvas = canvas ? canvas : document.createElement("canvas");
_canvas.width = _canvasSize.x;
_canvas.height = _canvasSize.y;
_ctx = _canvas.getContext("2d");
_data = new Uint8Array(_size.x * _size.y);
console.log("FrameGrabber", JSON.stringify({
size: _size,
topRight: topRight,
videoSize: _video_size,
canvasSize: _canvasSize
}));
_canvas = canvas ? canvas : document.createElement("canvas");
_canvas.width = _canvasSize.x;
_canvas.height = _canvasSize.y;
_ctx = _canvas.getContext("2d");
_data = new Uint8Array(_size.x * _size.y);
console.log("FrameGrabber", JSON.stringify({
size: _size,
topRight: topRight,
videoSize: _video_size,
canvasSize: _canvasSize
}));
/**
* Uses the given array as frame-buffer
*/
_that.attachData = function(data) {
_data = data;
};
/**
* Returns the used frame-buffer
*/
_that.getData = function() {
return _data;
};
/**
* Fetches a frame from the input-stream and puts into the frame-buffer.
* The image-data is converted to gray-scale and then half-sampled if configured.
*/
_that.grab = function() {
var doHalfSample = _streamConfig.halfSample,
frame = inputStream.getFrame(),
ctxData;
if (frame) {
_ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y);
ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data;
if(doHalfSample){
CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data);
} else {
CVUtils.computeGray(ctxData, _data, _streamConfig);
}
return true;
} else {
return false;
}
};
_that.getSize = function() {
return _size;
};
return _that;
/**
* Uses the given array as frame-buffer
*/
_that.attachData = function(data) {
_data = data;
};
return (FrameGrabber);
});
/**
* Returns the used frame-buffer
*/
_that.getData = function() {
return _data;
};
/**
* Fetches a frame from the input-stream and puts into the frame-buffer.
* The image-data is converted to gray-scale and then half-sampled if configured.
*/
_that.grab = function() {
var doHalfSample = _streamConfig.halfSample,
frame = inputStream.getFrame(),
ctxData;
if (frame) {
_ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y);
ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data;
if (doHalfSample){
CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data);
} else {
CVUtils.computeGray(ctxData, _data, _streamConfig);
}
return true;
} else {
return false;
}
};
_that.getSize = function() {
return _size;
};
return _that;
};
export default FrameGrabber;
-40
View File
@@ -1,40 +0,0 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define([], function() {
"use strict";
function createNode(htmlStr) {
var temp = document.createElement('div');
temp.innerHTML = htmlStr;
while (temp.firstChild) {
return temp.firstChild;
}
}
function mergeObjects(obj1, obj2) {
for (var p in obj2) {
try {
if (obj2[p].constructor == Object) {
obj1[p] = mergeObjects(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
} catch(e) {
obj1[p] = obj2[p];
}
}
return obj1;
}
return {
createNode : function(htmlStr) {
return createNode(htmlStr);
},
mergeObjects : function(obj1, obj2) {
return mergeObjects(obj1, obj2);
}
};
});
+332 -342
View File
@@ -1,344 +1,334 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import BarcodeReader from './barcode_reader';
const merge = require('lodash/object/merge');
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);
function I2of5Reader(opts) {
opts = merge(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'
}
};
export default I2of5Reader;
+40 -48
View File
@@ -1,49 +1,41 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define(function() {
"use strict";
return {
drawRect: function(pos, size, ctx, style){
ctx.strokeStyle = style.color;
ctx.fillStyle = style.color;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.strokeRect(pos.x, pos.y, size.x, size.y);
},
drawPath: function(path, def, ctx, style) {
ctx.strokeStyle = style.color;
ctx.fillStyle = style.color;
ctx.lineWidth = style.lineWidth;
ctx.beginPath();
ctx.moveTo(path[0][def.x], path[0][def.y]);
for (var j = 1; j < path.length; j++) {
ctx.lineTo(path[j][def.x], path[j][def.y]);
}
ctx.closePath();
ctx.stroke();
},
drawImage: function(imageData, size, ctx) {
var canvasData = ctx.getImageData(0, 0, size.x, size.y),
data = canvasData.data,
imageDataPos = imageData.length,
canvasDataPos = data.length,
value;
if (canvasDataPos/imageDataPos !== 4) {
return false;
}
while(imageDataPos--){
value = imageData[imageDataPos];
data[--canvasDataPos] = 255;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
}
ctx.putImageData(canvasData, 0, 0);
return true;
export default {
drawRect: function(pos, size, ctx, style){
ctx.strokeStyle = style.color;
ctx.fillStyle = style.color;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.strokeRect(pos.x, pos.y, size.x, size.y);
},
drawPath: function(path, def, ctx, style) {
ctx.strokeStyle = style.color;
ctx.fillStyle = style.color;
ctx.lineWidth = style.lineWidth;
ctx.beginPath();
ctx.moveTo(path[0][def.x], path[0][def.y]);
for (var j = 1; j < path.length; j++) {
ctx.lineTo(path[j][def.x], path[j][def.y]);
}
};
});
ctx.closePath();
ctx.stroke();
},
drawImage: function(imageData, size, ctx) {
var canvasData = ctx.getImageData(0, 0, size.x, size.y),
data = canvasData.data,
imageDataPos = imageData.length,
canvasDataPos = data.length,
value;
if (canvasDataPos / imageDataPos !== 4) {
return false;
}
while (imageDataPos--){
value = imageData[imageDataPos];
data[--canvasDataPos] = 255;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
}
ctx.putImageData(canvasData, 0, 0);
return true;
}
};
+50 -57
View File
@@ -1,63 +1,56 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
var ImageLoader = {};
ImageLoader.load = function(directory, callback, offset, size, sequence) {
var htmlImagesSrcArray = new Array(size),
htmlImagesArray = new Array(htmlImagesSrcArray.length),
i,
img,
num;
define(function() {
"use strict";
var ImageLoader = {};
ImageLoader.load = function(directory, callback, offset, size, sequence) {
var htmlImagesSrcArray = new Array(size),
htmlImagesArray = new Array(htmlImagesSrcArray.length),
i,
img,
num;
if (sequence === false) {
htmlImagesSrcArray[0] = directory;
} else {
for ( i = 0; i < htmlImagesSrcArray.length; i++) {
num = (offset + i);
htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg";
if (sequence === false) {
htmlImagesSrcArray[0] = directory;
} else {
for ( i = 0; i < htmlImagesSrcArray.length; i++) {
num = (offset + i);
htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg";
}
}
htmlImagesArray.notLoaded = [];
htmlImagesArray.addImage = function(image) {
htmlImagesArray.notLoaded.push(image);
};
htmlImagesArray.loaded = function(loadedImg) {
var notloadedImgs = htmlImagesArray.notLoaded;
for (var x = 0; x < notloadedImgs.length; x++) {
if (notloadedImgs[x] === loadedImg) {
notloadedImgs.splice(x, 1);
for (var y = 0; y < htmlImagesSrcArray.length; y++) {
var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/"));
if (loadedImg.src.lastIndexOf(imgName) !== -1) {
htmlImagesArray[y] = loadedImg;
break;
}
}
break;
}
}
htmlImagesArray.notLoaded = [];
htmlImagesArray.addImage = function(img) {
htmlImagesArray.notLoaded.push(img);
};
htmlImagesArray.loaded = function(loadedImg) {
var notloadedImgs = htmlImagesArray.notLoaded;
for (var x = 0; x < notloadedImgs.length; x++) {
if (notloadedImgs[x] == loadedImg) {
notloadedImgs.splice(x, 1);
for (var y = 0; y < htmlImagesSrcArray.length; y++) {
var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/"));
if (loadedImg.src.lastIndexOf(imgName) != -1) {
htmlImagesArray[y] = loadedImg;
break;
}
}
break;
}
}
if (notloadedImgs.length === 0) {
console.log("Images loaded");
callback.apply(null, [htmlImagesArray]);
}
};
for ( i = 0; i < htmlImagesSrcArray.length; i++) {
img = new Image();
htmlImagesArray.addImage(img);
addOnloadHandler(img, htmlImagesArray);
img.src = htmlImagesSrcArray[i];
if (notloadedImgs.length === 0) {
console.log("Images loaded");
callback.apply(null, [htmlImagesArray]);
}
};
function addOnloadHandler(img, htmlImagesArray) {
img.onload = function() {
htmlImagesArray.loaded(this);
};
}
return (ImageLoader);
});
for ( i = 0; i < htmlImagesSrcArray.length; i++) {
img = new Image();
htmlImagesArray.addImage(img);
addOnloadHandler(img, htmlImagesArray);
img.src = htmlImagesSrcArray[i];
}
};
function addOnloadHandler(img, htmlImagesArray) {
img.onload = function() {
htmlImagesArray.loaded(this);
};
}
export default (ImageLoader);
+329 -409
View File
@@ -1,427 +1,347 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import SubImage from './subImage';
import CVUtils from './cv_utils';
import ArrayHelper from './array_helper';
import {vec2} from 'gl-matrix';
define([
"subImage",
"cv_utils",
"array_helper",
"gl-matrix"
],
function(SubImage, CVUtils, ArrayHelper, glMatrix) {
'use strict';
var vec2 = glMatrix.vec2,
mat2 = glMatrix.mat2;
/**
* Represents a basic image combining the data and size.
* In addition, some methods for manipulation are contained.
* @param size {x,y} The size of the image in pixel
* @param data {Array} If given, a flat array containing the pixel data
* @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed)
* @param initialize {Boolean} Indicating if the array should be initialized on creation.
* @returns {ImageWrapper}
*/
function ImageWrapper(size, data, ArrayType, initialize) {
if (!data) {
if (ArrayType) {
this.data = new ArrayType(size.x * size.y);
if (ArrayType === Array && initialize) {
ArrayHelper.init(this.data, 0);
}
} else {
this.data = new Uint8Array(size.x * size.y);
if (Uint8Array === Array && initialize) {
ArrayHelper.init(this.data, 0);
}
}
} else {
this.data = data;
}
this.size = size;
}
/**
* Represents a basic image combining the data and size.
* In addition, some methods for manipulation are contained.
* @param size {x,y} The size of the image in pixel
* @param data {Array} If given, a flat array containing the pixel data
* @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed)
* @param initialize {Boolean} Indicating if the array should be initialized on creation.
* @returns {ImageWrapper}
*/
function ImageWrapper(size, data, ArrayType, initialize) {
if (!data) {
if (ArrayType) {
this.data = new ArrayType(size.x * size.y);
if (ArrayType === Array && initialize) {
ArrayHelper.init(this.data, 0);
}
} else {
this.data = new Uint8Array(size.x * size.y);
if (Uint8Array === Array && initialize) {
ArrayHelper.init(this.data, 0);
/**
* tests if a position is within the image with a given offset
* @param imgRef {x, y} The location to test
* @param border Number the padding value in pixel
* @returns {Boolean} true if location inside the image's border, false otherwise
* @see cvd/image.h
*/
ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) {
return (imgRef.x >= border)
&& (imgRef.y >= border)
&& (imgRef.x < (this.size.x - border))
&& (imgRef.y < (this.size.y - border));
};
/**
* Performs bilinear sampling
* @param inImg Image to extract sample from
* @param x the x-coordinate
* @param y the y-coordinate
* @returns the sampled value
* @see cvd/vision.h
*/
ImageWrapper.sample = function(inImg, x, y) {
var lx = Math.floor(x);
var ly = Math.floor(y);
var w = inImg.size.x;
var base = ly * inImg.size.x + lx;
var a = inImg.data[base + 0];
var b = inImg.data[base + 1];
var c = inImg.data[base + w];
var d = inImg.data[base + w + 1];
var e = a - b;
x -= lx;
y -= ly;
var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a);
return result;
};
/**
* Initializes a given array. Sets each element to zero.
* @param array {Array} The array to initialize
*/
ImageWrapper.clearArray = function(array) {
var l = array.length;
while (l--) {
array[l] = 0;
}
};
/**
* Creates a {SubImage} from the current image ({this}).
* @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner)
* @param size {ImageRef} The size of the resulting image
* @returns {SubImage} A shared part of the original image
*/
ImageWrapper.prototype.subImage = function(from, size) {
return new SubImage(from, size, this);
};
/**
* Creates an {ImageWrapper) and copies the needed underlying image-data area
* @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied
* @param from {ImageRef} The location where to copy from (top-left location)
*/
ImageWrapper.prototype.subImageAsCopy = function(imageWrapper, from) {
var sizeY = imageWrapper.size.y, sizeX = imageWrapper.size.x;
var x, y;
for ( x = 0; x < sizeX; x++) {
for ( y = 0; y < sizeY; y++) {
imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x];
}
}
};
ImageWrapper.prototype.copyTo = function(imageWrapper) {
var length = this.data.length, srcData = this.data, dstData = imageWrapper.data;
while (length--) {
dstData[length] = srcData[length];
}
};
/**
* Retrieves a given pixel position from the image
* @param x {Number} The x-position
* @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position
*/
ImageWrapper.prototype.get = function(x, y) {
return this.data[y * this.size.x + x];
};
/**
* Retrieves a given pixel position from the image
* @param x {Number} The x-position
* @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position
*/
ImageWrapper.prototype.getSafe = function(x, y) {
var i;
if (!this.indexMapping) {
this.indexMapping = {
x: [],
y: []
};
for (i = 0; i < this.size.x; i++) {
this.indexMapping.x[i] = i;
this.indexMapping.x[i + this.size.x] = i;
}
for (i = 0; i < this.size.y; i++) {
this.indexMapping.y[i] = i;
this.indexMapping.y[i + this.size.y] = i;
}
}
return this.data[(this.indexMapping.y[y + this.size.y]) * this.size.x + this.indexMapping.x[x + this.size.x]];
};
/**
* Sets a given pixel position in the image
* @param x {Number} The x-position
* @param y {Number} The y-position
* @param value {Number} The grayscale value to set
* @returns {ImageWrapper} The Image itself (for possible chaining)
*/
ImageWrapper.prototype.set = function(x, y, value) {
this.data[y * this.size.x + x] = value;
return this;
};
/**
* Sets the border of the image (1 pixel) to zero
*/
ImageWrapper.prototype.zeroBorder = function() {
var i, width = this.size.x, height = this.size.y, data = this.data;
for ( i = 0; i < width; i++) {
data[i] = data[(height - 1) * width + i] = 0;
}
for ( i = 1; i < height - 1; i++) {
data[i * width] = data[i * width + (width - 1)] = 0;
}
};
/**
* Inverts a binary image in place
*/
ImageWrapper.prototype.invert = function() {
var data = this.data, length = data.length;
while (length--) {
data[length] = data[length] ? 0 : 1;
}
};
ImageWrapper.prototype.convolve = function(kernel) {
var x, y, kx, ky, kSize = (kernel.length / 2) | 0, accu = 0;
for ( y = 0; y < this.size.y; y++) {
for ( x = 0; x < this.size.x; x++) {
accu = 0;
for ( ky = -kSize; ky <= kSize; ky++) {
for ( kx = -kSize; kx <= kSize; kx++) {
accu += kernel[ky + kSize][kx + kSize] * this.getSafe(x + kx, y + ky);
}
}
} else {
this.data = data;
this.data[y * this.size.x + x] = accu;
}
this.size = size;
}
};
ImageWrapper.prototype.moments = function(labelcount) {
var data = this.data,
x,
y,
height = this.size.y,
width = this.size.x,
val,
ysq,
labelsum = [],
i,
label,
mu11,
mu02,
mu20,
x_,
y_,
tmp,
result = [],
PI = Math.PI,
PI_4 = PI / 4;
if (labelcount <= 0) {
return result;
}
/**
* tests if a position is within the image with a given offset
* @param imgRef {x, y} The location to test
* @param border Number the padding value in pixel
* @returns {Boolean} true if location inside the image's border, false otherwise
* @see cvd/image.h
*/
ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) {
return (imgRef.x >= border) && (imgRef.y >= border) && (imgRef.x < (this.size.x - border)) && (imgRef.y < (this.size.y - border));
};
for ( i = 0; i < labelcount; i++) {
labelsum[i] = {
m00: 0,
m01: 0,
m10: 0,
m11: 0,
m02: 0,
m20: 0,
theta: 0,
rad: 0
};
}
/**
* Transforms an image according to the given affine-transformation matrix.
* @param inImg ImageWrapper a image containing the information to be extracted.
* @param outImg ImageWrapper the image to be filled. The whole image out image is filled by the in image.
* @param M mat2 the matrix used to map point in the out matrix to those in the in matrix
* @param inOrig vec2 origin in the in image
* @param outOrig vec2 origin in the out image
* @returns Number the number of pixels not in the in image
* @see cvd/vision.h
*/
ImageWrapper.transform = function(inImg, outImg, M, inOrig, outOrig) {
var w = outImg.size.x, h = outImg.size.y, iw = inImg.size.x, ih = inImg.size.y;
var across = vec2.clone([M[0], M[2]]);
var down = vec2.clone([M[1], M[3]]);
var defaultValue = 0;
var p0 = vec2.subtract(inOrig, mat2.xVec2(M, outOrig, vec2.clone()), vec2.clone());
var min_x = p0[0], min_y = p0[1];
var max_x = min_x, max_y = min_y;
var p, i, j;
var sampleFunc = ImageWrapper.sample;
if (across[0] < 0)
min_x += w * across[0];
else
max_x += w * across[0];
if (down[0] < 0)
min_x += h * down[0];
else
max_x += h * down[0];
if (across[1] < 0)
min_y += w * across[1];
else
max_y += w * across[1];
if (down[1] < 0)
min_y += h * down[1];
else
max_y += h * down[1];
var carrigeReturn = vec2.subtract(down, vec2.scale(across, w, vec2.clone()), vec2.clone());
if (min_x >= 0 && min_y >= 0 && max_x < iw - 1 && max_y < ih - 1) {
p = p0;
for ( i = 0; i < h; ++i, vec2.add(p, carrigeReturn))
for ( j = 0; j < w; ++j, vec2.add(p, across))
outImg.set(j, i, sampleFunc(inImg, p[0], p[1]));
return 0;
} else {
var x_bound = iw - 1;
var y_bound = ih - 1;
var count = 0;
p = p0;
for ( i = 0; i < h; ++i, vec2.add(p, carrigeReturn)) {
for ( j = 0; j < w; ++j, vec2.add(p, across)) {
if (0 <= p[0] && 0 <= p[1] && p[0] < x_bound && p[1] < y_bound) {
outImg.set(j, i, sampleFunc(inImg, p[0], p[1]));
} else {
outImg.set(j, i, defaultValue); ++count;
}
}
}
return count;
}
};
/**
* Performs bilinear sampling
* @param inImg Image to extract sample from
* @param x the x-coordinate
* @param y the y-coordinate
* @returns the sampled value
* @see cvd/vision.h
*/
ImageWrapper.sample = function(inImg, x, y) {
var lx = Math.floor(x);
var ly = Math.floor(y);
var w = inImg.size.x;
var base = ly * inImg.size.x + lx;
var a = inImg.data[base + 0];
var b = inImg.data[base + 1];
var c = inImg.data[base + w];
var d = inImg.data[base + w + 1];
var e = a - b;
x -= lx;
y -= ly;
var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a);
return result;
};
/**
* Initializes a given array. Sets each element to zero.
* @param array {Array} The array to initialize
*/
ImageWrapper.clearArray = function(array) {
var l = array.length;
while (l--) {
array[l] = 0;
}
};
/**
* Creates a {SubImage} from the current image ({this}).
* @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner)
* @param size {ImageRef} The size of the resulting image
* @returns {SubImage} A shared part of the original image
*/
ImageWrapper.prototype.subImage = function(from, size) {
return new SubImage(from, size, this);
};
/**
* Creates an {ImageWrapper) and copies the needed underlying image-data area
* @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied
* @param from {ImageRef} The location where to copy from (top-left location)
*/
ImageWrapper.prototype.subImageAsCopy = function(imageWrapper, from) {
var sizeY = imageWrapper.size.y, sizeX = imageWrapper.size.x;
var x, y;
for ( x = 0; x < sizeX; x++) {
for ( y = 0; y < sizeY; y++) {
imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x];
for ( y = 0; y < height; y++) {
ysq = y * y;
for ( x = 0; x < width; x++) {
val = data[y * width + x];
if (val > 0) {
label = labelsum[val - 1];
label.m00 += 1;
label.m01 += y;
label.m10 += x;
label.m11 += x * y;
label.m02 += ysq;
label.m20 += x * x;
}
}
};
}
ImageWrapper.prototype.copyTo = function(imageWrapper) {
var length = this.data.length, srcData = this.data, dstData = imageWrapper.data;
while (length--) {
dstData[length] = srcData[length];
}
};
/**
* Retrieves a given pixel position from the image
* @param x {Number} The x-position
* @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position
*/
ImageWrapper.prototype.get = function(x, y) {
return this.data[y * this.size.x + x];
};
/**
* Retrieves a given pixel position from the image
* @param x {Number} The x-position
* @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position
*/
ImageWrapper.prototype.getSafe = function(x, y) {
var i;
if (!this.indexMapping) {
this.indexMapping = {
x : [],
y : []
};
for (i = 0; i < this.size.x; i++) {
this.indexMapping.x[i] = i;
this.indexMapping.x[i + this.size.x] = i;
}
for (i = 0; i < this.size.y; i++) {
this.indexMapping.y[i] = i;
this.indexMapping.y[i + this.size.y] = i;
for ( i = 0; i < labelcount; i++) {
label = labelsum[i];
if (!isNaN(label.m00) && label.m00 !== 0) {
x_ = label.m10 / label.m00;
y_ = label.m01 / label.m00;
mu11 = label.m11 / label.m00 - x_ * y_;
mu02 = label.m02 / label.m00 - y_ * y_;
mu20 = label.m20 / label.m00 - x_ * x_;
tmp = (mu02 - mu20) / (2 * mu11);
tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4 ) + PI;
label.theta = (tmp * 180 / PI + 90) % 180 - 90;
if (label.theta < 0) {
label.theta += 180;
}
label.rad = tmp > PI ? tmp - PI : tmp;
label.vec = vec2.clone([Math.cos(tmp), Math.sin(tmp)]);
result.push(label);
}
return this.data[(this.indexMapping.y[y + this.size.y]) * this.size.x + this.indexMapping.x[x + this.size.x]];
};
}
/**
* Sets a given pixel position in the image
* @param x {Number} The x-position
* @param y {Number} The y-position
* @param value {Number} The grayscale value to set
* @returns {ImageWrapper} The Image itself (for possible chaining)
*/
ImageWrapper.prototype.set = function(x, y, value) {
this.data[y * this.size.x + x] = value;
return this;
};
return result;
};
/**
* Sets the border of the image (1 pixel) to zero
*/
ImageWrapper.prototype.zeroBorder = function() {
var i, width = this.size.x, height = this.size.y, data = this.data;
for ( i = 0; i < width; i++) {
data[i] = data[(height - 1) * width + i] = 0;
/**
* Displays the {ImageWrapper} in a given canvas
* @param canvas {Canvas} The canvas element to write to
* @param scale {Number} Scale which is applied to each pixel-value
*/
ImageWrapper.prototype.show = function(canvas, scale) {
var ctx,
frame,
data,
current,
pixel,
x,
y;
if (!scale) {
scale = 1.0;
}
ctx = canvas.getContext('2d');
canvas.width = this.size.x;
canvas.height = this.size.y;
frame = ctx.getImageData(0, 0, canvas.width, canvas.height);
data = frame.data;
current = 0;
for (y = 0; y < this.size.y; y++) {
for (x = 0; x < this.size.x; x++) {
pixel = y * this.size.x + x;
current = this.get(x, y) * scale;
data[pixel * 4 + 0] = current;
data[pixel * 4 + 1] = current;
data[pixel * 4 + 2] = current;
data[pixel * 4 + 3] = 255;
}
for ( i = 1; i < height - 1; i++) {
data[i * width] = data[i * width + (width - 1)] = 0;
}
};
}
//frame.data = data;
ctx.putImageData(frame, 0, 0);
};
/**
* Inverts a binary image in place
*/
ImageWrapper.prototype.invert = function() {
var data = this.data, length = data.length;
/**
* Displays the {SubImage} in a given canvas
* @param canvas {Canvas} The canvas element to write to
* @param scale {Number} Scale which is applied to each pixel-value
*/
ImageWrapper.prototype.overlay = function(canvas, scale, from) {
if (!scale || scale < 0 || scale > 360) {
scale = 360;
}
var hsv = [0, 1, 1];
var rgb = [0, 0, 0];
var whiteRgb = [255, 255, 255];
var blackRgb = [0, 0, 0];
var result = [];
var ctx = canvas.getContext('2d');
var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y);
var data = frame.data;
var length = this.data.length;
while (length--) {
hsv[0] = this.data[length] * scale;
result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : CVUtils.hsv2rgb(hsv, rgb);
data[length * 4 + 0] = result[0];
data[length * 4 + 1] = result[1];
data[length * 4 + 2] = result[2];
data[length * 4 + 3] = 255;
}
ctx.putImageData(frame, from.x, from.y);
};
while (length--) {
data[length] = data[length] ? 0 : 1;
}
};
ImageWrapper.prototype.convolve = function(kernel) {
var x, y, kx, ky, kSize = (kernel.length / 2) | 0, accu = 0;
for ( y = 0; y < this.size.y; y++) {
for ( x = 0; x < this.size.x; x++) {
accu = 0;
for ( ky = -kSize; ky <= kSize; ky++) {
for ( kx = -kSize; kx <= kSize; kx++) {
accu += kernel[ky+kSize][kx + kSize] * this.getSafe(x + kx, y + ky);
}
}
this.data[y * this.size.x + x] = accu;
}
}
};
ImageWrapper.prototype.moments = function(labelcount) {
var data = this.data,
x,
y,
height = this.size.y,
width = this.size.x,
val,
ysq,
labelsum = [],
i,
label,
mu11,
mu02,
mu20,
x_,
y_,
tmp,
result = [],
PI = Math.PI,
PI_4 = PI / 4;
if (labelcount <= 0) {
return result;
}
for ( i = 0; i < labelcount; i++) {
labelsum[i] = {
m00 : 0,
m01 : 0,
m10 : 0,
m11 : 0,
m02 : 0,
m20 : 0,
theta : 0,
rad : 0
};
}
for ( y = 0; y < height; y++) {
ysq = y * y;
for ( x = 0; x < width; x++) {
val = data[y * width + x];
if (val > 0) {
label = labelsum[val - 1];
label.m00 += 1;
label.m01 += y;
label.m10 += x;
label.m11 += x * y;
label.m02 += ysq;
label.m20 += x * x;
}
}
}
for ( i = 0; i < labelcount; i++) {
label = labelsum[i];
if (!isNaN(label.m00) && label.m00 !== 0) {
x_ = label.m10 / label.m00;
y_ = label.m01 / label.m00;
mu11 = label.m11 / label.m00 - x_ * y_;
mu02 = label.m02 / label.m00 - y_ * y_;
mu20 = label.m20 / label.m00 - x_ * x_;
tmp = (mu02 - mu20) / (2 * mu11);
tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4 ) + PI;
label.theta = (tmp * 180 / PI + 90) % 180 - 90;
if (label.theta < 0) {
label.theta += 180;
}
label.rad = tmp > PI ? tmp - PI : tmp;
label.vec = vec2.clone([Math.cos(tmp), Math.sin(tmp)]);
result.push(label);
}
}
return result;
};
/**
* Displays the {ImageWrapper} in a given canvas
* @param canvas {Canvas} The canvas element to write to
* @param scale {Number} Scale which is applied to each pixel-value
*/
ImageWrapper.prototype.show = function(canvas, scale) {
var ctx,
frame,
data,
current,
pixel,
x,
y;
if (!scale) {
scale = 1.0;
}
ctx = canvas.getContext('2d');
canvas.width = this.size.x;
canvas.height = this.size.y;
frame = ctx.getImageData(0, 0, canvas.width, canvas.height);
data = frame.data;
current = 0;
for (y = 0; y < this.size.y; y++) {
for (x = 0; x < this.size.x; x++) {
pixel = y * this.size.x + x;
current = this.get(x, y) * scale;
data[pixel * 4 + 0] = current;
data[pixel * 4 + 1] = current;
data[pixel * 4 + 2] = current;
data[pixel * 4 + 3] = 255;
}
}
//frame.data = data;
ctx.putImageData(frame, 0, 0);
};
/**
* Displays the {SubImage} in a given canvas
* @param canvas {Canvas} The canvas element to write to
* @param scale {Number} Scale which is applied to each pixel-value
*/
ImageWrapper.prototype.overlay = function(canvas, scale, from) {
if (!scale || scale < 0 || scale > 360) {
scale = 360;
}
var hsv = [0, 1, 1];
var rgb = [0, 0, 0];
var whiteRgb = [255, 255, 255];
var blackRgb = [0, 0, 0];
var result = [];
var ctx = canvas.getContext('2d');
var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y);
var data = frame.data;
var length = this.data.length;
while (length--) {
hsv[0] = this.data[length] * scale;
result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : CVUtils.hsv2rgb(hsv, rgb);
data[length * 4 + 0] = result[0];
data[length * 4 + 1] = result[1];
data[length * 4 + 2] = result[2];
data[length * 4 + 3] = 255;
}
ctx.putImageData(frame, from.x, from.y);
};
return (ImageWrapper);
});
export default ImageWrapper;
+293 -293
View File
@@ -1,317 +1,317 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import ImageLoader from './image_loader';
define(["image_loader"], function(ImageLoader) {
"use strict";
var InputStream = {};
InputStream.createVideoStream = function(video) {
var that = {},
_config = null,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_calculatedWidth,
_calculatedHeight,
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
var InputStream = {};
InputStream.createVideoStream = function(video) {
var that = {},
_config = null,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_calculatedWidth,
_calculatedHeight,
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
function initSize() {
var width = video.videoWidth,
height = video.videoHeight;
function initSize() {
var width = video.videoWidth,
height = video.videoHeight;
_calculatedWidth =
_config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width;
_calculatedHeight =
_config.size ? width / height > 1 ? Math.floor((height / width) * _config.size) : _config.size : height;
_calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width;
_calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height;
_canvasSize.x = _calculatedWidth;
_canvasSize.y = _calculatedHeight;
}
_canvasSize.x = _calculatedWidth;
_canvasSize.y = _calculatedHeight;
that.getRealWidth = function() {
return video.videoWidth;
};
that.getRealHeight = function() {
return video.videoHeight;
};
that.getWidth = function() {
return _calculatedWidth;
};
that.getHeight = function() {
return _calculatedHeight;
};
that.setWidth = function(width) {
_calculatedWidth = width;
};
that.setHeight = function(height) {
_calculatedHeight = height;
};
that.setInputStream = function(config) {
_config = config;
video.src = (typeof config.src !== 'undefined') ? config.src : '';
};
that.ended = function() {
return video.ended;
};
that.getConfig = function() {
return _config;
};
that.setAttribute = function(name, value) {
video.setAttribute(name, value);
};
that.pause = function() {
video.pause();
};
that.play = function() {
video.play();
};
that.setCurrentTime = function(time) {
if (_config.type !== "LiveStream") {
video.currentTime = time;
}
};
that.getRealWidth = function() {
return video.videoWidth;
};
that.getRealHeight = function() {
return video.videoHeight;
};
that.getWidth = function() {
return _calculatedWidth;
};
that.getHeight = function() {
return _calculatedHeight;
};
that.setWidth = function(width) {
_calculatedWidth = width;
};
that.setHeight = function(height) {
_calculatedHeight = height;
};
that.setInputStream = function(config) {
_config = config;
video.src = (typeof config.src !== 'undefined') ? config.src : '';
};
that.ended = function() {
return video.ended;
};
that.getConfig = function() {
return _config;
};
that.setAttribute = function(name, value) {
video.setAttribute(name, value);
};
that.pause = function() {
video.pause();
};
that.play = function() {
video.play();
};
that.setCurrentTime = function(time) {
if (_config.type !== "LiveStream")
video.currentTime = time;
};
that.addEventListener = function(event, f, bool) {
if (_eventNames.indexOf(event) !== -1) {
if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
}
_eventHandlers[event].push(f);
} else {
video.addEventListener(event, f, bool);
that.addEventListener = function(event, f, bool) {
if (_eventNames.indexOf(event) !== -1) {
if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
}
};
_eventHandlers[event].push(f);
} else {
video.addEventListener(event, f, bool);
}
};
that.clearEventHandlers = function() {
_eventNames.forEach(function(eventName) {
var handlers = _eventHandlers[eventName];
if (handlers && handlers.length > 0) {
handlers.forEach(function(handler) {
video.removeEventListener(eventName, handler);
});
}
});
};
that.trigger = function(eventName, args) {
var j,
handlers = _eventHandlers[eventName];
if (eventName === 'canrecord') {
initSize();
}
that.clearEventHandlers = function() {
_eventNames.forEach(function(eventName) {
var handlers = _eventHandlers[eventName];
if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
handlers.forEach(function(handler) {
video.removeEventListener(eventName, handler);
});
}
};
that.setTopRight = function(topRight) {
_topRight.x = topRight.x;
_topRight.y = topRight.y;
};
that.getTopRight = function() {
return _topRight;
};
that.setCanvasSize = function(size) {
_canvasSize.x = size.x;
_canvasSize.y = size.y;
};
that.getCanvasSize = function() {
return _canvasSize;
};
that.getFrame = function() {
return video;
};
return that;
});
};
InputStream.createLiveStream = function(video) {
video.setAttribute("autoplay", true);
var that = InputStream.createVideoStream(video);
that.trigger = function(eventName, args) {
var j,
handlers = _eventHandlers[eventName];
that.ended = function() {
return false;
};
return that;
if (eventName === 'canrecord') {
initSize();
}
if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
}
};
InputStream.createImageStream = function() {
var that = {};
var _config = null;
that.setTopRight = function(topRight) {
_topRight.x = topRight.x;
_topRight.y = topRight.y;
};
var width = 0,
height = 0,
frameIdx = 0,
paused = true,
loaded = false,
imgArray = null,
size = 0,
offset = 1,
baseUrl = null,
ended = false,
calculatedWidth,
calculatedHeight,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
that.getTopRight = function() {
return _topRight;
};
function loadImages() {
loaded = false;
ImageLoader.load(baseUrl, function(imgs) {
imgArray = imgs;
width = imgs[0].width;
height = imgs[0].height;
calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width;
calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height;
_canvasSize.x = calculatedWidth;
_canvasSize.y = calculatedHeight;
loaded = true;
frameIdx = 0;
that.setCanvasSize = function(size) {
_canvasSize.x = size.x;
_canvasSize.y = size.y;
};
that.getCanvasSize = function() {
return _canvasSize;
};
that.getFrame = function() {
return video;
};
return that;
};
InputStream.createLiveStream = function(video) {
video.setAttribute("autoplay", true);
var that = InputStream.createVideoStream(video);
that.ended = function() {
return false;
};
return that;
};
InputStream.createImageStream = function() {
var that = {};
var _config = null;
var width = 0,
height = 0,
frameIdx = 0,
paused = true,
loaded = false,
imgArray = null,
size = 0,
offset = 1,
baseUrl = null,
ended = false,
calculatedWidth,
calculatedHeight,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
function loadImages() {
loaded = false;
ImageLoader.load(baseUrl, function(imgs) {
imgArray = imgs;
width = imgs[0].width;
height = imgs[0].height;
calculatedWidth =
_config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width;
calculatedHeight =
_config.size ? width / height > 1 ? Math.floor((height / width) * _config.size) : _config.size : height;
_canvasSize.x = calculatedWidth;
_canvasSize.y = calculatedHeight;
loaded = true;
frameIdx = 0;
setTimeout(function() {
publishEvent("canrecord", []);
}, 0);
}, offset, size, _config.sequence);
}
function publishEvent(eventName, args) {
var j,
handlers = _eventHandlers[eventName];
if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
}
}
that.trigger = publishEvent;
that.getWidth = function() {
return calculatedWidth;
};
that.getHeight = function() {
return calculatedHeight;
};
that.setWidth = function(newWidth) {
calculatedWidth = newWidth;
};
that.setHeight = function(newHeight) {
calculatedHeight = newHeight;
};
that.getRealWidth = function() {
return width;
};
that.getRealHeight = function() {
return height;
};
that.setInputStream = function(stream) {
_config = stream;
if (stream.sequence === false) {
baseUrl = stream.src;
size = 1;
} else {
baseUrl = stream.src;
size = stream.length;
}
loadImages();
};
that.ended = function() {
return ended;
};
that.setAttribute = function() {};
that.getConfig = function() {
return _config;
};
that.pause = function() {
paused = true;
};
that.play = function() {
paused = false;
};
that.setCurrentTime = function(time) {
frameIdx = time;
};
that.addEventListener = function(event, f) {
if (_eventNames.indexOf(event) !== -1) {
if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
}
_eventHandlers[event].push(f);
}
};
that.setTopRight = function(topRight) {
_topRight.x = topRight.x;
_topRight.y = topRight.y;
};
that.getTopRight = function() {
return _topRight;
};
that.setCanvasSize = function(canvasSize) {
_canvasSize.x = canvasSize.x;
_canvasSize.y = canvasSize.y;
};
that.getCanvasSize = function() {
return _canvasSize;
};
that.getFrame = function() {
var frame;
if (!loaded){
return null;
}
if (!paused) {
frame = imgArray[frameIdx];
if (frameIdx < (size - 1)) {
frameIdx++;
} else {
setTimeout(function() {
publishEvent("canrecord", []);
ended = true;
publishEvent("ended", []);
}, 0);
}, offset, size, _config.sequence);
}
function publishEvent(eventName, args) {
var j,
handlers = _eventHandlers[eventName];
if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
}
}
that.trigger = publishEvent;
that.getWidth = function() {
return calculatedWidth;
};
that.getHeight = function() {
return calculatedHeight;
};
that.setWidth = function(width) {
calculatedWidth = width;
};
that.setHeight = function(height) {
calculatedHeight = height;
};
that.getRealWidth = function() {
return width;
};
that.getRealHeight = function() {
return height;
};
that.setInputStream = function(stream) {
_config = stream;
if (stream.sequence === false) {
baseUrl = stream.src;
size = 1;
} else {
baseUrl = stream.src;
size = stream.length;
}
loadImages();
};
that.ended = function() {
return ended;
};
that.setAttribute = function() {};
that.getConfig = function() {
return _config;
};
that.pause = function() {
paused = true;
};
that.play = function() {
paused = false;
};
that.setCurrentTime = function(time) {
frameIdx = time;
};
that.addEventListener = function(event, f) {
if (_eventNames.indexOf(event) !== -1) {
if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
}
_eventHandlers[event].push(f);
}
};
that.setTopRight = function(topRight) {
_topRight.x = topRight.x;
_topRight.y = topRight.y;
};
that.getTopRight = function() {
return _topRight;
};
that.setCanvasSize = function(size) {
_canvasSize.x = size.x;
_canvasSize.y = size.y;
};
that.getCanvasSize = function() {
return _canvasSize;
};
that.getFrame = function() {
var frame;
if (!loaded){
return null;
}
if (!paused) {
frame = imgArray[frameIdx];
if (frameIdx < (size - 1)) {
frameIdx++;
} else {
setTimeout(function() {
ended = true;
publishEvent("ended", []);
}, 0);
}
}
return frame;
};
return that;
return frame;
};
return (InputStream);
});
return that;
};
export default InputStream;
+457 -469
View File
@@ -1,504 +1,492 @@
/* jshint undef: true, unused: true, browser:true, devel: true, evil: true */
/* global define */
define([
"input_stream",
"image_wrapper",
"barcode_locator",
"barcode_decoder",
"frame_grabber",
"html_utils",
"config",
"events",
"camera_access",
"image_debug",
"gl-matrix",
"result_collector"],
function(InputStream,
ImageWrapper,
BarcodeLocator,
BarcodeDecoder,
FrameGrabber,
HtmlUtils,
_config,
Events,
CameraAccess,
ImageDebug,
glMatrix,
ResultCollector) {
"use strict";
var _inputStream,
_framegrabber,
_stopped,
_canvasContainer = {
ctx : {
image : null,
overlay : null
},
dom : {
image : null,
overlay : null
}
import TypeDefs from './typedefs'; // eslint-disable-line no-unused-vars
import ImageWrapper from './image_wrapper';
import BarcodeLocator from './barcode_locator';
import BarcodeDecoder from './barcode_decoder';
import Config from './config';
import Events from './events';
import CameraAccess from './camera_access';
import ImageDebug from './image_debug';
import {vec2} from 'gl-matrix';
import ResultCollector from './result_collector';
const merge = require('lodash/object/merge');
const InputStream = require('input_stream');
const FrameGrabber = require('frame_grabber');
var _inputStream,
_framegrabber,
_stopped,
_canvasContainer = {
ctx: {
image: null,
overlay: null
},
_inputImageWrapper,
_boxSize,
_decoder,
_workerPool = [],
_onUIThread = true,
vec2 = glMatrix.vec2,
_resultCollector;
function initializeData(imageWrapper) {
initBuffers(imageWrapper);
_decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper);
}
function initConfig() {
if (typeof document !== "undefined") {
var vis = [{
node: document.querySelector("div[data-controls]"),
prop: _config.controls
}, {
node: _canvasContainer.dom.overlay,
prop: _config.visual.show
}];
for (var i = 0; i < vis.length; i++) {
if (vis[i].node) {
if (vis[i].prop === true) {
vis[i].node.style.display = "block";
} else {
vis[i].node.style.display = "none";
}
}
}
dom: {
image: null,
overlay: null
}
}
},
_inputImageWrapper,
_boxSize,
_decoder,
_workerPool = [],
_onUIThread = true,
_resultCollector,
_config = {};
function initInputStream(cb) {
var video;
if (_config.inputStream.type == "VideoStream") {
video = document.createElement("video");
_inputStream = InputStream.createVideoStream(video);
} else if (_config.inputStream.type == "ImageStream") {
_inputStream = InputStream.createImageStream();
} else if (_config.inputStream.type == "LiveStream") {
var $viewport = document.querySelector("#interactive.viewport");
if ($viewport) {
video = $viewport.querySelector("video");
if (!video) {
video = document.createElement("video");
$viewport.appendChild(video);
}
}
_inputStream = InputStream.createLiveStream(video);
CameraAccess.request(video, _config.inputStream.constraints, function(err) {
if (!err) {
_inputStream.trigger("canrecord");
function initializeData(imageWrapper) {
initBuffers(imageWrapper);
_decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper);
}
function initConfig() {
if (typeof document !== "undefined") {
var vis = [{
node: document.querySelector("div[data-controls]"),
prop: _config.controls
}, {
node: _canvasContainer.dom.overlay,
prop: _config.visual.show
}];
for (var i = 0; i < vis.length; i++) {
if (vis[i].node) {
if (vis[i].prop === true) {
vis[i].node.style.display = "block";
} else {
return cb(err);
vis[i].node.style.display = "none";
}
});
}
}
}
}
_inputStream.setAttribute("preload", "auto");
_inputStream.setAttribute("autoplay", true);
_inputStream.setInputStream(_config.inputStream);
_inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb));
function initInputStream(cb) {
var video;
if (_config.inputStream.type === "VideoStream") {
video = document.createElement("video");
_inputStream = InputStream.createVideoStream(video);
} else if (_config.inputStream.type === "ImageStream") {
_inputStream = InputStream.createImageStream();
} else if (_config.inputStream.type === "LiveStream") {
var $viewport = document.querySelector("#interactive.viewport");
if ($viewport) {
video = $viewport.querySelector("video");
if (!video) {
video = document.createElement("video");
$viewport.appendChild(video);
}
}
_inputStream = InputStream.createLiveStream(video);
CameraAccess.request(video, _config.inputStream.constraints, function(err) {
if (!err) {
_inputStream.trigger("canrecord");
} else {
return cb(err);
}
});
}
function canRecord(cb) {
BarcodeLocator.checkImageConstraints(_inputStream, _config.locator);
initCanvas();
_framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image);
initConfig();
_inputStream.setAttribute("preload", "auto");
_inputStream.setAttribute("autoplay", true);
_inputStream.setInputStream(_config.inputStream);
_inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb));
}
if (_config.numOfWorkers > 0) {
initWorkers(function() {
console.log("Workers created");
ready(cb);
});
} else {
initializeData();
function canRecord(cb) {
BarcodeLocator.checkImageConstraints(_inputStream, _config.locator);
initCanvas();
_framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image);
initConfig();
if (_config.numOfWorkers > 0) {
initWorkers(function() {
console.log("Workers created");
ready(cb);
}
});
} else {
initializeData();
ready(cb);
}
}
function ready(cb){
_inputStream.play();
cb();
}
function ready(cb){
_inputStream.play();
cb();
}
function initCanvas() {
if (typeof document !== "undefined") {
var $viewport = document.querySelector("#interactive.viewport");
_canvasContainer.dom.image = document.querySelector("canvas.imgBuffer");
if (!_canvasContainer.dom.image) {
_canvasContainer.dom.image = document.createElement("canvas");
_canvasContainer.dom.image.className = "imgBuffer";
if ($viewport && _config.inputStream.type == "ImageStream") {
$viewport.appendChild(_canvasContainer.dom.image);
}
function initCanvas() {
if (typeof document !== "undefined") {
var $viewport = document.querySelector("#interactive.viewport");
_canvasContainer.dom.image = document.querySelector("canvas.imgBuffer");
if (!_canvasContainer.dom.image) {
_canvasContainer.dom.image = document.createElement("canvas");
_canvasContainer.dom.image.className = "imgBuffer";
if ($viewport && _config.inputStream.type === "ImageStream") {
$viewport.appendChild(_canvasContainer.dom.image);
}
_canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
_canvasContainer.dom.image.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y;
}
_canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
_canvasContainer.dom.image.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y;
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (!_canvasContainer.dom.overlay) {
_canvasContainer.dom.overlay = document.createElement("canvas");
_canvasContainer.dom.overlay.className = "drawingBuffer";
if ($viewport) {
$viewport.appendChild(_canvasContainer.dom.overlay);
}
var clearFix = document.createElement("br");
clearFix.setAttribute("clear", "all");
if ($viewport) {
$viewport.appendChild(clearFix);
}
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (!_canvasContainer.dom.overlay) {
_canvasContainer.dom.overlay = document.createElement("canvas");
_canvasContainer.dom.overlay.className = "drawingBuffer";
if ($viewport) {
$viewport.appendChild(_canvasContainer.dom.overlay);
}
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d");
_canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y;
var clearFix = document.createElement("br");
clearFix.setAttribute("clear", "all");
if ($viewport) {
$viewport.appendChild(clearFix);
}
}
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d");
_canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y;
}
}
function initBuffers(imageWrapper) {
if (imageWrapper) {
_inputImageWrapper = imageWrapper;
} else {
_inputImageWrapper = new ImageWrapper({
x: _inputStream.getWidth(),
y: _inputStream.getHeight()
});
}
console.log(_inputImageWrapper.size);
_boxSize = [
vec2.clone([0, 0]),
vec2.clone([0, _inputImageWrapper.size.y]),
vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]),
vec2.clone([_inputImageWrapper.size.x, 0])
];
BarcodeLocator.init(_inputImageWrapper, _config.locator);
}
function getBoundingBoxes() {
if (_config.locate) {
return BarcodeLocator.locate();
} else {
return [[
vec2.clone(_boxSize[0]),
vec2.clone(_boxSize[1]),
vec2.clone(_boxSize[2]),
vec2.clone(_boxSize[3])]];
}
}
function transformResult(result) {
var topRight = _inputStream.getTopRight(),
xOffset = topRight.x,
yOffset = topRight.y,
i;
if (!result || (xOffset === 0 && yOffset === 0)) {
return;
}
if (result.line && result.line.length === 2) {
moveLine(result.line);
}
if (result.boxes && result.boxes.length > 0) {
for (i = 0; i < result.boxes.length; i++) {
moveBox(result.boxes[i]);
}
}
function initBuffers(imageWrapper) {
if (imageWrapper) {
_inputImageWrapper = imageWrapper;
function moveBox(box) {
var corner = box.length;
while (corner--) {
box[corner][0] += xOffset;
box[corner][1] += yOffset;
}
}
function moveLine(line) {
line[0].x += xOffset;
line[0].y += yOffset;
line[1].x += xOffset;
line[1].y += yOffset;
}
}
function publishResult(result, imageData) {
if (_onUIThread) {
transformResult(result);
if (imageData && result && result.codeResult) {
if (_resultCollector) {
_resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult);
}
}
}
Events.publish("processed", result);
if (result && result.codeResult) {
Events.publish("detected", result);
}
}
function locateAndDecode() {
var result,
boxes;
boxes = getBoundingBoxes();
if (boxes) {
result = _decoder.decodeFromBoundingBoxes(boxes);
result = result || {};
result.boxes = boxes;
publishResult(result, _inputImageWrapper.data);
} else {
publishResult();
}
}
function update() {
var availableWorker;
if (_onUIThread) {
if (_workerPool.length > 0) {
availableWorker = _workerPool.filter(function(workerThread) {
return !workerThread.busy;
})[0];
if (availableWorker) {
_framegrabber.attachData(availableWorker.imageData);
} else {
return; // all workers are busy
}
} else {
_inputImageWrapper = new ImageWrapper({
x : _inputStream.getWidth(),
y : _inputStream.getHeight()
});
_framegrabber.attachData(_inputImageWrapper.data);
}
if (_framegrabber.grab()) {
if (availableWorker) {
availableWorker.busy = true;
availableWorker.worker.postMessage({
cmd: 'process',
imageData: availableWorker.imageData
}, [availableWorker.imageData.buffer]);
} else {
locateAndDecode();
}
}
} else {
locateAndDecode();
}
}
console.log(_inputImageWrapper.size);
_boxSize = [
vec2.clone([0, 0]),
vec2.clone([0, _inputImageWrapper.size.y]),
vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]),
vec2.clone([_inputImageWrapper.size.x, 0])
];
BarcodeLocator.init(_inputImageWrapper, _config.locator);
function start() {
_stopped = false;
( function frame() {
if (!_stopped) {
update();
if (_onUIThread && _config.inputStream.type === "LiveStream") {
window.requestAnimFrame(frame);
}
}
}());
}
function initWorkers(cb) {
var i;
_workerPool = [];
for (i = 0; i < _config.numOfWorkers; i++) {
initWorker(workerInitialized);
}
function getBoundingBoxes() {
if (_config.locate) {
return BarcodeLocator.locate();
} else {
return [[
vec2.clone(_boxSize[0]),
vec2.clone(_boxSize[1]),
vec2.clone(_boxSize[2]),
vec2.clone(_boxSize[3])]];
function workerInitialized(workerThread) {
_workerPool.push(workerThread);
if (_workerPool.length >= _config.numOfWorkers){
cb();
}
}
}
function transformResult(result) {
var topRight = _inputStream.getTopRight(),
xOffset = topRight.x,
yOffset = topRight.y,
i;
function initWorker(cb) {
var blobURL,
workerThread = {
worker: undefined,
imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()),
busy: true
};
if (!result || (xOffset === 0 && yOffset === 0)) {
blobURL = generateWorkerBlob();
workerThread.worker = new Worker(blobURL);
workerThread.worker.onmessage = function(e) {
if (e.data.event === 'initialized') {
URL.revokeObjectURL(blobURL);
workerThread.busy = false;
workerThread.imageData = new Uint8Array(e.data.imageData);
console.log("Worker initialized");
return cb(workerThread);
} else if (e.data.event === 'processed') {
workerThread.imageData = new Uint8Array(e.data.imageData);
workerThread.busy = false;
publishResult(e.data.result, workerThread.imageData);
} else if (e.data.event === 'error') {
console.log("Worker error: " + e.data.message);
}
};
workerThread.worker.postMessage({
cmd: 'init',
size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()},
imageData: workerThread.imageData,
config: _config
}, [workerThread.imageData.buffer]);
}
function workerInterface(factory) {
/* eslint-disable no-undef*/
if (factory) {
var Quagga = factory();
if (!Quagga) {
self.postMessage({'event': 'error', message: 'Quagga could not be created'});
return;
}
if (result.line && result.line.length === 2) {
moveLine(result.line);
}
if (result.boxes && result.boxes.length > 0) {
for (i = 0; i < result.boxes.length; i++) {
moveBox(result.boxes[i]);
}
}
function moveBox(box) {
var corner = box.length;
while(corner--) {
box[corner][0] += xOffset;
box[corner][1] += yOffset;
}
}
function moveLine(line) {
line[0].x += xOffset;
line[0].y += yOffset;
line[1].x += xOffset;
line[1].y += yOffset;
}
}
var imageWrapper;
function publishResult(result, imageData) {
if (_onUIThread) {
transformResult(result);
if (imageData && result && result.codeResult) {
if (_resultCollector) {
_resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult);
}
}
self.onmessage = function(e) {
if (e.data.cmd === 'init') {
var config = e.data.config;
config.numOfWorkers = 0;
imageWrapper = new Quagga.ImageWrapper({
x: e.data.size.x,
y: e.data.size.y
}, new Uint8Array(e.data.imageData));
Quagga.init(config, ready, imageWrapper);
Quagga.onProcessed(onProcessed);
} else if (e.data.cmd === 'process') {
imageWrapper.data = new Uint8Array(e.data.imageData);
Quagga.start();
} else if (e.data.cmd === 'setReaders') {
Quagga.setReaders(e.data.readers);
}
Events.publish("processed", result);
if (result && result.codeResult) {
Events.publish("detected", result);
}
}
function locateAndDecode() {
var result,
boxes;
boxes = getBoundingBoxes();
if (boxes) {
result = _decoder.decodeFromBoundingBoxes(boxes);
result = result || {};
result.boxes = boxes;
publishResult(result, _inputImageWrapper.data);
} else {
publishResult();
}
}
function update() {
var availableWorker;
if (_onUIThread) {
if (_workerPool.length > 0) {
availableWorker = _workerPool.filter(function(workerThread) {
return !workerThread.busy;
})[0];
if (availableWorker) {
_framegrabber.attachData(availableWorker.imageData);
} else {
return; // all workers are busy
}
} else {
_framegrabber.attachData(_inputImageWrapper.data);
}
if (_framegrabber.grab()) {
if (availableWorker) {
availableWorker.busy = true;
availableWorker.worker.postMessage({
cmd: 'process',
imageData: availableWorker.imageData
}, [availableWorker.imageData.buffer]);
} else {
locateAndDecode();
}
}
} else {
locateAndDecode();
}
}
function start() {
_stopped = false;
( function frame() {
if (!_stopped) {
update();
if (_onUIThread && _config.inputStream.type == "LiveStream") {
window.requestAnimFrame(frame);
}
}
}());
}
function initWorkers(cb) {
var i;
_workerPool = [];
for (i = 0; i < _config.numOfWorkers; i++) {
initWorker(workerInitialized);
}
function workerInitialized(workerThread) {
_workerPool.push(workerThread);
if (_workerPool.length >= _config.numOfWorkers){
cb();
}
}
}
function initWorker(cb) {
var blobURL,
workerThread = {
worker: undefined,
imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()),
busy: true
};
blobURL = generateWorkerBlob();
workerThread.worker = new Worker(blobURL);
workerThread.worker.onmessage = function(e) {
if (e.data.event === 'initialized') {
URL.revokeObjectURL(blobURL);
workerThread.busy = false;
workerThread.imageData = new Uint8Array(e.data.imageData);
console.log("Worker initialized");
return cb(workerThread);
} else if (e.data.event === 'processed') {
workerThread.imageData = new Uint8Array(e.data.imageData);
workerThread.busy = false;
publishResult(e.data.result, workerThread.imageData);
} else if (e.data.event === 'error') {
console.log("Worker error: " + e.data.message);
}
};
workerThread.worker.postMessage({
cmd: 'init',
size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()},
imageData: workerThread.imageData,
config: _config
}, [workerThread.imageData.buffer]);
}
function workerInterface(factory) {
if (factory) {
/* jshint ignore:start */
var Quagga = factory();
if (!Quagga) {
self.postMessage({'event': 'error', message: 'Quagga could not be created'});
return;
}
/* jshint ignore:end */
}
/* jshint ignore:start */
var imageWrapper;
self.onmessage = function(e) {
if (e.data.cmd === 'init') {
var config = e.data.config;
config.numOfWorkers = 0;
imageWrapper = new Quagga.ImageWrapper({
x : e.data.size.x,
y : e.data.size.y
}, new Uint8Array(e.data.imageData));
Quagga.init(config, ready, imageWrapper);
Quagga.onProcessed(onProcessed);
} else if (e.data.cmd === 'process') {
imageWrapper.data = new Uint8Array(e.data.imageData);
Quagga.start();
} else if (e.data.cmd === 'setReaders') {
Quagga.setReaders(e.data.readers);
}
};
function onProcessed(result) {
self.postMessage({'event': 'processed', imageData: imageWrapper.data, result: result}, [imageWrapper.data.buffer]);
}
function ready() {
self.postMessage({'event': 'initialized', imageData: imageWrapper.data}, [imageWrapper.data.buffer]);
}
/* jshint ignore:end */
}
function generateWorkerBlob() {
var blob,
factorySource;
/* jshint ignore:start */
if (typeof __factorySource__ !== 'undefined') {
factorySource = __factorySource__;
}
/* jshint ignore:end */
blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'],
{type : 'text/javascript'});
return window.URL.createObjectURL(blob);
}
function setReaders(readers) {
if (_decoder) {
_decoder.setReaders(readers);
} else if (_onUIThread && _workerPool.length > 0) {
_workerPool.forEach(function(workerThread) {
workerThread.worker.postMessage({cmd: 'setReaders', readers: readers});
});
}
}
return {
init : function(config, cb, imageWrapper) {
_config = HtmlUtils.mergeObjects(_config, config);
if (imageWrapper) {
_onUIThread = false;
initializeData(imageWrapper);
return cb();
} else {
initInputStream(cb);
}
},
start : function() {
start();
},
stop : function() {
_stopped = true;
_workerPool.forEach(function(workerThread) {
workerThread.worker.terminate();
console.log("Worker terminated!");
});
_workerPool.length = 0;
if (_config.inputStream.type === "LiveStream") {
CameraAccess.release();
_inputStream.clearEventHandlers();
}
},
pause: function() {
_stopped = true;
},
onDetected : function(callback) {
Events.subscribe("detected", callback);
},
offDetected: function(callback) {
Events.unsubscribe("detected", callback);
},
onProcessed: function(callback) {
Events.subscribe("processed", callback);
},
offProcessed: function(callback) {
Events.unsubscribe("processed", callback);
},
setReaders: function(readers) {
setReaders(readers);
},
registerResultCollector: function(resultCollector) {
if (resultCollector && typeof resultCollector.addResult === 'function') {
_resultCollector = resultCollector;
}
},
canvas : _canvasContainer,
decodeSingle : function(config, resultCallback) {
config = HtmlUtils.mergeObjects({
inputStream: {
type : "ImageStream",
sequence : false,
size: 800,
src: config.src
},
numOfWorkers: 1,
locator: {
halfSample: false
}
}, config);
this.init(config, function() {
Events.once("processed", function(result) {
_stopped = true;
resultCallback.call(null, result);
}, true);
start();
});
},
ImageWrapper: ImageWrapper,
ImageDebug: ImageDebug,
ResultCollector: ResultCollector
};
});
function onProcessed(result) {
self.postMessage({
'event': 'processed',
imageData: imageWrapper.data,
result: result
}, [imageWrapper.data.buffer]);
}
function ready() { // eslint-disable-line
self.postMessage({'event': 'initialized', imageData: imageWrapper.data}, [imageWrapper.data.buffer]);
}
/* eslint-enable */
}
function generateWorkerBlob() {
var blob,
factorySource;
/* jshint ignore:start */
if (typeof __factorySource__ !== 'undefined') {
factorySource = __factorySource__; // eslint-disable-line no-undef
}
/* jshint ignore:end */
blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'],
{type: 'text/javascript'});
return window.URL.createObjectURL(blob);
}
function setReaders(readers) {
if (_decoder) {
_decoder.setReaders(readers);
} else if (_onUIThread && _workerPool.length > 0) {
_workerPool.forEach(function(workerThread) {
workerThread.worker.postMessage({cmd: 'setReaders', readers: readers});
});
}
}
export default {
init: function(config, cb, imageWrapper) {
_config = merge({}, Config, config);
if (imageWrapper) {
_onUIThread = false;
initializeData(imageWrapper);
return cb();
} else {
initInputStream(cb);
}
},
start: function() {
start();
},
stop: function() {
_stopped = true;
_workerPool.forEach(function(workerThread) {
workerThread.worker.terminate();
console.log("Worker terminated!");
});
_workerPool.length = 0;
if (_config.inputStream.type === "LiveStream") {
CameraAccess.release();
_inputStream.clearEventHandlers();
}
},
pause: function() {
_stopped = true;
},
onDetected: function(callback) {
Events.subscribe("detected", callback);
},
offDetected: function(callback) {
Events.unsubscribe("detected", callback);
},
onProcessed: function(callback) {
Events.subscribe("processed", callback);
},
offProcessed: function(callback) {
Events.unsubscribe("processed", callback);
},
setReaders: function(readers) {
setReaders(readers);
},
registerResultCollector: function(resultCollector) {
if (resultCollector && typeof resultCollector.addResult === 'function') {
_resultCollector = resultCollector;
}
},
canvas: _canvasContainer,
decodeSingle: function(config, resultCallback) {
config = merge({
inputStream: {
type: "ImageStream",
sequence: false,
size: 800,
src: config.src
},
numOfWorkers: 1,
locator: {
halfSample: false
}
}, config);
this.init(config, function() {
Events.once("processed", function(result) {
_stopped = true;
resultCallback.call(null, result);
}, true);
start();
});
},
ImageWrapper: ImageWrapper,
ImageDebug: ImageDebug,
ResultCollector: ResultCollector
};
+177 -180
View File
@@ -1,198 +1,195 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import Tracer from './tracer';
/**
* http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization
*/
define(["tracer"], function(Tracer) {
"use strict";
var Rasterizer = {
createContour2D: function() {
return {
dir: null,
index: null,
firstVertex: null,
insideContours: null,
nextpeer: null,
prevpeer: null
};
},
CONTOUR_DIR: {
CW_DIR: 0,
CCW_DIR: 1,
UNKNOWN_DIR: 2
},
DIR: {
OUTSIDE_EDGE: -32767,
INSIDE_EDGE: -32766
},
create: function(imageWrapper, labelWrapper) {
var imageData = imageWrapper.data,
labelData = labelWrapper.data,
width = imageWrapper.size.x,
height = imageWrapper.size.y,
tracer = Tracer.create(imageWrapper, labelWrapper);
var Rasterizer = {
createContour2D : function() {
return {
dir : null,
index : null,
firstVertex : null,
insideContours : null,
nextpeer : null,
prevpeer : null
};
},
CONTOUR_DIR : {
CW_DIR : 0,
CCW_DIR : 1,
UNKNOWN_DIR : 2
},
DIR : {
OUTSIDE_EDGE : -32767,
INSIDE_EDGE : -32766
},
create : function(imageWrapper, labelWrapper) {
var imageData = imageWrapper.data,
labelData = labelWrapper.data,
width = imageWrapper.size.x,
height = imageWrapper.size.y,
tracer = Tracer.create(imageWrapper, labelWrapper);
return {
rasterize: function(depthlabel) {
var color,
bc,
lc,
labelindex,
cx,
cy,
colorMap = [],
vertex,
p,
cc,
sc,
pos,
connectedCount = 0,
i;
return {
rasterize : function(depthlabel) {
var color,
bc,
lc,
labelindex,
cx,
cy,
colorMap = [],
vertex,
p,
cc,
sc,
pos,
connectedCount = 0,
i;
for ( i = 0; i < 400; i++) {
colorMap[i] = 0;
}
for ( i = 0; i < 400; i++) {
colorMap[i] = 0;
}
colorMap[0] = imageData[0];
cc = null;
for ( cy = 1; cy < height - 1; cy++) {
labelindex = 0;
bc = colorMap[0];
for ( cx = 1; cx < width - 1; cx++) {
pos = cy * width + cx;
if (labelData[pos] === 0) {
color = imageData[pos];
if (color !== bc) {
if (labelindex === 0) {
lc = connectedCount + 1;
colorMap[lc] = color;
bc = color;
vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE);
if (vertex !== null) {
connectedCount++;
labelindex = lc;
p = Rasterizer.createContour2D();
p.dir = Rasterizer.CONTOUR_DIR.CW_DIR;
p.index = labelindex;
p.firstVertex = vertex;
p.nextpeer = cc;
p.insideContours = null;
if (cc !== null) {
cc.prevpeer = p;
}
cc = p;
}
} else {
vertex = tracer.contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex);
if (vertex !== null) {
p = Rasterizer.createContour2D();
p.firstVertex = vertex;
p.insideContours = null;
if (depthlabel === 0) {
p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR;
} else {
p.dir = Rasterizer.CONTOUR_DIR.CW_DIR;
}
p.index = depthlabel;
sc = cc;
while ((sc !== null) && sc.index !== labelindex) {
sc = sc.nextpeer;
}
if (sc !== null) {
p.nextpeer = sc.insideContours;
if (sc.insideContours !== null) {
sc.insideContours.prevpeer = p;
}
sc.insideContours = p;
}
colorMap[0] = imageData[0];
cc = null;
for ( cy = 1; cy < height - 1; cy++) {
labelindex = 0;
bc = colorMap[0];
for ( cx = 1; cx < width - 1; cx++) {
pos = cy * width + cx;
if (labelData[pos] === 0) {
color = imageData[pos];
if (color !== bc) {
if (labelindex === 0) {
lc = connectedCount + 1;
colorMap[lc] = color;
bc = color;
vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE);
if (vertex !== null) {
connectedCount++;
labelindex = lc;
p = Rasterizer.createContour2D();
p.dir = Rasterizer.CONTOUR_DIR.CW_DIR;
p.index = labelindex;
p.firstVertex = vertex;
p.nextpeer = cc;
p.insideContours = null;
if (cc !== null) {
cc.prevpeer = p;
}
cc = p;
}
} else {
labelData[pos] = labelindex;
}
} else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) {
labelindex = 0;
if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) {
bc = imageData[pos];
} else {
bc = colorMap[0];
vertex = tracer
.contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex);
if (vertex !== null) {
p = Rasterizer.createContour2D();
p.firstVertex = vertex;
p.insideContours = null;
if (depthlabel === 0) {
p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR;
} else {
p.dir = Rasterizer.CONTOUR_DIR.CW_DIR;
}
p.index = depthlabel;
sc = cc;
while ((sc !== null) && sc.index !== labelindex) {
sc = sc.nextpeer;
}
if (sc !== null) {
p.nextpeer = sc.insideContours;
if (sc.insideContours !== null) {
sc.insideContours.prevpeer = p;
}
sc.insideContours = p;
}
}
}
} else {
labelindex = labelData[pos];
bc = colorMap[labelindex];
labelData[pos] = labelindex;
}
} else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE
|| labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) {
labelindex = 0;
if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) {
bc = imageData[pos];
} else {
bc = colorMap[0];
}
}
}
sc = cc;
while (sc !== null) {
sc.index = depthlabel;
sc = sc.nextpeer;
}
return {
cc : cc,
count : connectedCount
};
},
debug: {
drawContour : function(canvas, firstContour) {
var ctx = canvas.getContext("2d"),
pq = firstContour,
iq,
q,
p;
ctx.strokeStyle = "red";
ctx.fillStyle = "red";
ctx.lineWidth = 1;
if (pq !== null) {
iq = pq.insideContours;
} else {
iq = null;
}
while (pq !== null) {
if (iq !== null) {
q = iq;
iq = iq.nextpeer;
} else {
q = pq;
pq = pq.nextpeer;
if (pq !== null) {
iq = pq.insideContours;
} else {
iq = null;
}
}
switch(q.dir) {
case Rasterizer.CONTOUR_DIR.CW_DIR:
ctx.strokeStyle = "red";
break;
case Rasterizer.CONTOUR_DIR.CCW_DIR:
ctx.strokeStyle = "blue";
break;
case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR:
ctx.strokeStyle = "green";
break;
}
p = q.firstVertex;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
do {
p = p.next;
ctx.lineTo(p.x, p.y);
} while(p !== q.firstVertex);
ctx.stroke();
labelindex = labelData[pos];
bc = colorMap[labelindex];
}
}
}
};
}
};
sc = cc;
while (sc !== null) {
sc.index = depthlabel;
sc = sc.nextpeer;
}
return {
cc: cc,
count: connectedCount
};
},
debug: {
drawContour: function(canvas, firstContour) {
var ctx = canvas.getContext("2d"),
pq = firstContour,
iq,
q,
p;
return (Rasterizer);
});
ctx.strokeStyle = "red";
ctx.fillStyle = "red";
ctx.lineWidth = 1;
if (pq !== null) {
iq = pq.insideContours;
} else {
iq = null;
}
while (pq !== null) {
if (iq !== null) {
q = iq;
iq = iq.nextpeer;
} else {
q = pq;
pq = pq.nextpeer;
if (pq !== null) {
iq = pq.insideContours;
} else {
iq = null;
}
}
switch (q.dir) {
case Rasterizer.CONTOUR_DIR.CW_DIR:
ctx.strokeStyle = "red";
break;
case Rasterizer.CONTOUR_DIR.CCW_DIR:
ctx.strokeStyle = "blue";
break;
case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR:
ctx.strokeStyle = "green";
break;
}
p = q.firstVertex;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
do {
p = p.next;
ctx.lineTo(p.x, p.y);
} while (p !== q.firstVertex);
ctx.stroke();
}
}
}
};
}
};
export default Rasterizer;
+46 -48
View File
@@ -1,59 +1,57 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import ImageDebug from './image_debug';
define(["image_debug"], function(ImageDebug) {
"use strict";
function contains(codeResult, list) {
if (list) {
return list.some(function (item) {
return Object.keys(item).every(function (key) {
return item[key] === codeResult[key];
});
function contains(codeResult, list) {
if (list) {
return list.some(function (item) {
return Object.keys(item).every(function (key) {
return item[key] === codeResult[key];
});
}
return false;
});
}
return false;
}
function passesFilter(codeResult, filter) {
if (typeof filter === 'function') {
return filter(codeResult);
}
return true;
function passesFilter(codeResult, filter) {
if (typeof filter === 'function') {
return filter(codeResult);
}
return true;
}
return {
create: function(config) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
results = [],
capacity = config.capacity || 20,
capture = config.capture === true;
export default {
create: function(config) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
results = [],
capacity = config.capacity || 20,
capture = config.capture === true;
function matchesConstraints(codeResult) {
return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter);
}
function matchesConstraints(codeResult) {
return capacity
&& codeResult
&& !contains(codeResult, config.blacklist)
&& passesFilter(codeResult, config.filter);
}
return {
addResult: function(data, imageSize, codeResult) {
var result = {};
return {
addResult: function(data, imageSize, codeResult) {
var result = {};
if (matchesConstraints(codeResult)) {
capacity--;
result.codeResult = codeResult;
if (capture) {
canvas.width = imageSize.x;
canvas.height = imageSize.y;
ImageDebug.drawImage(data, imageSize, ctx);
result.frame = canvas.toDataURL();
}
results.push(result);
if (matchesConstraints(codeResult)) {
capacity--;
result.codeResult = codeResult;
if (capture) {
canvas.width = imageSize.x;
canvas.height = imageSize.y;
ImageDebug.drawImage(data, imageSize, ctx);
result.frame = canvas.toDataURL();
}
},
getResults: function() {
return results;
results.push(result);
}
};
}
};
});
},
getResults: function() {
return results;
}
};
}
};
+200 -195
View File
@@ -1,204 +1,209 @@
/* jshint undef: true, unused: true, browser:true, devel: true, -W041: false */
/* global define */
/* @preserve ASM BEGIN */
/* eslint-disable eqeqeq*/
function Skeletonizer(stdlib, foreign, buffer) {
"use asm";
define(function() {
"use strict";
var images = new stdlib.Uint8Array(buffer),
size = foreign.size | 0,
imul = stdlib.Math.imul;
/* @preserve ASM BEGIN */
function Skeletonizer(stdlib, foreign, buffer) {
"use asm";
function erode(inImagePtr, outImagePtr) {
inImagePtr = inImagePtr | 0;
outImagePtr = outImagePtr | 0;
var images = new stdlib.Uint8Array(buffer),
size = foreign.size | 0,
imul = stdlib.Math.imul;
var v = 0,
u = 0,
sum = 0,
yStart1 = 0,
yStart2 = 0,
xStart1 = 0,
xStart2 = 0,
offset = 0;
function erode(inImagePtr, outImagePtr) {
inImagePtr = inImagePtr | 0;
outImagePtr = outImagePtr | 0;
var v = 0,
u = 0,
sum = 0,
yStart1 = 0,
yStart2 = 0,
xStart1 = 0,
xStart2 = 0,
offset = 0;
for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) {
offset = (offset + size) | 0;
for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) {
yStart1 = (offset - size) | 0;
yStart2 = (offset + size) | 0;
xStart1 = (u - 1) | 0;
xStart2 = (u + 1) | 0;
sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + (images[(inImagePtr + offset + u) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0;
if ((sum | 0) == (5 | 0)) {
images[(outImagePtr + offset + u) | 0] = 1;
} else {
images[(outImagePtr + offset + u) | 0] = 0;
}
for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) {
offset = (offset + size) | 0;
for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) {
yStart1 = (offset - size) | 0;
yStart2 = (offset + size) | 0;
xStart1 = (u - 1) | 0;
xStart2 = (u + 1) | 0;
sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0)
+ (images[(inImagePtr + yStart1 + xStart2) | 0] | 0)
+ (images[(inImagePtr + offset + u) | 0] | 0)
+ (images[(inImagePtr + yStart2 + xStart1) | 0] | 0)
+ (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0;
if ((sum | 0) == (5 | 0)) {
images[(outImagePtr + offset + u) | 0] = 1;
} else {
images[(outImagePtr + offset + u) | 0] = 0;
}
}
return;
}
function subtract(aImagePtr, bImagePtr, outImagePtr) {
aImagePtr = aImagePtr | 0;
bImagePtr = bImagePtr | 0;
outImagePtr = outImagePtr | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(outImagePtr + length) | 0] = ((images[(aImagePtr + length) | 0] | 0) - (images[(bImagePtr + length) | 0] | 0)) | 0;
}
}
function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) {
aImagePtr = aImagePtr | 0;
bImagePtr = bImagePtr | 0;
outImagePtr = outImagePtr | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(outImagePtr + length) | 0] = ((images[(aImagePtr + length) | 0] | 0) | (images[(bImagePtr + length) | 0] | 0)) | 0;
}
}
function countNonZero(imagePtr) {
imagePtr = imagePtr | 0;
var sum = 0,
length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
sum = ((sum | 0) + (images[(imagePtr + length) | 0] | 0)) | 0;
}
return (sum | 0);
}
function init(imagePtr, value) {
imagePtr = imagePtr | 0;
value = value | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(imagePtr + length) | 0] = value;
}
}
function dilate(inImagePtr, outImagePtr) {
inImagePtr = inImagePtr | 0;
outImagePtr = outImagePtr | 0;
var v = 0,
u = 0,
sum = 0,
yStart1 = 0,
yStart2 = 0,
xStart1 = 0,
xStart2 = 0,
offset = 0;
for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) {
offset = (offset + size) | 0;
for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) {
yStart1 = (offset - size) | 0;
yStart2 = (offset + size) | 0;
xStart1 = (u - 1) | 0;
xStart2 = (u + 1) | 0;
sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + (images[(inImagePtr + offset + u) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0;
if ((sum | 0) > (0 | 0)) {
images[(outImagePtr + offset + u) | 0] = 1;
} else {
images[(outImagePtr + offset + u) | 0] = 0;
}
}
}
return;
}
function memcpy(srcImagePtr, dstImagePtr) {
srcImagePtr = srcImagePtr | 0;
dstImagePtr = dstImagePtr | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(dstImagePtr + length) | 0] = (images[(srcImagePtr + length) | 0] | 0);
}
}
function zeroBorder(imagePtr) {
imagePtr = imagePtr | 0;
var x = 0,
y = 0;
for ( x = 0; (x | 0) < ((size - 1) | 0); x = (x + 1) | 0) {
images[(imagePtr + x) | 0] = 0;
images[(imagePtr + y) | 0] = 0;
y = ((y + size) - 1) | 0;
images[(imagePtr + y) | 0] = 0;
y = (y + 1) | 0;
}
for ( x = 0; (x | 0) < (size | 0); x = (x + 1) | 0) {
images[(imagePtr + y) | 0] = 0;
y = (y + 1) | 0;
}
}
function skeletonize() {
var subImagePtr = 0,
erodedImagePtr = 0,
tempImagePtr = 0,
skelImagePtr = 0,
sum = 0,
done = 0;
erodedImagePtr = imul(size, size) | 0;
tempImagePtr = (erodedImagePtr + erodedImagePtr) | 0;
skelImagePtr = (tempImagePtr + erodedImagePtr) | 0;
// init skel-image
init(skelImagePtr, 0);
zeroBorder(subImagePtr);
do {
erode(subImagePtr, erodedImagePtr);
dilate(erodedImagePtr, tempImagePtr);
subtract(subImagePtr, tempImagePtr, tempImagePtr);
bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr);
memcpy(erodedImagePtr, subImagePtr);
sum = countNonZero(subImagePtr) | 0;
done = ((sum | 0) == 0 | 0);
} while(!done);
}
return {
skeletonize : skeletonize
};
return;
}
/* @preserve ASM END */
return Skeletonizer;
});
function subtract(aImagePtr, bImagePtr, outImagePtr) {
aImagePtr = aImagePtr | 0;
bImagePtr = bImagePtr | 0;
outImagePtr = outImagePtr | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(outImagePtr + length) | 0] =
((images[(aImagePtr + length) | 0] | 0) - (images[(bImagePtr + length) | 0] | 0)) | 0;
}
}
function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) {
aImagePtr = aImagePtr | 0;
bImagePtr = bImagePtr | 0;
outImagePtr = outImagePtr | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(outImagePtr + length) | 0] =
((images[(aImagePtr + length) | 0] | 0) | (images[(bImagePtr + length) | 0] | 0)) | 0;
}
}
function countNonZero(imagePtr) {
imagePtr = imagePtr | 0;
var sum = 0,
length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
sum = ((sum | 0) + (images[(imagePtr + length) | 0] | 0)) | 0;
}
return (sum | 0);
}
function init(imagePtr, value) {
imagePtr = imagePtr | 0;
value = value | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(imagePtr + length) | 0] = value;
}
}
function dilate(inImagePtr, outImagePtr) {
inImagePtr = inImagePtr | 0;
outImagePtr = outImagePtr | 0;
var v = 0,
u = 0,
sum = 0,
yStart1 = 0,
yStart2 = 0,
xStart1 = 0,
xStart2 = 0,
offset = 0;
for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) {
offset = (offset + size) | 0;
for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) {
yStart1 = (offset - size) | 0;
yStart2 = (offset + size) | 0;
xStart1 = (u - 1) | 0;
xStart2 = (u + 1) | 0;
sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0)
+ (images[(inImagePtr + yStart1 + xStart2) | 0] | 0)
+ (images[(inImagePtr + offset + u) | 0] | 0)
+ (images[(inImagePtr + yStart2 + xStart1) | 0] | 0)
+ (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0;
if ((sum | 0) > (0 | 0)) {
images[(outImagePtr + offset + u) | 0] = 1;
} else {
images[(outImagePtr + offset + u) | 0] = 0;
}
}
}
return;
}
function memcpy(srcImagePtr, dstImagePtr) {
srcImagePtr = srcImagePtr | 0;
dstImagePtr = dstImagePtr | 0;
var length = 0;
length = imul(size, size) | 0;
while ((length | 0) > 0) {
length = (length - 1) | 0;
images[(dstImagePtr + length) | 0] = (images[(srcImagePtr + length) | 0] | 0);
}
}
function zeroBorder(imagePtr) {
imagePtr = imagePtr | 0;
var x = 0,
y = 0;
for ( x = 0; (x | 0) < ((size - 1) | 0); x = (x + 1) | 0) {
images[(imagePtr + x) | 0] = 0;
images[(imagePtr + y) | 0] = 0;
y = ((y + size) - 1) | 0;
images[(imagePtr + y) | 0] = 0;
y = (y + 1) | 0;
}
for ( x = 0; (x | 0) < (size | 0); x = (x + 1) | 0) {
images[(imagePtr + y) | 0] = 0;
y = (y + 1) | 0;
}
}
function skeletonize() {
var subImagePtr = 0,
erodedImagePtr = 0,
tempImagePtr = 0,
skelImagePtr = 0,
sum = 0,
done = 0;
erodedImagePtr = imul(size, size) | 0;
tempImagePtr = (erodedImagePtr + erodedImagePtr) | 0;
skelImagePtr = (tempImagePtr + erodedImagePtr) | 0;
// init skel-image
init(skelImagePtr, 0);
zeroBorder(subImagePtr);
do {
erode(subImagePtr, erodedImagePtr);
dilate(erodedImagePtr, tempImagePtr);
subtract(subImagePtr, tempImagePtr, tempImagePtr);
bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr);
memcpy(erodedImagePtr, subImagePtr);
sum = countNonZero(subImagePtr) | 0;
done = ((sum | 0) == 0 | 0);
} while (!done);
}
return {
skeletonize: skeletonize
};
}
export default Skeletonizer;
/* eslint-enable eqeqeq*/
/* @preserve ASM END */
+84 -91
View File
@@ -1,97 +1,90 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define(["typedefs"], function() {
"use strict";
/**
* Construct representing a part of another {ImageWrapper}. Shares data
* between the parent and the child.
* @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner)
* @param size {ImageRef} The size of the resulting image
* @param I {ImageWrapper} The {ImageWrapper} to share from
* @returns {SubImage} A shared part of the original image
*/
function SubImage(from, size, I) {
if (!I) {
I = {
data : null,
size : size
};
}
this.data = I.data;
this.originalSize = I.size;
this.I = I;
this.from = from;
this.size = size;
/**
* Construct representing a part of another {ImageWrapper}. Shares data
* between the parent and the child.
* @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner)
* @param size {ImageRef} The size of the resulting image
* @param I {ImageWrapper} The {ImageWrapper} to share from
* @returns {SubImage} A shared part of the original image
*/
function SubImage(from, size, I) {
if (!I) {
I = {
data: null,
size: size
};
}
this.data = I.data;
this.originalSize = I.size;
this.I = I;
/**
* Displays the {SubImage} in a given canvas
* @param canvas {Canvas} The canvas element to write to
* @param scale {Number} Scale which is applied to each pixel-value
*/
SubImage.prototype.show = function(canvas, scale) {
var ctx,
frame,
data,
current,
y,
x,
pixel;
if (!scale) {
scale = 1.0;
this.from = from;
this.size = size;
}
/**
* Displays the {SubImage} in a given canvas
* @param canvas {Canvas} The canvas element to write to
* @param scale {Number} Scale which is applied to each pixel-value
*/
SubImage.prototype.show = function(canvas, scale) {
var ctx,
frame,
data,
current,
y,
x,
pixel;
if (!scale) {
scale = 1.0;
}
ctx = canvas.getContext('2d');
canvas.width = this.size.x;
canvas.height = this.size.y;
frame = ctx.getImageData(0, 0, canvas.width, canvas.height);
data = frame.data;
current = 0;
for (y = 0; y < this.size.y; y++) {
for (x = 0; x < this.size.x; x++) {
pixel = y * this.size.x + x;
current = this.get(x, y) * scale;
data[pixel * 4 + 0] = current;
data[pixel * 4 + 1] = current;
data[pixel * 4 + 2] = current;
data[pixel * 4 + 3] = 255;
}
ctx = canvas.getContext('2d');
canvas.width = this.size.x;
canvas.height = this.size.y;
frame = ctx.getImageData(0, 0, canvas.width, canvas.height);
data = frame.data;
current = 0;
for (y = 0; y < this.size.y; y++) {
for (x = 0; x < this.size.x; x++) {
pixel = y * this.size.x + x;
current = this.get(x, y) * scale;
data[pixel * 4 + 0] = current;
data[pixel * 4 + 1] = current;
data[pixel * 4 + 2] = current;
data[pixel * 4 + 3] = 255;
}
}
frame.data = data;
ctx.putImageData(frame, 0, 0);
};
}
frame.data = data;
ctx.putImageData(frame, 0, 0);
};
/**
* Retrieves a given pixel position from the {SubImage}
* @param x {Number} The x-position
* @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position
*/
SubImage.prototype.get = function(x, y) {
return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x];
};
/**
* Retrieves a given pixel position from the {SubImage}
* @param x {Number} The x-position
* @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position
*/
SubImage.prototype.get = function(x, y) {
return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x];
};
/**
* Updates the underlying data from a given {ImageWrapper}
* @param image {ImageWrapper} The updated image
*/
SubImage.prototype.updateData = function(image) {
this.originalSize = image.size;
this.data = image.data;
};
/**
* Updates the underlying data from a given {ImageWrapper}
* @param image {ImageWrapper} The updated image
*/
SubImage.prototype.updateData = function(image) {
this.originalSize = image.size;
this.data = image.data;
};
/**
* Updates the position of the shared area
* @param from {x,y} The new location
* @returns {SubImage} returns {this} for possible chaining
*/
SubImage.prototype.updateFrom = function(from) {
this.from = from;
return this;
};
return (SubImage);
});
/**
* Updates the position of the shared area
* @param from {x,y} The new location
* @returns {SubImage} returns {this} for possible chaining
*/
SubImage.prototype.updateFrom = function(from) {
this.from = from;
return this;
};
export default (SubImage);
+88 -95
View File
@@ -1,108 +1,101 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
/**
* http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization
*/
define(function() {
"use strict";
var Tracer = {
searchDirections : [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]],
create : function(imageWrapper, labelWrapper) {
var imageData = imageWrapper.data,
labelData = labelWrapper.data,
searchDirections = this.searchDirections,
width = imageWrapper.size.x,
pos;
var Tracer = {
searchDirections: [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]],
create: function(imageWrapper, labelWrapper) {
var imageData = imageWrapper.data,
labelData = labelWrapper.data,
searchDirections = this.searchDirections,
width = imageWrapper.size.x,
pos;
function trace(current, color, label, edgelabel) {
var i,
y,
x;
function trace(current, color, label, edgelabel) {
var i,
y,
x;
for ( i = 0; i < 7; i++) {
y = current.cy + searchDirections[current.dir][0];
x = current.cx + searchDirections[current.dir][1];
pos = y * width + x;
if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) {
labelData[pos] = label;
current.cy = y;
current.cx = x;
return true;
} else {
if (labelData[pos] === 0) {
labelData[pos] = edgelabel;
}
current.dir = (current.dir + 1) % 8;
for ( i = 0; i < 7; i++) {
y = current.cy + searchDirections[current.dir][0];
x = current.cx + searchDirections[current.dir][1];
pos = y * width + x;
if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) {
labelData[pos] = label;
current.cy = y;
current.cx = x;
return true;
} else {
if (labelData[pos] === 0) {
labelData[pos] = edgelabel;
}
current.dir = (current.dir + 1) % 8;
}
return false;
}
function vertex2D(x, y, dir) {
return {
dir : dir,
x : x,
y : y,
next : null,
prev : null
};
}
function contourTracing(sy, sx, label, color, edgelabel) {
var Fv = null,
Cv,
P,
ldir,
current = {
cx : sx,
cy : sy,
dir : 0
};
if (trace(current, color, label, edgelabel)) {
Fv = vertex2D(sx, sy, current.dir);
Cv = Fv;
ldir = current.dir;
P = vertex2D(current.cx, current.cy, 0);
P.prev = Cv;
Cv.next = P;
P.next = null;
Cv = P;
do {
current.dir = (current.dir + 6) % 8;
trace(current, color, label, edgelabel);
if (ldir != current.dir) {
Cv.dir = current.dir;
P = vertex2D(current.cx, current.cy, 0);
P.prev = Cv;
Cv.next = P;
P.next = null;
Cv = P;
} else {
Cv.dir = ldir;
Cv.x = current.cx;
Cv.y = current.cy;
}
ldir = current.dir;
} while(current.cx != sx || current.cy != sy);
Fv.prev = Cv.prev;
Cv.prev.next = Fv;
}
return Fv;
}
return false;
}
function vertex2D(x, y, dir) {
return {
trace : function(current, color, label, edgelabel) {
return trace(current, color, label, edgelabel);
},
contourTracing : function(sy, sx, label, color, edgelabel) {
return contourTracing(sy, sx, label, color, edgelabel);
}
dir: dir,
x: x,
y: y,
next: null,
prev: null
};
}
};
return (Tracer);
});
function contourTracing(sy, sx, label, color, edgelabel) {
var Fv = null,
Cv,
P,
ldir,
current = {
cx: sx,
cy: sy,
dir: 0
};
if (trace(current, color, label, edgelabel)) {
Fv = vertex2D(sx, sy, current.dir);
Cv = Fv;
ldir = current.dir;
P = vertex2D(current.cx, current.cy, 0);
P.prev = Cv;
Cv.next = P;
P.next = null;
Cv = P;
do {
current.dir = (current.dir + 6) % 8;
trace(current, color, label, edgelabel);
if (ldir !== current.dir) {
Cv.dir = current.dir;
P = vertex2D(current.cx, current.cy, 0);
P.prev = Cv;
Cv.next = P;
P.next = null;
Cv = P;
} else {
Cv.dir = ldir;
Cv.x = current.cx;
Cv.y = current.cy;
}
ldir = current.dir;
} while (current.cx !== sx || current.cy !== sy);
Fv.prev = Cv.prev;
Cv.prev.next = Fv;
}
return Fv;
}
return {
trace: function(current, color, label, edgelabel) {
return trace(current, color, label, edgelabel);
},
contourTracing: function(sy, sx, label, color, edgelabel) {
return contourTracing(sy, sx, label, color, edgelabel);
}
};
}
};
export default (Tracer);
+5 -5
View File
@@ -3,7 +3,6 @@
* Normalizes browser-specific prefixes
*/
glMatrixArrayType = Float32Array;
if (typeof window !== 'undefined') {
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
@@ -11,12 +10,13 @@ if (typeof window !== 'undefined') {
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
function (/* function FrameRequestCallback */ callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
}
Math.imul = Math.imul || function(a, b) {
@@ -26,5 +26,5 @@ Math.imul = Math.imul || function(a, b) {
bl = b & 0xffff;
// the shift by 0 fixes the sign on the high part
// the final |0 converts the unsigned value into a signed value
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0);
};
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
};
+99 -110
View File
@@ -1,114 +1,103 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import EANReader from './ean_reader';
define(
[
"./ean_reader"
],
function(EANReader) {
"use strict";
function UPCEReader() {
EANReader.call(this);
}
function UPCEReader() {
EANReader.call(this);
var properties = {
CODE_FREQUENCY: {value: [
[ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ],
[7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]},
STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]},
FORMAT: {value: "upc_e", writeable: false}
};
UPCEReader.prototype = Object.create(EANReader.prototype, properties);
UPCEReader.prototype.constructor = UPCEReader;
UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) {
var i,
self = this,
codeFrequency = 0x0;
for ( i = 0; i < 6; i++) {
code = self._decodeCode(code.end);
if (!code) {
return null;
}
var properties = {
CODE_FREQUENCY : {value: [
[ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ],
[7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]},
STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]},
FORMAT: {value: "upc_e", writeable: false}
};
UPCEReader.prototype = Object.create(EANReader.prototype, properties);
UPCEReader.prototype.constructor = UPCEReader;
UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) {
var i,
self = this,
codeFrequency = 0x0;
for ( i = 0; i < 6; i++) {
code = self._decodeCode(code.end);
if (!code) {
return null;
}
if (code.code >= self.CODE_G_START) {
code.code = code.code - self.CODE_G_START;
codeFrequency |= 1 << (5 - i);
}
result.push(code.code);
decodedCodes.push(code);
}
if (!self._determineParity(codeFrequency, result)) {
return null;
}
return code;
};
UPCEReader.prototype._determineParity = function(codeFrequency, result) {
var self =this,
i,
nrSystem;
for (nrSystem = 0; nrSystem < self.CODE_FREQUENCY.length; nrSystem++){
for ( i = 0; i < self.CODE_FREQUENCY[nrSystem].length; i++) {
if (codeFrequency === self.CODE_FREQUENCY[nrSystem][i]) {
result.unshift(nrSystem);
result.push(i);
return true;
}
}
}
return false;
};
UPCEReader.prototype._convertToUPCA = function(result) {
var upca = [result[0]],
lastDigit = result[result.length - 2];
if (lastDigit <= 2) {
upca = upca.concat(result.slice(1, 3))
.concat([lastDigit, 0, 0, 0, 0])
.concat(result.slice(3, 6));
} else if (lastDigit === 3) {
upca = upca.concat(result.slice(1, 4))
.concat([0 ,0, 0, 0, 0])
.concat(result.slice(4,6));
} else if (lastDigit === 4) {
upca = upca.concat(result.slice(1, 5))
.concat([0, 0, 0, 0, 0, result[5]]);
} else {
upca = upca.concat(result.slice(1, 6))
.concat([0, 0, 0, 0, lastDigit]);
}
upca.push(result[result.length - 1]);
return upca;
};
UPCEReader.prototype._checksum = function(result) {
return EANReader.prototype._checksum.call(this, this._convertToUPCA(result));
};
UPCEReader.prototype._findEnd = function(offset, isWhite) {
isWhite = true;
return EANReader.prototype._findEnd.call(this, offset, isWhite);
};
UPCEReader.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 (UPCEReader);
if (code.code >= self.CODE_G_START) {
code.code = code.code - self.CODE_G_START;
codeFrequency |= 1 << (5 - i);
}
result.push(code.code);
decodedCodes.push(code);
}
);
if (!self._determineParity(codeFrequency, result)) {
return null;
}
return code;
};
UPCEReader.prototype._determineParity = function(codeFrequency, result) {
var i,
nrSystem;
for (nrSystem = 0; nrSystem < this.CODE_FREQUENCY.length; nrSystem++){
for ( i = 0; i < this.CODE_FREQUENCY[nrSystem].length; i++) {
if (codeFrequency === this.CODE_FREQUENCY[nrSystem][i]) {
result.unshift(nrSystem);
result.push(i);
return true;
}
}
}
return false;
};
UPCEReader.prototype._convertToUPCA = function(result) {
var upca = [result[0]],
lastDigit = result[result.length - 2];
if (lastDigit <= 2) {
upca = upca.concat(result.slice(1, 3))
.concat([lastDigit, 0, 0, 0, 0])
.concat(result.slice(3, 6));
} else if (lastDigit === 3) {
upca = upca.concat(result.slice(1, 4))
.concat([0, 0, 0, 0, 0])
.concat(result.slice(4, 6));
} else if (lastDigit === 4) {
upca = upca.concat(result.slice(1, 5))
.concat([0, 0, 0, 0, 0, result[5]]);
} else {
upca = upca.concat(result.slice(1, 6))
.concat([0, 0, 0, 0, lastDigit]);
}
upca.push(result[result.length - 1]);
return upca;
};
UPCEReader.prototype._checksum = function(result) {
return EANReader.prototype._checksum.call(this, this._convertToUPCA(result));
};
UPCEReader.prototype._findEnd = function(offset, isWhite) {
isWhite = true;
return EANReader.prototype._findEnd.call(this, offset, isWhite);
};
UPCEReader.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;
}
}
};
export default UPCEReader;
+18 -29
View File
@@ -1,35 +1,24 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
import EANReader from './ean_reader';
define(
[
"./ean_reader"
],
function(EANReader) {
"use strict";
function UPCReader() {
EANReader.call(this);
}
function UPCReader() {
EANReader.call(this);
}
var properties = {
FORMAT: {value: "upc_a", writeable: false}
};
var properties = {
FORMAT: {value: "upc_a", writeable: false}
};
UPCReader.prototype = Object.create(EANReader.prototype, properties);
UPCReader.prototype.constructor = UPCReader;
UPCReader.prototype = Object.create(EANReader.prototype, properties);
UPCReader.prototype.constructor = UPCReader;
UPCReader.prototype._decode = function() {
var result = EANReader.prototype._decode.call(this);
UPCReader.prototype._decode = function() {
var result = EANReader.prototype._decode.call(this);
if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") {
result.code = result.code.substring(1);
return result;
}
return null;
};
return (UPCReader);
if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") {
result.code = result.code.substring(1);
return result;
}
);
return null;
};
export default UPCReader;
+24 -17
View File
@@ -11,14 +11,17 @@ module.exports = function(grunt) {
var code = fs.readFileSync('dist/quagga.js', 'utf-8'),
minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'),
commentEnd = '/* @preserve ASM END */',
asmStartIdx = code.indexOf('/* @preserve ASM BEGIN */'),
asmEndIdx = code.indexOf(commentEnd),
asmCode = code.substring(asmStartIdx, asmEndIdx + commentEnd.length),
asmFunctionRegex = /function (\w+)\(\w+,\s*\w+,\s*\w+\)\s*\{\s*"use asm";/,
moduleFunctionRegex = /function\s*\((\w+,\s*\w+)\)\s*\{\s*\/\* \@preserve ASM BEGIN \*\//,
commentStartIdx = code.indexOf("/* @preserve ASM BEGIN */"),
asmEndIdxTmp = code.indexOf(commentEnd),
asmEndIdx = code.indexOf("}", asmEndIdxTmp),
asmCodeTmp = code.substring(commentStartIdx - Math.min(500, commentStartIdx),
asmEndIdx + 1),
asmStartIdx = asmCodeTmp.search(moduleFunctionRegex),
asmCode = asmCodeTmp.substring(asmStartIdx),
asmModule,
asmModuleName,
asmCodeMinified,
asmMinifiedModuleName;
moduleArg1,
asmCodeMinified;
asmCodeMinified = asmCode
.replace(/\s*\/\/.*/g, '') // remove single-line comments
@@ -29,28 +32,32 @@ module.exports = function(grunt) {
grunt.log.debug(asmCodeMinified);
asmModule = asmCode.match(asmFunctionRegex);
asmModule = moduleFunctionRegex.exec(asmCode);
if (!asmModule) {
grunt.log.error("No ASM module found");
return;
}
asmModuleName = asmModule[1];
grunt.log.debug(asmModuleName);
moduleArg1 = asmModule[1];
grunt.log.debug(moduleArg1);
asmModule = minifiedCode.match(asmFunctionRegex);
if (!asmModule) {
var insertionPoint = minifiedCode.search(moduleFunctionRegex);
if (insertionPoint === -1) {
grunt.log.error("No ASM module found in minified file");
return;
}
grunt.log.debug(insertionPoint);
asmMinifiedModuleName = asmModule[1];
grunt.log.debug(asmMinifiedModuleName);
var insertionPointEnd = minifiedCode.indexOf(commentEnd, insertionPoint);
insertionPointEnd = minifiedCode.indexOf("}", insertionPointEnd) + 1;
asmCodeMinified = asmCodeMinified.replace(asmModuleName, asmMinifiedModuleName);
grunt.log.debug(insertionPointEnd);
minifiedCode = minifiedCode.substring(0, insertionPoint)
+ asmCodeMinified
+ minifiedCode.substring(insertionPointEnd);
minifiedCode = minifiedCode.replace(/\/\* @preserve ASM BEGIN \*\/[^]*?\/\* @preserve ASM END \*\//, asmCodeMinified);
fs.writeFileSync('dist/quagga.min.js', minifiedCode);
grunt.log.ok('dist/quagga.min.js written');
});
};
};
-55
View File
@@ -1,55 +0,0 @@
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;
var pathToModule = function(path) {
return path.replace(/^\/base\//, '').replace(/\.js$/, '');
};
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
allTestFiles.push(pathToModule(file));
}
});
require.config({
baseUrl: '/base',
paths: {
'array_helper': 'src/array_helper',
'cv_utils': 'src/cv_utils',
'typedefs': 'src/typedefs',
'cluster': 'src/cluster',
'camera_access': 'src/camera_access',
'events': 'src/events',
'html_utils': 'src/html_utils',
'quagga': 'src/quagga',
'barcode_decoder': 'src/barcode_decoder',
'barcode_locator': 'src/barcode_locator',
'barcode_reader': 'src/barcode_reader',
'bresenham': 'src/bresenham',
'codabar_reader': 'src/codabar_reader',
'code_39_reader': 'src/code_39_reader',
'code_39_vin_reader': 'src/code_39_vin_reader',
'code_128_reader': 'src/code_128_reader',
'config': 'src/config',
'ean_8_reader': 'src/ean_8_reader',
'ean_reader': 'src/ean_reader',
'frame_grabber': 'src/frame_grabber',
'image_debug': 'src/image_debug',
'image_loader': 'src/image_loader',
'image_wrapper': 'src/image_wrapper',
'input_stream': 'src/input_stream',
'rasterizer': 'src/rasterizer',
'skeletonizer': 'src/skeletonizer',
'subImage': 'src/subImage',
'tracer': 'src/tracer',
'upc_e_reader': 'src/upc_e_reader',
'upc_reader': 'src/upc_reader',
'async': 'node_modules/async/lib/async',
'gl-matrix': 'node_modules/gl-matrix/dist/gl-matrix-min',
'result_collector': 'src/result_collector',
'i2of5_reader': 'src/i2of5_reader'
},
deps: allTestFiles,
callback: window.__karma__.start
});
+245
View File
@@ -0,0 +1,245 @@
const Quagga = require('../../src/quagga');
const async = require('async');
describe('decodeSingle', function () {
var baseFolder = "base/test/fixtures/";
function generateConfig() {
return {
inputStream: {
size: 640
},
locator: {
patchSize: "medium",
halfSample: true
},
numOfWorkers: 0,
decoder: {
readers: ["ean_reader"]
},
locate: true,
src: null
};
}
this.timeout(10000);
function _runTestSet(testSet, config) {
var readers = config.decoder.readers.slice(),
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) {
async.eachSeries(testSet, function (sample, callback) {
config.src = folder + sample.name;
config.readers = readers;
Quagga.decodeSingle(config, function(result) {
console.log(sample.name);
expect(result.codeResult.code).to.equal(sample.result);
expect(result.codeResult.format).to.equal(sample.format);
callback();
});
}, function() {
done();
});
});
}
describe("EAN", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "3574660239843"},
{"name": "image-002.jpg", "result": "8032754490297"},
{"name": "image-003.jpg", "result": "4006209700068"},
/* {"name": "image-004.jpg", "result": "9002233139084"}, */
/* {"name": "image-005.jpg", "result": "8004030044005"}, */
{"name": "image-006.jpg", "result": "4003626011159"},
{"name": "image-007.jpg", "result": "2111220009686"},
{"name": "image-008.jpg", "result": "9000275609022"},
{"name": "image-009.jpg", "result": "9004593978587"},
{"name": "image-010.jpg", "result": "9002244845578"}
];
testSet.forEach(function(sample) {
sample.format = "ean_13";
});
config.decoder.readers = ['ean_reader'];
_runTestSet(testSet, config);
});
describe("Code128", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "0001285112001000040801"},
// {"name": "image-002.jpg", "result": "FANAVF1461710"},
// {"name": "image-003.jpg", "result": "673023"},
// {"name": "image-004.jpg", "result": "010210150301625334"},
{"name": "image-005.jpg", "result": "419055603900009001012999"},
{"name": "image-006.jpg", "result": "419055603900009001012999"},
{"name": "image-007.jpg", "result": "T 000003552345"},
{"name": "image-008.jpg", "result": "FANAVF1461710"},
{"name": "image-009.jpg", "result": "0001285112001000040801"},
{"name": "image-010.jpg", "result": "673023"}
];
testSet.forEach(function(sample) {
sample.format = "code_128";
});
config.decoder.readers = ['code_128_reader'];
_runTestSet(testSet, config);
});
describe("Code39", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "B3% $DAD$"},
{"name": "image-003.jpg", "result": "CODE39"},
{"name": "image-004.jpg", "result": "QUAGGAJS"},
/* {"name": "image-005.jpg", "result": "CODE39"}, */
{"name": "image-006.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-009.jpg", "result": "2/4-8/16-32"},
{"name": "image-010.jpg", "result": "CODE39"}
];
testSet.forEach(function(sample) {
sample.format = "code_39";
});
config.decoder.readers = ['code_39_reader'];
_runTestSet(testSet, config);
});
describe("EAN-8", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "42191605"},
{"name": "image-002.jpg", "result": "42191605"},
{"name": "image-003.jpg", "result": "90311208"},
{"name": "image-004.jpg", "result": "24057257"},
{"name": "image-005.jpg", "result": "90162602"},
{"name": "image-006.jpg", "result": "24036153"},
{"name": "image-007.jpg", "result": "42176817"},
{"name": "image-008.jpg", "result": "42191605"},
{"name": "image-009.jpg", "result": "42242215"},
{"name": "image-010.jpg", "result": "42184799"}
];
testSet.forEach(function(sample) {
sample.format = "ean_8";
});
config.decoder.readers = ['ean_8_reader'];
_runTestSet(testSet, config);
});
describe("UPC", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "882428015268"},
{"name": "image-002.jpg", "result": "882428015268"},
{"name": "image-003.jpg", "result": "882428015084"},
{"name": "image-004.jpg", "result": "882428015343"},
{"name": "image-005.jpg", "result": "882428015343"},
/* {"name": "image-006.jpg", "result": "882428015046"}, */
{"name": "image-007.jpg", "result": "882428015084"},
{"name": "image-008.jpg", "result": "882428015046"},
{"name": "image-009.jpg", "result": "039047013551"},
{"name": "image-010.jpg", "result": "039047013551"}
];
testSet.forEach(function(sample) {
sample.format = "upc_a";
});
config.decoder.readers = ['upc_reader'];
_runTestSet(testSet, config);
});
describe("UPC-E", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "04965802"},
{"name": "image-002.jpg", "result": "04965802"},
{"name": "image-003.jpg", "result": "03897425"},
{"name": "image-004.jpg", "result": "05096893"},
{"name": "image-005.jpg", "result": "05096893"},
{"name": "image-006.jpg", "result": "05096893"},
{"name": "image-007.jpg", "result": "03897425"},
{"name": "image-008.jpg", "result": "01264904"},
/*{"name": "image-009.jpg", "result": "01264904"},*/
{"name": "image-010.jpg", "result": "01264904"}
];
testSet.forEach(function(sample) {
sample.format = "upc_e";
});
config.decoder.readers = ['upc_e_reader'];
_runTestSet(testSet, config);
});
describe("Codabar", function() {
var config = generateConfig(),
testSet = [
{"name": "image-001.jpg", "result": "A10/53+17-70D"},
{"name": "image-002.jpg", "result": "B546745735B"},
{"name": "image-003.jpg", "result": "C$399.95A"},
{"name": "image-004.jpg", "result": "B546745735B"},
{"name": "image-005.jpg", "result": "C$399.95A"},
{"name": "image-006.jpg", "result": "B546745735B"},
{"name": "image-007.jpg", "result": "C$399.95A"},
{"name": "image-008.jpg", "result": "A16:9/4:3/3:2D"},
{"name": "image-009.jpg", "result": "C$399.95A"},
{"name": "image-010.jpg", "result": "C$399.95A"}
];
testSet.forEach(function(sample) {
sample.format = "codabar";
});
config.decoder.readers = ['codabar_reader'];
_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);
});
});
+53
View File
@@ -0,0 +1,53 @@
const ArrayHelper = require('../../src/array_helper');
describe('init', function() {
it('initializes an array with the given value', function() {
var input = [0, 0, 0];
ArrayHelper.init(input, 5);
expect(input).to.deep.equal([5, 5, 5]);
});
});
describe('shuffle', function() {
before(function() {
sinon.stub(Math, 'random').returns(0.5);
});
after(function() {
sinon.restore(Math);
});
it('shuffles the content of an array', function() {
var input = [1, 2, 3];
expect(ArrayHelper.shuffle(input)).to.deep.equal([3, 1, 2]);
});
});
describe('toPointList', function() {
it('converts an Array to a List of poitns', function() {
var input = [[1, 2], [2, 2], [3, 2]];
expect(ArrayHelper.toPointList(input)).to.equal("[[1,2],\r\n[2,2],\r\n[3,2]]");
});
});
describe('threshold', function() {
it('returns all elements above the given threshold', function() {
var input = [1, 2, 3];
expect(ArrayHelper.threshold(input, 2, function(score) {
return score * 1.5;
})).to.deep.equal([2, 3]);
});
});
describe('maxIndex', function() {
it('gets the index of the biggest element in the array', function() {
var input = [1, 2, 3];
expect(ArrayHelper.maxIndex(input)).to.equal(2);
});
});
describe('max', function() {
it('gets the biggest element in the array', function() {
var input = [1, 3, 2];
expect(ArrayHelper.max(input)).to.equal(3);
});
});
+130
View File
@@ -0,0 +1,130 @@
const BarcodeLocator = require('../../src/barcode_locator');
const Config = require('../../src/config');
const merge = require('lodash/object/merge');
describe('checkImageConstraints', function() {
var config,
inputStream,
imageSize,
streamConfig = {};
beforeEach(function() {
imageSize = {
x: 640, y: 480
};
config = merge({}, Config);
inputStream = {
getWidth: function() {
return imageSize.x;
},
getHeight: function() {
return imageSize.y;
},
setWidth: function() {},
setHeight: function() {},
setTopRight: function() {},
setCanvasSize: function() {},
getConfig: function() {
return streamConfig;
}
};
sinon.stub(inputStream, "setWidth", function(width) {
imageSize.x = width;
});
sinon.stub(inputStream, "setHeight", function(height) {
imageSize.y = height;
});
sinon.stub(inputStream, "setTopRight");
sinon.stub(inputStream, "setCanvasSize");
});
afterEach(function() {
inputStream.setWidth.restore();
inputStream.setHeight.restore();
});
it('should not adjust the image-size if not needed', function() {
var expected = {x: imageSize.x, y: imageSize.y};
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getWidth()).to.be.equal(expected.x);
expect(inputStream.getHeight()).to.be.equal(expected.y);
});
it('should adjust the image-size', function() {
var expected = {x: imageSize.x, y: imageSize.y};
config.locator.halfSample = true;
imageSize.y += 1;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getWidth()).to.be.equal(expected.x);
expect(inputStream.getHeight()).to.be.equal(expected.y);
});
it('should adjust the image-size', function() {
var expected = {x: imageSize.x, y: imageSize.y};
imageSize.y += 1;
config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expected.y);
expect(inputStream.getWidth()).to.be.equal(expected.x);
});
it("should take the defined area into account", function() {
var expectedSize = {
x: 420,
y: 315
},
expectedTopRight = {
x: 115,
y: 52
},
expectedCanvasSize = {
x: 640,
y: 480
};
streamConfig.area = {
top: "11%",
right: "15%",
bottom: "20%",
left: "18%"
};
config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expectedSize.y);
expect(inputStream.getWidth()).to.be.equal(expectedSize.x);
expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight);
expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize);
});
it("should return the original size if set to full image", function() {
var expectedSize = {
x: 640,
y: 480
},
expectedTopRight = {
x: 0,
y: 0
},
expectedCanvasSize = {
x: 640,
y: 480
};
streamConfig.area = {
top: "0%",
right: "0%",
bottom: "0%",
left: "0%"
};
config.locator.halfSample = false;
BarcodeLocator.checkImageConstraints(inputStream, config.locator);
expect(inputStream.getHeight()).to.be.equal(expectedSize.y);
expect(inputStream.getWidth()).to.be.equal(expectedSize.x);
expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight);
expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize);
});
});
+116
View File
@@ -0,0 +1,116 @@
const CameraAccess = require('../../src/camera_access');
var originalURL,
originalMediaStreamTrack,
video,
stream;
beforeEach(function() {
var tracks = [{
stop: function() {}
}];
originalURL = window.URL;
originalMediaStreamTrack = window.MediaStreamTrack;
window.MediaStreamTrack = {};
window.URL = null;
stream = {
getVideoTracks: function() {
return tracks;
}
};
sinon.spy(tracks[0], "stop");
video = {
src: null,
addEventListener: function() {},
removeEventListener: function() {},
play: function() {},
videoWidth: 320,
videoHeight: 480
};
sinon.stub(video, "addEventListener", function(event, cb) {
cb();
});
sinon.stub(video, "play");
});
afterEach(function() {
window.URL = originalURL;
window.MediaStreamTrack = originalMediaStreamTrack;
});
describe('success', function() {
beforeEach(function() {
sinon.stub(navigator, "getUserMedia", function(constraints, success) {
success(stream);
});
});
afterEach(function() {
navigator.getUserMedia.restore();
});
describe('request', function () {
it('should request the camera', function (done) {
CameraAccess.request(video, {}, function () {
expect(navigator.getUserMedia.calledOnce).to.equal(true);
expect(video.src).to.deep.equal(stream);
done();
});
});
});
describe('release', function () {
it('should release the camera', function (done) {
CameraAccess.request(video, {}, function () {
expect(video.src).to.deep.equal(stream);
CameraAccess.release();
expect(video.src.getVideoTracks()).to.have.length(1);
expect(video.src.getVideoTracks()[0].stop.calledOnce).to.equal(true);
done();
});
});
});
});
describe('failure', function() {
describe("permission denied", function(){
before(function() {
sinon.stub(navigator, "getUserMedia", function(constraints, success, failure) {
failure(new Error());
});
});
after(function() {
navigator.getUserMedia.restore();
});
it('should throw if getUserMedia not available', function(done) {
CameraAccess.request(video, {}, function(err) {
expect(err).to.be.defined;
done();
});
});
});
describe("not available", function(){
var originalGetUserMedia;
before(function() {
originalGetUserMedia = navigator.getUserMedia;
navigator.getUserMedia = undefined;
});
after(function() {
navigator.getUserMedia = originalGetUserMedia;
});
it('should throw if getUserMedia not available', function(done) {
CameraAccess.request(video, {}, function(err) {
expect(err).to.be.defined;
done();
});
});
});
});
+144
View File
@@ -0,0 +1,144 @@
const CVUtils = require('../../src/cv_utils');
describe('imageRef', function() {
it('gets the image Reference for a coordinate', function() {
var res = CVUtils.imageRef(1, 2);
expect(res.x).to.equal(1);
expect(res.y).to.equal(2);
expect(res.toVec2()[0]).to.equal(1);
});
});
describe('calculatePatchSize', function() {
it('should not throw an error in case of valid image size', function() {
var expected = {x: 32, y: 32},
patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480});
expect(patchSize).to.be.deep.equal(expected);
});
it('should thow an error if image size it not valid', function() {
var expected = {x: 32, y: 32},
patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480});
expect(patchSize).to.be.deep.equal(expected);
});
});
describe('_parseCSSDimensionValues', function() {
it("should convert a percentual value correctly", function() {
var expected = {
value: 10,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("10%");
expect(result).to.be.deep.equal(expected);
});
it("should convert a 0% value correctly", function() {
var expected = {
value: 100,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("100%");
expect(result).to.be.deep.equal(expected);
});
it("should convert a 100% value correctly", function() {
var expected = {
value: 0,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("0%");
expect(result).to.be.deep.equal(expected);
});
it("should convert a pixel value to percentage", function() {
var expected = {
value: 26.3,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("26.3px");
console.log(result);
expect(result).to.be.deep.equal(expected);
});
});
describe("_dimensionsConverters", function(){
var context;
beforeEach(function() {
context = {
width: 640,
height: 480
};
});
it("should convert a top-value correclty", function() {
var expected = 48,
result = CVUtils._dimensionsConverters.top({value: 10, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
it("should convert a right-value correclty", function() {
var expected = 640 - 128,
result = CVUtils._dimensionsConverters.right({value: 20, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
it("should convert a bottom-value correclty", function() {
var expected = 480 - 77,
result = CVUtils._dimensionsConverters.bottom({value: 16, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
it("should convert a left-value correclty", function() {
var expected = 57,
result = CVUtils._dimensionsConverters.left({value: 9, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
});
describe("computeImageArea", function() {
it("should calculate an image-area", function() {
var expected = {
sx: 115,
sy: 48,
sw: 429,
sh: 336
},
result = CVUtils.computeImageArea(640, 480, {
top: "10%",
right: "15%",
bottom: "20%",
left: "18%"
});
expect(result).to.be.deep.equal(expected);
});
it("should calculate full image-area", function() {
var expected = {
sx: 0,
sy: 0,
sw: 640,
sh: 480
},
result = CVUtils.computeImageArea(640, 480, {
top: "0%",
right: "0%",
bottom: "0%",
left: "0%"
});
expect(result).to.be.deep.equal(expected);
});
});
+114
View File
@@ -0,0 +1,114 @@
const Events = require('../../src/events');
beforeEach(function() {
Events.unsubscribe();
});
describe("subscribe", function() {
it("should call one callback for a single event", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub();
Events.subscribe("test", callbackA);
Events.subscribe("test", callbackB);
Events.publish("test");
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
});
it("should call one callback twice if published twice", function() {
var callback = sinon.stub();
Events.subscribe("test", callback);
Events.publish("test");
Events.publish("test");
expect(callback.calledTwice).to.be.equal(true);
});
it("should call the callback asynchronuously", function(done) {
var test = {
callback: function() {
}
};
sinon.stub(test, "callback", function() {
expect(test.callback.calledOnce).to.be.true;
done();
});
Events.subscribe("test", test.callback, true);
Events.publish("test");
expect(test.callback.called).to.be.false;
});
});
describe("once", function() {
it("should call the callback once, even when published twice", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub();
Events.once("test", callbackA);
Events.subscribe("test", callbackB);
Events.publish("test");
Events.publish("test");
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledTwice).to.be.equal(true);
});
});
describe("unsubscribe", function() {
it("should unsubscribe all callbacks from a single event", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub(),
callbackC = sinon.stub();
Events.subscribe("test", callbackA);
Events.subscribe("test", callbackB);
Events.subscribe("testC", callbackC);
Events.publish("test");
expect(callbackC.called).to.be.equal(false);
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
Events.publish("testC");
expect(callbackC.calledOnce).to.be.equal(true);
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
Events.unsubscribe("test");
Events.publish("test");
expect(callbackC.calledOnce).to.be.equal(true);
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
});
it("should unsubscribe a single callback from a single event", function() {
var callbackA = sinon.stub(),
callbackB = sinon.stub();
Events.subscribe("test", callbackA);
Events.subscribe("test", callbackB);
Events.publish("test");
expect(callbackA.calledOnce).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
Events.unsubscribe("test", callbackB);
Events.publish("test");
expect(callbackA.calledTwice).to.be.equal(true);
expect(callbackB.calledOnce).to.be.equal(true);
});
});
+103
View File
@@ -0,0 +1,103 @@
const ResultCollector = require('../../src/result_collector');
const ImageDebug = require('../../src/image_debug');
var canvasMock,
imageSize,
config;
beforeEach(function() {
imageSize = {x: 320, y: 240};
config = {
capture: true,
capacity: 20,
blacklist: [{code: "3574660239843", format: "ean_13"}],
filter: function() {
return true;
}
};
canvasMock = {
getContext: function() {
return {};
},
toDataURL: sinon.spy(),
width: 0,
height: 0
};
sinon.stub(document, "createElement", function(type) {
if (type === "canvas") {
return canvasMock;
}
});
});
afterEach(function() {
document.createElement.restore();
});
describe('create', function () {
it("should return a new collector", function() {
ResultCollector.create(config);
expect(document.createElement.calledOnce).to.be.equal(true);
expect(document.createElement.getCall(0).args[0]).to.equal("canvas");
});
});
describe('addResult', function() {
beforeEach(function() {
sinon.stub(ImageDebug, "drawImage", function() {});
});
afterEach(function() {
ImageDebug.drawImage.restore();
});
it("should not add result if capacity is full", function(){
config.capacity = 1;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {});
expect(collector.getResults()).to.have.length(1);
});
it("should only add results which match constraints", function(){
var collector = ResultCollector.create(config),
results;
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"});
results = collector.getResults();
expect(results).to.have.length(2);
results.forEach(function(result) {
expect(result).not.to.deep.equal(config.blacklist[0]);
});
});
it("should add result if no filter is set", function() {
delete config.filter;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(1);
});
it("should not add results if filter returns false", function() {
config.filter = () => (false);
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(0);
});
it("should add result if no blacklist is set", function() {
delete config.blacklist;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
expect(collector.getResults()).to.have.length(1);
});
});
+8
View File
@@ -0,0 +1,8 @@
require('events').EventEmitter.prototype._maxListeners = 0;
require('core-js/es5');
const testsContext = require.context("./integration", true, /.*js$/);
testsContext.keys().forEach(testsContext);
const componentsContext = require.context('../src/', true, /\.*js$/);
componentsContext.keys().forEach(componentsContext);
+8
View File
@@ -0,0 +1,8 @@
require('events').EventEmitter.prototype._maxListeners = 0;
require('core-js/es5');
const testsContext = require.context("./spec", true, /.*js$/);
testsContext.keys().forEach(testsContext);
const componentsContext = require.context('../src/', true, /\.*js$/);
componentsContext.keys().forEach(componentsContext);
+39
View File
@@ -0,0 +1,39 @@
var webpack = require('webpack'),
MyUmdPlugin = require('./plugins/umd'),
path = require('path');
module.exports = {
entry: [
'./src/quagga.js'
],
devtool: 'source-map',
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
}]
},
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
'input_stream$': path.resolve(__dirname, 'src/input_stream'),
'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber')
}
},
output: {
path: __dirname + '/dist',
publicPath: '/',
filename: 'quagga.js',
sourceMapFilename: 'quagga.map'
},
devServer: {
contentBase: './',
hot: true
},
plugins: [
new MyUmdPlugin({
library: 'Quagga'
})
]
};
+11
View File
@@ -0,0 +1,11 @@
var webpack = require('webpack');
module.exports = require('./webpack.config.js');
module.exports.plugins.unshift(
new webpack.optimize.UglifyJsPlugin()
);
module.exports.output.filename = 'quagga.min.js';
module.exports.output.sourceMapFilename = null;
module.exports.devtool = null;
+24
View File
@@ -0,0 +1,24 @@
var webpack = require('webpack'),
path = require('path');
module.exports = require('./webpack.config.js');
module.exports.resolve = {
extensions: ['', '.js', '.jsx'],
alias: {
'input_stream$': path.resolve(__dirname, 'lib/input_stream'),
'frame_grabber$': path.resolve(__dirname, 'lib/frame_grabber')
}
};
module.exports.externals = [
"get-pixels",
"gl-matrix",
"lodash",
"ndarray",
"ndarray-linear-interpolate"
];
module.exports.output.libraryTarget = "commonjs2";
module.exports.plugins = [];
module.exports.output.path = __dirname + '/lib';
module.exports.output.filename = 'quagga.js';