Added advanced configuration

pull/35/head
Christoph Oberhofer 10 years ago
parent 36f75d2a95
commit f75da96040

@ -1,7 +1,7 @@
quaggaJS
========
- [Changelog](#changelog) (2015-04-16)
- [Changelog](#changelog) (2015-04-25)
## What is QuaggaJS?
@ -237,6 +237,7 @@ The default `config` object is set as followed:
},
locator: {
halfSample: true,
patchSize: "medium", // x-small, small, medium, large, x-large
showCanvas: false,
showPatches: false,
showFoundPatches: false,
@ -291,6 +292,12 @@ work.
## <a name="changelog">Changelog</a>
### 2015-04-25
- Improvements
- Added extended configuration to the file-input example
- Configurable ``patchSize`` for better adjustment to small/medium/large
barcodes
### 2015-04-16
- Features
- Added support for [Codabar][codabar_wiki] barcodes

138
dist/quagga.js vendored

@ -4724,6 +4724,71 @@ define('cv_utils',['cluster', 'glMatrixAddon', "array_helper"], function(Cluster
return rgb;
};
CVUtils._computeDivisors = function(n) {
var largeDivisors = [],
divisors = [],
i;
for (i = 1; i < Math.sqrt(n) + 1; i++) {
if (n % i === 0) {
divisors.push(i);
if (i !== n/i) {
largeDivisors.unshift(Math.floor(n/i));
}
}
}
return divisors.concat(largeDivisors);
};
CVUtils._computeIntersection = function(arr1, arr2) {
var i = 0,
j = 0,
result = [];
while (i < arr1.length && j < arr2.length) {
if (arr1[i] === arr2[j]) {
result.push(arr1[i]);
i++;
j++;
} else if (arr1[i] > arr2[j]) {
j++;
} else {
i++;
}
}
return result;
};
CVUtils.calculatePatchSize = function(patchSize, imgSize) {
var divisorsX = this._computeDivisors(imgSize.x),
divisorsY = this._computeDivisors(imgSize.y),
wideSide = Math.max(imgSize.x, imgSize.y),
common = this._computeIntersection(divisorsX, divisorsY),
nrOfPatchesMap = {
"x-small": 60,
"small": 32,
"medium": 20,
"large": 15,
"x-large": 10
},
nrOfPatches = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium,
i = 0,
found = common[Math.floor(common.length/2)],
desiredPatchSize = wideSide/nrOfPatches;
while(i < (common.length - 1) && common[i] < desiredPatchSize) {
i++;
}
if (i > 0) {
if (Math.abs(common[i] - desiredPatchSize) > Math.abs(common[i-1] - desiredPatchSize)) {
found = common[i-1];
} else {
found = common[i];
}
}
return {x: found, y: found};
};
return (CVUtils);
});
@ -5736,10 +5801,7 @@ function(ImageWrapper, CVUtils, Rasterizer, Tracer, skeletonizer, ArrayHelper, I
_currentImageWrapper = _inputImageWrapper;
}
_patchSize = {
x : _config.patchSize * ( _config.halfSample ? 0.5 : 1),
y : _config.patchSize * ( _config.halfSample ? 0.5 : 1)
};
_patchSize = CVUtils.calculatePatchSize(_config.patchSize, _currentImageWrapper.size);
_numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0;
_numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0;
@ -7355,7 +7417,7 @@ define('config',[],function(){
},
locator: {
halfSample: true,
patchSize: 32,
patchSize: "medium", // x-small, small, medium, large, x-large
showCanvas: false,
showPatches: false,
showFoundPatches: false,
@ -7597,8 +7659,33 @@ define('camera_access',["html_utils"], function(HtmlUtils) {
/* global define, vec2 */
define('quagga',["code_128_reader", "ean_reader", "input_stream", "image_wrapper", "barcode_locator", "barcode_decoder", "frame_grabber", "html_utils", "config", "events", "camera_access", "image_debug"],
function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, BarcodeDecoder, FrameGrabber, HtmlUtils, _config, Events, CameraAccess, ImageDebug) {
define('quagga',[
"code_128_reader",
"ean_reader",
"input_stream",
"image_wrapper",
"barcode_locator",
"barcode_decoder",
"frame_grabber",
"html_utils",
"config",
"events",
"camera_access",
"image_debug",
"cv_utils"],
function(Code128Reader,
EANReader,
InputStream,
ImageWrapper,
BarcodeLocator,
BarcodeDecoder,
FrameGrabber,
HtmlUtils,
_config,
Events,
CameraAccess,
ImageDebug,
CVUtils) {
var _inputStream,
@ -7680,17 +7767,23 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
function checkImageConstraints() {
var patchSize,
width = _inputStream.getWidth(),
height = _inputStream.getHeight();
height = _inputStream.getHeight(),
halfSample = _config.locator.halfSample,
size = {
x: Math.floor(width * (halfSample ? 0.5 : 1)),
y: Math.floor(height * (halfSample ? 0.5 : 1))
};
if (_config.locate) {
patchSize = _config.locator.patchSize * ( _config.locator.halfSample ? 0.5 : 1);
if ((width % patchSize) === 0 && (height % patchSize) === 0) {
patchSize = CVUtils.calculatePatchSize(_config.locator.patchSize, size);
console.log("Patch-Size: " + JSON.stringify(patchSize));
if ((width % patchSize.x) === 0 && (height % patchSize.y) === 0) {
return true;
}
}
throw new Error("Image dimensions do not comply with the current settings: Width (" +
width + " )and height (" + height +
") must a multiple of " + patchSize);
") must a multiple of " + patchSize.x);
}
function canRecord(cb) {
@ -7989,17 +8082,18 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
},
canvas : _canvasContainer,
decodeSingle : function(config, resultCallback) {
config.inputStream = {
type : "ImageStream",
src : config.src,
sequence : false,
size: 800
};
config.numOfWorkers = 1;
config.locator = {
halfSample: false,
patchSize: 25
};
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("detected", function(result) {
_stopped = true;

File diff suppressed because one or more lines are too long

@ -19,12 +19,14 @@
/* line 1, ../sass/_viewport.scss */
#interactive.viewport {
width: 640px;
height: 480px;
}
/* line 6, ../sass/_viewport.scss */
#interactive.viewport canvas, video {
float: left;
width: 640px;
height: 480px;
}
/* line 10, ../sass/_viewport.scss */
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
@ -32,19 +34,46 @@
}
/* line 16, ../sass/_viewport.scss */
.controls .reader-group {
float: right;
.controls fieldset {
border: none;
}
/* line 19, ../sass/_viewport.scss */
.controls .input-group {
float: left;
}
/* line 21, ../sass/_viewport.scss */
.controls .input-group input, .controls .input-group button {
display: block;
}
/* line 25, ../sass/_viewport.scss */
.controls .reader-config-group {
float: right;
}
/* line 28, ../sass/_viewport.scss */
.controls .reader-config-group label {
display: block;
}
/* line 30, ../sass/_viewport.scss */
.controls .reader-config-group label span {
width: 11rem;
display: inline-block;
text-align: right;
}
/* line 37, ../sass/_viewport.scss */
.controls:after {
content: '';
display: block;
clear: both;
}
/* line 22, ../sass/_viewport.scss */
/* line 44, ../sass/_viewport.scss */
#result_strip {
margin: 10px 0;
border-top: 1px solid #EEE;
border-bottom: 1px solid #EEE;
padding: 10px 0;
}
/* line 28, ../sass/_viewport.scss */
/* line 50, ../sass/_viewport.scss */
#result_strip ul.thumbnails {
padding: 0;
margin: 0;
@ -54,34 +83,34 @@
overflow-y: hidden;
white-space: nowrap;
}
/* line 37, ../sass/_viewport.scss */
/* line 59, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li {
display: inline-block;
vertical-align: middle;
width: 160px;
}
/* line 41, ../sass/_viewport.scss */
/* line 63, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail {
padding: 5px;
margin: 4px;
border: 1px dashed #CCC;
}
/* line 46, ../sass/_viewport.scss */
/* line 68, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail img {
max-width: 140px;
}
/* line 49, ../sass/_viewport.scss */
/* line 71, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail .caption {
white-space: normal;
}
/* line 51, ../sass/_viewport.scss */
/* line 73, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail .caption h4 {
text-align: center;
word-wrap: break-word;
height: 40px;
margin: 0px;
}
/* line 61, ../sass/_viewport.scss */
/* line 83, ../sass/_viewport.scss */
#result_strip ul.thumbnails:after {
content: "";
display: table;

@ -34,14 +34,55 @@
access through <code>getUserMedia</code> is still very limited.</p>
<div class="controls">
<input type="file" capture/>
<fieldset class="reader-group">
<label>Code128</label>
<input type="radio" name="reader" value="code_128"/>
<label>EAN</label>
<input type="radio" name="reader" value="ean"/>
<label>Code39</label>
<input type="radio" name="reader" value="code_39" checked/>
<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="ean">EAN</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 value="640">640px</option>
<option selected="selected" 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" name="locator_half-sample" />
</label>
<label>
<span>Workers</span>
<select name="numOfWorkers">
<option value="0">0</option>
<option value="1" selected="selected">1</option>
<option value="2">2</option>
<option value="4">4</option>
<option value="8">8</option>
</select>
</label>
</fieldset>
</div>
<div id="result_strip">

@ -4,35 +4,106 @@ $(function() {
App.attachListeners();
},
config: {
reader: "code_39",
reader: "code_128",
length: 10
},
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 .reader-group").on("change", "input", function(e) {
$(".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();
App.detachListeners();
App.config.reader = e.target.value;
App.init();
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-group").off("change", "input");
$(".controls .reader-config-group").off("change", "input, select");
$(".controls button").off("click");
},
decode: function(src) {
Quagga.decodeSingle({
decoder: {
readers : [App.config.reader + '_reader']
},
locate : true,
src : src
}, function(result) {});
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: 800
},
locator: {
patchSize: "medium",
halfSample: false
},
numOfWorkers: 1,
decoder: {
readers: ["code_128_reader"]
},
locate: true,
src: null
}
};

@ -13,10 +13,32 @@
}
.controls {
.reader-group {
float: right;
fieldset {
border: none;
}
.input-group {
float: left;
input, button {
display: block;
}
}
.reader-config-group {
float: right;
label {
display: block;
span {
width: 11rem;
display: inline-block;
text-align: right;
}
}
}
&:after {
content:'';
display: block;
clear: both;
}
}
#result_strip{

@ -4,7 +4,7 @@
"description": "An advanced barcode-scanner written in JavaScript",
"main": "dist/quagga.js",
"devDependencies": {
"grunt": "~0.4.5",
"grunt": "~0.4.6",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-nodeunit": "~0.4.1",
"grunt-contrib-uglify": "~0.5.0",

@ -39,10 +39,7 @@ function(ImageWrapper, CVUtils, Rasterizer, Tracer, skeletonizer, ArrayHelper, I
_currentImageWrapper = _inputImageWrapper;
}
_patchSize = {
x : _config.patchSize * ( _config.halfSample ? 0.5 : 1),
y : _config.patchSize * ( _config.halfSample ? 0.5 : 1)
};
_patchSize = CVUtils.calculatePatchSize(_config.patchSize, _currentImageWrapper.size);
_numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0;
_numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0;

@ -31,7 +31,7 @@ define(function(){
},
locator: {
halfSample: true,
patchSize: 32,
patchSize: "medium", // x-small, small, medium, large, x-large
showCanvas: false,
showPatches: false,
showFoundPatches: false,

@ -539,6 +539,71 @@ define(['cluster', 'glMatrixAddon', "array_helper"], function(Cluster2, glMatrix
return rgb;
};
CVUtils._computeDivisors = function(n) {
var largeDivisors = [],
divisors = [],
i;
for (i = 1; i < Math.sqrt(n) + 1; i++) {
if (n % i === 0) {
divisors.push(i);
if (i !== n/i) {
largeDivisors.unshift(Math.floor(n/i));
}
}
}
return divisors.concat(largeDivisors);
};
CVUtils._computeIntersection = function(arr1, arr2) {
var i = 0,
j = 0,
result = [];
while (i < arr1.length && j < arr2.length) {
if (arr1[i] === arr2[j]) {
result.push(arr1[i]);
i++;
j++;
} else if (arr1[i] > arr2[j]) {
j++;
} else {
i++;
}
}
return result;
};
CVUtils.calculatePatchSize = function(patchSize, imgSize) {
var divisorsX = this._computeDivisors(imgSize.x),
divisorsY = this._computeDivisors(imgSize.y),
wideSide = Math.max(imgSize.x, imgSize.y),
common = this._computeIntersection(divisorsX, divisorsY),
nrOfPatchesMap = {
"x-small": 60,
"small": 32,
"medium": 20,
"large": 15,
"x-large": 10
},
nrOfPatches = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium,
i = 0,
found = common[Math.floor(common.length/2)],
desiredPatchSize = wideSide/nrOfPatches;
while(i < (common.length - 1) && common[i] < desiredPatchSize) {
i++;
}
if (i > 0) {
if (Math.abs(common[i] - desiredPatchSize) > Math.abs(common[i-1] - desiredPatchSize)) {
found = common[i-1];
} else {
found = common[i];
}
}
return {x: found, y: found};
};
return (CVUtils);
});

@ -2,8 +2,33 @@
/* global define, vec2 */
define(["code_128_reader", "ean_reader", "input_stream", "image_wrapper", "barcode_locator", "barcode_decoder", "frame_grabber", "html_utils", "config", "events", "camera_access", "image_debug"],
function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, BarcodeDecoder, FrameGrabber, HtmlUtils, _config, Events, CameraAccess, ImageDebug) {
define([
"code_128_reader",
"ean_reader",
"input_stream",
"image_wrapper",
"barcode_locator",
"barcode_decoder",
"frame_grabber",
"html_utils",
"config",
"events",
"camera_access",
"image_debug",
"cv_utils"],
function(Code128Reader,
EANReader,
InputStream,
ImageWrapper,
BarcodeLocator,
BarcodeDecoder,
FrameGrabber,
HtmlUtils,
_config,
Events,
CameraAccess,
ImageDebug,
CVUtils) {
"use strict";
var _inputStream,
@ -85,17 +110,23 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
function checkImageConstraints() {
var patchSize,
width = _inputStream.getWidth(),
height = _inputStream.getHeight();
height = _inputStream.getHeight(),
halfSample = _config.locator.halfSample,
size = {
x: Math.floor(width * (halfSample ? 0.5 : 1)),
y: Math.floor(height * (halfSample ? 0.5 : 1))
};
if (_config.locate) {
patchSize = _config.locator.patchSize * ( _config.locator.halfSample ? 0.5 : 1);
if ((width % patchSize) === 0 && (height % patchSize) === 0) {
patchSize = CVUtils.calculatePatchSize(_config.locator.patchSize, size);
console.log("Patch-Size: " + JSON.stringify(patchSize));
if ((width % patchSize.x) === 0 && (height % patchSize.y) === 0) {
return true;
}
}
throw new Error("Image dimensions do not comply with the current settings: Width (" +
width + " )and height (" + height +
") must a multiple of " + patchSize);
") must a multiple of " + patchSize.x);
}
function canRecord(cb) {
@ -394,17 +425,18 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
},
canvas : _canvasContainer,
decodeSingle : function(config, resultCallback) {
config.inputStream = {
type : "ImageStream",
src : config.src,
sequence : false,
size: 800
};
config.numOfWorkers = 1;
config.locator = {
halfSample: false,
patchSize: 25
};
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("detected", function(result) {
_stopped = true;

Loading…
Cancel
Save