merged with remote master

pull/99/head
JauernigIT 9 years ago
commit 718a274393

@ -0,0 +1,33 @@
{
"plugins": [
"check-es2015-constants",
"transform-es2015-arrow-functions",
"transform-es2015-block-scoped-functions",
"transform-es2015-block-scoping",
["transform-es2015-classes", { "loose": true }],
["transform-es2015-computed-properties", { "loose": true }],
["transform-es2015-destructuring", { "loose": true }],
["transform-es2015-for-of", { "loose": true }],
"transform-es2015-function-name",
"transform-es2015-literals",
"transform-es2015-object-super",
"transform-es2015-parameters",
"transform-es2015-shorthand-properties",
["transform-es2015-spread", { "loose": true }],
"transform-es2015-sticky-regex",
["transform-es2015-template-literals", { "loose": true }],
"transform-es2015-unicode-regex",
"transform-es2015-typeof-symbol",
"transform-object-rest-spread",
"lodash"
],
"env": {
"commonjs": {
"plugins": [
["transform-es2015-modules-commonjs", { "loose": true }],
"add-module-exports"
]
}
}
}

@ -1,7 +1,8 @@
quaggaJS
========
- [Changelog](#changelog) (2016-02-15)
- [Changelog](#changelog) (2016-03-31)
- [Browser Support](#browser-support)
- [Installing](#installing)
- [Getting Started](#gettingstarted)
- [API](#api)
@ -34,18 +35,30 @@ invariant to scale and rotation, whereas other libraries require the barcode to
be aligned with the viewport.
## Requirements
## <a name="browser-support">Browser Support</a>
In order to take full advantage of quaggaJS, the browser needs to support the
`getUserMedia` API which is already implemented in recent versions of Firefox,
Chrome, IE (Edge) and Opera. The API is also available on their mobile
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
[caniuse][caniuse_getusermedia] for updates.
Quagga makes use of many modern Web-APIs which are not implemented by all
browsers yet. There are two modes in which Quagga operates: 1. analyzing static
images and 2. using a camera to decode the images from a live-stream. The latter
requires the presence of the MediaDevices API. You can track the compatibility
of the used Web-APIs for each mode:
In cases where real-time decoding is not needed, or the platform does not
support `getUserMedia` QuaggaJS is also capable of decoding image-files using
the File API or other URL sources.
- [Static Images](http://caniuse.com/#feat=webworkers,canvas,typedarrays,bloburls,blobbuilder)
- [Live Stream](http://caniuse.com/#feat=webworkers,canvas,typedarrays,bloburls,blobbuilder,stream)
### Static Images
The following APIs need to be implemented in your browser:
- [webworkers](http://caniuse.com/#feat=webworkers)
- [canvas](http://caniuse.com/#feat=canvas)
- [typedarrays](http://caniuse.com/#feat=typedarrays)
- [bloburls](http://caniuse.com/#feat=bloburls)
- [blobbuilder](http://caniuse.com/#feat=blobbuilder)
### Live Stream
In addition to the APIs mentioned above:
- [MediaDevices](http://caniuse.com/#feat=stream)
## <a name="installing">Installing</a>
@ -61,7 +74,8 @@ the __script__ tag.
And then import it as dependency in your project:
```javascript
var Quagga = require('quagga');
import Quagga from 'quagga'; // ES6
const Quagga = require('quagga').default; // Common JS (important: default)
```
Currently, the full functionality is only available through the browser. When
@ -79,7 +93,7 @@ You can also install QuaggaJS through __bower__:
### Script-Tag Anno 1998
You can simply include `dist/quagga.min.js` in your project and you are ready
to go.
to go. The script exposes the library on the global namespace under `Quagga`.
## <a name="gettingstarted">Getting Started</a>
@ -278,6 +292,7 @@ high-level properties:
numOfWorkers: 4,
locate: true,
inputStream: {...},
frequency: 10,
decoder:{...},
locator: {...},
debug: false,
@ -323,7 +338,8 @@ The `inputStream` property defines the sources of images/videos within QuaggaJS.
constraints: {
width: 640,
height: 480,
facing: "environment"
facingMode: "environment",
deviceId: "7832475934759384534"
},
area: { // defines rectangle of the detection/localization area
top: "0%", // top offset
@ -340,8 +356,11 @@ First, the `type` property can be set to three different values:
depending on the use-case. Most probably, the default value is sufficient.
Second, the `constraint` key defines the physical dimensions of the input image
and additional properties, such as `facing` which sets the source of the user's
camera in case of multiple attached devices.
and additional properties, such as `facingMode` which sets the source of the
user's camera in case of multiple attached devices. Additionally, if required,
the `deviceId` can be set if the selection of the camera is given to the user.
This can be easily achieved via
[MediaDevices.enumerateDevices()][enumerateDevices]
Thirdly, the `area` prop restricts the decoding area of the image. The values
are given in percentage, similar to the CSS style property when using
@ -354,6 +373,13 @@ color-channel is read instead of calculating the gray-scale values of the
source's RGB. This is useful in combination with the `ResultCollector` where
the gray-scale representations of the wrongly identified images are saved.
### frequency
This top-level property controls the scan-frequency of the video-stream. It's
optional and defines the maximum number of scans per second. This renders
useful for cases where the scan-session is long-running and resources such as
CPU power are of concern.
### decoder
QuaggaJS usually runs in a two-stage manner (`locate` is set to `true`) where,
@ -363,13 +389,15 @@ options within the `decoder` are for debugging/visualization purposes only.
```javascript
{
drawBoundingBox: false,
showFrequency: false,
drawScanline: true,
showPattern: false,
readers: [
'code_128_reader'
],
debug: {
drawBoundingBox: false,
showFrequency: false,
drawScanline: false,
showPattern: false
}
multiple: false
}
```
@ -404,25 +432,53 @@ The remaining properties `drawBoundingBox`, `showFrequency`, `drawScanline` and
### locator
The `locator` config is only relevant if the `locate` flag is set to `true`.
It controls the behavior of the localization-process and needs to be adjusted
for each specific use-case. The default settings are simply a combination of
values which worked best during development.
Only two properties are relevant for the use in Quagga (`halfSample` and
`patchSize`) whereas the rest is only needed for development and debugging.
```javascript
{
halfSample: true,
patchSize: "medium", // x-small, small, medium, large, x-large
showCanvas: false,
showPatches: false,
showFoundPatches: false,
showSkeleton: false,
showLabels: false,
showPatchLabels: false,
showRemainingPatchLabels: false,
boxFromPatches: {
showTransformed: false,
showTransformedBox: false,
showBB: false
debug: {
showCanvas: false,
showPatches: false,
showFoundPatches: false,
showSkeleton: false,
showLabels: false,
showPatchLabels: false,
showRemainingPatchLabels: false,
boxFromPatches: {
showTransformed: false,
showTransformedBox: false,
showBB: false
}
}
}
```
The `halfSample` flag tells the locator-process whether it should operate on an
image scaled down (half width/height, quarter pixel-count ) or not. Turning
`halfSample` on reduces the processing-time significantly and also helps
finding a barcode pattern due to implicit smoothing.
It should be turned off in cases where the barcode is really small and the full
resolution is needed to find the position. It's recommended to keep it turned
on and use a higher resolution video-image if needed.
The second property `patchSize` defines the density of the search-grid. The
property accepts strings of the value `x-small`, `small`, `medium`, `large` and
`x-large`. The `patchSize` is proportional to the size of the scanned barcodes.
If you have really large barcodes which can be read close-up, then the use of
`large` or `x-large` is recommended. In cases where the barcode is further away
from the camera lens (lack of auto-focus, or small barcodes) then it's advised
to set the size to `small` or even `x-small`. For the latter it's also
recommended to crank up the resolution in order to find a barcode.
## Examples
The following example takes an image `src` as input and prints the result on the
@ -551,6 +607,10 @@ on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
## <a name="changelog">Changelog</a>
### 2016-03-31
Take a look at the release-notes (
[0.10.0](https://github.com/serratus/quaggaJS/releases/tag/v0.10.0))
### 2016-02-18
- Internal Changes
@ -559,8 +619,8 @@ on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
### 2016-02-15
Take a look at the release-notes ([0.9.0]
(https://github.com/serratus/quaggaJS/releases/tag/v0.9.0))
Take a look at the release-notes (
[0.9.0](https://github.com/serratus/quaggaJS/releases/tag/v0.9.0))
### 2015-11-22
@ -715,3 +775,4 @@ introduced to the API.
[oberhofer_co_how]: http://www.oberhofer.co/how-barcode-localization-works-in-quaggajs/
[github_examples]: https://serratus.github.io/quaggaJS/examples
[i2of5_wiki]: https://en.wikipedia.org/wiki/Interleaved_2_of_5
[enumerateDevices]: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices

19793
dist/quagga.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -98,8 +98,7 @@ $(function() {
var values = value.split('x');
return {
width: parseInt(values[0]),
height: parseInt(values[1]),
facing: "environment"
height: parseInt(values[1])
}
}
},
@ -118,7 +117,7 @@ $(function() {
constraints: {
width: 640,
height: 480,
facing: "environment" // or user
facingMode: "environment"
}
},
locator: {

@ -1,4 +1,4 @@
var Quagga = require('../lib/quagga');
var Quagga = require('../lib/quagga').default;
Quagga.decodeSingle({
src: "../test/fixtures/code_128/image-001.jpg",

@ -15,23 +15,17 @@ module.exports = function(config) {
},
webpack: {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: [
/node_modules/
],
loader: 'babel'
}
]
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}]
},
resolve: {
extensions: ['', '.js', '.jsx'],
root: path.resolve(__dirname),
alias: {
'input_stream$': 'src/input/input_stream',
'frame_grabber$': 'src/input/frame_grabber'
}
modules: [
path.resolve('./src/input/'),
'node_modules'
]
},
plugins: [
new webpack.DefinePlugin({

@ -14,31 +14,23 @@ module.exports = function(config) {
},
webpack: {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: [
/node_modules/
],
loader: 'babel'
},
{
test: /\.js$/,
include: [
path.resolve('src')
],
exclude: /node_modules/,
loader: 'isparta'
}
]
loaders: [{
test: /\.jsx?$/,
exclude: [
path.resolve('node_modules/')
],
loader: 'babel-loader'
}, {
test: /\.js$/,
include: path.resolve('src'),
loader: 'babel-istanbul'
}]
},
resolve: {
extensions: ['', '.js', '.jsx'],
root: path.resolve(__dirname),
alias: {
'input_stream$': 'src/input/input_stream',
'frame_grabber$': 'src/input/frame_grabber'
}
modules: [
path.resolve('./src/input/'),
'node_modules'
]
},
plugins: [
new webpack.DefinePlugin({
@ -48,6 +40,7 @@ module.exports = function(config) {
},
plugins: [
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-coverage',
'karma-mocha',
'karma-chai',

File diff suppressed because one or more lines are too long

@ -1,14 +1,39 @@
{
"name": "quagga",
"version": "0.9.2",
"version": "0.10.2",
"description": "An advanced barcode-scanner written in JavaScript",
"main": "lib/quagga.js",
"browser": "dist/quagga.min.js",
"devDependencies": {
"async": "^1.4.2",
"babel-core": "^5.8.25",
"babel-eslint": "^4.1.3",
"babel-loader": "^5.3.2",
"babel-cli": "^6.5.1",
"babel-core": "^6.7.4",
"babel-eslint": "^6.0.0",
"babel-istanbul-loader": "^0.1.0",
"babel-loader": "^6.2.4",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-plugin-check-es2015-constants": "^6.3.13",
"babel-plugin-lodash": "^2.2.1",
"babel-plugin-transform-es2015-arrow-functions": "^6.3.13",
"babel-plugin-transform-es2015-block-scoped-functions": "^6.3.13",
"babel-plugin-transform-es2015-block-scoping": "^6.3.13",
"babel-plugin-transform-es2015-classes": "^6.3.13",
"babel-plugin-transform-es2015-computed-properties": "^6.3.13",
"babel-plugin-transform-es2015-destructuring": "^6.3.13",
"babel-plugin-transform-es2015-for-of": "^6.3.13",
"babel-plugin-transform-es2015-function-name": "^6.3.13",
"babel-plugin-transform-es2015-literals": "^6.3.13",
"babel-plugin-transform-es2015-modules-commonjs": "^6.3.13",
"babel-plugin-transform-es2015-object-super": "^6.3.13",
"babel-plugin-transform-es2015-parameters": "^6.3.13",
"babel-plugin-transform-es2015-shorthand-properties": "^6.3.13",
"babel-plugin-transform-es2015-spread": "^6.3.13",
"babel-plugin-transform-es2015-sticky-regex": "^6.3.13",
"babel-plugin-transform-es2015-template-literals": "^6.3.13",
"babel-plugin-transform-es2015-typeof-symbol": "^6.3.13",
"babel-plugin-transform-es2015-unicode-regex": "^6.3.13",
"babel-plugin-transform-object-rest-spread": "^6.5.0",
"babel-plugin-transform-regenerator": "^6.3.13",
"chai": "^3.4.1",
"core-js": "^1.2.1",
"cross-env": "^1.0.7",
@ -17,32 +42,35 @@
"grunt-cli": "^0.1.13",
"grunt-contrib-nodeunit": "^0.4.1",
"grunt-karma": "^0.12.1",
"isparta-loader": "^1.0.0",
"isparta-loader": "^2.0.0",
"karma": "^0.13.9",
"karma-chai": "0.1.0",
"karma-chrome-launcher": "^0.2.0",
"karma-coverage": "^0.5.2",
"karma-firefox-launcher": "^0.1.7",
"karma-mocha": "~0.2.0",
"karma-phantomjs-launcher": "^0.2.1",
"karma-sinon": "^1.0.4",
"karma-sinon-chai": "^1.1.0",
"karma-source-map-support": "^1.1.0",
"karma-webpack": "^1.7.0",
"lolex": "^1.4.0",
"mocha": "^2.3.2",
"phantomjs": "^1.9.18",
"sinon": "^1.16.1",
"webpack": "^1.12.2",
"webpack-core": "^0.6.8"
"sinon-chai": "^2.8.0",
"webpack": "^2.1.0-beta.4",
"webpack-sources": "^0.1.1"
},
"directories": {
"doc": "doc"
},
"scripts": {
"test": "grunt test",
"test": "cross-env BABEL_ENV=commonjs grunt test",
"integrationtest": "grunt integrationtest",
"build:dev": "cross-env BUILD_ENV=development webpack",
"build:prod": "cross-env BUILD_ENV=production webpack --config webpack.config.min.js && grunt uglyasm",
"build:node": "cross-env BUILD_ENV=node webpack --config webpack.node.config.js",
"build:node": "cross-env BABEL_ENV=commonjs BUILD_ENV=node webpack --config webpack.node.config.js",
"build": "npm run build:dev && npm run build:prod && npm run build:node",
"watch": "cross-env BUILD_ENV=development webpack --watch",
"lint": "eslint src"
@ -74,9 +102,12 @@
},
"dependencies": {
"get-pixels": "^3.2.3",
"gl-matrix": "^2.3.1",
"lodash": "^3.10.1",
"gl-mat2": "^1.0.0",
"gl-vec2": "^1.0.0",
"gl-vec3": "^1.0.3",
"lodash": "^4.6.1",
"ndarray": "^1.0.18",
"ndarray-linear-interpolate": "^1.0.0"
"ndarray-linear-interpolate": "^1.0.0",
"webrtc-adapter": "^1.0.6"
}
}

@ -1,5 +1,5 @@
var ConcatSource = require("webpack-core/lib/ConcatSource");
var OriginalSource = require("webpack-core/lib/OriginalSource");
var ConcatSource = require("webpack-sources").ConcatSource;
var OriginalSource = require("webpack-sources").OriginalSource;
function MyUmdPlugin(options) {
this.name = options.library;
@ -13,11 +13,11 @@ MyUmdPlugin.prototype.apply = function(compiler) {
return new ConcatSource(new OriginalSource(
"(function webpackUniversalModuleDefinition(root, factory) {\n" +
" if(typeof exports === 'object' && typeof module === 'object')\n" +
" module.exports = factory(factory.toString());\n" +
" module.exports = factory(factory.toString()).default;\n" +
" else if(typeof exports === 'object')\n" +
" exports[\"" + this.name + "\"] = factory(factory.toString());\n" +
" exports[\"" + this.name + "\"] = factory(factory.toString()).default;\n" +
" else\n" +
" root[\"" + this.name + "\"] = factory(factory.toString());\n" +
" root[\"" + this.name + "\"] = factory(factory.toString()).default;\n" +
"})(this, function(__factorySource__) {\nreturn ", "webpack/myModuleDefinition"), source, "\n});\n");
}.bind(this));
mainTemplate.plugin("global-hash-paths", function(paths) {

@ -1,4 +1,7 @@
import {vec2} from 'gl-matrix';
const vec2 = {
clone: require('gl-vec2/clone'),
dot: require('gl-vec2/dot')
}
/**
* Creates a cluster for grouping similar orientations of datapoints
*/

@ -1,15 +1,18 @@
import Cluster2 from './cluster';
import ArrayHelper from './array_helper';
import {vec2, vec3} from 'gl-matrix';
var CVUtils = {};
const vec2 = {
clone: require('gl-vec2/clone'),
};
const vec3 = {
clone: require('gl-vec3/clone'),
};
/**
* @param x x-coordinate
* @param y y-coordinate
* @return ImageReference {x,y} Coordinate
*/
CVUtils.imageRef = function(x, y) {
export function imageRef(x, y) {
var that = {
x: x,
y: y,
@ -32,7 +35,7 @@ CVUtils.imageRef = function(x, y) {
* Computes an integral image of a given grayscale image.
* @param imageDataContainer {ImageDataContainer} the image to be integrated
*/
CVUtils.computeIntegralImage2 = function(imageWrapper, integralWrapper) {
export function computeIntegralImage2(imageWrapper, integralWrapper) {
var imageData = imageWrapper.data;
var width = imageWrapper.size.x;
var height = imageWrapper.size.y;
@ -75,7 +78,7 @@ CVUtils.computeIntegralImage2 = function(imageWrapper, integralWrapper) {
}
};
CVUtils.computeIntegralImage = function(imageWrapper, integralWrapper) {
export function computeIntegralImage(imageWrapper, integralWrapper) {
var imageData = imageWrapper.data;
var width = imageWrapper.size.x;
var height = imageWrapper.size.y;
@ -97,7 +100,7 @@ CVUtils.computeIntegralImage = function(imageWrapper, integralWrapper) {
}
};
CVUtils.thresholdImage = function(imageWrapper, threshold, targetWrapper) {
export function thresholdImage(imageWrapper, threshold, targetWrapper) {
if (!targetWrapper) {
targetWrapper = imageWrapper;
}
@ -108,7 +111,7 @@ CVUtils.thresholdImage = function(imageWrapper, threshold, targetWrapper) {
}
};
CVUtils.computeHistogram = function(imageWrapper, bitsPerPixel) {
export function computeHistogram(imageWrapper, bitsPerPixel) {
if (!bitsPerPixel) {
bitsPerPixel = 8;
}
@ -124,7 +127,7 @@ CVUtils.computeHistogram = function(imageWrapper, bitsPerPixel) {
return hist;
};
CVUtils.sharpenLine = function(line) {
export function sharpenLine(line) {
var i,
length = line.length,
left = line[0],
@ -141,7 +144,7 @@ CVUtils.sharpenLine = function(line) {
return line;
};
CVUtils.determineOtsuThreshold = function(imageWrapper, bitsPerPixel) {
export function determineOtsuThreshold(imageWrapper, bitsPerPixel) {
if (!bitsPerPixel) {
bitsPerPixel = 8;
}
@ -171,7 +174,7 @@ CVUtils.determineOtsuThreshold = function(imageWrapper, bitsPerPixel) {
var vet = [0], p1, p2, p12, k, m1, m2, m12,
max = (1 << bitsPerPixel) - 1;
hist = CVUtils.computeHistogram(imageWrapper, bitsPerPixel);
hist = computeHistogram(imageWrapper, bitsPerPixel);
for ( k = 1; k < max; k++) {
p1 = px(0, k);
p2 = px(k + 1, max);
@ -191,16 +194,16 @@ CVUtils.determineOtsuThreshold = function(imageWrapper, bitsPerPixel) {
return threshold << bitShift;
};
CVUtils.otsuThreshold = function(imageWrapper, targetWrapper) {
var threshold = CVUtils.determineOtsuThreshold(imageWrapper);
export function otsuThreshold(imageWrapper, targetWrapper) {
var threshold = determineOtsuThreshold(imageWrapper);
CVUtils.thresholdImage(imageWrapper, threshold, targetWrapper);
thresholdImage(imageWrapper, threshold, targetWrapper);
return threshold;
};
// local thresholding
CVUtils.computeBinaryImage = function(imageWrapper, integralWrapper, targetWrapper) {
CVUtils.computeIntegralImage(imageWrapper, integralWrapper);
export function computeBinaryImage(imageWrapper, integralWrapper, targetWrapper) {
computeIntegralImage(imageWrapper, integralWrapper);
if (!targetWrapper) {
targetWrapper = imageWrapper;
@ -241,7 +244,7 @@ CVUtils.computeBinaryImage = function(imageWrapper, integralWrapper, targetWrapp
}
};
CVUtils.cluster = function(points, threshold, property) {
export function cluster(points, threshold, property) {
var i, k, cluster, point, clusters = [];
if (!property) {
@ -270,7 +273,7 @@ CVUtils.cluster = function(points, threshold, property) {
return clusters;
};
CVUtils.Tracer = {
export const Tracer = {
trace: function(points, vec) {
var iteration, maxIterations = 10, top = [], result = [], centerPos = 0, currentPos = 0;
@ -340,10 +343,10 @@ CVUtils.Tracer = {
}
};
CVUtils.DILATE = 1;
CVUtils.ERODE = 2;
export const DILATE = 1;
export const ERODE = 2;
CVUtils.dilate = function(inImageWrapper, outImageWrapper) {
export function dilate(inImageWrapper, outImageWrapper) {
var v,
u,
inImageData = inImageWrapper.data,
@ -370,7 +373,7 @@ CVUtils.dilate = function(inImageWrapper, outImageWrapper) {
}
};
CVUtils.erode = function(inImageWrapper, outImageWrapper) {
export function erode(inImageWrapper, outImageWrapper) {
var v,
u,
inImageData = inImageWrapper.data,
@ -397,7 +400,7 @@ CVUtils.erode = function(inImageWrapper, outImageWrapper) {
}
};
CVUtils.subtract = function(aImageWrapper, bImageWrapper, resultImageWrapper) {
export function subtract(aImageWrapper, bImageWrapper, resultImageWrapper) {
if (!resultImageWrapper) {
resultImageWrapper = aImageWrapper;
}
@ -411,7 +414,7 @@ CVUtils.subtract = function(aImageWrapper, bImageWrapper, resultImageWrapper) {
}
};
CVUtils.bitwiseOr = function(aImageWrapper, bImageWrapper, resultImageWrapper) {
export function bitwiseOr(aImageWrapper, bImageWrapper, resultImageWrapper) {
if (!resultImageWrapper) {
resultImageWrapper = aImageWrapper;
}
@ -425,7 +428,7 @@ CVUtils.bitwiseOr = function(aImageWrapper, bImageWrapper, resultImageWrapper) {
}
};
CVUtils.countNonZero = function(imageWrapper) {
export function countNonZero(imageWrapper) {
var length = imageWrapper.data.length, data = imageWrapper.data, sum = 0;
while (length--) {
@ -434,7 +437,7 @@ CVUtils.countNonZero = function(imageWrapper) {
return sum;
};
CVUtils.topGeneric = function(list, top, scoreFunc) {
export function topGeneric(list, top, scoreFunc) {
var i, minIdx = 0, min = 0, queue = [], score, hit, pos;
for ( i = 0; i < top; i++) {
@ -463,18 +466,18 @@ CVUtils.topGeneric = function(list, top, scoreFunc) {
return queue;
};
CVUtils.grayArrayFromImage = function(htmlImage, offsetX, ctx, array) {
export function grayArrayFromImage(htmlImage, offsetX, ctx, array) {
ctx.drawImage(htmlImage, offsetX, 0, htmlImage.width, htmlImage.height);
var ctxData = ctx.getImageData(offsetX, 0, htmlImage.width, htmlImage.height).data;
CVUtils.computeGray(ctxData, array);
computeGray(ctxData, array);
};
CVUtils.grayArrayFromContext = function(ctx, size, offset, array) {
export function grayArrayFromContext(ctx, size, offset, array) {
var ctxData = ctx.getImageData(offset.x, offset.y, size.x, size.y).data;
CVUtils.computeGray(ctxData, array);
computeGray(ctxData, array);
};
CVUtils.grayAndHalfSampleFromCanvasData = function(canvasData, size, outArray) {
export function grayAndHalfSampleFromCanvasData(canvasData, size, outArray) {
var topRowIdx = 0;
var bottomRowIdx = size.x;
var endIdx = Math.floor(canvasData.length / 4);
@ -507,7 +510,7 @@ CVUtils.grayAndHalfSampleFromCanvasData = function(canvasData, size, outArray) {
}
};
CVUtils.computeGray = function(imageData, outArray, config) {
export function computeGray(imageData, outArray, config) {
var l = (imageData.length / 4) | 0,
i,
singleChannel = config && config.singleChannel === true;
@ -524,7 +527,7 @@ CVUtils.computeGray = function(imageData, outArray, config) {
}
};
CVUtils.loadImageArray = function(src, callback, canvas) {
export function loadImageArray(src, callback, canvas) {
if (!canvas) {
canvas = document.createElement('canvas');
}
@ -538,7 +541,7 @@ CVUtils.loadImageArray = function(src, callback, canvas) {
var array = new Uint8Array(this.width * this.height);
ctx.drawImage(this, 0, 0);
var data = ctx.getImageData(0, 0, this.width, this.height).data;
CVUtils.computeGray(data, array);
computeGray(data, array);
this.callback(array, {
x: this.width,
y: this.height
@ -551,7 +554,7 @@ CVUtils.loadImageArray = function(src, callback, canvas) {
* @param inImg {ImageWrapper} input image to be sampled
* @param outImg {ImageWrapper} to be stored in
*/
CVUtils.halfSample = function(inImgWrapper, outImgWrapper) {
export function halfSample(inImgWrapper, outImgWrapper) {
var inImg = inImgWrapper.data;
var inWidth = inImgWrapper.size.x;
var outImg = outImgWrapper.data;
@ -573,7 +576,7 @@ CVUtils.halfSample = function(inImgWrapper, outImgWrapper) {
}
};
CVUtils.hsv2rgb = function(hsv, rgb) {
export function hsv2rgb(hsv, rgb) {
var h = hsv[0],
s = hsv[1],
v = hsv[2],
@ -611,7 +614,7 @@ CVUtils.hsv2rgb = function(hsv, rgb) {
return rgb;
};
CVUtils._computeDivisors = function(n) {
export function _computeDivisors(n) {
var largeDivisors = [],
divisors = [],
i;
@ -627,7 +630,7 @@ CVUtils._computeDivisors = function(n) {
return divisors.concat(largeDivisors);
};
CVUtils._computeIntersection = function(arr1, arr2) {
function _computeIntersection(arr1, arr2) {
var i = 0,
j = 0,
result = [];
@ -646,11 +649,11 @@ CVUtils._computeIntersection = function(arr1, arr2) {
return result;
};
CVUtils.calculatePatchSize = function(patchSize, imgSize) {
var divisorsX = this._computeDivisors(imgSize.x),
divisorsY = this._computeDivisors(imgSize.y),
export function calculatePatchSize(patchSize, imgSize) {
var divisorsX = _computeDivisors(imgSize.x),
divisorsY = _computeDivisors(imgSize.y),
wideSide = Math.max(imgSize.x, imgSize.y),
common = this._computeIntersection(divisorsX, divisorsY),
common = _computeIntersection(divisorsX, divisorsY),
nrOfPatchesList = [8, 10, 15, 20, 32, 60, 80],
nrOfPatchesMap = {
"x-small": 5,
@ -687,15 +690,15 @@ CVUtils.calculatePatchSize = function(patchSize, imgSize) {
optimalPatchSize = findPatchSizeForDivisors(common);
if (!optimalPatchSize) {
optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(wideSide));
optimalPatchSize = findPatchSizeForDivisors(_computeDivisors(wideSide));
if (!optimalPatchSize) {
optimalPatchSize = findPatchSizeForDivisors((this._computeDivisors(desiredPatchSize * nrOfPatches)));
optimalPatchSize = findPatchSizeForDivisors((_computeDivisors(desiredPatchSize * nrOfPatches)));
}
}
return optimalPatchSize;
};
CVUtils._parseCSSDimensionValues = function(value) {
export function _parseCSSDimensionValues(value) {
var dimension = {
value: parseFloat(value),
unit: value.indexOf("%") === value.length - 1 ? "%" : "px"
@ -704,36 +707,36 @@ CVUtils._parseCSSDimensionValues = function(value) {
return dimension;
};
CVUtils._dimensionsConverters = {
top: function top(dimension, context) {
export const _dimensionsConverters = {
top: function (dimension, context) {
return Math.floor((dimension.unit === "%")
? (context.height * (dimension.value / 100))
: dimension.value);
},
right: function right(dimension, context) {
right: function (dimension, context) {
return Math.floor((dimension.unit === "%")
? (context.width - context.width * (dimension.value / 100))
: (context.width - dimension.value));
},
bottom: function bottom(dimension, context) {
bottom: function (dimension, context) {
return Math.floor((dimension.unit === "%")
? (context.height - context.height * (dimension.value / 100))
: (context.height - dimension.value));
},
left: function left(dimension, context) {
left: function (dimension, context) {
return Math.floor((dimension.unit === "%")
? (context.width * (dimension.value / 100))
: dimension.value);
}
};
CVUtils.computeImageArea = function(inputWidth, inputHeight, area) {
export function computeImageArea(inputWidth, inputHeight, area) {
var context = {width: inputWidth, height: inputHeight};
var parsedArea = Object.keys(area).reduce(function(result, key) {
var value = area[key],
parsed = CVUtils._parseCSSDimensionValues(value),
calculated = CVUtils._dimensionsConverters[key](parsed, context);
parsed = _parseCSSDimensionValues(value),
calculated = _dimensionsConverters[key](parsed, context);
result[key] = calculated;
return result;
@ -746,5 +749,3 @@ CVUtils.computeImageArea = function(inputWidth, inputHeight, area) {
sh: parsedArea.bottom - parsedArea.top
};
};
export default CVUtils;

@ -1,7 +1,9 @@
import SubImage from './subImage';
import CVUtils from '../common/cv_utils';
import {hsv2rgb} from '../common/cv_utils';
import ArrayHelper from '../common/array_helper';
import {vec2} from 'gl-matrix';
const vec2 = {
clone: require('gl-vec2/clone'),
};
/**
* Represents a basic image combining the data and size.
@ -335,7 +337,7 @@ ImageWrapper.prototype.overlay = function(canvas, scale, from) {
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);
result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : hsv2rgb(hsv, rgb);
data[length * 4 + 0] = result[0];
data[length * 4 + 1] = result[1];
data[length * 4 + 2] = result[2];

@ -14,10 +14,6 @@ if (typeof window !== 'undefined') {
window.setTimeout(callback, 1000 / 60);
};
})();
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
}
Math.imul = Math.imul || function(a, b) {
var ah = (a >>> 16) & 0xffff,

@ -5,9 +5,9 @@ module.exports = {
constraints: {
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment" // or user
// aspectRatio: 640/480, // optional
facingMode: "environment", // or user
// deviceId: "38745983457387598375983759834"
},
area: {
top: "0%",

@ -5,9 +5,9 @@ module.exports = {
constraints: {
width: 640,
height: 480,
minAspectRatio: 0,
maxAspectRatio: 100,
facing: "environment" // or user
// aspectRatio: 640/480, // optional
facingMode: "environment", // or user
// deviceId: "38745983457387598375983759834"
},
area: {
top: "0%",

@ -1,4 +1,3 @@
import CVUtils from '../common/cv_utils';
import ImageWrapper from '../common/image_wrapper';
var Bresenham = {};
@ -90,20 +89,6 @@ Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) {
};
};
Bresenham.toOtsuBinaryLine = function(result) {
var line = result.line,
image = new ImageWrapper({x: line.length - 1, y: 1}, line),
threshold = CVUtils.determineOtsuThreshold(image, 5);
line = CVUtils.sharpenLine(line);
CVUtils.thresholdImage(image, threshold);
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

@ -1,45 +1,29 @@
const merge = require('lodash/object/merge');
import {merge, pick} from 'lodash';
var streamRef,
loadedDataHandler;
/**
* Wraps browser-specific getUserMedia
* @param {Object} constraints
* @param {Object} success Callback
* @param {Object} failure Callback
*/
function getUserMedia(constraints, success, failure) {
if (typeof navigator.getUserMedia !== 'undefined') {
navigator.getUserMedia(constraints, function (stream) {
streamRef = stream;
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
success.apply(null, [videoSrc]);
}, failure);
} else {
failure(new TypeError("getUserMedia not available"));
}
}
function loadedData(video, callback) {
var attempts = 10;
function waitForVideo(video) {
return new Promise((resolve, reject) => {
let attempts = 10;
function checkVideo() {
if (attempts > 0) {
if (video.videoWidth > 0 && video.videoHeight > 0) {
if (ENV.development) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
function checkVideo() {
if (attempts > 0) {
if (video.videoWidth > 0 && video.videoHeight > 0) {
if (ENV.development) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
}
resolve();
} else {
window.setTimeout(checkVideo, 500);
}
callback();
} else {
window.setTimeout(checkVideo, 500);
reject('Unable to play video stream. Is webcam working?');
}
} else {
callback('Unable to play video stream. Is webcam working?');
attempts--;
}
attempts--;
}
checkVideo();
checkVideo();
});
}
/**
@ -47,89 +31,72 @@ function loadedData(video, callback) {
* 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);
});
function initCamera(video, constraints) {
return navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
return new Promise((resolve, reject) => {
streamRef = stream;
video.src = window.URL.createObjectURL(stream);
video.onloadedmetadata = (e) => {
video.play();
resolve();
};
});
})
.then(waitForVideo.bind(null, video));
}
/**
* 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);
function deprecatedConstraints(videoConstraints) {
const normalized = pick(videoConstraints, ["width", "height", "facingMode",
"aspectRatio", "deviceId"]);
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;
if (typeof videoConstraints["minAspectRatio"] !== 'undefined' &&
videoConstraints["minAspectRatio"] > 0) {
normalized["aspectRatio"] = videoConstraints["minAspectRatio"];
console.log("WARNING: Constraint 'minAspectRatio' is deprecated; Use 'aspectRatio' instead");
}
if (typeof videoConstraints["facing"] !== 'undefined') {
normalized["facingMode"] = videoConstraints["facing"];
console.log("WARNING: Constraint 'facing' is deprecated. Use 'facingMode' instead'");
}
return normalized;
}
function applyCameraFacing(facing, constraints) {
if (typeof constraints.video.deviceId !== 'undefined' || !facing){
return Promise.resolve(constraints);
}
if ( typeof MediaStreamTrack !== 'undefined' &&
typeof MediaStreamTrack.getSources !== 'undefined') {
return new Promise((resolve, reject) => {
MediaStreamTrack.getSources((sourceInfos) => {
const videoSource = sourceInfos.filter((sourceInfo) => (
sourceInfo.kind === "video" && sourceInfo.facing === facing
))[0];
if (videoSource) {
return resolve(merge({}, constraints,
{video: {deviceId: videoSource.id}}));
}
}
constraints.video = {
mandatory: {
minWidth: videoConstraints.width,
minHeight: videoConstraints.height,
minAspectRatio: videoConstraints.minAspectRatio,
maxAspectRatio: videoConstraints.maxAspectRatio
},
optional: [{
sourceId: videoSourceId
}]
};
return cb(constraints);
return resolve(constraints);
});
});
} 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 Promise.resolve(merge({}, constraints, {video: {facingMode: facing}}));
}
/**
* 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);
});
function pickConstraints(videoConstraints) {
const constraints = {
audio: false,
video: deprecatedConstraints(videoConstraints)
};
return applyCameraFacing(constraints.video.facingMode, constraints);
}
export default {
request: function(video, constraints, callback) {
request(video, constraints, callback);
request: function(video, videoConstraints) {
return pickConstraints(videoConstraints)
.then(initCamera.bind(null, video));
},
release: function() {
var tracks = streamRef && streamRef.getVideoTracks();

@ -1,13 +1,17 @@
import CVUtils from '../common/cv_utils';
import {
imageRef,
grayAndHalfSampleFromCanvasData,
computeGray
} from '../common/cv_utils';
var FrameGrabber = {};
FrameGrabber.create = function(inputStream, canvas) {
var _that = {},
_streamConfig = inputStream.getConfig(),
_video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()),
_video_size = imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()),
_canvasSize = inputStream.getCanvasSize(),
_size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()),
_size = imageRef(inputStream.getWidth(), inputStream.getHeight()),
topRight = inputStream.getTopRight(),
_sx = topRight.x,
_sy = topRight.y,
@ -55,9 +59,9 @@ FrameGrabber.create = function(inputStream, canvas) {
_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);
grayAndHalfSampleFromCanvasData(ctxData, _size, _data);
} else {
CVUtils.computeGray(ctxData, _data, _streamConfig);
computeGray(ctxData, _data, _streamConfig);
}
return true;
} else {

@ -1,11 +1,30 @@
import ImageWrapper from '../common/image_wrapper';
import CVUtils from '../common/cv_utils';
import {
calculatePatchSize,
otsuThreshold,
hsv2rgb,
cluster,
topGeneric,
imageRef,
halfSample,
computeImageArea
} from '../common/cv_utils';
import ArrayHelper from '../common/array_helper';
import ImageDebug from '../common/image_debug';
import Rasterizer from './rasterizer';
import Tracer from './tracer';
import skeletonizer from './skeletonizer';
import {vec2, mat2} from 'gl-matrix';
const vec2 = {
clone: require('gl-vec2/clone'),
dot: require('gl-vec2/dot'),
scale: require('gl-vec2/scale'),
transformMat2: require('gl-vec2/transformMat2')
};
const mat2 = {
copy: require('gl-mat2/copy'),
create: require('gl-mat2/create'),
invert: require('gl-mat2/invert')
}
var _config,
_currentImageWrapper,
@ -41,7 +60,7 @@ function initBuffers() {
_currentImageWrapper = _inputImageWrapper;
}
_patchSize = CVUtils.calculatePatchSize(_config.patchSize, _currentImageWrapper.size);
_patchSize = calculatePatchSize(_config.patchSize, _currentImageWrapper.size);
_numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0;
_numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0;
@ -117,7 +136,7 @@ function boxFromPatches(patches) {
}
overAvg = (180 - overAvg) * Math.PI / 180;
transMat = mat2.clone([Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]);
transMat = mat2.copy(mat2.create(), [Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]);
// iterate over patches and rotate by angle
for ( i = 0; i < patches.length; i++) {
@ -178,9 +197,9 @@ function boxFromPatches(patches) {
* Creates a binary image of the current image
*/
function binarizeImage() {
CVUtils.otsuThreshold(_currentImageWrapper, _binaryImageWrapper);
otsuThreshold(_currentImageWrapper, _binaryImageWrapper);
_binaryImageWrapper.zeroBorder();
if (_config.showCanvas) {
if (_config.debug.showCanvas) {
_binaryImageWrapper.show(_canvasContainer.dom.binary, 255);
}
}
@ -309,7 +328,7 @@ function findBoxes(topLabels, maxLabel) {
for ( j = 0; j < patches.length; j++) {
patch = patches[j];
hsv[0] = (topLabels[i].label / (maxLabel + 1)) * 360;
CVUtils.hsv2rgb(hsv, rgb);
hsv2rgb(hsv, rgb);
ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary,
{color: "rgb(" + rgb.join(",") + ")", lineWidth: 2});
}
@ -324,8 +343,8 @@ function findBoxes(topLabels, maxLabel) {
* @param {Object} moments
*/
function similarMoments(moments) {
var clusters = CVUtils.cluster(moments, 0.90);
var topCluster = CVUtils.topGeneric(clusters, 1, function(e) {
var clusters = cluster(moments, 0.90);
var topCluster = topGeneric(clusters, 1, function(e) {
return e.getPoints().length;
});
var points = [], result = [];
@ -339,12 +358,12 @@ function similarMoments(moments) {
}
function skeletonize(x, y) {
_binaryImageWrapper.subImageAsCopy(_subImageWrapper, CVUtils.imageRef(x, y));
_binaryImageWrapper.subImageAsCopy(_subImageWrapper, imageRef(x, y));
_skeletonizer.skeletonize();
// Show skeleton if requested
if (ENV.development && _config.debug.showSkeleton) {
_skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, CVUtils.imageRef(x, y));
_skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, imageRef(x, y));
}
}
@ -496,7 +515,7 @@ function rasterizeAngularSimilarity(patchesFound) {
if (_patchLabelGrid.data[j] > 0 && _patchLabelGrid.data[j] <= label) {
patch = _imageToPatchGrid.data[j];
hsv[0] = (_patchLabelGrid.data[j] / (label + 1)) * 360;
CVUtils.hsv2rgb(hsv, rgb);
hsv2rgb(hsv, rgb);
ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary,
{color: "rgb(" + rgb.join(",") + ")", lineWidth: 2});
}
@ -521,7 +540,7 @@ export default {
boxes;
if (_config.halfSample) {
CVUtils.halfSample(_inputImageWrapper, _currentImageWrapper);
halfSample(_inputImageWrapper, _currentImageWrapper);
}
binarizeImage();
@ -557,7 +576,7 @@ export default {
// calculate width and height based on area
if (inputStream.getConfig().area) {
area = CVUtils.computeImageArea(width, height, inputStream.getConfig().area);
area = computeImageArea(width, height, inputStream.getConfig().area);
inputStream.setTopRight({x: area.sx, y: area.sy});
inputStream.setCanvasSize({x: width, y: height});
width = area.sw;
@ -569,7 +588,7 @@ export default {
y: Math.floor(height * halfSample)
};
patchSize = CVUtils.calculatePatchSize(config.patchSize, size);
patchSize = calculatePatchSize(config.patchSize, size);
if (ENV.development) {
console.log("Patch-Size: " + JSON.stringify(patchSize));
}

@ -198,12 +198,10 @@ function Skeletonizer(stdlib, foreign, buffer) {
done = ((sum | 0) == 0 | 0);
} while (!done);
}
return {
skeletonize: skeletonize
};
}
/* @preserve ASM END */
export default Skeletonizer;
/* eslint-enable eqeqeq*/
/* @preserve ASM END */

@ -1,17 +1,19 @@
import TypeDefs from './common/typedefs'; // eslint-disable-line no-unused-vars
import WebrtcAdapter from 'webrtc-adapter'; // eslint-disable-line no-unused-vars
import ImageWrapper from './common/image_wrapper';
import BarcodeLocator from './locator/barcode_locator';
import BarcodeDecoder from './decoder/barcode_decoder';
import Events from './common/events';
import CameraAccess from './input/camera_access';
import ImageDebug from './common/image_debug';
import {vec2} from 'gl-matrix';
import ResultCollector from './analytics/result_collector';
import Config from './config/config';
import InputStream from 'input_stream';
import FrameGrabber from 'frame_grabber';
const merge = require('lodash/object/merge');
import {merge} from 'lodash';
const vec2 = {
clone: require('gl-vec2/clone')
};
var _inputStream,
_framegrabber,
@ -56,12 +58,11 @@ function initInputStream(cb) {
}
}
_inputStream = InputStream.createLiveStream(video);
CameraAccess.request(video, _config.inputStream.constraints, function(err) {
if (!err) {
_inputStream.trigger("canrecord");
} else {
return cb(err);
}
CameraAccess.request(video, _config.inputStream.constraints)
.then(() => {
_inputStream.trigger("canrecord");
}).catch((err) => {
return cb(err);
});
}
@ -236,11 +237,12 @@ function hasCodeResult (result) {
}
function publishResult(result, imageData) {
const resultToPublish = result && (result.barcodes || result);
let resultToPublish = result;
if (result && _onUIThread) {
transformResult(result);
addResult(result, imageData);
resultToPublish = result.barcodes || result;
}
Events.publish("processed", resultToPublish);
@ -356,15 +358,24 @@ function initWorker(cb) {
cmd: 'init',
size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()},
imageData: workerThread.imageData,
config: _config
config: configForWorker(_config)
}, [workerThread.imageData.buffer]);
}
function configForWorker(config) {
return {
...config,
inputStream: {
...config.inputStream,
target: null
}
};
}
function workerInterface(factory) {
/* eslint-disable no-undef*/
if (factory) {
var Quagga = factory();
var Quagga = factory().default;
if (!Quagga) {
self.postMessage({'event': 'error', message: 'Quagga could not be created'});
return;
@ -519,9 +530,9 @@ export default {
halfSample: false
}
}, config);
this.init(config, function() {
Events.once("processed", function(result) {
_stopped = true;
this.init(config, () => {
Events.once("processed", (result) => {
this.stop();
resultCallback.call(null, result);
}, true);
start();

@ -1,5 +1,5 @@
import BarcodeReader from './barcode_reader';
const merge = require('lodash/object/merge');
import {merge} from 'lodash';
function I2of5Reader(opts) {
opts = merge(getDefaulConfig(), opts);

@ -11,7 +11,7 @@ module.exports = function(grunt) {
var code = fs.readFileSync('dist/quagga.js', 'utf-8'),
minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'),
commentEnd = '/* @preserve ASM END */',
moduleFunctionRegex = /function\s*\((\w+,\s*\w+)\)\s*\{\s*\/\* \@preserve ASM BEGIN \*\//,
moduleFunctionRegex = /function\s*\((\w+,\s*\w+,\s*\w+)\)\s*\{\s*\/\* \@preserve ASM BEGIN \*\//,
commentStartIdx = code.indexOf("/* @preserve ASM BEGIN */"),
asmEndIdxTmp = code.indexOf(commentEnd),
asmEndIdx = code.indexOf("}", asmEndIdxTmp),

@ -1,4 +1,4 @@
const Quagga = require('../../src/quagga');
const Quagga = require('../../src/quagga').default;
const async = require('async');
describe('decodeSingle', function () {

@ -1,4 +1,4 @@
const ArrayHelper = require('../../src/common/array_helper');
import ArrayHelper from '../../src/common/array_helper';
describe('init', function() {
it('initializes an array with the given value', function() {

@ -1,6 +1,6 @@
const BarcodeLocator = require('../../src/locator/barcode_locator');
const Config = require('../../src/config/config');
const merge = require('lodash/object/merge');
import BarcodeLocator from '../../src/locator/barcode_locator';
import Config from '../../src/config/config';
import {merge} from 'lodash';
describe('checkImageConstraints', function() {
var config,

@ -1,4 +1,4 @@
const CameraAccess = require('../../src/input/camera_access');
import CameraAccess from '../../src/input/camera_access';
var originalURL,
originalMediaStreamTrack,
@ -13,7 +13,11 @@ beforeEach(function() {
originalURL = window.URL;
originalMediaStreamTrack = window.MediaStreamTrack;
window.MediaStreamTrack = {};
window.URL = null;
window.URL = {
createObjectURL(stream) {
return stream;
}
};
stream = {
getVideoTracks: function() {
@ -43,51 +47,119 @@ afterEach(function() {
describe('success', function() {
beforeEach(function() {
sinon.stub(navigator, "getUserMedia", function(constraints, success) {
success(stream);
sinon.stub(navigator.mediaDevices, "getUserMedia", function(constraints) {
return Promise.resolve(stream);
});
});
afterEach(function() {
navigator.getUserMedia.restore();
navigator.mediaDevices.getUserMedia.restore();
});
describe('request', function () {
it('should request the camera', function (done) {
CameraAccess.request(video, {}, function () {
expect(navigator.getUserMedia.calledOnce).to.equal(true);
CameraAccess.request(video, {})
.then(function () {
expect(navigator.mediaDevices.getUserMedia.calledOnce).to.equal(true);
expect(video.src).to.deep.equal(stream);
done();
});
})
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
});
it("should allow deprecated constraints to be used", (done) => {
CameraAccess.request(video, {
width: 320,
height: 240,
facing: "user",
minAspectRatio: 2,
maxAspectRatio: 100
})
.then(function () {
const call = navigator.mediaDevices.getUserMedia.getCall(0),
args = call.args;
expect(call).to.be.defined;
expect(args[0].video.width).to.equal(320);
expect(args[0].video.height).to.equal(240);
expect(args[0].video.facingMode).to.equal("user");
expect(args[0].video.aspectRatio).to.equal(2);
expect(args[0].video.facing).not.to.be.defined;
expect(args[0].video.minAspectRatio).not.to.be.defined;
expect(args[0].video.maxAspectRatio).not.to.be.defined;
done();
})
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
});
});
describe('facingMode fallback in Chrome', () => {
beforeEach(() => {
window.MediaStreamTrack.getSources = (cb) => {
return cb([
{kind: "video", facing: "environment", id: "environment"},
{kind: "audio", id: "audio"},
{kind: "video", facing: "user", id: "user"}
]);
};
});
afterEach(() => {
window.MediaStreamTrack = {};
})
it("should set deviceId in case facingMode is not supported", (done) => {
CameraAccess.request(video, {
facing: "user"
})
.then(function () {
const call = navigator.mediaDevices.getUserMedia.getCall(0),
args = call.args;
expect(call).to.be.defined;
expect(args[0].video.facingMode).not.to.be.defined;
expect(args[0].video.deviceId).to.equal("user");
done();
})
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
});
});
describe('release', function () {
it('should release the camera', function (done) {
CameraAccess.request(video, {}, function () {
CameraAccess.request(video, {})
.then(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();
});
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
});
});
});
describe('failure', function() {
describe("permission denied", function(){
before(function() {
sinon.stub(navigator, "getUserMedia", function(constraints, success, failure) {
failure(new Error());
beforeEach(function() {
sinon.stub(navigator.mediaDevices, "getUserMedia", function(constraints, success, failure) {
return Promise.reject(new Error());
});
});
after(function() {
navigator.getUserMedia.restore();
afterEach(function() {
navigator.mediaDevices.getUserMedia.restore();
});
it('should throw if getUserMedia not available', function(done) {
CameraAccess.request(video, {}, function(err) {
CameraAccess.request(video, {})
.catch(function (err) {
expect(err).to.be.defined;
done();
});
@ -97,17 +169,18 @@ describe('failure', function() {
describe("not available", function(){
var originalGetUserMedia;
before(function() {
originalGetUserMedia = navigator.getUserMedia;
navigator.getUserMedia = undefined;
beforeEach(function() {
originalGetUserMedia = navigator.mediaDevices.getUserMedia;
navigator.mediaDevices.getUserMedia = undefined;
});
after(function() {
navigator.getUserMedia = originalGetUserMedia;
afterEach(function() {
navigator.mediaDevices.getUserMedia = originalGetUserMedia;
});
it('should throw if getUserMedia not available', function(done) {
CameraAccess.request(video, {}, function(err) {
CameraAccess.request(video, {})
.catch((err) => {
expect(err).to.be.defined;
done();
});

@ -1,8 +1,14 @@
const CVUtils = require('../../src/common/cv_utils');
const {
imageRef,
calculatePatchSize,
_parseCSSDimensionValues,
_dimensionsConverters,
computeImageArea
} = require('../../src/common/cv_utils');
describe('imageRef', function() {
it('gets the image Reference for a coordinate', function() {
var res = CVUtils.imageRef(1, 2);
var res = imageRef(1, 2);
expect(res.x).to.equal(1);
expect(res.y).to.equal(2);
expect(res.toVec2()[0]).to.equal(1);
@ -12,14 +18,14 @@ describe('imageRef', function() {
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});
patchSize = 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});
patchSize = calculatePatchSize("medium", {x: 640, y: 480});
expect(patchSize).to.be.deep.equal(expected);
});
@ -31,7 +37,7 @@ describe('_parseCSSDimensionValues', function() {
value: 10,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("10%");
result = _parseCSSDimensionValues("10%");
expect(result).to.be.deep.equal(expected);
});
@ -41,7 +47,7 @@ describe('_parseCSSDimensionValues', function() {
value: 100,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("100%");
result = _parseCSSDimensionValues("100%");
expect(result).to.be.deep.equal(expected);
});
@ -51,7 +57,7 @@ describe('_parseCSSDimensionValues', function() {
value: 0,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("0%");
result = _parseCSSDimensionValues("0%");
expect(result).to.be.deep.equal(expected);
});
@ -61,7 +67,7 @@ describe('_parseCSSDimensionValues', function() {
value: 26.3,
unit: "%"
},
result = CVUtils._parseCSSDimensionValues("26.3px");
result = _parseCSSDimensionValues("26.3px");
console.log(result);
expect(result).to.be.deep.equal(expected);
@ -80,28 +86,28 @@ describe("_dimensionsConverters", function(){
it("should convert a top-value correclty", function() {
var expected = 48,
result = CVUtils._dimensionsConverters.top({value: 10, unit: "%"}, context);
result = _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);
result = _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);
result = _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);
result = _dimensionsConverters.left({value: 9, unit: "%"}, context);
expect(result).to.be.equal(expected);
});
@ -115,7 +121,7 @@ describe("computeImageArea", function() {
sw: 429,
sh: 336
},
result = CVUtils.computeImageArea(640, 480, {
result = computeImageArea(640, 480, {
top: "10%",
right: "15%",
bottom: "20%",
@ -132,7 +138,7 @@ describe("computeImageArea", function() {
sw: 640,
sh: 480
},
result = CVUtils.computeImageArea(640, 480, {
result = computeImageArea(640, 480, {
top: "0%",
right: "0%",
bottom: "0%",

@ -1,4 +1,4 @@
const Events = require('../../src/common/events');
import Events from '../../src/common/events';
beforeEach(function() {
Events.unsubscribe();

@ -1,5 +1,5 @@
const ResultCollector = require('../../src/analytics/result_collector');
const ImageDebug = require('../../src/common/image_debug');
import ResultCollector from '../../src/analytics/result_collector';
import ImageDebug from '../../src/common/image_debug';
var canvasMock,
imageSize,

@ -11,16 +11,14 @@ module.exports = {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
loader: 'babel-loader'
}]
},
resolve: {
extensions: ['', '.js', '.jsx'],
root: path.resolve(__dirname),
alias: {
'input_stream$': 'src/input/input_stream',
'frame_grabber$': 'src/input/frame_grabber'
}
modules: [
path.resolve('./src/input/'),
'node_modules'
]
},
output: {
path: __dirname + '/dist',

@ -1,9 +1,21 @@
var webpack = require('webpack');
module.exports = require('./webpack.config.js');
module.exports.plugins.unshift(
new webpack.optimize.UglifyJsPlugin()
);
module.exports.plugins = module.exports.plugins.concat([
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
comments: /@preserve/
},
sourceMap: false
}),
]);
module.exports.output.filename = 'quagga.min.js';

@ -4,12 +4,10 @@ var webpack = require('webpack'),
module.exports = require('./webpack.config.js');
module.exports.resolve = {
extensions: ['', '.js', '.jsx'],
root: path.resolve(__dirname),
alias: {
'input_stream': 'lib/input_stream',
'frame_grabber': 'lib/frame_grabber'
}
modules: [
path.resolve('./lib/'),
'node_modules'
]
};
module.exports.externals = [
@ -19,7 +17,8 @@ module.exports.externals = [
"ndarray",
"ndarray-linear-interpolate"
];
module.exports.output.libraryTarget = "commonjs2";
module.exports.output.libraryTarget = "umd";
module.exports.output.library = "Quagga";
module.exports.plugins = [
new webpack.DefinePlugin({
ENV: require(path.join(__dirname, './env/', process.env.BUILD_ENV))

Loading…
Cancel
Save