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) { module.exports = function(grunt) {
// Project configuration. // Project configuration.
grunt.initConfig({ grunt.initConfig({
pkg : grunt.file.readJSON('package.json'), pkg: grunt.file.readJSON('package.json'),
karma: { karma: {
unit: { unit: {
configFile: 'karma.conf.js' configFile: 'karma.conf.js'
@@ -10,67 +9,14 @@ module.exports = function(grunt) {
integration: { integration: {
configFile: 'karma-integration.conf.js' 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-requirejs');
grunt.loadNpmTasks('grunt-karma'); grunt.loadNpmTasks('grunt-karma');
grunt.loadTasks('tasks'); grunt.loadTasks('tasks');
grunt.registerTask('build', ['check', 'requirejs']);
grunt.registerTask('check', ['jshint']);
grunt.registerTask('dist', ['build', 'uglify', 'uglyasm']);
grunt.registerTask('test', ['karma']); grunt.registerTask('test', ['karma']);
grunt.registerTask('integrationtest', ['karma:integration']); grunt.registerTask('integrationtest', ['karma:integration']);
};
grunt.registerTask('default', ['build']);
};
+17 -6
View File
@@ -1,7 +1,7 @@
quaggaJS quaggaJS
======== ========
- [Changelog](#changelog) (2015-09-15) - [Changelog](#changelog) (2015-10-13)
## What is QuaggaJS? ## 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 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, `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 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. [caniuse][caniuse_getusermedia] for updates.
In cases where real-time decoding is not needed, or the platform does not 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 ```console
> npm install > 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. 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 ## API
@@ -360,7 +364,7 @@ automatically generated in the coverage/ folder.
```console ```console
> npm install > npm install
> grunt test > npm run test
``` ```
## Image Debugging ## Image Debugging
@@ -430,6 +434,13 @@ on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
## <a name="changelog">Changelog</a> ## <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 ### 2015-09-15
Take a look at the release-notes ([0.7.0] Take a look at the release-notes ([0.7.0]
(https://github.com/serratus/quaggaJS/releases/tag/v0.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 &copy; Copyright by Christoph Oberhofer
</p> </p>
</footer> </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="../dist/quagga.js" type="text/javascript"></script>
<script src="file_input.js" type="text/javascript"></script> <script src="file_input.js" type="text/javascript"></script>
</body> </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) { module.exports = function(config) {
config.set({ config.set({
basePath: '', basePath: '',
frameworks: ['mocha', 'requirejs', 'chai', 'sinon', 'sinon-chai'], frameworks: ['mocha', 'chai', 'sinon', 'sinon-chai'],
files: [ files: [
'test-main.js', 'test/test-main-integration.js',
'src/typedefs.js', {pattern: 'test/integration/**/*.js', included: false},
{pattern: 'node_modules/async/lib/async.js', included: false}, {pattern: 'test/fixtures/**/*.*', included: false}
{pattern: 'node_modules/gl-matrix/dist/gl-matrix-min.js', included: false}, ],
{pattern: 'src/*.js', included: false}, preprocessors: {
{pattern: 'spec/**/*integration.spec.js', included: false}, 'test/test-main-integration.js': ['webpack']
{pattern: 'test/**/*.*', included: false} },
], webpack: {
exclude: [ module: {
], preLoaders: [
plugins: [ {
'karma-chrome-launcher', test: /\.js$/,
'karma-mocha', exclude: [
'karma-requirejs', /node_modules/
'karma-chai', ],
'karma-sinon', loader: 'babel'
'karma-sinon-chai' }
], ]
reporters: ['progress'], },
port: 9876, resolve: {
colors: true, extensions: ['', '.js', '.jsx'],
logLevel: config.LOG_INFO, alias: {
autoWatch: true, 'input_stream$': path.resolve(__dirname, 'src/input_stream'),
browsers: ['Chrome'], 'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber')
singleRun: false }
}); }
},
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) { module.exports = function(config) {
config.set({ config.set({
basePath: '', basePath: '',
frameworks: ['mocha', 'requirejs', 'chai', 'sinon', 'sinon-chai'], frameworks: ['source-map-support', 'mocha', 'chai', 'sinon', 'sinon-chai'],
files: [ files: [
'test-main.js', 'test/test-main.js',
'src/typedefs.js', {pattern: 'test/spec/**/*.js', included: false}
{pattern: 'node_modules/async/lib/async.js', included: false}, ],
{pattern: 'node_modules/gl-matrix/dist/gl-matrix-min.js', included: false}, preprocessors: {
{pattern: 'src/*.js', included: false}, 'test/test-main.js': ['webpack']
{pattern: 'spec/**/*.js', included: false}, },
{pattern: 'test/**/*.*', included: false} webpack: {
], module: {
exclude: [ preLoaders: [
'spec/**/*integration.spec.js' {
], test: /\.js$/,
preprocessors: { exclude: [
'src/*.js': ['coverage'] /node_modules/
}, ],
plugins: [ loader: 'babel'
'karma-chrome-launcher', },
'karma-coverage', {
'karma-mocha', test: /\.js$/,
'karma-requirejs', include: [
'karma-chai', path.resolve('src')
'karma-sinon', ],
'karma-sinon-chai', exclude: /node_modules/,
'karma-phantomjs-launcher' loader: 'isparta'
], }
reporters: ['progress', 'coverage'], ]
port: 9876, },
colors: true, resolve: {
logLevel: config.LOG_INFO, extensions: ['', '.js', '.jsx'],
autoWatch: true, alias: {
browsers: ['Chrome'], 'input_stream$': path.resolve(__dirname, 'src/input_stream'),
singleRun: false, 'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber')
coverageReporter: { }
type : 'html', },
dir : 'coverage/' },
} 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 */ const CVUtils = require('../src/cv_utils'),
/* global define */ Ndarray = require("ndarray"),
Interp2D = require("ndarray-linear-interpolate").d2;
define(["cv_utils"], function(CVUtils) { var FrameGrabber = {};
"use strict";
var Ndarray = require("ndarray"), FrameGrabber.create = function(inputStream) {
Interp2D = require("ndarray-linear-interpolate").d2; 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 = {}, * Uses the given array as frame-buffer
_streamConfig = inputStream.getConfig(), */
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), _that.attachData = function(data) {
_canvasSize = inputStream.getCanvasSize(), _data = data;
_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;
}; };
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 */ const GetPixels = require("get-pixels");
/* global define */
define(function() { var InputStream = {};
"use strict";
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() { function loadImages() {
var that = {}; loaded = false;
var _config = null; 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, _canvasSize.x = calculatedWidth;
height = 0, _canvasSize.y = calculatedHeight;
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};
function loadImages() { setTimeout(function() {
loaded = false; publishEvent("canrecord", []);
GetPixels(baseUrl, function(err, pixels) { }, 0);
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;
_canvasSize.x = calculatedWidth; function publishEvent(eventName, args) {
_canvasSize.y = calculatedHeight; var j,
handlers = _eventHandlers[eventName];
setTimeout(function() { if (handlers && handlers.length > 0) {
publishEvent("canrecord", []); for ( j = 0; j < handlers.length; j++) {
}, 0); handlers[j].apply(that, args);
});
}
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.trigger = publishEvent;
that.getWidth = function() { that.getWidth = function() {
return calculatedWidth; 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;
}; };
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", "name": "quagga",
"version": "0.7.0", "version": "0.8.0",
"description": "An advanced barcode-scanner written in JavaScript", "description": "An advanced barcode-scanner written in JavaScript",
"main": "lib/quagga.js", "main": "lib/quagga.js",
"browser": "dist/quagga.js", "browser": "dist/quagga.min.js",
"devDependencies": { "devDependencies": {
"async": "^1.4.2", "async": "^1.4.2",
"babel-core": "^5.8.25",
"babel-eslint": "^4.1.3",
"babel-loader": "^5.3.2",
"chai": "^3.2.0", "chai": "^3.2.0",
"core-js": "^1.2.1",
"grunt": "^0.4.5", "grunt": "^0.4.5",
"grunt-contrib-jshint": "^0.11.3", "grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-nodeunit": "^0.4.1", "grunt-contrib-nodeunit": "^0.4.1",
"grunt-contrib-uglify": "^0.9.2", "grunt-contrib-uglify": "^0.9.2",
"grunt-karma": "^0.12.1", "grunt-karma": "^0.12.1",
"grunt-requirejs": "^0.4.2", "grunt-requirejs": "^0.4.2",
"isparta-loader": "^1.0.0",
"karma": "^0.13.9", "karma": "^0.13.9",
"karma-chai": "0.1.0", "karma-chai": "0.1.0",
"karma-chrome-launcher": "^0.2.0", "karma-chrome-launcher": "^0.2.0",
@@ -22,14 +27,20 @@
"karma-requirejs": "~0.2.2", "karma-requirejs": "~0.2.2",
"karma-sinon": "^1.0.4", "karma-sinon": "^1.0.4",
"karma-sinon-chai": "~0.2.0", "karma-sinon-chai": "~0.2.0",
"karma-source-map-support": "^1.1.0",
"karma-webpack": "^1.7.0",
"mocha": "^2.3.2", "mocha": "^2.3.2",
"sinon": "^1.16.1" "sinon": "^1.16.1",
"webpack": "^1.12.2"
}, },
"directories": { "directories": {
"doc": "doc" "doc": "doc"
}, },
"scripts": { "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": { "repository": {
"type": "git", "type": "git",
@@ -56,8 +67,8 @@
"dependencies": { "dependencies": {
"get-pixels": "^3.2.3", "get-pixels": "^3.2.3",
"gl-matrix": "^2.3.1", "gl-matrix": "^2.3.1",
"lodash": "^3.10.1",
"ndarray": "^1.0.18", "ndarray": "^1.0.18",
"ndarray-linear-interpolate": "^1.0.0", "ndarray-linear-interpolate": "^1.0.0"
"requirejs": "^2.1.20"
} }
} }
+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 */ export default {
/* global define */ init: function(arr, val) {
var l = arr.length;
define(function() { while (l--) {
"use strict"; arr[l] = val;
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;
} }
}; },
});
/**
* 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 */ import Bresenham from './bresenham';
/* global define */ 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([ const READERS = {
"bresenham", code_128_reader: Code128Reader,
"image_debug", ean_reader: EANReader,
'code_128_reader', ean_8_reader: EAN8Reader,
'ean_reader', code_39_reader: Code39Reader,
'code_39_reader', code_39_vin_reader: Code39VINReader,
'code_39_vin_reader', codabar_reader: CodabarReader,
'codabar_reader', upc_reader: UPCReader,
'upc_reader', upc_e_reader: UPCEReader,
'ean_8_reader', i2of5_reader: I2of5Reader
'upc_e_reader', };
'i2of5_reader' export default {
], function( create: function(config, inputImageWrapper) {
Bresenham, var _canvas = {
ImageDebug, ctx: {
Code128Reader, frequency: null,
EANReader, pattern: null,
Code39Reader, overlay: null
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
}
}, },
_barcodeReaders = []; dom: {
frequency: null,
pattern: null,
overlay: null
}
},
_barcodeReaders = [];
initCanvas(); initCanvas();
initReaders(); initReaders();
initConfig(); initConfig();
function initCanvas() { function initCanvas() {
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
var $debug = document.querySelector("#debug.detection"); var $debug = document.querySelector("#debug.detection");
_canvas.dom.frequency = document.querySelector("canvas.frequency"); _canvas.dom.frequency = document.querySelector("canvas.frequency");
if (!_canvas.dom.frequency) { if (!_canvas.dom.frequency) {
_canvas.dom.frequency = document.createElement("canvas"); _canvas.dom.frequency = document.createElement("canvas");
_canvas.dom.frequency.className = "frequency"; _canvas.dom.frequency.className = "frequency";
if($debug) { if ($debug) {
$debug.appendChild(_canvas.dom.frequency); $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"); _canvas.dom.pattern = document.querySelector("canvas.patternBuffer");
if (!_canvas.dom.pattern) { if (!_canvas.dom.pattern) {
_canvas.dom.pattern = document.createElement("canvas"); _canvas.dom.pattern = document.createElement("canvas");
_canvas.dom.pattern.className = "patternBuffer"; _canvas.dom.pattern.className = "patternBuffer";
if($debug) { if ($debug) {
$debug.appendChild(_canvas.dom.pattern); $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"); _canvas.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (_canvas.dom.overlay) { if (_canvas.dom.overlay) {
_canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d"); _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) { * extend the line on both ends
var reader, * @param {Array} line
config = {}; * @param {Number} angle
*/
if (typeof readerConfig === 'object') { function getExtendedLine(line, angle, ext) {
reader = readerConfig.format; function extendLine(amount) {
config = readerConfig.config; var extension = {
} else if (typeof readerConfig === 'string') { y: amount * Math.sin(angle),
reader = readerConfig; x: amount * Math.cos(angle)
}
_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
}; };
line[0].y -= extension.y;
line[0].x -= extension.x;
line[1].y += extension.y;
line[1].x += extension.x;
} }
/** // check if inside image
* This method slices the given area apart and tries to detect a barcode-pattern extendLine(ext);
* for each slice. It returns the decoded barcode, or null if nothing was found while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0)
* @param {Array} box || !inputImageWrapper.inImageWithBorder(line[1], 0))) {
* @param {Array} line ext -= Math.ceil(ext / 2);
* @param {Number} lineAngle extendLine(-ext);
*/ }
function tryDecodeBruteForce(box, line, lineAngle) { return line;
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);
for ( i = 1; i < slices && result === null; i++) { function getLine(box) {
// move line perpendicular to angle return [{
dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1); x: (box[1][0] - box[0][0]) / 2 + box[0][0],
extension = { y: (box[1][1] - box[0][1]) / 2 + box[0][1]
y : dir * xdir, }, {
x : dir * ydir x: (box[3][0] - box[2][0]) / 2 + box[2][0],
}; y: (box[3][1] - box[2][1]) / 2 + box[2][1]
line[0].y += extension.x; }];
line[0].x -= extension.y; }
line[1].y += extension.x;
line[1].x -= extension.y;
result = tryDecode(line); function tryDecode(line) {
} var result = null,
return result; 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) { for ( i = 0; i < _barcodeReaders.length && result === null; i++) {
return Math.sqrt( result = _barcodeReaders[i].decodePattern(barcodeLine.line);
Math.pow(Math.abs(line[1].y - line[0].y), 2) +
Math.pow(Math.abs(line[1].x - line[0].x), 2));
} }
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 * This method slices the given area apart and tries to detect a barcode-pattern
* valid barcode pattern within the given area. * for each slice. It returns the decoded barcode, or null if nothing was found
* @param {Object} box The area to search in * @param {Array} box
* @returns {Object} the result {codeResult, line, angle, pattern, threshold} * @param {Array} line
*/ * @param {Number} lineAngle
function decodeFromBoundingBox(box) { */
var line, function tryDecodeBruteForce(box, line, lineAngle) {
lineAngle, var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)),
ctx = _canvas.ctx.overlay, i,
result, slices = 16,
lineLength; result = null,
dir,
extension,
xdir = Math.sin(lineAngle),
ydir = Math.cos(lineAngle);
if (config.drawBoundingBox && ctx) { for ( i = 1; i < slices && result === null; i++) {
ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2}); // move line perpendicular to angle
} dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1);
extension = {
line = getLine(box); y: dir * xdir,
lineLength = getLineLength(line); x: dir * ydir
lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); };
line = getExtendedLine(line, lineAngle, Math.floor(lineLength*0.1)); line[0].y += extension.x;
if(line === null){ line[0].x -= extension.y;
return null; line[1].y += extension.x;
} line[1].x -= extension.y;
result = tryDecode(line); result = tryDecode(line);
if(result === null) { }
result = tryDecodeBruteForce(box, line, lineAngle); return result;
} }
if(result === null) { function getLineLength(line) {
return null; 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 { if (config.drawBoundingBox && ctx) {
codeResult : result.codeResult, ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2});
line : line, }
angle : lineAngle,
pattern : result.barcodeLine.line, line = getLine(box);
threshold : result.barcodeLine.threshold 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 { return {
decodeFromBoundingBox : function(box) { codeResult: result.codeResult,
return decodeFromBoundingBox(box); line: line,
}, angle: lineAngle,
decodeFromBoundingBoxes : function(boxes) { pattern: result.barcodeLine.line,
var i, result; threshold: result.barcodeLine.threshold
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();
}
}; };
} }
};
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 */ function BarcodeReader(config) {
/* global define */ this._row = [];
this.config = config || {};
return this;
}
define( BarcodeReader.prototype._nextUnset = function(line, start) {
function() { var i;
"use strict";
if (start === undefined) {
function BarcodeReader(config) { start = 0;
this._row = []; }
this.config = config || {}; for (i = start; i < line.length; i++) {
return this; if (!line[i]) {
return i;
} }
}
BarcodeReader.prototype._nextUnset = function(line, start) { return line.length;
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;
};
BarcodeReader.prototype._nextSet = function(line, offset) { BarcodeReader.prototype._matchPattern = function(counter, code) {
var i; var i,
error = 0,
singleError = 0,
modulo = this.MODULO,
maxSingleError = this.SINGLE_CODE_ERROR || 1;
offset = offset || 0; for (i = 0; i < counter.length; i++) {
for (i = offset; i < line.length; i++) { singleError = Math.abs(code[i] - counter[i]);
if (line[i]) { if (singleError > maxSingleError) {
return i; return Number.MAX_VALUE;
} }
} error += singleError;
return line.length; }
}; return error / modulo;
};
BarcodeReader.prototype._normalize = function(counter, modulo) { BarcodeReader.prototype._nextSet = function(line, offset) {
var i, var i;
self = this,
sum = 0, offset = offset || 0;
ratio, for (i = offset; i < line.length; i++) {
numOnes = 0, if (line[i]) {
normalized = [], return i;
norm = 0; }
}
if (!modulo) { return line.length;
modulo = self.MODULO; };
}
for (i = 0; i < counter.length; i++) { BarcodeReader.prototype._normalize = function(counter, modulo) {
if (counter[i] === 1) { var i,
numOnes++; self = this,
} else { sum = 0,
sum += counter[i]; ratio,
} numOnes = 0,
} normalized = [],
ratio = sum / (modulo - numOnes); norm = 0;
if (ratio > 1.0) {
for (i = 0; i < counter.length; i++) { if (!modulo) {
norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; modulo = self.MODULO;
normalized.push(norm); }
} 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 { } else {
ratio = (sum + numOnes)/modulo; if (counterPos === counter.length - 1) {
for (i = 0; i < counter.length; i++) { error = self._matchPattern(counter, cmpCounter);
norm = counter[i] / ratio;
normalized.push(norm);
}
}
return normalized;
};
BarcodeReader.prototype._matchTrace = function(cmpCounter, epsilon) { if (error < epsilon) {
var counter = [], bestMatch.start = i - offset;
i, bestMatch.end = i;
self = this, bestMatch.counter = counter;
offset = self._nextSet(self._row), return bestMatch;
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 { } else {
if (counterPos === counter.length - 1) { return null;
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;
} }
}
} 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 { } else {
counterPos++; counterPos++;
counters[counterPos] = 1;
isWhite = !isWhite;
} }
counter[counterPos] = 1;
isWhite = !isWhite;
} }
return counters; }
}; } else {
counter.push(0);
Object.defineProperty(BarcodeReader.prototype, "FORMAT", { for ( i = offset; i < self._row.length; i++) {
value: 'unknown', if (self._row[i] ^ isWhite) {
writeable: false counter[counterPos]++;
}); } else {
counterPos++;
BarcodeReader.DIRECTION = { counter.push(0);
FORWARD : 1, counter[counterPos] = 1;
REVERSE : -1 isWhite = !isWhite;
}; }
}
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);
} }
);
// 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 */ import CVUtils from './cv_utils';
/* global define */ import ImageWrapper from './image_wrapper';
define(["cv_utils", "image_wrapper"], function(CVUtils, ImageWrapper) { var Bresenham = {};
"use strict";
var Bresenham = {};
var Slope = { var Slope = {
DIR : { DIR: {
UP : 1, UP: 1,
DOWN : -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) { Bresenham.toOtsuBinaryLine = function(result) {
val = imageData[b * width + a]; var line = result.line,
sum += val; image = new ImageWrapper({x: line.length - 1, y: 1}, line),
min = val < min ? val : min; threshold = CVUtils.determineOtsuThreshold(image, 5);
max = val > max ? val : max;
line.push(val);
}
if (steep) { line = CVUtils.sharpenLine(line);
tmp = x0; CVUtils.thresholdImage(image, threshold);
x0 = y0;
y0 = tmp;
tmp = x1; return {
x1 = y1; line: line,
y1 = tmp; threshold: threshold
}
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
};
}; };
};
Bresenham.toOtsuBinaryLine = function(result) { /**
var line = result.line, * Converts the result from getBarcodeLine into a binary representation
image = new ImageWrapper({x: line.length - 1, y: 1}, line), * also considering the frequency and slope of the signal for more robust results
threshold = CVUtils.determineOtsuThreshold(image, 5); * @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); // 1. find extrema
CVUtils.thresholdImage(image, threshold); 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 { if (currentDir !== dir) {
line: line, extrema.push({
threshold: threshold 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, * Used for development only
line = result.line, */
slope, Bresenham.debug = {
slope2, printFrequency: function(line, canvas) {
center = min + (max - min) / 2, var i,
extrema = [], ctx = canvas.getContext("2d");
currentDir, canvas.width = line.length;
dir, canvas.height = 256;
threshold = (max - min) / 12,
rThreshold = -threshold,
i,
j;
// 1. find extrema ctx.beginPath();
currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN; ctx.strokeStyle = "blue";
extrema.push({ for ( i = 0; i < line.length; i++) {
pos : 0, ctx.moveTo(i, 255);
val : line[0] ctx.lineTo(i, 255 - line[i]);
}); }
for ( i = 0; i < line.length - 2; i++) { ctx.stroke();
slope = (line[i + 1] - line[i]); ctx.closePath();
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;
}
if (currentDir !== dir) { printPattern: function(line, canvas) {
extrema.push({ var ctx = canvas.getContext("2d"), i;
pos : i,
val : line[i] canvas.width = line.length;
}); ctx.fillColor = "black";
currentDir = dir; 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++) { export default Bresenham;
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);
});
+124 -128
View File
@@ -1,143 +1,139 @@
/* jshint undef: true, unused: true, browser:true, devel: true */ const merge = require('lodash/object/merge');
/* global define, MediaStreamTrack */
define(["html_utils"], function(HtmlUtils) { var streamRef,
"use strict"; loadedDataHandler;
var streamRef,
loadedDataHandler;
/** /**
* Wraps browser-specific getUserMedia * Wraps browser-specific getUserMedia
* @param {Object} constraints * @param {Object} constraints
* @param {Object} success Callback * @param {Object} success Callback
* @param {Object} failure Callback * @param {Object} failure Callback
*/ */
function getUserMedia(constraints, success, failure) { function getUserMedia(constraints, success, failure) {
if (typeof navigator.getUserMedia !== 'undefined') { if (typeof navigator.getUserMedia !== 'undefined') {
navigator.getUserMedia(constraints, function (stream) { navigator.getUserMedia(constraints, function (stream) {
streamRef = stream; streamRef = stream;
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream; var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
success.apply(null, [videoSrc]); success.apply(null, [videoSrc]);
}, failure); }, failure);
} else { } else {
failure(new TypeError("getUserMedia not available")); failure(new TypeError("getUserMedia not available"));
}
} }
}
function loadedData(video, callback) { function loadedData(video, callback) {
var attempts = 10; var attempts = 10;
function checkVideo() { function checkVideo() {
if (attempts > 0) { if (attempts > 0) {
if (video.videoWidth > 0 && video.videoHeight > 0) { if (video.videoWidth > 0 && video.videoHeight > 0) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px"); console.log(video.videoWidth + "px x " + video.videoHeight + "px");
callback(); callback();
} else {
window.setTimeout(checkVideo, 500);
}
} else { } 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 { } 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 = { constraints.video = {
mediaSource: "camera", mandatory: {
width: { min: videoConstraints.width, max: videoConstraints.width }, minWidth: videoConstraints.width,
height: { min: videoConstraints.height, max: videoConstraints.height }, minHeight: videoConstraints.height,
require: ["width", "height"] minAspectRatio: videoConstraints.minAspectRatio,
maxAspectRatio: videoConstraints.maxAspectRatio
},
optional: [{
sourceId: videoSourceId
}]
}; };
return cb(constraints); 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) { * Requests the back-facing camera of the user. The callback is called
request(video, constraints, callback); * whenever the stream is ready to be consumed, or if an error occures.
}, * @param {Object} video
release : function() { * @param {Object} callback
var tracks = streamRef && streamRef.getVideoTracks(); */
if (tracks.length) { function request(video, videoConstraints, callback) {
tracks[0].stop(); normalizeConstraints(videoConstraints, function(constraints) {
} initCamera(constraints, video, callback);
streamRef = null; });
}
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 */ import {vec2} from 'gl-matrix';
/* global define */
define(["gl-matrix"], function(glMatrix) {
"use strict";
var vec2 = glMatrix.vec2;
/** /**
* Creates a cluster for grouping similar orientations of datapoints * Creates a cluster for grouping similar orientations of datapoints
*/ */
var Cluster = { export default {
create : function(point, threshold) { create: function(point, threshold) {
var points = [], center = { var points = [],
rad : 0, center = {
vec : vec2.clone([0, 0]) rad: 0,
}, pointMap = {}; vec: vec2.clone([0, 0])
},
pointMap = {};
function init() { function init() {
add(point); add(point);
updateCenter(); 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
};
} }
};
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 */ import BarcodeReader from './barcode_reader';
/* global define */
define( function CodabarReader() {
[ BarcodeReader.call(this);
"./barcode_reader" this._counters = [];
], }
function(BarcodeReader) {
"use strict";
function CodabarReader() { var properties = {
BarcodeReader.call(this); ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"},
this._counters = []; 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 = { CodabarReader.prototype = Object.create(BarcodeReader.prototype, properties);
ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"}, CodabarReader.prototype.constructor = CodabarReader;
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._decode = function() {
CodabarReader.prototype.constructor = CodabarReader; var self = this,
result = [],
start,
decodedChar,
pattern,
nextStart,
end;
CodabarReader.prototype._decode = function() { this._counters = self._fillCounters();
var self = this, start = self._findStart();
result = [], if (!start) {
start, return null;
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);
} }
); 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 */ import BarcodeReader from './barcode_reader';
/* global define */
define( function Code128Reader() {
[ BarcodeReader.call(this);
"./barcode_reader" }
],
function(BarcodeReader) { var properties = {
"use strict"; CODE_SHIFT: {value: 98},
CODE_C: {value: 99},
function Code128Reader() { CODE_B: {value: 100},
BarcodeReader.call(this); 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 = { return null;
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++) { Code128Reader.prototype._findStart = function() {
if (self._row[i] ^ isWhite) { var counter = [0, 0, 0, 0, 0, 0],
counter[counterPos]++; i,
} else { self = this,
if (counterPos === counter.length - 1) { offset = self._nextSet(self._row),
normalized = self._normalize(counter); isWhite = false,
if (normalized) { counterPos = 0,
for (code = 0; code < self.CODE_PATTERN.length; code++) { bestMatch = {
error = self._matchPattern(normalized, self.CODE_PATTERN[code]); error: Number.MAX_VALUE,
if (error < bestMatch.error) { code: -1,
bestMatch.code = code; start: 0,
bestMatch.error = error; end: 0
} },
} code,
bestMatch.end = i; error,
return bestMatch; j,
} sum,
} else { normalized;
counterPos++;
} for ( i = offset; i < self._row.length; i++) {
counter[counterPos] = 1; if (self._row[i] ^ isWhite) {
isWhite = !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);
return null; if (normalized) {
}; for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) {
error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
Code128Reader.prototype._findStart = function() { if (error < bestMatch.error) {
var counter = [0, 0, 0, 0, 0, 0], bestMatch.code = code;
i, bestMatch.error = error;
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;
}
}
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; if (bestMatch.error < self.AVG_CODE_ERROR) {
isWhite = !isWhite; bestMatch.start = i - sum;
bestMatch.end = i;
return bestMatch;
}
} }
}
return null;
};
Code128Reader.prototype._decode = function() { for ( j = 0; j < 4; j++) {
var self = this, counter[j] = counter[j + 2];
startInfo = self._findStart(), }
code = null, counter[4] = 0;
done = false, counter[5] = 0;
result = [], counterPos--;
multiplier = 0, } else {
checksum = 0, counterPos++;
codeset, }
rawResult = [], counter[counterPos] = 1;
decodedCodes = [], isWhite = !isWhite;
shiftNext = false, }
unshift, }
lastCharacterWasPrintable; return null;
};
if (startInfo === null) {
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); 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) { switch (codeset) {
unshift = shiftNext; case self.CODE_A:
shiftNext = false; if (code.code < 64) {
code = self._decodeCode(code.end); result.push(String.fromCharCode(32 + code.code));
if (code !== null) { } else if (code.code < 96) {
if (code.code !== self.STOP_CODE) { result.push(String.fromCharCode(code.code - 64));
rawResult.push(code.code); } else {
multiplier++; switch (code.code) {
checksum += multiplier * code.code; case self.CODE_SHIFT:
} shiftNext = true;
decodedCodes.push(code); codeset = self.CODE_B;
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;
}
}
break; break;
case self.CODE_B: case self.CODE_B:
if (code.code < 96) { codeset = self.CODE_B;
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;
}
}
break; break;
case self.CODE_C: case self.CODE_C:
if (code.code < 100) { codeset = self.CODE_C;
result.push(code.code < 10 ? "0" + code.code : code.code); break;
} case self.STOP_CODE:
switch (code.code) { done = true;
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;
}
break; break;
} }
}
break;
case self.CODE_B:
if (code.code < 96) {
result.push(String.fromCharCode(32 + code.code));
} else { } 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; done = true;
break;
} }
if (unshift) { break;
codeset = codeset == self.CODE_A ? self.CODE_B : self.CODE_A;
}
} }
} else {
if (code === null) { done = true;
return null; }
} if (unshift) {
codeset = codeset === self.CODE_A ? self.CODE_B : self.CODE_A;
// 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);
} }
);
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 */ import BarcodeReader from './barcode_reader';
/* global define */ import ArrayHelper from './array_helper';
define( function Code39Reader() {
[ BarcodeReader.call(this);
"./barcode_reader", }
"./array_helper"
],
function(BarcodeReader, ArrayHelper) {
"use strict";
function Code39Reader() { var properties = {
BarcodeReader.call(this); 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 = { if (numWideBars === 3) {
ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, for (i = 0; i < numCounters && numWideBars > 0; i++) {
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]}, if (counters[i] > maxNarrowWidth) {
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]}, numWideBars--;
ASTERISK: {value: 0x094}, if ((counters[i] * 2) >= wideBarWidth) {
FORMAT: {value: "code_39", writeable: false} return -1;
};
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 pattern;
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 -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 */ import Code39Reader from './code_39_reader';
/* global define */
define( function Code39VINReader() {
[ Code39Reader.call(this);
"./code_39_reader" }
],
function(Code39Reader) {
"use strict";
function Code39VINReader() { var patterns = {
Code39Reader.call(this); IOQ: /[IOQ]/g,
} AZ09: /[A-Z0-9]{17}/
};
var patterns = { Code39VINReader.prototype = Object.create(Code39Reader.prototype);
IOQ: /[IOQ]/g, Code39VINReader.prototype.constructor = Code39VINReader;
AZ09: /[A-Z0-9]{17}/
};
Code39VINReader.prototype = Object.create(Code39Reader.prototype); // Cribbed from:
Code39VINReader.prototype.constructor = Code39VINReader; // https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java
Code39VINReader.prototype._decode = function() {
// Cribbed from: var result = Code39Reader.prototype._decode.apply(this);
// https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java if (!result) {
Code39VINReader.prototype._decode = function() { return null;
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);
} }
);
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 @@
/** export default {
* The basic configuration inputStream: {
*/ name: "Live",
type: "LiveStream",
define(function(){ constraints: {
var config = { width: 640,
inputStream: { name: "Live", height: 480,
type: "LiveStream", minAspectRatio: 0,
constraints: { maxAspectRatio: 100,
width: 640, facing: "environment" // or user
height: 480, },
minAspectRatio: 0, area: {
maxAspectRatio: 100, top: "0%",
facing: "environment" // or user right: "0%",
}, left: "0%",
area: { bottom: "0%"
top: "0%", },
right: "0%", singleChannel: false // true: only the red color-channel is read
left: "0%", },
bottom: "0%" tracking: false,
}, debug: false,
singleChannel: false // true: only the red color-channel is read controls: false,
}, locate: true,
tracking: false, numOfWorkers: 4,
debug: false, visual: {
controls: false,
locate: true,
numOfWorkers: 4,
visual: {
show: true show: true
}, },
decoder:{ decoder: {
drawBoundingBox: false, drawBoundingBox: false,
showFrequency: false, showFrequency: false,
drawScanline: false, drawScanline: false,
showPattern: false, showPattern: false,
readers: [ readers: [
'code_128_reader' 'code_128_reader'
] ]
}, },
locator: { locator: {
halfSample: true, halfSample: true,
patchSize: "medium", // x-small, small, medium, large, x-large patchSize: "medium", // x-small, small, medium, large, x-large
showCanvas: false, showCanvas: false,
@@ -49,12 +45,9 @@ define(function(){
showPatchLabels: false, showPatchLabels: false,
showRemainingPatchLabels: false, showRemainingPatchLabels: false,
boxFromPatches: { boxFromPatches: {
showTransformed: false, showTransformed: false,
showTransformedBox: false, showTransformedBox: false,
showBB: 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 */ import EANReader from './ean_reader';
/* global define */
define( function EAN8Reader() {
[ EANReader.call(this);
"./ean_reader" }
],
function(EANReader) {
"use strict";
function EAN8Reader() { var properties = {
EANReader.call(this); 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);
var properties = { decodedCodes.push(code);
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);
} }
);
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 */ import BarcodeReader from './barcode_reader';
/* global define */
define( function EANReader(opts) {
[ BarcodeReader.call(this, opts);
"./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;
if (!coderange) { var properties = {
coderange = self.CODE_PATTERN.length; 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++) { EANReader.prototype = Object.create(BarcodeReader.prototype, properties);
if (self._row[i] ^ isWhite) { EANReader.prototype.constructor = EANReader;
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) { EANReader.prototype._decodeCode = function(start, coderange) {
var counter = [], var counter = [0, 0, 0, 0],
self = this, i,
i, self = this,
counterPos = 0, offset = start,
bestMatch = { isWhite = !self._row[offset],
error : Number.MAX_VALUE, counterPos = 0,
code : -1, bestMatch = {
start : 0, error: Number.MAX_VALUE,
end : 0 code: -1,
}, start: start,
error, end: start
j, },
sum, code,
normalized; error,
normalized;
if (!offset) { if (!coderange) {
offset = self._nextSet(self._row); coderange = self.CODE_PATTERN.length;
}
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);
} }
);
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 */ export default (function() {
/* global define */ var events = {};
define(function() { function getEvent(eventName) {
"use strict"; if (!events[eventName]) {
events[eventName] = {
var _events = function() { subscribers: []
var events = {}; };
function getEvent(eventName) {
if (!events[eventName]) {
events[eventName] = {
subscribers : []
};
}
return events[eventName];
}
function clearEvents(){
events = {};
} }
return events[eventName];
}
function publishSubscription(subscription, data) { function clearEvents(){
if (subscription.async) { events = {};
setTimeout(function() { }
subscription.callback(data);
}, 4); function publishSubscription(subscription, data) {
} else { if (subscription.async) {
setTimeout(function() {
subscription.callback(data); 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") { getEvent(event).subscribers.push(subscription);
subscription = { }
callback : callback,
async : async
};
} else {
subscription = callback;
if (!subscription.callback) {
throw "Callback was not specified on options";
}
}
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 { event.subscribers = subscribers.filter(function(subscriber) {
subscribe : function(event, callback, async) { publishSubscription(subscriber, data);
return subscribe(event, callback, async); return !subscriber.once;
}, });
publish : function(eventName, data) { },
var event = getEvent(eventName), once: function(event, callback, async) {
subscribers = event.subscribers; subscribe(event, {
callback: callback,
event.subscribers = subscribers.filter(function(subscriber) { async: async,
publishSubscription(subscriber, data); once: true
return !subscriber.once; });
}); },
}, unsubscribe: function(eventName, callback) {
once: function(event, callback, async) { var event;
subscribe(event, {
callback: callback, if (eventName) {
async: async, event = getEvent(eventName);
once: true if (event && callback) {
}); event.subscribers = event.subscribers.filter(function(subscriber){
}, return subscriber.callback !== callback;
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 = [];
}
} else { } 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 */ import CVUtils from './cv_utils';
/* global define */
define(["cv_utils"], function(CVUtils) { var FrameGrabber = {};
"use strict";
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) { _canvas = canvas ? canvas : document.createElement("canvas");
var _that = {}, _canvas.width = _canvasSize.x;
_streamConfig = inputStream.getConfig(), _canvas.height = _canvasSize.y;
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), _ctx = _canvas.getContext("2d");
_canvasSize = inputStream.getCanvasSize(), _data = new Uint8Array(_size.x * _size.y);
_size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), console.log("FrameGrabber", JSON.stringify({
topRight = inputStream.getTopRight(), size: _size,
_sx = topRight.x, topRight: topRight,
_sy = topRight.y, videoSize: _video_size,
_canvas, canvasSize: _canvasSize
_ctx = null, }));
_data = null;
_canvas = canvas ? canvas : document.createElement("canvas"); /**
_canvas.width = _canvasSize.x; * Uses the given array as frame-buffer
_canvas.height = _canvasSize.y; */
_ctx = _canvas.getContext("2d"); _that.attachData = function(data) {
_data = new Uint8Array(_size.x * _size.y); _data = data;
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;
}; };
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 */ import BarcodeReader from './barcode_reader';
/* global define */ const merge = require('lodash/object/merge');
define( function I2of5Reader(opts) {
[ opts = merge(getDefaulConfig(), opts);
"./barcode_reader", BarcodeReader.call(this, opts);
"./html_utils" this.barSpaceRatio = [1, 1];
], if (opts.normalizeBarSpaceWidth) {
function(BarcodeReader, HTMLUtils) { this.SINGLE_CODE_ERROR = 0.38;
"use strict"; this.AVG_CODE_ERROR = 0.09;
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 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 */ export default {
/* global define */ drawRect: function(pos, size, ctx, style){
ctx.strokeStyle = style.color;
define(function() { ctx.fillStyle = style.color;
"use strict"; ctx.lineWidth = 1;
ctx.beginPath();
return { ctx.strokeRect(pos.x, pos.y, size.x, size.y);
drawRect: function(pos, size, ctx, style){ },
ctx.strokeStyle = style.color; drawPath: function(path, def, ctx, style) {
ctx.fillStyle = style.color; ctx.strokeStyle = style.color;
ctx.lineWidth = 1; ctx.fillStyle = style.color;
ctx.beginPath(); ctx.lineWidth = style.lineWidth;
ctx.strokeRect(pos.x, pos.y, size.x, size.y); ctx.beginPath();
}, ctx.moveTo(path[0][def.x], path[0][def.y]);
drawPath: function(path, def, ctx, style) { for (var j = 1; j < path.length; j++) {
ctx.strokeStyle = style.color; ctx.lineTo(path[j][def.x], path[j][def.y]);
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;
} }
}; 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 */ var ImageLoader = {};
/* global define */ ImageLoader.load = function(directory, callback, offset, size, sequence) {
var htmlImagesSrcArray = new Array(size),
htmlImagesArray = new Array(htmlImagesSrcArray.length),
i,
img,
num;
define(function() { if (sequence === false) {
"use strict"; htmlImagesSrcArray[0] = directory;
} else {
var ImageLoader = {}; for ( i = 0; i < htmlImagesSrcArray.length; i++) {
ImageLoader.load = function(directory, callback, offset, size, sequence) { num = (offset + i);
var htmlImagesSrcArray = new Array(size), htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg";
htmlImagesArray = new Array(htmlImagesSrcArray.length), }
i, }
img, htmlImagesArray.notLoaded = [];
num; htmlImagesArray.addImage = function(image) {
htmlImagesArray.notLoaded.push(image);
if (sequence === false) { };
htmlImagesSrcArray[0] = directory; htmlImagesArray.loaded = function(loadedImg) {
} else { var notloadedImgs = htmlImagesArray.notLoaded;
for ( i = 0; i < htmlImagesSrcArray.length; i++) { for (var x = 0; x < notloadedImgs.length; x++) {
num = (offset + i); if (notloadedImgs[x] === loadedImg) {
htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg"; 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 = []; if (notloadedImgs.length === 0) {
htmlImagesArray.addImage = function(img) { console.log("Images loaded");
htmlImagesArray.notLoaded.push(img); callback.apply(null, [htmlImagesArray]);
};
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];
} }
}; };
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 */ import SubImage from './subImage';
/* global define */ import CVUtils from './cv_utils';
import ArrayHelper from './array_helper';
import {vec2} from 'gl-matrix';
define([ /**
"subImage", * Represents a basic image combining the data and size.
"cv_utils", * In addition, some methods for manipulation are contained.
"array_helper", * @param size {x,y} The size of the image in pixel
"gl-matrix" * @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)
function(SubImage, CVUtils, ArrayHelper, glMatrix) { * @param initialize {Boolean} Indicating if the array should be initialized on creation.
* @returns {ImageWrapper}
'use strict'; */
var vec2 = glMatrix.vec2, function ImageWrapper(size, data, ArrayType, initialize) {
mat2 = glMatrix.mat2; 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. * tests if a position is within the image with a given offset
* In addition, some methods for manipulation are contained. * @param imgRef {x, y} The location to test
* @param size {x,y} The size of the image in pixel * @param border Number the padding value in pixel
* @param data {Array} If given, a flat array containing the pixel data * @returns {Boolean} true if location inside the image's border, false otherwise
* @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed) * @see cvd/image.h
* @param initialize {Boolean} Indicating if the array should be initialized on creation. */
* @returns {ImageWrapper} ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) {
*/ return (imgRef.x >= border)
function ImageWrapper(size, data, ArrayType, initialize) { && (imgRef.y >= border)
if (!data) { && (imgRef.x < (this.size.x - border))
if (ArrayType) { && (imgRef.y < (this.size.y - border));
this.data = new ArrayType(size.x * size.y); };
if (ArrayType === Array && initialize) {
ArrayHelper.init(this.data, 0); /**
} * Performs bilinear sampling
} else { * @param inImg Image to extract sample from
this.data = new Uint8Array(size.x * size.y); * @param x the x-coordinate
if (Uint8Array === Array && initialize) { * @param y the y-coordinate
ArrayHelper.init(this.data, 0); * @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);
} }
} }
this.data[y * this.size.x + x] = accu;
} else {
this.data = data;
} }
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;
} }
/** for ( i = 0; i < labelcount; i++) {
* tests if a position is within the image with a given offset labelsum[i] = {
* @param imgRef {x, y} The location to test m00: 0,
* @param border Number the padding value in pixel m01: 0,
* @returns {Boolean} true if location inside the image's border, false otherwise m10: 0,
* @see cvd/image.h m11: 0,
*/ m02: 0,
ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) { m20: 0,
return (imgRef.x >= border) && (imgRef.y >= border) && (imgRef.x < (this.size.x - border)) && (imgRef.y < (this.size.y - border)); theta: 0,
}; rad: 0
};
}
/** for ( y = 0; y < height; y++) {
* Transforms an image according to the given affine-transformation matrix. ysq = y * y;
* @param inImg ImageWrapper a image containing the information to be extracted. for ( x = 0; x < width; x++) {
* @param outImg ImageWrapper the image to be filled. The whole image out image is filled by the in image. val = data[y * width + x];
* @param M mat2 the matrix used to map point in the out matrix to those in the in matrix if (val > 0) {
* @param inOrig vec2 origin in the in image label = labelsum[val - 1];
* @param outOrig vec2 origin in the out image label.m00 += 1;
* @returns Number the number of pixels not in the in image label.m01 += y;
* @see cvd/vision.h label.m10 += x;
*/ label.m11 += x * y;
ImageWrapper.transform = function(inImg, outImg, M, inOrig, outOrig) { label.m02 += ysq;
var w = outImg.size.x, h = outImg.size.y, iw = inImg.size.x, ih = inImg.size.y; label.m20 += x * x;
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];
} }
} }
}; }
ImageWrapper.prototype.copyTo = function(imageWrapper) { for ( i = 0; i < labelcount; i++) {
var length = this.data.length, srcData = this.data, dstData = imageWrapper.data; label = labelsum[i];
if (!isNaN(label.m00) && label.m00 !== 0) {
while (length--) { x_ = label.m10 / label.m00;
dstData[length] = srcData[length]; 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);
* Retrieves a given pixel position from the image tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4 ) + PI;
* @param x {Number} The x-position label.theta = (tmp * 180 / PI + 90) % 180 - 90;
* @param y {Number} The y-position if (label.theta < 0) {
* @returns {Number} The grayscale value at the pixel-position label.theta += 180;
*/
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;
} }
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]]; }
};
/** return result;
* 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 * Displays the {ImageWrapper} in a given canvas
*/ * @param canvas {Canvas} The canvas element to write to
ImageWrapper.prototype.zeroBorder = function() { * @param scale {Number} Scale which is applied to each pixel-value
var i, width = this.size.x, height = this.size.y, data = this.data; */
for ( i = 0; i < width; i++) { ImageWrapper.prototype.show = function(canvas, scale) {
data[i] = data[(height - 1) * width + i] = 0; 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 * Displays the {SubImage} in a given canvas
*/ * @param canvas {Canvas} The canvas element to write to
ImageWrapper.prototype.invert = function() { * @param scale {Number} Scale which is applied to each pixel-value
var data = this.data, length = data.length; */
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--) { export default ImageWrapper;
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);
});
+293 -293
View File
@@ -1,317 +1,317 @@
/* jshint undef: true, unused: true, browser:true, devel: true */ import ImageLoader from './image_loader';
/* global define */
define(["image_loader"], function(ImageLoader) { var InputStream = {};
"use strict"; 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 = {}; function initSize() {
InputStream.createVideoStream = function(video) { var width = video.videoWidth,
var that = {}, height = video.videoHeight;
_config = null,
_eventNames = ['canrecord', 'ended'],
_eventHandlers = {},
_calculatedWidth,
_calculatedHeight,
_topRight = {x: 0, y: 0},
_canvasSize = {x: 0, y: 0};
function initSize() { _calculatedWidth =
var width = video.videoWidth, _config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width;
height = video.videoHeight; _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; _canvasSize.x = _calculatedWidth;
_calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; _canvasSize.y = _calculatedHeight;
}
_canvasSize.x = _calculatedWidth; that.getRealWidth = function() {
_canvasSize.y = _calculatedHeight; 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() { that.addEventListener = function(event, f, bool) {
return video.videoWidth; if (_eventNames.indexOf(event) !== -1) {
}; if (!_eventHandlers[event]) {
_eventHandlers[event] = [];
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);
} }
}; _eventHandlers[event].push(f);
} else {
video.addEventListener(event, f, bool);
}
};
that.clearEventHandlers = function() { that.clearEventHandlers = function() {
_eventNames.forEach(function(eventName) { _eventNames.forEach(function(eventName) {
var handlers = _eventHandlers[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();
}
if (handlers && handlers.length > 0) { if (handlers && handlers.length > 0) {
for ( j = 0; j < handlers.length; j++) { handlers.forEach(function(handler) {
handlers[j].apply(that, args); 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) { that.trigger = function(eventName, args) {
video.setAttribute("autoplay", true); var j,
var that = InputStream.createVideoStream(video); handlers = _eventHandlers[eventName];
that.ended = function() { if (eventName === 'canrecord') {
return false; initSize();
}; }
if (handlers && handlers.length > 0) {
return that; for ( j = 0; j < handlers.length; j++) {
handlers[j].apply(that, args);
}
}
}; };
InputStream.createImageStream = function() { that.setTopRight = function(topRight) {
var that = {}; _topRight.x = topRight.x;
var _config = null; _topRight.y = topRight.y;
};
var width = 0, that.getTopRight = function() {
height = 0, return _topRight;
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() { that.setCanvasSize = function(size) {
loaded = false; _canvasSize.x = size.x;
ImageLoader.load(baseUrl, function(imgs) { _canvasSize.y = size.y;
imgArray = imgs; };
width = imgs[0].width;
height = imgs[0].height; that.getCanvasSize = function() {
calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; return _canvasSize;
calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; };
_canvasSize.x = calculatedWidth;
_canvasSize.y = calculatedHeight; that.getFrame = function() {
loaded = true; return video;
frameIdx = 0; };
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() { setTimeout(function() {
publishEvent("canrecord", []); ended = true;
publishEvent("ended", []);
}, 0); }, 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);
}
} }
} }
return frame;
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 (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 */ import TypeDefs from './typedefs'; // eslint-disable-line no-unused-vars
/* global define */ import ImageWrapper from './image_wrapper';
define([ import BarcodeLocator from './barcode_locator';
"input_stream", import BarcodeDecoder from './barcode_decoder';
"image_wrapper", import Config from './config';
"barcode_locator", import Events from './events';
"barcode_decoder", import CameraAccess from './camera_access';
"frame_grabber", import ImageDebug from './image_debug';
"html_utils", import {vec2} from 'gl-matrix';
"config", import ResultCollector from './result_collector';
"events",
"camera_access", const merge = require('lodash/object/merge');
"image_debug", const InputStream = require('input_stream');
"gl-matrix", const FrameGrabber = require('frame_grabber');
"result_collector"],
function(InputStream, var _inputStream,
ImageWrapper, _framegrabber,
BarcodeLocator, _stopped,
BarcodeDecoder, _canvasContainer = {
FrameGrabber, ctx: {
HtmlUtils, image: null,
_config, overlay: null
Events,
CameraAccess,
ImageDebug,
glMatrix,
ResultCollector) {
"use strict";
var _inputStream,
_framegrabber,
_stopped,
_canvasContainer = {
ctx : {
image : null,
overlay : null
},
dom : {
image : null,
overlay : null
}
}, },
_inputImageWrapper, dom: {
_boxSize, image: null,
_decoder, overlay: null
_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";
}
}
}
} }
} },
_inputImageWrapper,
_boxSize,
_decoder,
_workerPool = [],
_onUIThread = true,
_resultCollector,
_config = {};
function initInputStream(cb) { function initializeData(imageWrapper) {
var video; initBuffers(imageWrapper);
if (_config.inputStream.type == "VideoStream") { _decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper);
video = document.createElement("video"); }
_inputStream = InputStream.createVideoStream(video);
} else if (_config.inputStream.type == "ImageStream") { function initConfig() {
_inputStream = InputStream.createImageStream(); if (typeof document !== "undefined") {
} else if (_config.inputStream.type == "LiveStream") { var vis = [{
var $viewport = document.querySelector("#interactive.viewport"); node: document.querySelector("div[data-controls]"),
if ($viewport) { prop: _config.controls
video = $viewport.querySelector("video"); }, {
if (!video) { node: _canvasContainer.dom.overlay,
video = document.createElement("video"); prop: _config.visual.show
$viewport.appendChild(video); }];
}
} for (var i = 0; i < vis.length; i++) {
_inputStream = InputStream.createLiveStream(video); if (vis[i].node) {
CameraAccess.request(video, _config.inputStream.constraints, function(err) { if (vis[i].prop === true) {
if (!err) { vis[i].node.style.display = "block";
_inputStream.trigger("canrecord");
} else { } else {
return cb(err); vis[i].node.style.display = "none";
} }
}); }
} }
}
}
_inputStream.setAttribute("preload", "auto"); function initInputStream(cb) {
_inputStream.setAttribute("autoplay", true); var video;
_inputStream.setInputStream(_config.inputStream); if (_config.inputStream.type === "VideoStream") {
_inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); 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) { _inputStream.setAttribute("preload", "auto");
BarcodeLocator.checkImageConstraints(_inputStream, _config.locator); _inputStream.setAttribute("autoplay", true);
initCanvas(); _inputStream.setInputStream(_config.inputStream);
_framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image); _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb));
initConfig(); }
if (_config.numOfWorkers > 0) { function canRecord(cb) {
initWorkers(function() { BarcodeLocator.checkImageConstraints(_inputStream, _config.locator);
console.log("Workers created"); initCanvas();
ready(cb); _framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image);
}); initConfig();
} else {
initializeData(); if (_config.numOfWorkers > 0) {
initWorkers(function() {
console.log("Workers created");
ready(cb); ready(cb);
} });
} else {
initializeData();
ready(cb);
} }
}
function ready(cb){ function ready(cb){
_inputStream.play(); _inputStream.play();
cb(); cb();
} }
function initCanvas() { function initCanvas() {
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
var $viewport = document.querySelector("#interactive.viewport"); var $viewport = document.querySelector("#interactive.viewport");
_canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer");
if (!_canvasContainer.dom.image) { if (!_canvasContainer.dom.image) {
_canvasContainer.dom.image = document.createElement("canvas"); _canvasContainer.dom.image = document.createElement("canvas");
_canvasContainer.dom.image.className = "imgBuffer"; _canvasContainer.dom.image.className = "imgBuffer";
if ($viewport && _config.inputStream.type == "ImageStream") { if ($viewport && _config.inputStream.type === "ImageStream") {
$viewport.appendChild(_canvasContainer.dom.image); $viewport.appendChild(_canvasContainer.dom.image);
}
} }
_canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); }
_canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y;
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer"); _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (!_canvasContainer.dom.overlay) { if (!_canvasContainer.dom.overlay) {
_canvasContainer.dom.overlay = document.createElement("canvas"); _canvasContainer.dom.overlay = document.createElement("canvas");
_canvasContainer.dom.overlay.className = "drawingBuffer"; _canvasContainer.dom.overlay.className = "drawingBuffer";
if ($viewport) { if ($viewport) {
$viewport.appendChild(_canvasContainer.dom.overlay); $viewport.appendChild(_canvasContainer.dom.overlay);
}
var clearFix = document.createElement("br");
clearFix.setAttribute("clear", "all");
if ($viewport) {
$viewport.appendChild(clearFix);
}
} }
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); var clearFix = document.createElement("br");
_canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; clearFix.setAttribute("clear", "all");
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; 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) { function moveBox(box) {
if (imageWrapper) { var corner = box.length;
_inputImageWrapper = imageWrapper;
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 { } else {
_inputImageWrapper = new ImageWrapper({ _framegrabber.attachData(_inputImageWrapper.data);
x : _inputStream.getWidth(),
y : _inputStream.getHeight()
});
} }
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); function start() {
_boxSize = [ _stopped = false;
vec2.clone([0, 0]), ( function frame() {
vec2.clone([0, _inputImageWrapper.size.y]), if (!_stopped) {
vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]), update();
vec2.clone([_inputImageWrapper.size.x, 0]) if (_onUIThread && _config.inputStream.type === "LiveStream") {
]; window.requestAnimFrame(frame);
BarcodeLocator.init(_inputImageWrapper, _config.locator); }
}
}());
}
function initWorkers(cb) {
var i;
_workerPool = [];
for (i = 0; i < _config.numOfWorkers; i++) {
initWorker(workerInitialized);
} }
function getBoundingBoxes() { function workerInitialized(workerThread) {
if (_config.locate) { _workerPool.push(workerThread);
return BarcodeLocator.locate(); if (_workerPool.length >= _config.numOfWorkers){
} else { cb();
return [[
vec2.clone(_boxSize[0]),
vec2.clone(_boxSize[1]),
vec2.clone(_boxSize[2]),
vec2.clone(_boxSize[3])]];
} }
} }
}
function transformResult(result) { function initWorker(cb) {
var topRight = _inputStream.getTopRight(), var blobURL,
xOffset = topRight.x, workerThread = {
yOffset = topRight.y, worker: undefined,
i; 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; 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) { self.onmessage = function(e) {
if (_onUIThread) { if (e.data.cmd === 'init') {
transformResult(result); var config = e.data.config;
if (imageData && result && result.codeResult) { config.numOfWorkers = 0;
if (_resultCollector) { imageWrapper = new Quagga.ImageWrapper({
_resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult); 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 */ import Tracer from './tracer';
/* global define */
/** /**
* http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization
*/ */
define(["tracer"], function(Tracer) { var Rasterizer = {
"use strict"; 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 = { return {
createContour2D : function() { rasterize: function(depthlabel) {
return { var color,
dir : null, bc,
index : null, lc,
firstVertex : null, labelindex,
insideContours : null, cx,
nextpeer : null, cy,
prevpeer : null colorMap = [],
}; vertex,
}, p,
CONTOUR_DIR : { cc,
CW_DIR : 0, sc,
CCW_DIR : 1, pos,
UNKNOWN_DIR : 2 connectedCount = 0,
}, i;
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 { for ( i = 0; i < 400; i++) {
rasterize : function(depthlabel) { colorMap[i] = 0;
var color, }
bc,
lc,
labelindex,
cx,
cy,
colorMap = [],
vertex,
p,
cc,
sc,
pos,
connectedCount = 0,
i;
for ( i = 0; i < 400; i++) { colorMap[0] = imageData[0];
colorMap[i] = 0; cc = null;
} for ( cy = 1; cy < height - 1; cy++) {
labelindex = 0;
colorMap[0] = imageData[0]; bc = colorMap[0];
cc = null; for ( cx = 1; cx < width - 1; cx++) {
for ( cy = 1; cy < height - 1; cy++) { pos = cy * width + cx;
labelindex = 0; if (labelData[pos] === 0) {
bc = colorMap[0]; color = imageData[pos];
for ( cx = 1; cx < width - 1; cx++) { if (color !== bc) {
pos = cy * width + cx; if (labelindex === 0) {
if (labelData[pos] === 0) { lc = connectedCount + 1;
color = imageData[pos]; colorMap[lc] = color;
if (color !== bc) { bc = color;
if (labelindex === 0) { vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE);
lc = connectedCount + 1; if (vertex !== null) {
colorMap[lc] = color; connectedCount++;
bc = color; labelindex = lc;
vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE); p = Rasterizer.createContour2D();
if (vertex !== null) { p.dir = Rasterizer.CONTOUR_DIR.CW_DIR;
connectedCount++; p.index = labelindex;
labelindex = lc; p.firstVertex = vertex;
p = Rasterizer.createContour2D(); p.nextpeer = cc;
p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; p.insideContours = null;
p.index = labelindex; if (cc !== null) {
p.firstVertex = vertex; cc.prevpeer = p;
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;
}
} }
cc = p;
} }
} else { } else {
labelData[pos] = labelindex; vertex = tracer
} .contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex);
} else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { if (vertex !== null) {
labelindex = 0; p = Rasterizer.createContour2D();
if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { p.firstVertex = vertex;
bc = imageData[pos]; p.insideContours = null;
} else { if (depthlabel === 0) {
bc = colorMap[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 { } else {
labelindex = labelData[pos]; labelData[pos] = labelindex;
bc = colorMap[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 { } else {
iq = null; labelindex = labelData[pos];
} bc = colorMap[labelindex];
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();
} }
} }
} }
}; 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 */ import ImageDebug from './image_debug';
/* global define */
define(["image_debug"], function(ImageDebug) { function contains(codeResult, list) {
"use strict"; if (list) {
return list.some(function (item) {
function contains(codeResult, list) { return Object.keys(item).every(function (key) {
if (list) { return item[key] === codeResult[key];
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) { function passesFilter(codeResult, filter) {
if (typeof filter === 'function') { if (typeof filter === 'function') {
return filter(codeResult); return filter(codeResult);
}
return true;
} }
return true;
}
return { export default {
create: function(config) { create: function(config) {
var canvas = document.createElement("canvas"), var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"), ctx = canvas.getContext("2d"),
results = [], results = [],
capacity = config.capacity || 20, capacity = config.capacity || 20,
capture = config.capture === true; capture = config.capture === true;
function matchesConstraints(codeResult) { function matchesConstraints(codeResult) {
return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter); return capacity
} && codeResult
&& !contains(codeResult, config.blacklist)
&& passesFilter(codeResult, config.filter);
}
return { return {
addResult: function(data, imageSize, codeResult) { addResult: function(data, imageSize, codeResult) {
var result = {}; var result = {};
if (matchesConstraints(codeResult)) { if (matchesConstraints(codeResult)) {
capacity--; capacity--;
result.codeResult = codeResult; result.codeResult = codeResult;
if (capture) { if (capture) {
canvas.width = imageSize.x; canvas.width = imageSize.x;
canvas.height = imageSize.y; canvas.height = imageSize.y;
ImageDebug.drawImage(data, imageSize, ctx); ImageDebug.drawImage(data, imageSize, ctx);
result.frame = canvas.toDataURL(); result.frame = canvas.toDataURL();
}
results.push(result);
} }
}, results.push(result);
getResults: function() {
return results;
} }
}; },
} getResults: function() {
}; return results;
}); }
};
}
};
+200 -195
View File
@@ -1,204 +1,209 @@
/* jshint undef: true, unused: true, browser:true, devel: true, -W041: false */ /* @preserve ASM BEGIN */
/* global define */ /* eslint-disable eqeqeq*/
function Skeletonizer(stdlib, foreign, buffer) {
"use asm";
define(function() { var images = new stdlib.Uint8Array(buffer),
"use strict"; size = foreign.size | 0,
imul = stdlib.Math.imul;
/* @preserve ASM BEGIN */ function erode(inImagePtr, outImagePtr) {
function Skeletonizer(stdlib, foreign, buffer) { inImagePtr = inImagePtr | 0;
"use asm"; outImagePtr = outImagePtr | 0;
var images = new stdlib.Uint8Array(buffer), var v = 0,
size = foreign.size | 0, u = 0,
imul = stdlib.Math.imul; sum = 0,
yStart1 = 0,
yStart2 = 0,
xStart1 = 0,
xStart2 = 0,
offset = 0;
function erode(inImagePtr, outImagePtr) { for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) {
inImagePtr = inImagePtr | 0; offset = (offset + size) | 0;
outImagePtr = outImagePtr | 0; for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) {
yStart1 = (offset - size) | 0;
var v = 0, yStart2 = (offset + size) | 0;
u = 0, xStart1 = (u - 1) | 0;
sum = 0, xStart2 = (u + 1) | 0;
yStart1 = 0, sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0)
yStart2 = 0, + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0)
xStart1 = 0, + (images[(inImagePtr + offset + u) | 0] | 0)
xStart2 = 0, + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0)
offset = 0; + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0;
if ((sum | 0) == (5 | 0)) {
for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { images[(outImagePtr + offset + u) | 0] = 1;
offset = (offset + size) | 0; } else {
for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { images[(outImagePtr + offset + u) | 0] = 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;
} }
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
};
} }
/* @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 */ * Construct representing a part of another {ImageWrapper}. Shares data
* between the parent and the child.
define(["typedefs"], function() { * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner)
"use strict"; * @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
* Construct representing a part of another {ImageWrapper}. Shares data */
* between the parent and the child. function SubImage(from, size, I) {
* @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) if (!I) {
* @param size {ImageRef} The size of the resulting image I = {
* @param I {ImageWrapper} The {ImageWrapper} to share from data: null,
* @returns {SubImage} A shared part of the original image size: size
*/ };
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;
} }
this.data = I.data;
this.originalSize = I.size;
this.I = I;
/** this.from = from;
* Displays the {SubImage} in a given canvas this.size = size;
* @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) { * Displays the {SubImage} in a given canvas
var ctx, * @param canvas {Canvas} The canvas element to write to
frame, * @param scale {Number} Scale which is applied to each pixel-value
data, */
current, SubImage.prototype.show = function(canvas, scale) {
y, var ctx,
x, frame,
pixel; data,
current,
if (!scale) { y,
scale = 1.0; 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; frame.data = data;
canvas.height = this.size.y; ctx.putImageData(frame, 0, 0);
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);
};
/** /**
* Retrieves a given pixel position from the {SubImage} * Retrieves a given pixel position from the {SubImage}
* @param x {Number} The x-position * @param x {Number} The x-position
* @param y {Number} The y-position * @param y {Number} The y-position
* @returns {Number} The grayscale value at the pixel-position * @returns {Number} The grayscale value at the pixel-position
*/ */
SubImage.prototype.get = function(x, y) { SubImage.prototype.get = function(x, y) {
return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x]; return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x];
}; };
/** /**
* Updates the underlying data from a given {ImageWrapper} * Updates the underlying data from a given {ImageWrapper}
* @param image {ImageWrapper} The updated image * @param image {ImageWrapper} The updated image
*/ */
SubImage.prototype.updateData = function(image) { SubImage.prototype.updateData = function(image) {
this.originalSize = image.size; this.originalSize = image.size;
this.data = image.data; this.data = image.data;
}; };
/** /**
* Updates the position of the shared area * Updates the position of the shared area
* @param from {x,y} The new location * @param from {x,y} The new location
* @returns {SubImage} returns {this} for possible chaining * @returns {SubImage} returns {this} for possible chaining
*/ */
SubImage.prototype.updateFrom = function(from) { SubImage.prototype.updateFrom = function(from) {
this.from = from; this.from = from;
return this; return this;
}; };
return (SubImage); 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 * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization
*/ */
define(function() { var Tracer = {
"use strict"; searchDirections: [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]],
create: function(imageWrapper, labelWrapper) {
var Tracer = { var imageData = imageWrapper.data,
searchDirections : [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]], labelData = labelWrapper.data,
create : function(imageWrapper, labelWrapper) { searchDirections = this.searchDirections,
var imageData = imageWrapper.data, width = imageWrapper.size.x,
labelData = labelWrapper.data, pos;
searchDirections = this.searchDirections,
width = imageWrapper.size.x,
pos;
function trace(current, color, label, edgelabel) { function trace(current, color, label, edgelabel) {
var i, var i,
y, y,
x; x;
for ( i = 0; i < 7; i++) { for ( i = 0; i < 7; i++) {
y = current.cy + searchDirections[current.dir][0]; y = current.cy + searchDirections[current.dir][0];
x = current.cx + searchDirections[current.dir][1]; x = current.cx + searchDirections[current.dir][1];
pos = y * width + x; pos = y * width + x;
if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) { if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) {
labelData[pos] = label; labelData[pos] = label;
current.cy = y; current.cy = y;
current.cx = x; current.cx = x;
return true; return true;
} else { } else {
if (labelData[pos] === 0) { if (labelData[pos] === 0) {
labelData[pos] = edgelabel; labelData[pos] = edgelabel;
}
current.dir = (current.dir + 1) % 8;
} }
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 { return {
trace : function(current, color, label, edgelabel) { dir: dir,
return trace(current, color, label, edgelabel); x: x,
}, y: y,
contourTracing : function(sy, sx, label, color, edgelabel) { next: null,
return contourTracing(sy, sx, label, color, edgelabel); 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 * Normalizes browser-specific prefixes
*/ */
glMatrixArrayType = Float32Array;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.requestAnimFrame = (function () { window.requestAnimFrame = (function () {
return window.requestAnimationFrame || return window.requestAnimationFrame ||
@@ -11,12 +10,13 @@ if (typeof window !== 'undefined') {
window.mozRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame || window.oRequestAnimationFrame ||
window.msRequestAnimationFrame || window.msRequestAnimationFrame ||
function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { function (/* function FrameRequestCallback */ callback) {
window.setTimeout(callback, 1000 / 60); 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; window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
} }
Math.imul = Math.imul || function(a, b) { Math.imul = Math.imul || function(a, b) {
@@ -26,5 +26,5 @@ Math.imul = Math.imul || function(a, b) {
bl = b & 0xffff; bl = b & 0xffff;
// the shift by 0 fixes the sign on the high part // the shift by 0 fixes the sign on the high part
// the final |0 converts the unsigned value into a signed value // 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 */ import EANReader from './ean_reader';
/* global define */
define( function UPCEReader() {
[ EANReader.call(this);
"./ean_reader" }
],
function(EANReader) {
"use strict";
function UPCEReader() { var properties = {
EANReader.call(this); 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) {
var properties = { code.code = code.code - self.CODE_G_START;
CODE_FREQUENCY : {value: [ codeFrequency |= 1 << (5 - i);
[ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ], }
[7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]}, result.push(code.code);
STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]}, decodedCodes.push(code);
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 (!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 */ import EANReader from './ean_reader';
/* global define */
define( function UPCReader() {
[ EANReader.call(this);
"./ean_reader" }
],
function(EANReader) {
"use strict";
function UPCReader() { var properties = {
EANReader.call(this); FORMAT: {value: "upc_a", writeable: false}
} };
var properties = { UPCReader.prototype = Object.create(EANReader.prototype, properties);
FORMAT: {value: "upc_a", writeable: false} UPCReader.prototype.constructor = UPCReader;
};
UPCReader.prototype = Object.create(EANReader.prototype, properties); UPCReader.prototype._decode = function() {
UPCReader.prototype.constructor = UPCReader; var result = EANReader.prototype._decode.call(this);
UPCReader.prototype._decode = function() { if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") {
var result = EANReader.prototype._decode.call(this); result.code = result.code.substring(1);
return result;
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);
} }
); 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'), var code = fs.readFileSync('dist/quagga.js', 'utf-8'),
minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'), minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'),
commentEnd = '/* @preserve ASM END */', commentEnd = '/* @preserve ASM END */',
asmStartIdx = code.indexOf('/* @preserve ASM BEGIN */'), moduleFunctionRegex = /function\s*\((\w+,\s*\w+)\)\s*\{\s*\/\* \@preserve ASM BEGIN \*\//,
asmEndIdx = code.indexOf(commentEnd), commentStartIdx = code.indexOf("/* @preserve ASM BEGIN */"),
asmCode = code.substring(asmStartIdx, asmEndIdx + commentEnd.length), asmEndIdxTmp = code.indexOf(commentEnd),
asmFunctionRegex = /function (\w+)\(\w+,\s*\w+,\s*\w+\)\s*\{\s*"use asm";/, asmEndIdx = code.indexOf("}", asmEndIdxTmp),
asmCodeTmp = code.substring(commentStartIdx - Math.min(500, commentStartIdx),
asmEndIdx + 1),
asmStartIdx = asmCodeTmp.search(moduleFunctionRegex),
asmCode = asmCodeTmp.substring(asmStartIdx),
asmModule, asmModule,
asmModuleName, moduleArg1,
asmCodeMinified, asmCodeMinified;
asmMinifiedModuleName;
asmCodeMinified = asmCode asmCodeMinified = asmCode
.replace(/\s*\/\/.*/g, '') // remove single-line comments .replace(/\s*\/\/.*/g, '') // remove single-line comments
@@ -29,28 +32,32 @@ module.exports = function(grunt) {
grunt.log.debug(asmCodeMinified); grunt.log.debug(asmCodeMinified);
asmModule = asmCode.match(asmFunctionRegex); asmModule = moduleFunctionRegex.exec(asmCode);
if (!asmModule) { if (!asmModule) {
grunt.log.error("No ASM module found"); grunt.log.error("No ASM module found");
return; return;
} }
asmModuleName = asmModule[1]; moduleArg1 = asmModule[1];
grunt.log.debug(asmModuleName); grunt.log.debug(moduleArg1);
asmModule = minifiedCode.match(asmFunctionRegex); var insertionPoint = minifiedCode.search(moduleFunctionRegex);
if (!asmModule) { if (insertionPoint === -1) {
grunt.log.error("No ASM module found in minified file"); grunt.log.error("No ASM module found in minified file");
return; return;
} }
grunt.log.debug(insertionPoint);
asmMinifiedModuleName = asmModule[1]; var insertionPointEnd = minifiedCode.indexOf(commentEnd, insertionPoint);
grunt.log.debug(asmMinifiedModuleName); 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); fs.writeFileSync('dist/quagga.min.js', minifiedCode);
grunt.log.ok('dist/quagga.min.js written'); 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';