diff --git a/example/file_input.html b/example/file_input.html index dee9aae..06ca78b 100644 --- a/example/file_input.html +++ b/example/file_input.html @@ -77,7 +77,7 @@ Half-Sample - + Single Channel diff --git a/example/live_w_locator.js b/example/live_w_locator.js index 8d4fd9e..fd9892b 100644 --- a/example/live_w_locator.js +++ b/example/live_w_locator.js @@ -3,24 +3,29 @@ $(function() { init : function() { this.overlay = document.querySelector('#interactive canvas.drawing'); - this.scanner = Quagga - .fromConfig(this.state); - - this.scanner - .addEventListener("processed", drawResult.bind(this, this.scanner)) - .addEventListener("detected", addToResults.bind(this, this.scanner)); - - this.scanner.start() - .then(function (){ - console.log("started"); - this.attachListeners(); - }.bind(this)) - .catch(function(err) { - console.log("Error: " + err); - }); + this.state.inputStream.constraints.zoom = {exact: 2}; + Quagga.fromCamera({ + constraints: this.state.inputStream.constraints, + locator: this.state.locator, + decoder: this.state.decoder, + }).then(function(scanner) { + this.scanner = scanner; + this.scanner + .addEventListener("processed", drawResult.bind(this, this.scanner)) + .addEventListener("detected", addToResults.bind(this, this.scanner)); + + this.scanner.start() + .then(function (){ + console.log("started"); + this.attachListeners(); + }.bind(this)) + .catch(function(err) { + console.error(err); + }); + }.bind(this)); }, - initCameraSelection: function(){ - var streamLabel = Quagga.CameraAccess.getActiveStreamLabel(); + initCameraSelection: function() { + var streamLabel = this.scanner.getSource().getLabel(); return Quagga.CameraAccess.enumerateVideoDevices() .then(function(devices) { @@ -106,8 +111,8 @@ $(function() { if (/^(\d+)x(\d+)$/.test(value)) { var values = value.split('x'); return { - width: {min: parseInt(values[0])}, - height: {min: parseInt(values[1])} + width: {ideal: parseInt(values[0])}, + height: {ideal: parseInt(values[1])} }; } return { @@ -141,8 +146,8 @@ $(function() { inputStream: { type : "LiveStream", constraints: { - width: {min: 640}, - height: {min: 480}, + width: {ideal: 640}, + height: {ideal: 480}, facingMode: "environment", aspectRatio: {min: 1, max: 2} } diff --git a/src/common/utils.js b/src/common/utils.js index 1b9c4af..f1cb666 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -4,3 +4,13 @@ export function sleep(millis) { window.setTimeout(resolve, millis); }); } + +export function getViewport(target) { + if (target && target.nodeName && target.nodeType === 1) { + return target; + } else { + // Use '#interactive.viewport' as a fallback selector (backwards compatibility) + var selector = typeof target === 'string' ? target : '#interactive.viewport'; + return document.querySelector(selector); + } +} diff --git a/src/config/config.dev.js b/src/config/config.dev.js index cd039fa..f388b7e 100644 --- a/src/config/config.dev.js +++ b/src/config/config.dev.js @@ -30,7 +30,7 @@ module.exports = { } }, locator: { - halfSample: false, + halfSample: true, patchSize: "medium", // x-small, small, medium, large, x-large debug: { showCanvas: false, diff --git a/src/input/PixelCapture.js b/src/input/PixelCapture.js index 690c898..0a49591 100644 --- a/src/input/PixelCapture.js +++ b/src/input/PixelCapture.js @@ -2,6 +2,7 @@ import { computeGray } from '../common/cv_utils'; import {sleep} from '../common/utils'; +import {getViewport} from '../common/utils'; function adjustCanvasSize(input, canvas) { if (input instanceof HTMLVideoElement) { @@ -25,18 +26,8 @@ function adjustCanvasSize(input, canvas) { } } -function getViewPort(target) { - if (target && target.nodeName && target.nodeType === 1) { - return target; - } else { - // Use '#interactive.viewport' as a fallback selector (backwards compatibility) - var selector = typeof target === 'string' ? target : '#interactive.viewport'; - return document.querySelector(selector); - } -} - function getOrCreateCanvas(source, target) { - const $viewport = getViewPort(target); + const $viewport = getViewport(target); let $canvas = $viewport.querySelector("canvas.imgBuffer"); if (!$canvas) { $canvas = document.createElement("canvas"); diff --git a/src/input/Source.js b/src/input/Source.js index 0e34535..474c843 100644 --- a/src/input/Source.js +++ b/src/input/Source.js @@ -1,21 +1,40 @@ +import {clone} from 'lodash'; import {determineOrientation, PORTRAIT} from '../common/device'; import CameraAccess from './camera_access'; +import {getViewport} from '../common/utils'; -export function fromCamera(constraints) { - if (!constraints) { - constraints = {width: {ideal: 540}, height: {ideal: 540}, aspectRatio: {ideal: 1}, facingMode: 'environment'}; +function getOrCreateVideo(source, target) { + const $viewport = getViewport(target); + if ($viewport) { + let $video = $viewport.querySelector("video"); + if (!$video) { + $video = document.createElement("video"); + $viewport.appendChild($video); + } + return $video; } + return document.createElement("video"); +} + +export function fromCamera(constraints) { var orientation = determineOrientation(); - var videoConstraints = constraints; + var videoConstraints = clone(constraints); if (orientation === PORTRAIT) { - constraints = Object.assign({}, constraints, { - width: constraints.height, - height: constraints.width, + videoConstraints = Object.assign({}, videoConstraints, { + width: videoConstraints.height, + height: videoConstraints.width, }); } - const video = document.querySelector('video'); - CameraAccess.request(videoConstraints, video) + if (videoConstraints.zoom && videoConstraints.zoom.exact > 1) { + videoConstraints.width.ideal = Math.floor(videoConstraints.width.ideal * videoConstraints.zoom.exact); + videoConstraints.height.ideal = Math.floor(videoConstraints.height.ideal * videoConstraints.zoom.exact); + delete videoConstraints.zoom; + } + console.log(videoConstraints); + + const video = getOrCreateVideo(); + return CameraAccess.request(video, videoConstraints) .then(function(mediastream) { const track = mediastream.getVideoTracks()[0]; return { @@ -39,8 +58,8 @@ export function fromCamera(constraints) { return { viewport, canvas: { - width: constraints.width, // AR - height: constraints.height, // AR + width: viewport.width, // AR + height: viewport.height, // AR }, }; }, @@ -65,7 +84,7 @@ export function fromCamera(constraints) { constraints.height.ideal = Math.floor(constraints.height.ideal * constraints.zoom.exact); delete constraints.zoom; } - return CameraAccess.request(videoConstraints, video); + return CameraAccess.request(video, videoConstraints); }, getLabel: function() { return track.label; diff --git a/src/input/camera_access.js b/src/input/camera_access.js index 13fbe7f..abb7c21 100644 --- a/src/input/camera_access.js +++ b/src/input/camera_access.js @@ -10,7 +10,7 @@ var streamRef; function waitForVideo(video, stream) { return new Promise((resolve, reject) => { - let attempts = 10; + let attempts = 20; function checkVideo() { if (attempts > 0) { @@ -20,7 +20,7 @@ function waitForVideo(video, stream) { } resolve(stream); } else { - window.setTimeout(checkVideo, 500); + window.setTimeout(checkVideo, 200); } } else { reject('Unable to play video stream. Is webcam working?'); diff --git a/src/quagga.js b/src/quagga.js index d51098e..6892610 100644 --- a/src/quagga.js +++ b/src/quagga.js @@ -5,13 +5,14 @@ import ImageDebug from './common/image_debug'; import ResultCollector from './analytics/result_collector'; import Config from './config/config'; import {merge} from 'lodash'; -import {createConfigForImage} from './input/config_factory'; +import CameraAccess from './input/camera_access'; import * as PixelCapture from './input/PixelCapture'; import * as Source from './input/Source'; function fromConfig(pixelCapturer, config) { const scanner = createScanner(pixelCapturer); const source = pixelCapturer.getSource(); + let currentConfig = config; let pendingStart = null; let initialized = false; return { @@ -35,7 +36,7 @@ function fromConfig(pixelCapturer, config) { return Promise.resolve(true); } pendingStart = new Promise((resolve, reject) => { - scanner.init(config, (error) => { + scanner.init(currentConfig, (error) => { if (error) { console.log(error); reject(error); @@ -80,7 +81,7 @@ function fromConfig(pixelCapturer, config) { }; } else { return new Promise((resolve, reject) => { - scanner.decodeSingle(config, (result) => { + scanner.decodeSingle(currentConfig, (result) => { if (result && result.codeResult && result.codeResult.code) { return resolve(result); } @@ -95,6 +96,21 @@ function fromConfig(pixelCapturer, config) { getCanvas() { return pixelCapturer.getCanvas(); }, + applyConfig(newConfig) { + // during runtime? + // running scanners must know that! + // apply constraints to source, only if changed + + const normalizedConfig = merge({}, Config, newConfig); + this.stop(); + currentConfig = normalizedConfig; + + return source.applyConstraints(currentConfig.constraints) + .then(() => this.start()); + }, + getSource() { + return pixelCapturer.getSource(); + } }; } @@ -116,6 +132,12 @@ function createApi() { .fromImage(image, config.constraints) .then(fromSource.bind(null, config)); }, + fromCamera(options) { + const config = merge({}, Config, options); + return Source + .fromCamera(config.constraints) + .then(fromSource.bind(null, config)); + }, fromSource(src, inputConfig) { return fromSource(configuration, src, inputConfig); }, @@ -134,6 +156,7 @@ function createApi() { config(conf) { return createApi(merge({}, configuration, conf)); }, + CameraAccess, ImageWrapper, ImageDebug, ResultCollector, diff --git a/src/scanner.js b/src/scanner.js index f9042b1..372a0d8 100644 --- a/src/scanner.js +++ b/src/scanner.js @@ -209,10 +209,11 @@ function createScanner(pixelCapturer) { } else { //_framegrabber.attachData(_inputImageWrapper.data); } - pixelCapturer.grabFrameData() + + return pixelCapturer.grabFrameData() .then((bitmap) => { - _inputImageWrapper.data = bitmap.data; if (bitmap) { + _inputImageWrapper.data = bitmap.data; if (availableWorker) { availableWorker.busy = true; availableWorker.worker.postMessage({ @@ -226,15 +227,15 @@ function createScanner(pixelCapturer) { }) .catch(err => { console.error(err); - }) - } else { - locateAndDecode(); + }); } + + return locateAndDecode(); } function startContinuousUpdate() { var next = null, - delay = 1000 / (_config.frequency === 0 ? 60 : (_config.frequency || 60)); + delay = 1000 / (_config.frequency === 0 ? 10 : (_config.frequency || 10)); _stopped = false; (function frame(timestamp) { @@ -242,9 +243,12 @@ function createScanner(pixelCapturer) { if (!_stopped) { if (timestamp >= next) { next += delay; - update(); + update().then(() => { + window.requestAnimFrame(frame); + }); + } else { + window.requestAnimFrame(frame); } - window.requestAnimFrame(frame); } }(performance.now())); }