Added scanning barcodes with a camera
3
quagga2/quagga2-1.12.1/docs/_config.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
title: Quagga2 Documentation
|
||||
description: Barcode scanning for browser and node!
|
||||
theme: jekyll-theme-cayman
|
||||
BIN
quagga2/quagga2-1.12.1/docs/assets/bb-binary.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/bb-rotated.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/binary.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/component-labeling-line.png
Normal file
|
After Width: | Height: | Size: 672 B |
BIN
quagga2/quagga2-1.12.1/docs/assets/component-labeling-text.png
Normal file
|
After Width: | Height: | Size: 926 B |
BIN
quagga2/quagga2-1.12.1/docs/assets/component-labeling.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/connected-patch-labels.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/mobile-detected.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/mobile-located.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/patches_found.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/quaggaJS-code128.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
quagga2/quagga2-1.12.1/docs/assets/remaining-patch-labels.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/skeleton.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
quagga2/quagga2-1.12.1/docs/assets/teaser.png
Normal file
|
After Width: | Height: | Size: 405 KiB |
119
quagga2/quagga2-1.12.1/docs/contributing.md
Normal file
@@ -0,0 +1,119 @@
|
||||
## Contributing {#contributing}
|
||||
|
||||
### Questions, Bugs, Enhancements / Suggestions {#questions-bugs-enhancements}
|
||||
|
||||
For questions, bug reports, and enhancement requests / suggestions, please use the GitHub issue
|
||||
tracker at https://github.com/ericblade/quagga2/issues
|
||||
|
||||
### Chat / Real Time Communication? {#chat}
|
||||
|
||||
We have a Gitter chat channel available at https://gitter.im/quaggaJS/Lobby . Please join us there,
|
||||
and be patient. Thanks!
|
||||
|
||||
### Code {#code}
|
||||
|
||||
#### Developing enhancements to Quagga2 {#developing-enhancements}
|
||||
|
||||
If you'd like to work directly on the Quagga2 code, it is quite easy to build a local development
|
||||
copy and get started hacking on the base code. Simply clone this repository, then run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### Running tests {#running-tests}
|
||||
|
||||
There are several tests included in Quagga2, in the test/ folder. Please make sure before you
|
||||
send in any pull requests, that you have run ```npm test``` against the existing tests, as well as
|
||||
implemented any tests that may be needed to test your new code. If you're not sure how to properly
|
||||
unit test your new code, then go ahead and make that pull request, and we'll try to help you before
|
||||
merging.
|
||||
|
||||
**Test Structure**:
|
||||
|
||||
- Unit tests are in `src/` alongside source files (`.spec.ts` files)
|
||||
- Integration tests are in `test/integration/` organized by decoder type
|
||||
- See `test/integration/README.md` for detailed information about integration test structure and configuration
|
||||
|
||||
**Running Specific Tests**:
|
||||
|
||||
```bash
|
||||
# All tests (Node unit/integration + full Cypress suite)
|
||||
npm test
|
||||
|
||||
# Run Node and Cypress in parallel (faster local iteration)
|
||||
npm run test:parallel
|
||||
|
||||
# Node tests only
|
||||
npm run test:node
|
||||
|
||||
# Browser tests only (requires Cypress)
|
||||
npm run test:browser-all
|
||||
|
||||
# E2E examples spec with server (headless)
|
||||
npm run test:e2e
|
||||
|
||||
# E2E examples spec (interactive, Electron)
|
||||
npm run test:e2e:open
|
||||
|
||||
# Cypress: run only specific spec(s)
|
||||
# Pass --spec through npm using --
|
||||
npm run cypress:run -- --spec cypress/e2e/browser.cy.ts
|
||||
|
||||
# Multiple specs (comma-separated)
|
||||
npm run cypress:run -- --spec cypress/e2e/browser.cy.ts,cypress/e2e/integration.cy.ts
|
||||
|
||||
# Glob pattern
|
||||
npm run cypress:run -- --spec "cypress/e2e/**/browser*.cy.ts"
|
||||
|
||||
# Open interactive runner
|
||||
npm run cypress:open
|
||||
|
||||
# Open only the examples E2E spec (Electron)
|
||||
npm run cypress:open:e2e
|
||||
|
||||
# Choose a browser (if installed)
|
||||
npx cross-env NODE_ENV=development BUILD_ENV=development NODE_OPTIONS=--openssl-legacy-provider cypress run --browser chrome --env BUILD_ENV=development --spec cypress/e2e/browser.cy.ts
|
||||
|
||||
# Integration tests only (Node)
|
||||
npx ts-mocha -p test/tsconfig.json test/integration/**/*.spec.ts
|
||||
|
||||
# Specific decoder integration tests
|
||||
npx ts-mocha -p test/tsconfig.json test/integration/decoders/ean_8.spec.ts
|
||||
```
|
||||
|
||||
**Adding Integration Tests**:
|
||||
|
||||
When adding new test cases to decoder integration tests, tests should pass in both Node and browser
|
||||
environments by default. If a test is known to fail in a specific environment, mark it explicitly with:
|
||||
|
||||
- `allowFailInNode: true` - Test can fail in Node without failing CI
|
||||
- `allowFailInBrowser: true` - Test can fail in browser without failing CI
|
||||
- Both flags - Test can fail in both environments
|
||||
|
||||
See `test/integration/README.md` for complete details on the test failure marking system.
|
||||
|
||||
**Notes**:
|
||||
|
||||
- The Cypress E2E examples (`cypress/e2e/examples.cy.ts`) require the local examples server on port 8080; the `test`, `test:e2e`, and `test:e2e:open` scripts start it automatically via `start-server-and-test`.
|
||||
- The parallel runner uses prefixed logs (`e2e`, `node`) and exits non-zero if either side fails (`--kill-others-on-fail --success=all`).
|
||||
|
||||
#### Working on a changed copy of Quagga2 from another repository (ie, developing an external plugin) {#working-on-external-plugin}
|
||||
|
||||
If you need to make changes to Quagga2 to support some external code (such as an external reader plugin),
|
||||
you will probably need to be able to test the code in your other repo. One such way to do that is
|
||||
to ```npm install @ericblade/quagga2``` inside the external repo, which will initialize the module
|
||||
structure, and fill it with the current release of quagga2. Once that is completed, then copy the
|
||||
lib/quagga.js (node) and/or dist/quagga.js and/or dist/quagga.min.js files into
|
||||
ExternalRepo/node_modules/@ericblade/quagga2, preserving the "lib" or "dist" folder. Then your code
|
||||
will have the new changes that you have implemented in your copy of the quagga2 repo.
|
||||
|
||||
#### Pull requests {#pull-requests}
|
||||
|
||||
Please, submit pull requests! Open source works best when we all work together. If you find a change
|
||||
to be useful, the odds are that other users will, as well!
|
||||
|
||||
## BE AWESOME {#be-awesome}
|
||||
|
||||
Don't forget to BE AWESOME.
|
||||
7
quagga2/quagga2-1.12.1/docs/examples/.eslintrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
}
|
||||
}
|
||||
86
quagga2/quagga2-1.12.1/docs/examples/camera_example.html
Normal file
@@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<title>Camera</title>
|
||||
<script type="text/javascript">
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
||||
|
||||
function getUserMedia(constraints, success, failure) {
|
||||
navigator.getUserMedia(constraints, function(stream) {
|
||||
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
success.apply(null, [videoSrc]);
|
||||
}, failure);
|
||||
}
|
||||
|
||||
|
||||
function initCamera(constraints, video, callback) {
|
||||
getUserMedia(constraints, function (src) {
|
||||
video.src = src;
|
||||
video.addEventListener('loadeddata', function() {
|
||||
var attempts = 10;
|
||||
|
||||
function checkVideo() {
|
||||
if (attempts > 0) {
|
||||
if (video.videoWidth > 0 && video.videoHeight > 0) {
|
||||
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
|
||||
video.play();
|
||||
callback();
|
||||
} else {
|
||||
window.setTimeout(checkVideo, 100);
|
||||
}
|
||||
} else {
|
||||
callback('Unable to play video stream.');
|
||||
}
|
||||
attempts--;
|
||||
}
|
||||
|
||||
checkVideo();
|
||||
}, false);
|
||||
}, function(e) {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
|
||||
function copyToCanvas(video, ctx) {
|
||||
( function frame() {
|
||||
ctx.drawImage(video, 0, 0);
|
||||
window.requestAnimationFrame(frame);
|
||||
}());
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var constraints = {
|
||||
video: {
|
||||
mandatory: {
|
||||
minWidth: 1280,
|
||||
minHeight: 720
|
||||
}
|
||||
}
|
||||
},
|
||||
video = document.createElement('video'),
|
||||
canvas = document.createElement('canvas');
|
||||
|
||||
document.body.appendChild(video);
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
initCamera(constraints, video, function() {
|
||||
canvas.setAttribute('width', video.videoWidth);
|
||||
canvas.setAttribute('height', video.videoHeight);
|
||||
copyToCanvas(video, canvas.getContext('2d'));
|
||||
});
|
||||
}, false);
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
Quagga2 Project © 2014-2025 Christoph Oberhofer, Eric Blade, and all other Contributors. See <a href="https://github.com/ericblade/quagga2/blob/master/package.json">package.json</a>. Licensed under MIT license. See <a href="https://github.com/ericblade/quagga2/blob/master/LICENSE">LICENSE</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
27
quagga2/quagga2-1.12.1/docs/examples/config.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "css"
|
||||
sass_dir = "sass"
|
||||
images_dir = "img"
|
||||
javascripts_dir = "js"
|
||||
fonts_dir="fonts"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
|
||||
# If you prefer the indented syntax, you might want to regenerate this
|
||||
# project again passing --syntax sass, or you can uncomment this:
|
||||
# preferred_syntax = :sass
|
||||
# and then run:
|
||||
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
|
||||
16
quagga2/quagga2-1.12.1/docs/examples/css/colors.css
Normal file
@@ -0,0 +1,16 @@
|
||||
@charset "UTF-8";
|
||||
/* LESS - http://lesscss.org style sheet */
|
||||
/* Palette color codes */
|
||||
/* Palette URL: http://paletton.com/#uid=31g0q0kHZAviRSkrHLOGomVNzac */
|
||||
/* Feel free to copy&paste color codes to your application */
|
||||
/* MIXINS */
|
||||
/* As hex codes */
|
||||
/* Main Primary color */
|
||||
/* Main Secondary color (1) */
|
||||
/* Main Secondary color (2) */
|
||||
/* As RGBa codes */
|
||||
/* Main Primary color */
|
||||
/* Main Secondary color (1) */
|
||||
/* Main Secondary color (2) */
|
||||
/* Generated by Paletton.com ├é┬® 2002-2014 */
|
||||
/* http://paletton.com */
|
||||
1
quagga2/quagga2-1.12.1/docs/examples/css/fonts.css
Normal file
@@ -0,0 +1 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600");
|
||||
298
quagga2/quagga2-1.12.1/docs/examples/css/styles.css
Normal file
@@ -0,0 +1,298 @@
|
||||
@charset "UTF-8";
|
||||
@import url("https://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600");
|
||||
|
||||
body {
|
||||
background-color: #FFF;
|
||||
margin: 0px;
|
||||
font-family: Ubuntu, sans-serif;
|
||||
color: #1e1e1e;
|
||||
font-weight: normal;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-family: "Cabin Condensed", sans-serif;
|
||||
}
|
||||
|
||||
header {
|
||||
background: #FFC600;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
header .headline {
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header .headline h1 {
|
||||
color: #FFDD69;
|
||||
font-size: 3em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
header .headline h2 {
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: #0A4DB7;
|
||||
color: #6C9CE8;
|
||||
padding: 1em 2em 2em;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 640px;
|
||||
margin: 20px auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#interactive.viewport {
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
|
||||
#interactive.viewport canvas, video {
|
||||
float: left;
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
|
||||
margin-left: -640px;
|
||||
}
|
||||
|
||||
.controls fieldset {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.controls .input-group {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.controls .input-group input, .controls .input-group button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.controls .reader-config-group {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.controls .reader-config-group label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.controls .reader-config-group label span {
|
||||
width: 9rem;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.controls:after {
|
||||
content: '';
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
#result_strip {
|
||||
margin: 10px 0;
|
||||
border-top: 1px solid #EEE;
|
||||
border-bottom: 1px solid #EEE;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
#result_strip > ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
width: auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#result_strip > ul > li {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#result_strip > ul > li .thumbnail {
|
||||
padding: 5px;
|
||||
margin: 4px;
|
||||
border: 1px dashed #CCC;
|
||||
}
|
||||
|
||||
#result_strip > ul > li .thumbnail img {
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
#result_strip > ul > li .thumbnail .caption {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#result_strip > ul > li .thumbnail .caption h4 {
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
height: 40px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#result_strip > ul:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.scanner-overlay {
|
||||
display: none;
|
||||
width: 640px;
|
||||
height: 510px;
|
||||
position: absolute;
|
||||
padding: 20px;
|
||||
top: 50%;
|
||||
margin-top: -275px;
|
||||
left: 50%;
|
||||
margin-left: -340px;
|
||||
background-color: #FFF;
|
||||
-moz-box-shadow: #333333 0px 4px 10px;
|
||||
-webkit-box-shadow: #333333 0px 4px 10px;
|
||||
box-shadow: #333333 0px 4px 10px;
|
||||
}
|
||||
|
||||
.scanner-overlay > .header {
|
||||
position: relative;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.scanner-overlay > .header h4, .scanner-overlay > .header .close {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.scanner-overlay > .header h4 {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.scanner-overlay > .header .close {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
i.icon-24-scan {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzFFMjMzNTBFNjcwMTFFMkIzMERGOUMzMzEzM0E1QUMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzFFMjMzNTFFNjcwMTFFMkIzMERGOUMzMzEzM0E1QUMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMUUyMzM0RUU2NzAxMUUyQjMwREY5QzMzMTMzQTVBQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMUUyMzM0RkU2NzAxMUUyQjMwREY5QzMzMTMzQTVBQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtQr90wAAAUuSURBVHjanFVLbFRVGP7ua97T9DGPthbamAYYBNSMVbBpjCliWWGIEBMWsnDJxkh8RDeEDW5MDGticMmGBWnSlRSCwgLFNkqmmrRIqzjTznTazkxn5s7c6/efzm0G0Jhwkj/nP+d/nv91tIWFBTQaDQWapkGW67p4ltUub5qmAi0UCqF/a/U2m81tpmddotwwDGSz2dzi4uKSaOucnJycGhsbe1XXdQiIIcdxEAgEtgXq9brySHCht79UXi/8QheawN27d385fPjwuEl6XyKR6LdtW7t06RLK5TKOHj2K/fv3Q87Dw8OYn5/HiRMnMDs7i5mZGQwODiqlPp8PuVwO6XRaOXb16lXl1OnTp5FMJvtosF8M+MWLarWqGJaWlpBKpRRcu3YN4+PjmJ6exsTEhDJw5coVjI6OKgPhcBiZTAbxeBx+vx+XL19Gd3c3Tp48Ka9zqDYgBlTQxYNgMIhIJKLCILkQb+TZsgvdsiyFi+feWRR7oRNZyanQtvW2V4DEUUBiK2eJpeDirSyhCe7F2QPh8fiEp72i9PbsC5G52DbiKZA771yr1dTuGfJ4PQNPFoAyQNR1aNEmsS5eyB3PgjeooMZd2AWvNmzYci/Gea7TeFOcI93jV/K67noGmi4vdRI9gPSDeMLSdKUBZZczlWm1rTtHjLZ24d+WER2tc8N1m+Y+ID74wx0zGYvhg9UNrJdtHJyZRdQfwPsrq9g99xsGlgsYmr6BNzO/IVwsYfjBQ6XYz6JI/72MV366B5/lw0elOkJWGUM3bmKtWjXSLuLaBWhnPnnp0FfoiFi4+TMfVAb2poBkDLjO845uYLEAjL4ALGWBP5YAOsP4AJYBFDaB1HOSVWD2PuV95H2RdV93Lv74/cf6p6Zxq/h6OofeOPJBC39JtONdwOAAViOs4p4OFGTf0Uc8iiyrr9YdQrUnDLsngrVOC0jQib44HlF2RafRZBz1Qy+vfhgK3NJZBlrm+LEm9qWwzFgLU7Ozg0JxZP06jQSRpQ7EerAWDSt6PuhHPmChEAog56fCLvJT5hHTm3OZkz3DyLx7XNWTGEA1GkV14gjWgwbW0ESVjYRwCOuai03L5E7OUBAV4kXSS4auoGIaKOma4m8EA5R1sMEGLh95C+XuLph0WJWpxepYYLtfT0RRgY1KgNODY6BoaChRuEhDCIZQYseuki5KN6hcQHiq7OZNv4/Zq2O6P4Lfkwn46vZjjaYZrIpvWbpzjLErrc4xUGE4avRedpYJalRcIl5hQius/SrPm9xrNOQYJhao6BvNUeWqtY8KaWuNjHOFAr7mM9f4NA4UbKysoUJ8PV9UzVOx6wxDDWUOxnK1pmCD07fOMAvtIsM3l89Dl3HRGhVma9AZMqjOnz2LQqWCxs6dqr3T7x1DTzKJaG8SekcHhg4cgI/56uKdlKnBV/WndqN3YAB/7tyBd3oT6GBIOzs7kc/nDfFdDFT5bS73cp06dQoaPa/Rw/rtO/resTHxxE2m9rCrbSR27UJCcMf1BpiA5rAAGgdfc868fUR1sMwj0cm9Iu9IctweisViB3hhKTHDcHc5jv/LspbyaZrR1OD82/fIlOkuB9LnEWRmDX2TsddUPg3D5gvuc0je0rZaD5EW6G3yjS+A3eeBEWq3XW/Abw1HhUspXADufQb86oW7tZytkYCN//3hHwBvDALPi8EnSOYK8DAOfCc2h4aGcO7cuafkzampqf9UripH12/DtOZbx8ciVGzYy5OO40o25ascGRl5Ssc/AgwAjW3JwqIUjSYAAAAASUVORK5CYII=");
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
line-height: 24px;
|
||||
margin-top: 1px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
@media (max-width: 603px) {
|
||||
|
||||
#container {
|
||||
width: 300px;
|
||||
margin: 10px auto;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#container form.voucher-form input.voucher-code {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 603px) {
|
||||
|
||||
.reader-config-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reader-config-group label > span {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.reader-config-group label > select, .reader-config-group label > input {
|
||||
max-width: calc(50% - 2px);
|
||||
}
|
||||
|
||||
#interactive.viewport {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
#interactive.viewport canvas, video {
|
||||
margin-top: -50px;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
|
||||
margin-left: -300px;
|
||||
}
|
||||
|
||||
|
||||
#result_strip {
|
||||
margin-top: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#result_strip ul.thumbnails > li {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#result_strip ul.thumbnails > li .thumbnail .imgWrapper {
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#result_strip ul.thumbnails > li .thumbnail .imgWrapper img {
|
||||
margin-top: -25px;
|
||||
width: 130px;
|
||||
height: 180px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 603px) {
|
||||
|
||||
.overlay.scanner {
|
||||
width: 640px;
|
||||
height: 510px;
|
||||
padding: 20px;
|
||||
margin-top: -275px;
|
||||
margin-left: -340px;
|
||||
background-color: #FFF;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.overlay.scanner > .header {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.overlay.scanner > .header h4, .overlay.scanner > .header .close {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.overlay.scanner > .header .close {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
110
quagga2/quagga2-1.12.1/docs/examples/file_input.html
Normal file
@@ -0,0 +1,110 @@
|
||||
<!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" accept="image/*" capture="camera"/>
|
||||
<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_extended">EAN-extended</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>
|
||||
<option value="i2of5">Interleaved 2 of 5</option>
|
||||
<option value="2of5">Standard 2 of 5</option>
|
||||
<option value="code_93">Code 93</option>
|
||||
<option value="pharmacode">Pharmacode</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 value="medium">medium</option>
|
||||
<option selected="selected" 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>Single Channel</span>
|
||||
<input type="checkbox" name="input-stream_single-channel" />
|
||||
</label>
|
||||
<label>
|
||||
<span>Locate (barcode finder)</span>
|
||||
<input type="checkbox" checked="checked" name="locate" />
|
||||
</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>
|
||||
Quagga2 Project © 2014-2025 Christoph Oberhofer, Eric Blade, and all other Contributors. See <a href="https://github.com/ericblade/quagga2/blob/master/package.json">package.json</a>. Licensed under MIT license. See <a href="https://github.com/ericblade/quagga2/blob/master/LICENSE">LICENSE</a>
|
||||
</p>
|
||||
</footer>
|
||||
<script src="vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
|
||||
<script src="dist/quagga.min.js" type="text/javascript"></script>
|
||||
<script src="file_input.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
203
quagga2/quagga2-1.12.1/docs/examples/file_input.js
Normal file
@@ -0,0 +1,203 @@
|
||||
$(function() {
|
||||
var App = {
|
||||
init: function() {
|
||||
App.syncCheckboxesFromState();
|
||||
App.attachListeners();
|
||||
},
|
||||
syncCheckboxesFromState: function() {
|
||||
var self = this;
|
||||
$(".controls .reader-config-group input[type=checkbox]").each(function() {
|
||||
var $checkbox = $(this),
|
||||
name = $checkbox.attr("name"),
|
||||
state = self._convertNameToState(name),
|
||||
value = self._accessByPath(self.state, state);
|
||||
$checkbox.prop("checked", !!value);
|
||||
});
|
||||
},
|
||||
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);
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: function(value) {
|
||||
if (value === 'ean_extended') {
|
||||
return [{
|
||||
format: "ean_reader",
|
||||
config: {
|
||||
supplements: [
|
||||
'ean_5_reader', 'ean_2_reader'
|
||||
]
|
||||
}
|
||||
}];
|
||||
}
|
||||
return [{
|
||||
format: value + "_reader",
|
||||
config: {}
|
||||
}];
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
inputStream: {
|
||||
size: 800,
|
||||
singleChannel: false,
|
||||
area: {
|
||||
top: "30%",
|
||||
right: "10%",
|
||||
left: "10%",
|
||||
bottom: "30%",
|
||||
backgroundColor: "rgba(255,0,0,0.15)"
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
halfSample: true
|
||||
},
|
||||
decoder: {
|
||||
readers: [{
|
||||
format: "code_128_reader",
|
||||
config: {}
|
||||
}]
|
||||
},
|
||||
locate: true,
|
||||
src: null
|
||||
}
|
||||
};
|
||||
|
||||
App.init();
|
||||
|
||||
function calculateRectFromArea(canvas, area) {
|
||||
var canvasWidth = canvas.width,
|
||||
canvasHeight = canvas.height,
|
||||
top = parseInt(area.top)/100,
|
||||
right = parseInt(area.right)/100,
|
||||
bottom = parseInt(area.bottom)/100,
|
||||
left = parseInt(area.left)/100;
|
||||
|
||||
top *= canvasHeight;
|
||||
right = canvasWidth - canvasWidth*right;
|
||||
bottom = canvasHeight - canvasHeight*bottom;
|
||||
left *= canvasWidth;
|
||||
|
||||
return {
|
||||
x: left,
|
||||
y: top,
|
||||
width: right - left,
|
||||
height: bottom - top
|
||||
};
|
||||
}
|
||||
|
||||
Quagga.onProcessed(function(result) {
|
||||
console.warn('* onProcessed', result);
|
||||
var drawingCtx = Quagga.canvas.ctx.overlay,
|
||||
drawingCanvas = Quagga.canvas.dom.overlay,
|
||||
area;
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
if (App.state.inputStream.area) {
|
||||
area = calculateRectFromArea(drawingCanvas, App.state.inputStream.area);
|
||||
drawingCtx.strokeStyle = "#0F0";
|
||||
drawingCtx.strokeRect(area.x, area.y, area.width, area.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Quagga.onDetected(function(result) {
|
||||
console.warn('* onDetected', 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_strip ul.thumbnails").prepend($node);
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 13 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-001.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-002.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-003.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-004.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-005.jpg
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-006.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-007.jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-008.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-009.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
quagga2/quagga2-1.12.1/docs/examples/fixtures/ean/image-010.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
31
quagga2/quagga2-1.12.1/docs/examples/index.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Quagga2 Live Examples</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Quagga2 Live Examples</h1>
|
||||
<p>Choose an example to try out Quagga2 in your browser:</p>
|
||||
</header>
|
||||
<main>
|
||||
<ul>
|
||||
<li><a href="camera_example.html">Camera Example</a></li>
|
||||
<li><a href="file_input.html">File Input Example</a></li>
|
||||
<li><a href="live_w_locator.html">Live with Locator Example</a></li>
|
||||
<li><a href="static_images.html">Static Images Example</a></li>
|
||||
</ul>
|
||||
<h2>Node.js Examples</h2>
|
||||
<ul>
|
||||
<li><a href="node-test.js">Node Test</a></li>
|
||||
<li><a href="node-test-with-buffer.js">Node Test with Buffer</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
<footer>
|
||||
<p>Quagga2 Project © 2014-2025 Christoph Oberhofer, Eric Blade, and all other Contributors. See <a href="https://github.com/ericblade/quagga2/blob/master/package.json">package.json</a>. Licensed under MIT license. See <a href="https://github.com/ericblade/quagga2/blob/master/LICENSE">LICENSE</a></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
116
quagga2/quagga2-1.12.1/docs/examples/live_w_locator.html
Normal file
@@ -0,0 +1,116 @@
|
||||
<!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, user-scalable=no" />
|
||||
<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>The user's camera</h3>
|
||||
<p>If your platform supports the <strong>getUserMedia</strong> API call, you can try the real-time locating and decoding features.
|
||||
Simply allow the page to access your web-cam and point it to a barcode. You can switch between <strong>Code128</strong>
|
||||
and <strong>EAN</strong> to test different scenarios.
|
||||
It works best if your camera has built-in auto-focus.
|
||||
</p>
|
||||
<div class="controls">
|
||||
<fieldset class="input-group">
|
||||
<button class="stop">Stop</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_extended">EAN-extended</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>
|
||||
<option value="i2of5">Interleaved 2 of 5</option>
|
||||
<option value="2of5">Standard 2 of 5</option>
|
||||
<option value="code_93">Code 93</option>
|
||||
<option value="pharmacode">Pharmacode</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Resolution (width)</span>
|
||||
<select name="input-stream_constraints">
|
||||
<option value="320x240">320px</option>
|
||||
<option selected="selected" value="640x480">640px</option>
|
||||
<option value="800x600">800px</option>
|
||||
<option value="1280x720">1280px</option>
|
||||
<option value="1600x960">1600px</option>
|
||||
<option value="1920x1080">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>Locate (barcode finder)</span>
|
||||
<input type="checkbox" checked="checked" name="locate" />
|
||||
</label>
|
||||
<label>
|
||||
<span>Camera</span>
|
||||
<select name="input-stream_constraints" id="deviceSelection">
|
||||
</select>
|
||||
</label>
|
||||
<label style="display: none">
|
||||
<span>Zoom</span>
|
||||
<select name="settings_zoom"></select>
|
||||
</label>
|
||||
<label style="display: none">
|
||||
<span>Torch</span>
|
||||
<input type="checkbox" name="settings_torch" />
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id="result_strip">
|
||||
<ul class="thumbnails"></ul>
|
||||
<ul class="collector"></ul>
|
||||
</div>
|
||||
<div id="interactive" class="viewport"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<p>
|
||||
© Made with ❤️ by Christoph Oberhofer
|
||||
</p>
|
||||
<p>
|
||||
Quagga2 Project © 2014-2025 Christoph Oberhofer, Eric Blade, and all other Contributors. See <a href="https://github.com/ericblade/quagga2/blob/master/package.json">package.json</a>. Licensed under MIT license. See <a href="https://github.com/ericblade/quagga2/blob/master/LICENSE">LICENSE</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script src="vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
|
||||
<script src="//webrtc.github.io/adapter/adapter-latest.js" type="text/javascript"></script>
|
||||
<script src="dist/quagga.min.js" type="text/javascript"></script>
|
||||
<script src="live_w_locator.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
402
quagga2/quagga2-1.12.1/docs/examples/live_w_locator.js
Normal file
@@ -0,0 +1,402 @@
|
||||
$(function() {
|
||||
var resultCollector = Quagga.ResultCollector.create({
|
||||
capture: true,
|
||||
capacity: 20,
|
||||
blacklist: [{
|
||||
code: "WIWV8ETQZ1", format: "code_93"
|
||||
}, {
|
||||
code: "EH3C-%GU23RK3", format: "code_93"
|
||||
}, {
|
||||
code: "O308SIHQOXN5SA/PJ", format: "code_93"
|
||||
}, {
|
||||
code: "DG7Q$TV8JQ/EN", format: "code_93"
|
||||
}, {
|
||||
code: "VOFD1DB5A.1F6QU", format: "code_93"
|
||||
}, {
|
||||
code: "4SO64P4X8 U4YUU1T-", format: "code_93"
|
||||
}],
|
||||
filter: function(codeResult) {
|
||||
// only store results which match this constraint
|
||||
// e.g.: codeResult
|
||||
return true;
|
||||
}
|
||||
});
|
||||
var App = {
|
||||
_initDebounceTimer: null,
|
||||
_pendingReinit: false,
|
||||
init: function() {
|
||||
var self = this;
|
||||
Quagga.init(this.state, function(err) {
|
||||
if (err) {
|
||||
return self.handleError(err);
|
||||
}
|
||||
//Quagga.registerResultCollector(resultCollector);
|
||||
App.attachListeners();
|
||||
Quagga.start();
|
||||
App.initCameraSelection();
|
||||
App.checkCapabilities();
|
||||
// Sync UI checkboxes to match actual state after init
|
||||
$('input[name="locate"]').prop('checked', App.state.locate);
|
||||
});
|
||||
},
|
||||
reinit: function() {
|
||||
// Debounced reinit: cancel pending and schedule a new one
|
||||
console.log('[App.reinit] Called, current timer:', App._initDebounceTimer !== null, 'Current state.locate:', App.state.locate);
|
||||
if (App._initDebounceTimer) {
|
||||
console.log('[App.reinit] Cancelling pending init');
|
||||
clearTimeout(App._initDebounceTimer);
|
||||
}
|
||||
App._initDebounceTimer = setTimeout(function() {
|
||||
console.log('[App.reinit] Debounce expired, calling init() with state.locate:', App.state.locate);
|
||||
App._initDebounceTimer = null;
|
||||
App._pendingReinit = false;
|
||||
App.init();
|
||||
}, 250);
|
||||
},
|
||||
handleError: function(err) {
|
||||
console.log(err);
|
||||
// If we attempted to open a specific device and failed, revert to last known-good constraints
|
||||
try {
|
||||
var attempted = (App.state && App.state.inputStream && App.state.inputStream.constraints) || {};
|
||||
var hadSpecificDevice = attempted && attempted.deviceId;
|
||||
if (hadSpecificDevice) {
|
||||
// Detach to avoid stacked handlers
|
||||
// Try reinitializing to restore a working stream
|
||||
Quagga.stop();
|
||||
App.init();
|
||||
}
|
||||
} catch(e2) {
|
||||
// Ignore and leave UI available for user to pick another device
|
||||
}
|
||||
},
|
||||
checkCapabilities: function() {
|
||||
var track = Quagga.CameraAccess.getActiveTrack();
|
||||
var capabilities = {};
|
||||
if (typeof track.getCapabilities === 'function') {
|
||||
capabilities = track.getCapabilities();
|
||||
}
|
||||
this.applySettingsVisibility('zoom', capabilities.zoom);
|
||||
this.applySettingsVisibility('torch', capabilities.torch);
|
||||
},
|
||||
updateOptionsForMediaRange: function(node, range) {
|
||||
console.log('updateOptionsForMediaRange', node, range);
|
||||
var NUM_STEPS = 6;
|
||||
var stepSize = (range.max - range.min) / NUM_STEPS;
|
||||
var option;
|
||||
var value;
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
for (var i = 0; i <= NUM_STEPS; i++) {
|
||||
value = range.min + (stepSize * i);
|
||||
option = document.createElement('option');
|
||||
option.value = value;
|
||||
option.innerHTML = value;
|
||||
node.appendChild(option);
|
||||
}
|
||||
},
|
||||
applySettingsVisibility: function(setting, capability) {
|
||||
// depending on type of capability
|
||||
if (typeof capability === 'boolean') {
|
||||
var node = document.querySelector('input[name="settings_' + setting + '"]');
|
||||
if (node) {
|
||||
node.parentNode.style.display = capability ? 'block' : 'none';
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (window.MediaSettingsRange && capability instanceof window.MediaSettingsRange) {
|
||||
var node = document.querySelector('select[name="settings_' + setting + '"]');
|
||||
if (node) {
|
||||
this.updateOptionsForMediaRange(node, capability);
|
||||
node.parentNode.style.display = 'block';
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
initCameraSelection: function(){
|
||||
var streamLabel = Quagga.CameraAccess.getActiveStreamLabel();
|
||||
var selectedDeviceId = null;
|
||||
try {
|
||||
// Prefer explicit deviceId from current state if present
|
||||
var constraints = (this.state && this.state.inputStream && this.state.inputStream.constraints) || {};
|
||||
if (constraints.deviceId) {
|
||||
selectedDeviceId = (typeof constraints.deviceId === 'object' && constraints.deviceId.exact) ? constraints.deviceId.exact : constraints.deviceId;
|
||||
} else {
|
||||
selectedDeviceId = null;
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
return Quagga.CameraAccess.enumerateVideoDevices()
|
||||
.then(function(devices) {
|
||||
function pruneText(text) {
|
||||
return text.length > 30 ? text.substr(0, 30) : text;
|
||||
}
|
||||
var $deviceSelection = document.getElementById("deviceSelection");
|
||||
while ($deviceSelection.firstChild) {
|
||||
$deviceSelection.removeChild($deviceSelection.firstChild);
|
||||
}
|
||||
devices.forEach(function(device) {
|
||||
var $option = document.createElement("option");
|
||||
$option.value = device.deviceId || device.id;
|
||||
$option.appendChild(document.createTextNode(pruneText(device.label || device.deviceId || device.id)));
|
||||
// Preserve explicit selection by deviceId when available; fallback to stream label
|
||||
if (selectedDeviceId) {
|
||||
$option.selected = ($option.value === selectedDeviceId);
|
||||
} else {
|
||||
$option.selected = (streamLabel === device.label);
|
||||
}
|
||||
$deviceSelection.appendChild($option);
|
||||
});
|
||||
});
|
||||
},
|
||||
attachListeners: function() {
|
||||
var self = this;
|
||||
|
||||
console.log('[App.attachListeners] Called - this should only happen once per init');
|
||||
self.initCameraSelection();
|
||||
|
||||
// Remove any existing handlers to prevent stacking
|
||||
$(".controls").off("click", "button.stop");
|
||||
$(".controls .reader-config-group").off("change", "input, select");
|
||||
|
||||
$(".controls").on("click", "button.stop", function(e) {
|
||||
e.preventDefault();
|
||||
Quagga.stop();
|
||||
self._printCollectedResults();
|
||||
});
|
||||
|
||||
$(".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, "checkbox checked prop:", $target.prop("checked"), "target type:", $target.attr("type"));
|
||||
self.setState(state, value);
|
||||
});
|
||||
},
|
||||
_printCollectedResults: function() {
|
||||
var results = resultCollector.getResults(),
|
||||
$ul = $("#result_strip ul.collector");
|
||||
|
||||
results.forEach(function(result) {
|
||||
var $li = $('<li><div class="thumbnail"><div class="imgWrapper"><img /></div><div class="caption"><h4 class="code"></h4></div></div></li>');
|
||||
|
||||
$li.find("img").attr("src", result.frame);
|
||||
$li.find("h4.code").html(result.codeResult.code + " (" + result.codeResult.format + ")");
|
||||
$ul.prepend($li);
|
||||
});
|
||||
},
|
||||
_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) {
|
||||
if (typeof o[key] === "object" && typeof val === "object") {
|
||||
Object.assign(o[key], val);
|
||||
} else {
|
||||
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").off("click", "button.stop");
|
||||
$(".controls .reader-config-group").off("change", "input, select");
|
||||
},
|
||||
applySetting: function(setting, value) {
|
||||
var track = Quagga.CameraAccess.getActiveTrack();
|
||||
if (track && typeof track.getCapabilities === 'function') {
|
||||
switch (setting) {
|
||||
case 'zoom':
|
||||
return track.applyConstraints({advanced: [{zoom: parseFloat(value)}]});
|
||||
case 'torch':
|
||||
return track.applyConstraints({advanced: [{torch: !!value}]});
|
||||
}
|
||||
}
|
||||
},
|
||||
setState: function(path, value) {
|
||||
var self = this;
|
||||
|
||||
console.log('[App.setState] ENTRY: path=', path, 'value=', value, 'current state.locate=', self.state.locate);
|
||||
|
||||
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
|
||||
value = self._accessByPath(self.inputMapper, path)(value);
|
||||
}
|
||||
|
||||
if (path.startsWith('settings.')) {
|
||||
var setting = path.substring(9);
|
||||
return self.applySetting(setting, value);
|
||||
}
|
||||
// If switching cameras, replace constraints entirely with { deviceId: { exact } }
|
||||
if (path === 'inputStream.constraints' && value && typeof value === 'object' && 'deviceId' in value && Object.keys(value).length === 1) {
|
||||
if (!self.state.inputStream) self.state.inputStream = {};
|
||||
var dev = value.deviceId;
|
||||
self.state.inputStream.constraints = { deviceId: (typeof dev === 'object' ? dev : { exact: dev }) };
|
||||
} else {
|
||||
self._accessByPath(self.state, path, value);
|
||||
}
|
||||
|
||||
console.log('[App.setState] AFTER _accessByPath: state.locate=', self.state.locate);
|
||||
console.log(JSON.stringify(self.state));
|
||||
|
||||
console.log('[App.setState] path:', path, 'pending:', App._pendingReinit);
|
||||
// Prevent overlapping stop/reinit sequences
|
||||
if (App._pendingReinit) {
|
||||
// Already stopping/reiniting, just update the debounce timer
|
||||
console.log('[App.setState] Already pending, just updating debounce');
|
||||
App.reinit();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[App.setState] Calling stop and scheduling reinit');
|
||||
App._pendingReinit = true;
|
||||
App.detachListeners();
|
||||
var stopResult = Quagga.stop();
|
||||
if (stopResult && typeof stopResult.then === 'function') {
|
||||
stopResult.then(function(){
|
||||
console.log('[App.setState] Stop completed (promise), calling reinit');
|
||||
App.reinit();
|
||||
});
|
||||
} else {
|
||||
// Older sync stop; reinit with debounce
|
||||
console.log('[App.setState] Stop completed (sync), calling reinit');
|
||||
App.reinit();
|
||||
}
|
||||
},
|
||||
inputMapper: {
|
||||
inputStream: {
|
||||
constraints: function(value){
|
||||
if (/^(\d+)x(\d+)$/.test(value)) {
|
||||
var values = value.split('x');
|
||||
// Update resolution while preserving any existing deviceId selection
|
||||
var current = App.state && App.state.inputStream && App.state.inputStream.constraints || {};
|
||||
var next = {
|
||||
width: {min: parseInt(values[0])},
|
||||
height: {min: parseInt(values[1])}
|
||||
};
|
||||
if (current.deviceId) {
|
||||
next.deviceId = current.deviceId;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
// Switching camera: use ONLY deviceId with exact match to avoid conflicting constraints
|
||||
return { deviceId: { exact: value } };
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: function(value) {
|
||||
if (value === 'ean_extended') {
|
||||
return [{
|
||||
format: "ean_reader",
|
||||
config: {
|
||||
supplements: [
|
||||
'ean_5_reader', 'ean_2_reader'
|
||||
]
|
||||
}
|
||||
}];
|
||||
}
|
||||
return [{
|
||||
format: value + "_reader",
|
||||
config: {}
|
||||
}];
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
inputStream: {
|
||||
type : "LiveStream",
|
||||
constraints: {
|
||||
width: {min: 640},
|
||||
height: {min: 480},
|
||||
facingMode: "environment",
|
||||
aspectRatio: {min: 1, max: 2}
|
||||
},
|
||||
area: {
|
||||
top: "30%",
|
||||
right: "10%",
|
||||
left: "10%",
|
||||
bottom: "30%",
|
||||
// borderColor: "#0F0",
|
||||
// borderWidth: 2,
|
||||
backgroundColor: "rgba(255,0,0,0.15)"
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
halfSample: true
|
||||
},
|
||||
frequency: 10,
|
||||
decoder: {
|
||||
readers : [{
|
||||
format: "code_128_reader",
|
||||
config: {}
|
||||
}]
|
||||
},
|
||||
locate: true
|
||||
},
|
||||
lastResult : null
|
||||
};
|
||||
|
||||
console.log('[Startup] Calling initial App.reinit()');
|
||||
App.reinit();
|
||||
|
||||
var processedCount = 0;
|
||||
Quagga.onProcessed(function(result) {
|
||||
processedCount++;
|
||||
/*
|
||||
console.log('onProcessed #' + processedCount + ':', result ? {
|
||||
hasBox: !!result.box,
|
||||
boxLength: result.box ? result.box.length : 0,
|
||||
hasBoxes: !!result.boxes,
|
||||
boxesLength: result.boxes ? result.boxes.length : 0,
|
||||
codeResult: !!result.codeResult
|
||||
} : 'null 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: "orange", 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;
|
||||
|
||||
if (App.lastResult !== code) {
|
||||
App.lastResult = code;
|
||||
var $node = null, 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_strip ul.thumbnails").prepend($node);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
var Quagga = require('../lib/quagga').default;
|
||||
|
||||
var buffer = require('fs').readFileSync('../test/fixtures/code_128/image-001.jpg');
|
||||
|
||||
decode(buffer);
|
||||
function decode(buff){
|
||||
Quagga.decodeSingle({
|
||||
src: buff,
|
||||
numOfWorkers: 0,
|
||||
inputStream: {
|
||||
mime: "image/jpeg",
|
||||
size: 800,
|
||||
area: {
|
||||
top: "10%",
|
||||
right: "5%",
|
||||
left: "5%",
|
||||
bottom: "10%"
|
||||
}
|
||||
}
|
||||
}, function(result) {
|
||||
if (result.codeResult) {
|
||||
console.log("result", result.codeResult.code);
|
||||
} else {
|
||||
console.log("not detected");
|
||||
}
|
||||
});
|
||||
}
|
||||
21
quagga2/quagga2-1.12.1/docs/examples/node-test.js
Normal file
@@ -0,0 +1,21 @@
|
||||
var Quagga = require('../lib/quagga').default;
|
||||
|
||||
Quagga.decodeSingle({
|
||||
src: "../test/fixtures/code_128/image-001.jpg",
|
||||
numOfWorkers: 0,
|
||||
inputStream: {
|
||||
size: 800,
|
||||
area: {
|
||||
top: "10%",
|
||||
right: "5%",
|
||||
left: "5%",
|
||||
bottom: "10%"
|
||||
}
|
||||
}
|
||||
}, function(result) {
|
||||
if(result.codeResult) {
|
||||
console.log("result", result.codeResult.code);
|
||||
} else {
|
||||
console.log("not detected");
|
||||
}
|
||||
});
|
||||
70
quagga2/quagga2-1.12.1/docs/examples/static_images.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!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 href='http://fonts.googleapis.com/css?family=Ubuntu:400,700,300' rel='stylesheet' type='text/css'>
|
||||
<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 static images</h3>
|
||||
<p>This examples uses static image files as input which are loaded from the server on startup.
|
||||
The locating and decoding process takes place inside the browser.
|
||||
Hit the <strong>next</strong> button to try a different image. You can also switch between
|
||||
two different test-sets. Each of those contains 10 images, demonstrating the capabilities of decoding
|
||||
<strong>Code128</strong> and <strong>EAN</strong> encoded barcodes.
|
||||
</p>
|
||||
<div class="controls">
|
||||
<fieldset class="input-group">
|
||||
<button class="next">Next</button>
|
||||
</fieldset>
|
||||
<fieldset class="reader-config-group">
|
||||
<span>Barcode-Type</span>
|
||||
<select name="decoder_readers;input-stream_src">
|
||||
<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_extended">EAN-extended</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>
|
||||
<option value="i2of5">I2of5</option>
|
||||
<option value="i2of5">Interleaved 2 of 5</option>
|
||||
<option value="2of5">Standard 2 of 5</option>
|
||||
<option value="code_93">Code 93</option>
|
||||
<option value="pharmacode">Pharmacode</option>
|
||||
</select>
|
||||
</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>
|
||||
Quagga2 Project © 2014-2025 Christoph Oberhofer, Eric Blade, and all other Contributors. See <a href="https://github.com/ericblade/quagga2/blob/master/package.json">package.json</a>. Licensed under MIT license. See <a href="https://github.com/ericblade/quagga2/blob/master/LICENSE">LICENSE</a>
|
||||
</p>
|
||||
</footer>
|
||||
<script src="vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
|
||||
<script src="dist/quagga.min.js" type="text/javascript"></script>
|
||||
<script src="static_images.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
177
quagga2/quagga2-1.12.1/docs/examples/static_images.js
Normal file
@@ -0,0 +1,177 @@
|
||||
$(function() {
|
||||
var App = {
|
||||
init: function() {
|
||||
var config = this.config[this.state.decoder.readers[0].format] || this.config.default;
|
||||
config = $.extend(true, {}, config, this.state);
|
||||
Quagga.init(config, function() {
|
||||
App.attachListeners();
|
||||
Quagga.start();
|
||||
});
|
||||
},
|
||||
config: {
|
||||
"default": {
|
||||
inputStream: { name: "Test",
|
||||
type: "ImageStream",
|
||||
length: 10,
|
||||
size: 800,
|
||||
area: {
|
||||
top: "30%",
|
||||
right: "10%",
|
||||
left: "10%",
|
||||
bottom: "30%",
|
||||
backgroundColor: "rgba(255,0,0,0.15)"
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
halfSample: true
|
||||
}
|
||||
},
|
||||
"i2of5_reader": {
|
||||
inputStream: {
|
||||
size: 800,
|
||||
type: "ImageStream",
|
||||
length: 5
|
||||
},
|
||||
locator: {
|
||||
patchSize: "small",
|
||||
halfSample: false
|
||||
}
|
||||
}
|
||||
},
|
||||
attachListeners: function() {
|
||||
var self = this;
|
||||
|
||||
$(".controls").on("click", "button.next", function(e) {
|
||||
e.preventDefault();
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
$(".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"),
|
||||
states = self._convertNameToStates(name);
|
||||
|
||||
console.log("Value of "+ states + " changed to " + value);
|
||||
self.setState(states, value);
|
||||
});
|
||||
},
|
||||
detachListeners: function() {
|
||||
$(".controls").off("click", "button.next");
|
||||
$(".controls .reader-config-group").off("change", "input, select");
|
||||
},
|
||||
_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);
|
||||
},
|
||||
_convertNameToStates: function(names) {
|
||||
return names.split(";").map(this._convertNameToState.bind(this));
|
||||
},
|
||||
_convertNameToState: function(name) {
|
||||
return name.replace("_", ".").split("-").reduce(function(result, value) {
|
||||
return result + value.charAt(0).toUpperCase() + value.substring(1);
|
||||
});
|
||||
},
|
||||
setState: function(paths, value) {
|
||||
var self = this;
|
||||
|
||||
paths.forEach(function(path) {
|
||||
var mappedValue;
|
||||
|
||||
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
|
||||
mappedValue = self._accessByPath(self.inputMapper, path)(value);
|
||||
}
|
||||
self._accessByPath(self.state, path, mappedValue);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(self.state));
|
||||
App.detachListeners();
|
||||
Quagga.stop();
|
||||
App.init();
|
||||
},
|
||||
inputMapper: {
|
||||
decoder: {
|
||||
readers: function(value) {
|
||||
if (value === 'ean_extended') {
|
||||
return [{
|
||||
format: "ean_reader",
|
||||
config: {
|
||||
supplements: [
|
||||
'ean_5_reader', 'ean_2_reader'
|
||||
]
|
||||
}
|
||||
}];
|
||||
}
|
||||
return [{
|
||||
format: value + "_reader",
|
||||
config: {}
|
||||
}];
|
||||
}
|
||||
},
|
||||
inputStream: {
|
||||
src: function(value) {
|
||||
return "fixtures/" + value + "/"
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
inputStream: {
|
||||
src: "fixtures/code_128/"
|
||||
},
|
||||
decoder : {
|
||||
readers : [{
|
||||
format: "code_128_reader",
|
||||
config: {}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
App.init();
|
||||
window.App = App;
|
||||
|
||||
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 $node,
|
||||
canvas = Quagga.canvas.dom.image,
|
||||
detectedCode = result.codeResult.code;
|
||||
|
||||
$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(detectedCode);
|
||||
$("#result_strip ul.thumbnails").prepend($node);
|
||||
});
|
||||
});
|
||||
4
quagga2/quagga2-1.12.1/docs/examples/vendor/jquery-1.9.0.min.js
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Algorithm Overview {#algorithm-overview}
|
||||
|
||||
This page provides a high-level overview of how Quagga2 processes images to detect and decode barcodes.
|
||||
|
||||
## Processing Pipeline {#processing-pipeline}
|
||||
|
||||
Quagga2 processes each frame through a multi-stage pipeline:
|
||||
|
||||
```
|
||||
Input Image → Preprocessing → Localization → Decoding → Result
|
||||
```
|
||||
|
||||
### 1. Preprocessing {#preprocessing}
|
||||
|
||||
- **Scaling**: Image is resized based on `inputStream.size`
|
||||
- **Grayscale conversion**: Color image converted to grayscale
|
||||
- **Area cropping**: If `inputStream.area` is set, crop to that region
|
||||
|
||||
### 2. Localization {#localization}
|
||||
|
||||
When `locate: true` (default):
|
||||
|
||||
1. **Binarization**: Convert to black/white using Otsu's method
|
||||
2. **Grid division**: Split image into patches
|
||||
3. **Skeletonization**: Extract line structures
|
||||
4. **Pattern analysis**: Find barcode-like patterns
|
||||
5. **Bounding box**: Calculate barcode region
|
||||
|
||||
See [How Barcode Localization Works](how-barcode-localization-works.md) for detailed explanation.
|
||||
|
||||
### 3. Decoding {#decoding}
|
||||
|
||||
1. **Scanline extraction**: Sample pixels along detected barcode
|
||||
2. **Pattern matching**: Match bar/space patterns to barcode format
|
||||
3. **Character decoding**: Convert patterns to characters
|
||||
4. **Checksum validation**: Verify barcode integrity
|
||||
|
||||
## Key Algorithms {#key-algorithms}
|
||||
|
||||
### Otsu's Method {#otsus-method}
|
||||
|
||||
Automatic threshold selection for binarization. Adapts to varying lighting conditions by analyzing the image histogram.
|
||||
|
||||
### Connected Component Labeling {#connected-component-labeling}
|
||||
|
||||
Groups adjacent pixels into distinct regions. Used during localization to identify potential barcode patterns.
|
||||
|
||||
### Image Moments {#image-moments}
|
||||
|
||||
Mathematical technique to calculate orientation and position of detected patterns.
|
||||
|
||||
## Performance Characteristics {#performance-characteristics}
|
||||
|
||||
| Factor | Impact | Mitigation |
|
||||
|--------|--------|------------|
|
||||
| Image size | Linear increase | Use `inputStream.size` |
|
||||
| Number of readers | Linear increase | Only enable needed readers |
|
||||
| Localization | ~60% of processing time | Use `locate: false` if position known |
|
||||
| Half sampling | 4x faster | Keep `halfSample: true` |
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [How Barcode Localization Works](how-barcode-localization-works.md) - Detailed localization explanation
|
||||
- [Architecture](architecture.md) - Code structure overview
|
||||
- [Optimize Performance](../how-to-guides/optimize-performance.md) - Performance tuning
|
||||
|
||||
---
|
||||
|
||||
[← Back to Explanation](index.md)
|
||||
117
quagga2/quagga2-1.12.1/docs/explanation/architecture.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Architecture {#architecture}
|
||||
|
||||
This page describes Quagga2's code structure and design decisions.
|
||||
|
||||
## Project Structure {#project-structure}
|
||||
|
||||
```
|
||||
quagga2/
|
||||
├── src/
|
||||
│ ├── quagga.ts # Main entry point
|
||||
│ ├── config/ # Configuration handling
|
||||
│ ├── decoder/ # Barcode decoding
|
||||
│ │ ├── barcode_decoder.ts
|
||||
│ │ └── readers/ # Individual barcode readers
|
||||
│ ├── locator/ # Barcode localization
|
||||
│ │ ├── barcode_locator.ts
|
||||
│ │ └── skeletonizer.ts
|
||||
│ ├── input/ # Input handling
|
||||
│ │ ├── camera_access.ts
|
||||
│ │ └── frame_grabber.ts
|
||||
│ └── common/ # Shared utilities
|
||||
│ ├── cv_utils.ts # Computer vision utilities
|
||||
│ └── image_wrapper.ts
|
||||
├── dist/ # Browser builds
|
||||
├── lib/ # Node.js build
|
||||
└── type-definitions/ # TypeScript types
|
||||
```
|
||||
|
||||
## Core Components {#core-components}
|
||||
|
||||
### Quagga (Main API) {#main-api}
|
||||
|
||||
The main entry point (`src/quagga.ts`) exposes the public API:
|
||||
|
||||
- `init()` / `start()` / `stop()`
|
||||
- `decodeSingle()`
|
||||
- `onDetected()` / `onProcessed()`
|
||||
- `CameraAccess` namespace
|
||||
|
||||
### Barcode Locator {#barcode-locator}
|
||||
|
||||
Located in `src/locator/`, responsible for finding barcode regions in images:
|
||||
|
||||
- **barcode_locator.ts**: Main localization logic
|
||||
- **skeletonizer.ts**: Line structure extraction (asm.js optimized)
|
||||
|
||||
### Barcode Decoder {#barcode-decoder}
|
||||
|
||||
Located in `src/decoder/`, handles barcode decoding:
|
||||
|
||||
- **barcode_decoder.ts**: Coordinates decoding process
|
||||
- **readers/**: Individual reader implementations
|
||||
- Each reader extends `BarcodeReader` base class
|
||||
- Implements format-specific pattern matching
|
||||
|
||||
### Input Handling {#input-handling}
|
||||
|
||||
Located in `src/input/`:
|
||||
|
||||
- **camera_access.ts**: Camera enumeration and control
|
||||
- **frame_grabber_browser.ts**: Browser frame capture
|
||||
- **frame_grabber_node.ts**: Node.js image loading
|
||||
|
||||
## Data Flow {#data-flow}
|
||||
|
||||
```
|
||||
Camera/Image
|
||||
↓
|
||||
FrameGrabber (captures frame)
|
||||
↓
|
||||
ImageWrapper (grayscale conversion)
|
||||
↓
|
||||
BarcodeLocator (finds barcode region)
|
||||
↓
|
||||
BarcodeDecoder (decodes barcode)
|
||||
↓
|
||||
Result callbacks
|
||||
```
|
||||
|
||||
## Design Decisions {#design-decisions}
|
||||
|
||||
### Bundle Everything {#bundle-everything}
|
||||
|
||||
All dependencies are bundled into the final build. This means:
|
||||
|
||||
- Consumers never install dependencies directly
|
||||
- All packages go in `devDependencies`
|
||||
- Builds are self-contained
|
||||
|
||||
### Dual Build Targets {#dual-build-targets}
|
||||
|
||||
- **Browser**: `dist/quagga.min.js` - UMD bundle
|
||||
- **Node.js**: `lib/quagga.js` - CommonJS module
|
||||
|
||||
### Reader Architecture {#reader-architecture}
|
||||
|
||||
Readers are pluggable:
|
||||
|
||||
- Built-in readers in `src/decoder/readers/`
|
||||
- External readers via `Quagga.registerReader()`
|
||||
- All readers extend common `BarcodeReader` class
|
||||
|
||||
## Build System {#build-system}
|
||||
|
||||
- **Webpack 4**: Module bundling
|
||||
- **Babel**: ES6+ transpilation
|
||||
- **TypeScript**: Type checking (but source is mixed JS/TS)
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Algorithm Overview](algorithm-overview.md) - How the algorithms work
|
||||
- [Create External Readers](../how-to-guides/external-readers.md) - Extend Quagga2
|
||||
- [Contributing](../contributing.md) - Development setup
|
||||
|
||||
---
|
||||
|
||||
[← Back to Explanation](index.md)
|
||||
@@ -0,0 +1,197 @@
|
||||
# How Barcode Localization Works {#how-barcode-localization-works}
|
||||
|
||||
> **Note on Terminology**: "Localization" in this context refers to finding the **physical location** (position, bounding box) of a barcode within an image - not language localization (i18n). This is standard computer vision terminology.
|
||||
|
||||
This article explains the technical details of how Quagga2 locates and decodes barcodes in images. Understanding this can help you optimize performance and troubleshoot issues.
|
||||
|
||||
## Overview {#overview}
|
||||
|
||||
Quagga2 uses a two-stage process:
|
||||
|
||||
1. **Barcode Locator** (blue box in the images below) - Finds regions that look like barcodes
|
||||
2. **Barcode Decoder** (red line in the images below) - Reads the actual barcode data
|
||||
|
||||
This approach is based on the paper [Locating and decoding EAN-13 barcodes from images captured by digital cameras](http://www.icics.org/2005/download/P0840.pdf) by Douglas et al., with adaptations and modifications for web browsers.
|
||||
|
||||
## Stage 1: Barcode Locator {#stage-1-barcode-locator}
|
||||
|
||||
The locator finds patterns that look like barcodes. A barcode is typically characterized by:
|
||||
|
||||
- **Lines** (black bars and white gaps)
|
||||
- That are **close to each other**
|
||||
- With a **similar angle** (parallel lines)
|
||||
|
||||
### Step 1: Creating a Binary Image {#step-1-binary-image}
|
||||
|
||||
The first step is converting the color image to binary (black and white). Instead of using a simple threshold (e.g., everything below 127 is black), Quagga2 uses **Otsu's method**, which adapts to lighting changes across the image.
|
||||
|
||||

|
||||
|
||||
Otsu's method analyzes the image histogram and automatically separates foreground (barcode) from background, even with uneven lighting.
|
||||
|
||||
### Step 2: Slicing into a Grid {#step-2-slicing-grid}
|
||||
|
||||
The binary image is divided into a **20×15 grid** (assuming 4:3 aspect ratio). Each cell is analyzed independently to determine if it contains barcode-like patterns.
|
||||
|
||||
### Step 3: Extract Skeleton {#step-3-extract-skeleton}
|
||||
|
||||
Each cell undergoes **skeletonization** - reducing bars to their centerline (1px width). This is done through iterative erosion and dilation.
|
||||
|
||||

|
||||
|
||||
The skeleton clearly shows where parallel lines exist, making it easier to identify barcode regions.
|
||||
|
||||
### Step 4: Component Labeling {#step-4-component-labeling}
|
||||
|
||||
Using **connected-component labeling**, each line in the skeletonized image is separated into individual components. This is done with a fast algorithm based on the paper ["A Linear-Time Component-Labeling Algorithm Using Contour Tracing Technique"](http://www.iis.sinica.edu.tw/papers/fchang/1362-F.pdf) by Fu Chang et al.
|
||||
|
||||

|
||||
|
||||
Each color represents a distinct labeled component (line). Notice how each cell is processed independently.
|
||||
|
||||
Here are zoomed examples of two cells:
|
||||
|
||||

|
||||
*Good: Parallel lines indicate a possible barcode*
|
||||
|
||||

|
||||
*Bad: Random components indicate noise/text*
|
||||
|
||||
### Step 5: Determining Orientation {#step-5-determining-orientation}
|
||||
|
||||
For each component, Quagga2 calculates its orientation using **central image moments**. This is a mathematical technique to extract the angle of a shape.
|
||||
|
||||
The orientation (θ) is calculated as:
|
||||
|
||||

|
||||
|
||||
Where μ (mu) are central moments calculated from raw moments (M):
|
||||
|
||||

|
||||
|
||||
The centroid (x̄, ȳ) is calculated from raw moments:
|
||||
|
||||

|
||||
|
||||
And the raw moments (M) are computed as:
|
||||
|
||||

|
||||
|
||||
Where I(x,y) is the pixel value at position (x,y) - either 0 or 1 in a binary image.
|
||||
|
||||
Don't worry if the math looks intimidating - the key insight is that these formulas calculate **which direction each line is pointing**.
|
||||
|
||||
### Step 6: Determining Cell Quality {#step-6-determining-cell-quality}
|
||||
|
||||
Cells are evaluated based on how parallel their lines are:
|
||||
|
||||
1. **Filter out noise**: Discard cells with fewer than 2 components, or components smaller than 6 pixels
|
||||
2. **Cluster angles**: Group similar angles together
|
||||
3. **Select dominant cluster**: Pick the cluster with the most members
|
||||
4. **Quality threshold**: Only accept cells where ≥75% of components share the same angle
|
||||
|
||||
Cells that pass this test are called **patches** and contain:
|
||||
|
||||
- Unique index
|
||||
- Bounding box
|
||||
- All components with their angles
|
||||
- Average angle
|
||||
- Direction vector
|
||||
|
||||

|
||||
|
||||
Yellow boxes show patches that were classified as possible barcode areas. Note some false positives (text regions).
|
||||
|
||||
### Step 7: Finding Connected Cells {#step-7-finding-connected-cells}
|
||||
|
||||
Patches are grouped together if they're neighbors with similar orientation (within 5% angle difference). This is done using recursive component labeling.
|
||||
|
||||

|
||||
|
||||
Each color represents a distinct group. Sometimes adjacent patches have different colors due to angle differences exceeding the 5% threshold.
|
||||
|
||||
### Step 8: Selecting Groups {#step-8-selecting-groups}
|
||||
|
||||
Groups are sorted by size (number of patches) and only the largest groups are kept - these are most likely to be actual barcodes.
|
||||
|
||||

|
||||
|
||||
Small groups and false positives have been filtered out.
|
||||
|
||||
### Step 9: Create Bounding Box {#step-9-create-bounding-box}
|
||||
|
||||
For each group, a **minimum bounding box** is calculated:
|
||||
|
||||
1. Calculate average angle of all patches in the group
|
||||
2. Rotate all patches by this angle
|
||||
3. Find outermost corners (min/max x and y)
|
||||
4. Create bounding box
|
||||
5. Rotate box back to original orientation
|
||||
|
||||

|
||||
*Patches rotated to horizontal, bounding box calculated*
|
||||
|
||||

|
||||
*Final bounding box rotated back to match barcode orientation*
|
||||
|
||||
The bounding box now precisely outlines the barcode, including its rotation and scale. This information is passed to the decoder.
|
||||
|
||||
## Stage 2: Barcode Decoder {#stage-2-barcode-decoder}
|
||||
|
||||
With the bounding box and orientation known, the decoder:
|
||||
|
||||
1. Samples pixel intensities along scan lines within the box
|
||||
2. Detects transitions from black to white (edges of bars)
|
||||
3. Calculates bar widths
|
||||
4. Matches patterns against the selected barcode format(s)
|
||||
5. Validates checksums
|
||||
6. Returns the decoded data
|
||||
|
||||
## Why This Approach? {#why-this-approach}
|
||||
|
||||
Unlike simpler barcode scanners that require the barcode to be:
|
||||
|
||||
- Horizontal (aligned with the viewport)
|
||||
- At a specific distance (scale)
|
||||
- Centered in the frame
|
||||
|
||||
Quagga2's localization algorithm is **invariant to rotation and scale**. It can find and decode barcodes:
|
||||
|
||||
- At any angle
|
||||
- At any size (within reason)
|
||||
- Anywhere in the image
|
||||
- Even with multiple barcodes present
|
||||
|
||||
This makes it much more practical for real-world camera scanning where users can't always position the camera perfectly.
|
||||
|
||||
## Performance Considerations {#performance-considerations}
|
||||
|
||||
The localization algorithm is computationally intensive. Key factors affecting performance:
|
||||
|
||||
- **Image size**: Smaller images process faster. Consider downscaling if speed is critical.
|
||||
- **Grid size**: The default 20×15 grid balances accuracy and speed
|
||||
- **Patch filtering**: Aggressive filtering reduces false positives but may miss difficult barcodes
|
||||
- **Number of readers**: Enabling multiple barcode formats increases decode time
|
||||
|
||||
See [Optimize Performance](../how-to-guides/optimize-performance.md) for practical tips.
|
||||
|
||||
## Related Reading {#related-reading}
|
||||
|
||||
- **Original Paper**: [Locating and decoding EAN-13 barcodes from images captured by digital cameras](http://www.icics.org/2005/download/P0840.pdf)
|
||||
- **Otsu's Method**: [Wikipedia - Otsu's method](http://en.wikipedia.org/wiki/Otsu%27s_method)
|
||||
- **Skeletonization**: [Wikipedia - Morphological skeleton](http://en.wikipedia.org/wiki/Morphological_skeleton)
|
||||
- **Component Labeling**: [Wikipedia - Connected-component labeling](http://en.wikipedia.org/wiki/Connected-component_labeling)
|
||||
- **Image Moments**: [Wikipedia - Image moment](http://en.wikipedia.org/wiki/Image_moment#Central_moments)
|
||||
- **Fast Labeling Algorithm**: [A Linear-Time Component-Labeling Algorithm (PDF)](http://www.iis.sinica.edu.tw/papers/fchang/1362-F.pdf)
|
||||
- **CodeProject Implementation**: [Connected Component Labeling and Vectorization](http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization)
|
||||
|
||||
## Source Code {#source-code}
|
||||
|
||||
The localization algorithm is implemented in:
|
||||
|
||||
- `src/locator/barcode_locator.js` - Main locator logic
|
||||
- `src/locator/skeletonizer.js` - Skeletonization algorithm (asm.js)
|
||||
- `src/common/cluster.js` - Clustering algorithms
|
||||
- `src/common/cv_utils.js` - Computer vision utilities
|
||||
|
||||
Contributions and improvements are welcome!
|
||||
80
quagga2/quagga2-1.12.1/docs/explanation/index.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Explanation
|
||||
|
||||
Background knowledge and context about Quagga2. These articles explain *why* things work the way they do, providing deeper understanding beyond just how to use the library.
|
||||
|
||||
## Algorithm & Theory
|
||||
|
||||
### [How Barcode Localization Works](how-barcode-localization-works.md) ⭐ Featured
|
||||
|
||||
Deep dive into the computer vision algorithms that find barcodes in images. Covers:
|
||||
|
||||
- Binary image creation with Otsu's method
|
||||
- Skeletonization
|
||||
- Component labeling
|
||||
- Orientation detection using image moments
|
||||
- Why this approach is rotation/scale invariant
|
||||
|
||||
**Read this if**: You want to understand the "magic" behind barcode detection, optimize performance, or contribute to the localization code.
|
||||
|
||||
## Architecture
|
||||
|
||||
### [How Input Streams Work](input-streams.md)
|
||||
|
||||
Technical deep dive into the three input stream types and their initialization flow. Covers:
|
||||
|
||||
- LiveStream, VideoStream, and ImageStream differences
|
||||
- The async initialization sequence
|
||||
- Why `framegrabber` indicates init completion
|
||||
- Race conditions and how to avoid them
|
||||
|
||||
**Read this if**: You're debugging initialization issues, dealing with React StrictMode, or want to understand the media pipeline.
|
||||
|
||||
### [Algorithm Overview](algorithm-overview.md) *(Coming Soon)*
|
||||
|
||||
High-level overview of Quagga2's processing pipeline from input to output.
|
||||
|
||||
### [Code Architecture](architecture.md)
|
||||
|
||||
Structure of the codebase, module organization, and design decisions.
|
||||
|
||||
## Concepts
|
||||
|
||||
### [Why asm.js for Skeletonization](why-asmjs.md) *(Coming Soon)*
|
||||
|
||||
Explanation of why performance-critical code uses asm.js and what that means.
|
||||
|
||||
### [Reader Design Patterns](reader-patterns.md) *(Coming Soon)*
|
||||
|
||||
How barcode readers are implemented and how to create custom ones.
|
||||
|
||||
## Differences from Other Doc Types
|
||||
|
||||
**Explanation** articles:
|
||||
|
||||
- Provide context and background
|
||||
- Discuss alternatives and trade-offs
|
||||
- Explain historical decisions
|
||||
- Connect concepts together
|
||||
- Are OK to read casually
|
||||
|
||||
**Tutorials**: Step-by-step learning by doing
|
||||
**How-To Guides**: Task-focused, get things done
|
||||
**Reference**: Precise technical specifications
|
||||
|
||||
## When to Read Explanations
|
||||
|
||||
Read these articles when you:
|
||||
|
||||
- Want to **understand** how things work under the hood
|
||||
- Need to **debug** complex issues
|
||||
- Want to **contribute** to the codebase
|
||||
- Are **curious** about design decisions
|
||||
- Want to **optimize** beyond basic tuning
|
||||
|
||||
## Contributing
|
||||
|
||||
Have insights to share about how Quagga2 works? We welcome explanation articles! See the [Contributing Guide](../contributing.md).
|
||||
|
||||
---
|
||||
|
||||
[← Back to Documentation Home](../index.md)
|
||||
270
quagga2/quagga2-1.12.1/docs/explanation/input-streams.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# How Input Streams Work {#how-input-streams-work}
|
||||
|
||||
This article explains the technical details of how Quagga2's input stream system works. Understanding this is helpful for troubleshooting initialization issues and understanding async behavior.
|
||||
|
||||
## Overview {#overview}
|
||||
|
||||
Quagga2 supports three types of input streams for reading barcode data:
|
||||
|
||||
| Type | Use Case | Input Source |
|
||||
|------|----------|--------------|
|
||||
| **LiveStream** | Real-time camera scanning | Device camera via getUserMedia |
|
||||
| **VideoStream** | Pre-recorded video files | Video file via `<video>` element |
|
||||
| **ImageStream** | Static images or image sequences | Image file(s) via URL |
|
||||
|
||||
All three stream types share the same interface (`InputStream`) and follow a common initialization pattern, but differ in how they acquire media.
|
||||
|
||||
## The InputStream Interface {#inputstream-interface}
|
||||
|
||||
Every input stream implements these core methods:
|
||||
|
||||
```typescript
|
||||
interface InputStream {
|
||||
// Dimensions
|
||||
getWidth(): number;
|
||||
getHeight(): number;
|
||||
getRealWidth(): number;
|
||||
getRealHeight(): number;
|
||||
setWidth(width: number): void;
|
||||
setHeight(height: number): void;
|
||||
|
||||
// Frame access
|
||||
getFrame(): HTMLVideoElement | HTMLImageElement | null;
|
||||
|
||||
// Event handling
|
||||
addEventListener(event: string, handler: Function): void;
|
||||
clearEventHandlers(): void;
|
||||
trigger(eventName: string, args?: any): void;
|
||||
|
||||
// Playback control
|
||||
play(): void;
|
||||
pause(): void;
|
||||
ended(): boolean;
|
||||
|
||||
// Configuration
|
||||
setInputStream(config: any): void;
|
||||
getConfig(): any;
|
||||
}
|
||||
```
|
||||
|
||||
## Initialization Flow {#initialization-flow}
|
||||
|
||||
All stream types follow the same initialization sequence:
|
||||
|
||||
```
|
||||
init() → initInputStream() → [async media access] → 'canrecord' event → canRecord() → framegrabber created
|
||||
```
|
||||
|
||||
Here's what happens at each step:
|
||||
|
||||
### 1. `init()` is called {#step-1-init}
|
||||
|
||||
The static `Quagga.init(config, callback)` function starts the process:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: 'LiveStream', // or 'VideoStream' or 'ImageStream'
|
||||
target: document.querySelector('#scanner'),
|
||||
// ... other options
|
||||
},
|
||||
// ... decoder config
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.error('Init failed:', err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
### 2. `initInputStream()` creates the stream {#step-2-initinputstream}
|
||||
|
||||
Based on the `type` configuration, the appropriate stream factory is called:
|
||||
|
||||
- `LiveStream` → `createLiveStream(video)`
|
||||
- `VideoStream` → `createVideoStream(video)`
|
||||
- `ImageStream` → `createImageStream()`
|
||||
|
||||
### 3. Async media access begins {#step-3-async-media}
|
||||
|
||||
This is where the streams diverge:
|
||||
|
||||
**LiveStream**: Calls `CameraAccess.request()` which uses `navigator.mediaDevices.getUserMedia()`. This is async because:
|
||||
- Browser shows a permission prompt
|
||||
- Camera hardware needs to spin up
|
||||
- Video dimensions aren't known until stream starts
|
||||
|
||||
**VideoStream**: Creates a `<video>` element and waits for the video to load metadata. Async because the video file must be fetched.
|
||||
|
||||
**ImageStream**: Uses `ImageLoader` to fetch and decode image(s). Async because images must be downloaded.
|
||||
|
||||
### 4. `canrecord` event fires {#step-4-canrecord}
|
||||
|
||||
When the media is ready, the stream triggers the `canrecord` event. This is the signal that:
|
||||
- Media dimensions are now available
|
||||
- Frames can be grabbed
|
||||
- Processing can begin
|
||||
|
||||
### 5. `canRecord()` completes initialization {#step-5-canrecord-callback}
|
||||
|
||||
The `canRecord()` callback:
|
||||
1. Validates the input stream is properly initialized
|
||||
2. Calls `checkImageConstraints()` to validate/adjust dimensions
|
||||
3. Creates the canvas for drawing frames
|
||||
4. Creates the **framegrabber** (the component that extracts frames)
|
||||
5. Sets up worker threads (if configured)
|
||||
6. Calls the user's callback to signal init is complete
|
||||
|
||||
### 6. Framegrabber indicates completion {#step-6-framegrabber}
|
||||
|
||||
The `framegrabber` being non-null is the reliable indicator that initialization completed successfully. This is why:
|
||||
|
||||
- The static `start()` function checks `if (!_context.framegrabber)` before proceeding
|
||||
- The `stop()` function uses `!framegrabber` to detect if init was still in progress
|
||||
|
||||
## Stream Type Details {#stream-type-details}
|
||||
|
||||
### LiveStream {#livestream}
|
||||
|
||||
**Purpose**: Real-time barcode scanning using the device camera.
|
||||
|
||||
**How it works**:
|
||||
1. Creates or finds a `<video>` element in the target container
|
||||
2. Requests camera access via `getUserMedia()`
|
||||
3. Attaches the camera stream to the video element
|
||||
4. Sets `autoplay="true"` so the video starts immediately
|
||||
5. Triggers `canrecord` when camera is ready
|
||||
|
||||
**Key characteristics**:
|
||||
- `ended()` always returns `false` (camera never "ends")
|
||||
- Requires HTTPS in production (browser security requirement)
|
||||
- Can specify camera constraints (facing mode, resolution)
|
||||
|
||||
**Configuration example**:
|
||||
```javascript
|
||||
inputStream: {
|
||||
type: 'LiveStream',
|
||||
target: document.querySelector('#camera'),
|
||||
constraints: {
|
||||
facingMode: 'environment', // Back camera
|
||||
width: { min: 640 },
|
||||
height: { min: 480 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### VideoStream {#videostream}
|
||||
|
||||
**Purpose**: Scanning barcodes from pre-recorded video files.
|
||||
|
||||
**How it works**:
|
||||
1. Creates a new `<video>` element
|
||||
2. Sets the `src` attribute to the video URL
|
||||
3. Waits for video metadata to load
|
||||
4. Triggers `canrecord` when dimensions are known
|
||||
|
||||
**Key characteristics**:
|
||||
- `ended()` returns the video element's ended state
|
||||
- Supports seeking via `setCurrentTime()`
|
||||
- Video plays frame-by-frame during scanning
|
||||
|
||||
**Configuration example**:
|
||||
```javascript
|
||||
inputStream: {
|
||||
type: 'VideoStream',
|
||||
src: '/path/to/video.mp4'
|
||||
}
|
||||
```
|
||||
|
||||
### ImageStream {#imagestream}
|
||||
|
||||
**Purpose**: Scanning barcodes from static images or image sequences.
|
||||
|
||||
**How it works**:
|
||||
1. Parses the image URL configuration
|
||||
2. Uses `ImageLoader` to fetch the image(s)
|
||||
3. Reads EXIF data to handle image orientation
|
||||
4. Calculates dimensions based on size config
|
||||
5. Triggers `canrecord` when image(s) are loaded
|
||||
|
||||
**Key characteristics**:
|
||||
- Can process a single image or a sequence
|
||||
- Handles EXIF orientation automatically
|
||||
- `ended()` returns true after all images are processed
|
||||
- Used internally by `decodeSingle()`
|
||||
|
||||
**Configuration example (single image)**:
|
||||
```javascript
|
||||
inputStream: {
|
||||
type: 'ImageStream',
|
||||
src: '/path/to/barcode.jpg',
|
||||
sequence: false
|
||||
}
|
||||
```
|
||||
|
||||
**Configuration example (image sequence)**:
|
||||
```javascript
|
||||
inputStream: {
|
||||
type: 'ImageStream',
|
||||
src: '/path/to/images/img_%d.jpg', // %d is replaced with frame number
|
||||
sequence: true,
|
||||
length: 10 // Number of images
|
||||
}
|
||||
```
|
||||
|
||||
## Race Conditions and Async Behavior {#race-conditions}
|
||||
|
||||
Because initialization involves async operations (camera access, file loading), race conditions can occur if:
|
||||
|
||||
1. **`stop()` is called during `init()`**: The `canrecord` event may fire after `stop()` has begun cleanup. Quagga2 handles this with an `initAborted` flag.
|
||||
|
||||
2. **React StrictMode double-invocation**: StrictMode mounts, unmounts, and remounts components, causing rapid `init() → stop() → init()` sequences.
|
||||
|
||||
3. **Component unmounting before camera ready**: User navigates away before `getUserMedia()` resolves.
|
||||
|
||||
**Best practices to avoid issues**:
|
||||
|
||||
```javascript
|
||||
useLayoutEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
Quagga.init(config, (err) => {
|
||||
if (cancelled) return; // Ignore if unmounted
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
Quagga.stop();
|
||||
};
|
||||
}, []);
|
||||
```
|
||||
|
||||
## Source Code {#source-code}
|
||||
|
||||
The input stream system is implemented in:
|
||||
|
||||
- `src/input/input_stream/input_stream_browser.ts` - Browser stream implementations
|
||||
- `src/input/input_stream/input_stream.ts` - Node.js stream implementation
|
||||
- `src/input/input_stream/input_stream.d.ts` - TypeScript interface
|
||||
- `src/quagga/setupInputStream.ts` - Stream factory selection
|
||||
- `src/input/camera_access.ts` - Camera permission handling
|
||||
- `src/input/frame_grabber.js` - Frame extraction for Node.js (uses ndarray)
|
||||
- `src/input/frame_grabber_browser.js` - Frame extraction for browsers (uses canvas)
|
||||
|
||||
> **Note**: Webpack replaces `frame_grabber.js` with `frame_grabber_browser.js` when building the browser bundle. The Node.js version uses `ndarray` for image manipulation, while the browser version uses the Canvas API.
|
||||
|
||||
## Related Reading {#related-reading}
|
||||
|
||||
- [How Barcode Localization Works](how-barcode-localization-works.md) - What happens after frames are grabbed
|
||||
- [Camera Access Reference](../reference/camera-access.md) - Camera configuration options
|
||||
- [Configuration Reference](../reference/configuration.md) - Full config documentation
|
||||
|
||||
---
|
||||
|
||||
[← Back to Explanation Index](index.md)
|
||||
213
quagga2/quagga2-1.12.1/docs/getting-started.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# Getting Started with Quagga2 {#getting-started}
|
||||
|
||||
This guide will help you install Quagga2 and get your first barcode scanner running quickly.
|
||||
|
||||
## What is Quagga2? {#what-is-quagga2}
|
||||
|
||||
Quagga2 is a JavaScript barcode scanner library that works in both browsers and Node.js. It can:
|
||||
|
||||
- **Scan barcodes in real-time** using your device's camera
|
||||
- **Decode barcodes from images** (photos, screenshots, etc.)
|
||||
- **Support multiple formats**: EAN, CODE 128, CODE 39, EAN 8, UPC-A, UPC-C, I2of5, 2of5, CODE 93, CODE 32, CODABAR, and PHARMACODE
|
||||
- **Work offline** - all processing happens in the browser/Node.js, no server required
|
||||
|
||||
Unlike some libraries, Quagga2 includes a barcode **locator** that can find and decode barcodes regardless of their rotation or scale in the image.
|
||||
|
||||
## Installation {#installation}
|
||||
|
||||
### Using NPM (Recommended) {#using-npm}
|
||||
|
||||
```bash
|
||||
npm install --save @ericblade/quagga2
|
||||
```
|
||||
|
||||
Then import it in your project:
|
||||
|
||||
```javascript
|
||||
// ES6 modules
|
||||
import Quagga from '@ericblade/quagga2';
|
||||
|
||||
// CommonJS (important: note the .default)
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
```
|
||||
|
||||
### Using CDN (Script Tag) {#using-cdn}
|
||||
|
||||
Add one of these script tags to your HTML:
|
||||
|
||||
**Unminified version** (useful for development):
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
|
||||
```
|
||||
|
||||
**Minified version** (recommended for production):
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.min.js"></script>
|
||||
```
|
||||
|
||||
**Specific version** (recommended to avoid breaking changes):
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script>
|
||||
```
|
||||
|
||||
The script tag exposes the library globally as `Quagga`.
|
||||
|
||||
## Basic Usage - Live Camera Scanning {#live-camera-scanning}
|
||||
|
||||
Here's a minimal example to scan barcodes using your device's camera:
|
||||
|
||||
### HTML {#html-example}
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Quagga2 Barcode Scanner</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scanner-container"></div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### JavaScript (app.js) {#javascript-example}
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
target: document.querySelector('#scanner-container')
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
console.log("Initialization finished. Ready to start");
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
Quagga.onDetected(function(result) {
|
||||
const code = result.codeResult.code;
|
||||
console.log("Barcode detected:", code);
|
||||
alert("Found barcode: " + code);
|
||||
});
|
||||
```
|
||||
|
||||
That's it! This will:
|
||||
|
||||
1. Request camera access
|
||||
2. Display the camera feed in `#scanner-container`
|
||||
3. Continuously scan for CODE 128 barcodes
|
||||
4. Alert you when a barcode is detected
|
||||
|
||||
## Basic Usage - Static Image Scanning {#static-image-scanning}
|
||||
|
||||
To decode a barcode from an existing image:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: "path/to/your/image.jpg",
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
// Note: Images are scaled to 800px (longest side) by default.
|
||||
// See inputStream.size in the Configuration Reference for details.
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log("Barcode found:", result.codeResult.code);
|
||||
} else {
|
||||
console.log("No barcode found");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Important Notes {#important-notes}
|
||||
|
||||
### HTTPS Required {#https-required}
|
||||
|
||||
For security reasons, browsers require **HTTPS** to access the camera, except on `localhost`. If you host your app on a domain, you must use `https://`.
|
||||
|
||||
### Browser Compatibility {#browser-compatibility}
|
||||
|
||||
Quagga2 works in modern browsers that support:
|
||||
|
||||
- Canvas API
|
||||
- Typed Arrays
|
||||
- MediaDevices API (for camera access)
|
||||
|
||||
See the [Browser Support](reference/browser-support.md) page for detailed compatibility information.
|
||||
|
||||
### Polyfill Recommendation {#polyfill-recommendation}
|
||||
|
||||
Different browsers implement camera APIs differently. We recommend including [webrtc-adapter](https://github.com/webrtc/adapter) for better compatibility:
|
||||
|
||||
```html
|
||||
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
|
||||
```
|
||||
|
||||
## Next Steps {#next-steps}
|
||||
|
||||
Now that you have the basics working:
|
||||
|
||||
- **[Try a complete tutorial](tutorials/first-scan.md)** - Build a working barcode scanner step-by-step
|
||||
- **[Learn about configuration options](reference/configuration.md)** - Customize behavior
|
||||
- **[Explore supported barcode types](reference/readers.md)** - Enable different formats
|
||||
- **[Check out live examples](https://serratus.github.io/quaggaJS/examples)** - See Quagga2 in action
|
||||
|
||||
## Common Issues {#common-issues}
|
||||
|
||||
### Camera Permission Denied {#camera-permission-denied}
|
||||
|
||||
If the user denies camera access, the `init` callback will receive an error. Always handle this gracefully:
|
||||
|
||||
```javascript
|
||||
Quagga.init(config, function(err) {
|
||||
if (err) {
|
||||
if (err.name === 'NotAllowedError') {
|
||||
alert('Please allow camera access to scan barcodes');
|
||||
}
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
### Camera Not Found {#camera-not-found}
|
||||
|
||||
Some devices don't have a camera (desktops, VMs). Check for camera availability:
|
||||
|
||||
```javascript
|
||||
if (navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {
|
||||
// Camera API available
|
||||
Quagga.init(config, callback);
|
||||
} else {
|
||||
// Fallback to file upload
|
||||
alert('Camera not available, please upload an image');
|
||||
}
|
||||
```
|
||||
|
||||
### No Barcode Detected {#no-barcode-detected}
|
||||
|
||||
If Quagga2 isn't detecting your barcode:
|
||||
|
||||
- Make sure you've enabled the correct reader (e.g., `ean_reader` for EAN-13)
|
||||
- Ensure good lighting
|
||||
- Try holding the barcode steady and filling most of the camera view
|
||||
- See [How to Handle Difficult Barcodes](how-to-guides/handle-difficult-barcodes.md)
|
||||
|
||||
## Getting Help {#getting-help}
|
||||
|
||||
- **[GitHub Issues](https://github.com/ericblade/quagga2/issues)** - Report bugs or request features
|
||||
- **[Gitter Chat](https://gitter.im/quaggaJS/Lobby)** - Ask questions and get help from the community
|
||||
- **[API Documentation](reference/api.md)** - Complete API reference
|
||||
@@ -0,0 +1,79 @@
|
||||
# Configure Barcode Readers {#configure-readers}
|
||||
|
||||
This guide explains how to configure which barcode formats Quagga2 will detect.
|
||||
|
||||
## Basic Configuration {#basic-configuration}
|
||||
|
||||
Specify readers in the `decoder.readers` array:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader"]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Available Readers {#available-readers}
|
||||
|
||||
| Reader | Format | Common Uses |
|
||||
|--------|--------|-------------|
|
||||
| `code_128_reader` | Code 128 | Shipping, packaging |
|
||||
| `ean_reader` | EAN-13 | Retail products |
|
||||
| `ean_8_reader` | EAN-8 | Small products |
|
||||
| `upc_reader` | UPC-A | North American retail |
|
||||
| `upc_e_reader` | UPC-E | Small products |
|
||||
| `code_39_reader` | Code 39 | Industrial |
|
||||
| `code_39_vin_reader` | Code 39 VIN | Vehicle identification |
|
||||
| `codabar_reader` | Codabar | Libraries, blood banks |
|
||||
| `i2of5_reader` | Interleaved 2 of 5 | Warehouse |
|
||||
| `2of5_reader` | Standard 2 of 5 | Industrial |
|
||||
| `code_93_reader` | Code 93 | Logistics |
|
||||
| `code_32_reader` | Code 32 | Italian pharmaceuticals |
|
||||
|
||||
## Reader Priority {#reader-priority}
|
||||
|
||||
Readers are processed in order. Put most likely formats first:
|
||||
|
||||
```javascript
|
||||
decoder: {
|
||||
readers: [
|
||||
"ean_reader", // Most common - checked first
|
||||
"upc_reader", // Second most common
|
||||
"code_128_reader" // Fallback
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## EAN Supplements {#ean-supplements}
|
||||
|
||||
To read EAN-2 or EAN-5 extension supplements:
|
||||
|
||||
```javascript
|
||||
decoder: {
|
||||
readers: [
|
||||
"ean_reader", // Regular EAN without supplements
|
||||
{
|
||||
format: "ean_reader",
|
||||
config: {
|
||||
supplements: ["ean_5_reader", "ean_2_reader"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices {#best-practices}
|
||||
|
||||
1. **Only enable needed readers** - More readers = slower performance
|
||||
2. **Order by frequency** - Put most common formats first
|
||||
3. **Test thoroughly** - Some formats can conflict
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Supported Barcode Types](../reference/readers.md) - Complete reader documentation
|
||||
- [Configuration Reference](../reference/configuration.md) - All configuration options
|
||||
|
||||
---
|
||||
|
||||
[← Back to How-To Guides](index.md)
|
||||
234
quagga2/quagga2-1.12.1/docs/how-to-guides/external-readers.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# How-To: Create and Use External Readers {#create-and-use-external-readers}
|
||||
|
||||
This guide explains how to create custom barcode reader plugins and integrate them into Quagga2.
|
||||
|
||||
## Using Existing External Readers {#using-existing-external-readers}
|
||||
|
||||
### QR Code Reader {#qr-code-reader}
|
||||
|
||||
The most common external reader is [quagga2-reader-qr](https://github.com/ericblade/quagga2-reader-qr) for QR code support:
|
||||
|
||||
```javascript
|
||||
import Quagga from '@ericblade/quagga2';
|
||||
import QRReader from 'quagga2-reader-qr';
|
||||
|
||||
// Register the external reader
|
||||
Quagga.registerReader('qr', QRReader);
|
||||
|
||||
// Use it in your config
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ['qr']
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## External Reader Priority {#external-reader-priority}
|
||||
|
||||
External readers follow the **same priority rules** as built-in readers:
|
||||
|
||||
1. **Registration first**: Call `Quagga.registerReader(name, ReaderClass)` before using the reader
|
||||
2. **Position determines priority**: The reader's position in the `readers` array determines when it attempts to decode
|
||||
3. **First success wins**: The first reader to return a valid result is used
|
||||
|
||||
### Example: Prioritizing External Readers {#prioritizing-external-readers}
|
||||
|
||||
```javascript
|
||||
// Register external reader
|
||||
Quagga.registerReader('my_custom_reader', MyCustomReader);
|
||||
|
||||
// External reader tried first, then built-in readers
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ['my_custom_reader', 'ean_reader', 'code_128_reader']
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Example: External Reader as Fallback {#external-reader-fallback}
|
||||
|
||||
```javascript
|
||||
// Register external reader
|
||||
Quagga.registerReader('my_fallback_reader', MyFallbackReader);
|
||||
|
||||
// Built-in readers tried first, external as fallback
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ['ean_reader', 'code_128_reader', 'my_fallback_reader']
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Creating Custom Readers {#creating-custom-readers}
|
||||
|
||||
Quagga2 exports the `BarcodeReader` prototype that you can extend to create custom readers.
|
||||
|
||||
### Basic Reader Structure {#basic-reader-structure}
|
||||
|
||||
```javascript
|
||||
import { Readers } from '@ericblade/quagga2';
|
||||
|
||||
const { BarcodeReader } = Readers;
|
||||
|
||||
class MyCustomReader extends BarcodeReader {
|
||||
FORMAT = 'my_custom_format';
|
||||
|
||||
constructor(config, supplements) {
|
||||
super(config, supplements);
|
||||
// Custom initialization
|
||||
}
|
||||
|
||||
decode(row, start) {
|
||||
// Implement barcode decoding logic
|
||||
// row: Array<number> - binary line data
|
||||
// start: number - starting position
|
||||
|
||||
// Return null if no barcode found
|
||||
// Return Barcode object if successful
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default MyCustomReader;
|
||||
```
|
||||
|
||||
### Reader Return Format {#reader-return-format}
|
||||
|
||||
Your `decode()` method should return either `null` (no match) or a result object:
|
||||
|
||||
```javascript
|
||||
{
|
||||
code: '1234567890', // The decoded barcode value
|
||||
start: 0, // Start position in the row
|
||||
end: 100, // End position in the row
|
||||
startInfo: { start: 0, end: 10 }, // Start pattern info
|
||||
format: 'my_custom_format', // Your reader's format name
|
||||
decodedCodes: [...] // Optional: decoded character info
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern Matching Utilities {#pattern-matching-utilities}
|
||||
|
||||
BarcodeReader provides useful methods for pattern matching:
|
||||
|
||||
- `_nextSet(line, offset)` - Find next set (1) bit
|
||||
- `_nextUnset(line, offset)` - Find next unset (0) bit
|
||||
- `_matchPattern(counter, code, maxSingleError)` - Match bar patterns
|
||||
- `_fillCounters(offset, end, isWhite)` - Count consecutive bars/spaces
|
||||
|
||||
### Example: Simple Pattern Reader {#simple-pattern-reader}
|
||||
|
||||
```javascript
|
||||
class SimpleBarReader extends BarcodeReader {
|
||||
FORMAT = 'simple_bar';
|
||||
START_PATTERN = [1, 1, 1]; // Three bars pattern
|
||||
|
||||
decode(row, start) {
|
||||
if (row) {
|
||||
this._row = row;
|
||||
}
|
||||
|
||||
// Find start pattern
|
||||
const startInfo = this._findPattern(this.START_PATTERN, 0, false);
|
||||
if (!startInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Decode the barcode content
|
||||
const result = this._decodeContent(startInfo.end);
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
code: result.code,
|
||||
start: startInfo.start,
|
||||
end: result.end,
|
||||
startInfo: startInfo,
|
||||
format: this.FORMAT
|
||||
};
|
||||
}
|
||||
|
||||
_decodeContent(offset) {
|
||||
// Implement your decoding logic
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Image-Based Readers {#image-based-readers}
|
||||
|
||||
For non-linear barcodes (like QR codes), implement `decodeImage()` instead:
|
||||
|
||||
```javascript
|
||||
class MyImageReader extends BarcodeReader {
|
||||
FORMAT = 'my_image_format';
|
||||
|
||||
// Override decode to return null (not a linear barcode)
|
||||
decode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Implement image-based decoding
|
||||
async decodeImage(imageWrapper) {
|
||||
// imageWrapper.data - pixel data
|
||||
// imageWrapper.size - { x, y } dimensions
|
||||
|
||||
// Process the image and decode
|
||||
const result = await this.processImage(imageWrapper);
|
||||
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
codeResult: {
|
||||
code: result.data,
|
||||
format: this.FORMAT
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Your Reader {#testing-your-reader}
|
||||
|
||||
1. **Unit tests**: Test pattern matching and decoding logic in isolation
|
||||
2. **Integration tests**: Use `Quagga.decodeSingle()` with test images
|
||||
3. **Live testing**: Test with real camera input
|
||||
|
||||
### Example Test {#example-test}
|
||||
|
||||
```javascript
|
||||
import Quagga from '@ericblade/quagga2';
|
||||
import MyCustomReader from './my-custom-reader';
|
||||
|
||||
Quagga.registerReader('my_custom', MyCustomReader);
|
||||
|
||||
const result = await Quagga.decodeSingle({
|
||||
src: './test-image.jpg',
|
||||
decoder: {
|
||||
readers: ['my_custom']
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Result:', result?.codeResult?.code);
|
||||
```
|
||||
|
||||
## Best Practices {#best-practices}
|
||||
|
||||
1. **Set unique FORMAT**: Use a distinctive format name to identify your reader
|
||||
2. **Handle edge cases**: Return `null` gracefully when patterns don't match
|
||||
3. **Validate checksums**: Implement checksum validation when the format supports it
|
||||
4. **Consider performance**: Optimize pattern matching for real-time scanning
|
||||
5. **Test thoroughly**: Test with various image qualities and conditions
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Supported Barcode Types](../reference/readers.md) - Built-in readers
|
||||
- [Configuration Reference](../reference/configuration.md) - Full config options
|
||||
- [quagga2-reader-qr](https://github.com/ericblade/quagga2-reader-qr) - Example external reader
|
||||
|
||||
---
|
||||
|
||||
[← Back to How-To Guides](index.md)
|
||||
@@ -0,0 +1,127 @@
|
||||
# Handle Difficult Barcodes {#handle-difficult-barcodes}
|
||||
|
||||
This guide provides techniques for improving barcode detection in challenging conditions.
|
||||
|
||||
## Common Challenges {#common-challenges}
|
||||
|
||||
- Poor lighting or shadows
|
||||
- Blurry or out-of-focus images
|
||||
- Small or distant barcodes
|
||||
- Damaged or partially obscured barcodes
|
||||
- Low contrast between bars and background
|
||||
|
||||
## Improving Detection {#improving-detection}
|
||||
|
||||
### Adjust Patch Size {#adjust-patch-size}
|
||||
|
||||
For small or distant barcodes, use a smaller patch size:
|
||||
|
||||
```javascript
|
||||
locator: {
|
||||
patchSize: "small" // or "x-small" for very small barcodes
|
||||
}
|
||||
```
|
||||
|
||||
### Increase Resolution {#increase-resolution}
|
||||
|
||||
Higher resolution provides more detail:
|
||||
|
||||
```javascript
|
||||
inputStream: {
|
||||
size: 1280, // Larger processing size
|
||||
constraints: {
|
||||
width: { ideal: 1920 },
|
||||
height: { ideal: 1080 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Disable Half Sampling {#disable-half-sampling}
|
||||
|
||||
For fine details, process at full resolution:
|
||||
|
||||
```javascript
|
||||
locator: {
|
||||
halfSample: false
|
||||
}
|
||||
```
|
||||
|
||||
## Handling False Positives {#handling-false-positives}
|
||||
|
||||
### Validate Results {#validate-results}
|
||||
|
||||
Check result confidence and format:
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
// Check if result has expected format
|
||||
if (result.codeResult.format !== 'ean_13') {
|
||||
return; // Ignore unexpected formats
|
||||
}
|
||||
|
||||
// Validate checksum externally if needed
|
||||
if (!validateBarcode(result.codeResult.code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
processBarcode(result.codeResult.code);
|
||||
});
|
||||
```
|
||||
|
||||
### Require Multiple Reads {#require-multiple-reads}
|
||||
|
||||
Confirm detection across multiple frames:
|
||||
|
||||
```javascript
|
||||
let lastCode = null;
|
||||
let readCount = 0;
|
||||
|
||||
Quagga.onDetected(function(result) {
|
||||
const code = result.codeResult.code;
|
||||
|
||||
if (code === lastCode) {
|
||||
readCount++;
|
||||
if (readCount >= 3) {
|
||||
// Confirmed detection
|
||||
processBarcode(code);
|
||||
readCount = 0;
|
||||
}
|
||||
} else {
|
||||
lastCode = code;
|
||||
readCount = 1;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Using Debug Flags {#using-debug-flags}
|
||||
|
||||
Enable visual debugging to understand detection issues:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
debug: true,
|
||||
decoder: {
|
||||
debug: {
|
||||
drawBoundingBox: true,
|
||||
drawScanline: true
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
debug: {
|
||||
showFoundPatches: true
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
See [Use Debug Flags](use-debug-flags.md) for complete details.
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Configuration Reference](../reference/configuration.md) - All configuration options
|
||||
- [Optimize Performance](optimize-performance.md) - Balance accuracy vs speed
|
||||
- [Use Debug Flags](use-debug-flags.md) - Diagnostic tools
|
||||
|
||||
---
|
||||
|
||||
[← Back to How-To Guides](index.md)
|
||||
77
quagga2/quagga2-1.12.1/docs/how-to-guides/index.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# How-To Guides
|
||||
|
||||
Task-oriented guides for solving specific problems with Quagga2. These assume you have basic familiarity with the library and focus on getting things done.
|
||||
|
||||
## Configuration & Setup
|
||||
|
||||
### [Configure Barcode Readers](configure-readers.md)
|
||||
|
||||
Select which barcode formats to scan (EAN, CODE 128, UPC, etc.) and understand the trade-offs.
|
||||
|
||||
### [Use Debug Flags](use-debug-flags.md) 🆕
|
||||
|
||||
Enable diagnostic output to troubleshoot issues with localization, decoding, or camera setup.
|
||||
|
||||
## Performance & Quality
|
||||
|
||||
### [Optimize Performance](optimize-performance.md)
|
||||
|
||||
Improve scanning speed and reduce CPU usage through configuration tuning.
|
||||
|
||||
### [Handle Difficult Barcodes](handle-difficult-barcodes.md)
|
||||
|
||||
Techniques for decoding barcodes with poor lighting, damage, rotation, or small size.
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### [Create External Readers](external-readers.md)
|
||||
|
||||
Build custom barcode decoder plugins to support additional formats.
|
||||
|
||||
## Camera & Input
|
||||
|
||||
### [Select Specific Camera](select-camera.md) *(Coming Soon)*
|
||||
|
||||
Choose between front/back cameras on mobile devices or select from multiple USB cameras.
|
||||
|
||||
### [Adjust Camera Settings](camera-settings.md) *(Coming Soon)*
|
||||
|
||||
Control focus, zoom, flash/torch, and other camera parameters.
|
||||
|
||||
## Drawing & Visualization
|
||||
|
||||
### [Working with Box Coordinates](working-with-coordinates.md) 🆕
|
||||
|
||||
Understand how `box`, `boxes`, and `line` coordinates relate to processed vs. original image dimensions, and how to properly scale coordinates for overlay rendering on video elements.
|
||||
|
||||
## Tips & Best Practices
|
||||
|
||||
### [Tips and Tricks](tips-and-tricks.md)
|
||||
|
||||
Practical advice for getting the best results with Quagga2, including camera setup, user experience, and handling results.
|
||||
|
||||
## Integration
|
||||
|
||||
### [Handle Multiple Barcodes](multiple-barcodes.md) *(Coming Soon)*
|
||||
|
||||
Detect and decode several barcodes in a single frame simultaneously.
|
||||
|
||||
### [Scan in Background](background-scanning.md) *(Coming Soon)*
|
||||
|
||||
Continue scanning while users interact with other UI elements.
|
||||
|
||||
## Differences from Tutorials
|
||||
|
||||
**Tutorials** are for learning - they're comprehensive, beginner-friendly, and explain *why* you're doing each step.
|
||||
|
||||
**How-To Guides** are for doing - they're focused, assume knowledge, and show *how* to accomplish specific goals.
|
||||
|
||||
If you're new to Quagga2, start with [Tutorials](../tutorials/) instead.
|
||||
|
||||
## Contributing
|
||||
|
||||
Have a useful technique or solution to share? We welcome contributions! See the [Contributing Guide](../contributing.md).
|
||||
|
||||
---
|
||||
|
||||
[← Back to Documentation Home](../index.md)
|
||||
@@ -0,0 +1,114 @@
|
||||
# Optimize Performance {#optimize-performance}
|
||||
|
||||
This guide covers techniques to improve Quagga2's barcode scanning performance.
|
||||
|
||||
## Overview {#overview}
|
||||
|
||||
Performance optimization in Quagga2 involves balancing accuracy against speed. The key areas to optimize are:
|
||||
|
||||
- Input resolution and scaling
|
||||
- Locator configuration
|
||||
- Reader selection
|
||||
- Processing frequency
|
||||
|
||||
## Input Resolution {#input-resolution}
|
||||
|
||||
### Using `inputStream.size` {#inputstream-size}
|
||||
|
||||
Reducing the processing resolution is the most effective way to improve performance:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
size: 640 // Process at 640px max dimension instead of full resolution
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Recommended values:**
|
||||
- **1280px** - High quality, slower (good for static images)
|
||||
- **800px** - Balanced (default for `decodeSingle`)
|
||||
- **640px** - Fast (recommended for live scanning)
|
||||
- **480px** - Very fast (may reduce accuracy)
|
||||
|
||||
### Camera Constraints {#camera-constraints}
|
||||
|
||||
Request only the resolution you need:
|
||||
|
||||
```javascript
|
||||
inputStream: {
|
||||
constraints: {
|
||||
width: { ideal: 1280 },
|
||||
height: { ideal: 720 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Locator Configuration {#locator-configuration}
|
||||
|
||||
### Half Sampling {#half-sampling}
|
||||
|
||||
Keep `halfSample: true` (default) for faster localization:
|
||||
|
||||
```javascript
|
||||
locator: {
|
||||
halfSample: true // Processes at half resolution
|
||||
}
|
||||
```
|
||||
|
||||
### Patch Size {#patch-size}
|
||||
|
||||
Larger patch sizes are faster but may miss small barcodes:
|
||||
|
||||
```javascript
|
||||
locator: {
|
||||
patchSize: "large" // Options: x-small, small, medium, large, x-large
|
||||
}
|
||||
```
|
||||
|
||||
## Reader Selection {#reader-selection}
|
||||
|
||||
Only enable the barcode formats you need:
|
||||
|
||||
```javascript
|
||||
decoder: {
|
||||
readers: ["code_128_reader"] // Don't enable all readers
|
||||
}
|
||||
```
|
||||
|
||||
## Processing Frequency {#processing-frequency}
|
||||
|
||||
Limit scan rate to reduce CPU usage:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
frequency: 10 // Max 10 scans per second
|
||||
});
|
||||
```
|
||||
|
||||
## Disable Localization {#disable-localization}
|
||||
|
||||
If barcode position is fixed, disable localization entirely:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locate: false,
|
||||
inputStream: {
|
||||
area: {
|
||||
top: "25%",
|
||||
right: "25%",
|
||||
bottom: "25%",
|
||||
left: "25%"
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Configuration Reference](../reference/configuration.md) - All configuration options
|
||||
- [How Barcode Localization Works](../explanation/how-barcode-localization-works.md) - Understanding the algorithm
|
||||
|
||||
---
|
||||
|
||||
[← Back to How-To Guides](index.md)
|
||||
115
quagga2/quagga2-1.12.1/docs/how-to-guides/tips-and-tricks.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Tips and Tricks {#tips-and-tricks}
|
||||
|
||||
Practical advice for getting the best results with Quagga2.
|
||||
|
||||
## Camera Setup {#camera-setup}
|
||||
|
||||
### Choosing the Right Camera {#choosing-camera}
|
||||
|
||||
- Use the **back camera** on mobile devices (higher resolution, autofocus)
|
||||
- Request environment-facing camera: `facingMode: "environment"`
|
||||
- Avoid wide-angle cameras for barcode scanning
|
||||
|
||||
### Optimal Distance {#optimal-distance}
|
||||
|
||||
- Barcode should fill 50-80% of the frame width
|
||||
- Too close: barcode may be out of focus
|
||||
- Too far: insufficient resolution for small bars
|
||||
|
||||
## Lighting {#lighting}
|
||||
|
||||
- Ensure even lighting across the barcode
|
||||
- Avoid harsh shadows or reflections
|
||||
- Enable torch/flash for dark environments:
|
||||
|
||||
```javascript
|
||||
await Quagga.CameraAccess.enableTorch();
|
||||
```
|
||||
|
||||
## User Experience {#user-experience}
|
||||
|
||||
### Visual Feedback {#visual-feedback}
|
||||
|
||||
Show users the scan area:
|
||||
|
||||
```javascript
|
||||
inputStream: {
|
||||
area: {
|
||||
top: "25%",
|
||||
right: "10%",
|
||||
bottom: "25%",
|
||||
left: "10%",
|
||||
borderColor: "rgba(0, 255, 0, 0.7)",
|
||||
borderWidth: 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Audio Feedback {#audio-feedback}
|
||||
|
||||
Play a sound on successful scan:
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
new Audio('/beep.mp3').play();
|
||||
processBarcode(result.codeResult.code);
|
||||
});
|
||||
```
|
||||
|
||||
## Performance {#performance}
|
||||
|
||||
### Reduce CPU Usage {#reduce-cpu-usage}
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
frequency: 10, // Limit to 10 scans/second
|
||||
inputStream: {
|
||||
size: 640 // Reduce processing resolution
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Stop When Not Needed {#stop-when-not-needed}
|
||||
|
||||
```javascript
|
||||
// Stop scanning when modal closes
|
||||
Quagga.stop();
|
||||
|
||||
// Remove event handlers
|
||||
Quagga.offDetected();
|
||||
Quagga.offProcessed();
|
||||
```
|
||||
|
||||
## Handling Results {#handling-results}
|
||||
|
||||
### Debounce Detections {#debounce-detections}
|
||||
|
||||
Avoid processing the same barcode multiple times:
|
||||
|
||||
```javascript
|
||||
let lastScanned = '';
|
||||
let lastTime = 0;
|
||||
|
||||
Quagga.onDetected(function(result) {
|
||||
const code = result.codeResult.code;
|
||||
const now = Date.now();
|
||||
|
||||
if (code === lastScanned && now - lastTime < 2000) {
|
||||
return; // Same barcode within 2 seconds
|
||||
}
|
||||
|
||||
lastScanned = code;
|
||||
lastTime = now;
|
||||
processBarcode(code);
|
||||
});
|
||||
```
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Optimize Performance](optimize-performance.md) - Detailed performance guide
|
||||
- [Handle Difficult Barcodes](handle-difficult-barcodes.md) - Improve detection
|
||||
- [Camera Access API](../reference/camera-access.md) - Camera control
|
||||
|
||||
---
|
||||
|
||||
[← Back to How-To Guides](index.md)
|
||||
776
quagga2/quagga2-1.12.1/docs/how-to-guides/use-debug-flags.md
Normal file
@@ -0,0 +1,776 @@
|
||||
# How to Use Debug Flags {#how-to-use-debug-flags}
|
||||
|
||||
Quagga2 includes several debug flags that enable diagnostic console output to help troubleshoot issues with barcode detection and decoding. This guide explains when and how to use them.
|
||||
|
||||
## Overview {#overview}
|
||||
|
||||
Debug flags control what information Quagga2 logs to the browser console. By default, **all debug output is suppressed** to keep your console clean. Enable specific flags when you need to diagnose problems.
|
||||
|
||||
## Important Note {#important-note}
|
||||
|
||||
Debug flags only work when `ENV.development` is `true` (development builds). Production builds strip out all debug code to minimize bundle size.
|
||||
|
||||
## Available Debug Flags {#available-debug-flags}
|
||||
|
||||
Debug flags are organized into three categories:
|
||||
|
||||
1. **Console logging flags** - Print diagnostic information to the console
|
||||
2. **Visual canvas overlays** - Draw debugging information on the canvas
|
||||
3. **Performance analysis** - Display frequency and pattern data
|
||||
|
||||
### Console Logging Flags {#console-logging-flags}
|
||||
|
||||
#### `inputStream.debug.showImageDetails` {#inputstream-debug-showimagedetails}
|
||||
|
||||
**What it shows**: Image loading and frame grabber operations
|
||||
|
||||
**Console output example**:
|
||||
|
||||
```text
|
||||
*** frame_grabber_browser: willReadFrequency=undefined canvas=<canvas>
|
||||
Image Loader: Loaded 3 images from /path/to/image.jpg
|
||||
```
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Camera feed not showing
|
||||
- Images not loading from file input
|
||||
- Canvas-related issues
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
// ... your input config
|
||||
debug: {
|
||||
showImageDetails: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `decoder.debug.printReaderInfo` {#decoder-debug-printreaderinfo}
|
||||
|
||||
**What it shows**: Barcode reader registration and initialization
|
||||
|
||||
**Console output example**:
|
||||
|
||||
```text
|
||||
* ImageWrapper getCanvasAndContext
|
||||
Registering reader: code_128_reader
|
||||
Before registering reader: EANReader
|
||||
Registered Readers: code_128, ean
|
||||
```
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Verifying which readers are active
|
||||
- Reader not detecting expected barcode type
|
||||
- Multiple reader configuration issues
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader"],
|
||||
debug: {
|
||||
printReaderInfo: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showPatchSize` {#locator-debug-showpatchsize}
|
||||
|
||||
**What it shows**: Patch dimensions during barcode localization
|
||||
|
||||
**Console output example**:
|
||||
|
||||
```text
|
||||
Patch-Size: 320x240
|
||||
```
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Barcode locator not finding barcodes
|
||||
- Understanding what image size the locator is processing
|
||||
- Performance optimization (smaller patches = faster)
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
debug: {
|
||||
showPatchSize: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showImageDetails` {#locator-debug-showimagedetails}
|
||||
|
||||
**What it shows**: Canvas and image wrapper initialization for locator
|
||||
|
||||
**Console output example**:
|
||||
|
||||
```text
|
||||
* initCanvas getCanvasAndContext
|
||||
* ImageWrapper getCanvasAndContext
|
||||
```
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Locator canvas not rendering
|
||||
- Debugging locator initialization failures
|
||||
- Understanding when canvas contexts are created
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
// ... your locator config
|
||||
debug: {
|
||||
showImageDetails: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
### Visual Canvas Overlay Flags {#visual-canvas-overlay-flags}
|
||||
|
||||
These flags draw debugging information directly on the canvas, allowing you to visualize the barcode detection algorithm's internal state.
|
||||
|
||||
Note: The scan area overlay (defined via `inputStream.area`) is not a debug flag. When `locate: false`, Quagga draws the scan area on the overlay canvas each processed frame. You can also draw it manually using `Quagga.drawScannerArea()`. See the [Configuration Reference](../reference/configuration.md#inputstream-area-drawing) and [API](../reference/api.md#quagga-drawscannerarea).
|
||||
|
||||
#### `decoder.debug.drawBoundingBox` {#decoder-debug-drawboundingbox}
|
||||
|
||||
**What it shows**: Draws a box around the detected barcode location
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Verify barcode is being located correctly
|
||||
- Debug positioning issues
|
||||
- Understand where the decoder thinks the barcode is
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
debug: {
|
||||
drawBoundingBox: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `decoder.debug.drawScanline` {#decoder-debug-drawscanline}
|
||||
|
||||
**What it shows**: Draws the scanline path used for decoding
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Verify scanner is reading through the barcode correctly
|
||||
- Debug angle/orientation issues
|
||||
- Understand why certain barcodes fail to decode
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
debug: {
|
||||
drawScanline: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showCanvas` {#locator-debug-showcanvas}
|
||||
|
||||
**What it shows**: Displays the locator's internal canvas used for image processing
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug localization algorithm
|
||||
- Verify image preprocessing is working
|
||||
- Understand what the locator "sees"
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showCanvas: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showPatches` {#locator-debug-showpatches}
|
||||
|
||||
**What it shows**: Draws all patches extracted during the localization phase
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug patch extraction issues
|
||||
- Verify patch size is appropriate
|
||||
- Understand what regions are being analyzed
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showPatches: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showFoundPatches` {#locator-debug-showfoundpatches}
|
||||
|
||||
**What it shows**: Highlights patches where potential barcodes were found
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Verify barcode candidates are being identified
|
||||
- Debug false positives/negatives in patch detection
|
||||
- Optimize patch threshold settings
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showFoundPatches: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showSkeleton` {#locator-debug-showskeleton}
|
||||
|
||||
**What it shows**: Displays the skeleton structure extracted from patches
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug advanced localization algorithm
|
||||
- Understand structure detection
|
||||
- Verify skeleton extraction is working
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showSkeleton: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showLabels` {#locator-debug-showlabels}
|
||||
|
||||
**What it shows**: Displays component labels during connected component analysis
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug component labeling phase
|
||||
- Verify components are being identified correctly
|
||||
- Understand clustering behavior
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showLabels: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showPatchLabels` {#locator-debug-showpatchlabels}
|
||||
|
||||
**What it shows**: Shows labels assigned to individual patches
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug patch classification
|
||||
- Verify patches are being labeled correctly
|
||||
- Understand patch grouping
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showPatchLabels: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.showRemainingPatchLabels` {#locator-debug-showremainingpatchlabels}
|
||||
|
||||
**What it shows**: Displays labels for patches remaining after filtering
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug patch filtering logic
|
||||
- Verify correct patches survive filtering
|
||||
- Optimize filter thresholds
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showRemainingPatchLabels: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.boxFromPatches.showTransformed` {#locator-debug-boxfrompatches-showtransformed}
|
||||
|
||||
**What it shows**: Shows transformed patch coordinates during box calculation
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug coordinate transformation
|
||||
- Verify spatial transformations are correct
|
||||
- Understand box calculation from patches
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
boxFromPatches: {
|
||||
showTransformed: true
|
||||
}
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.boxFromPatches.showTransformedBox` {#locator-debug-boxfrompatches-showtransformedbox}
|
||||
|
||||
**What it shows**: Displays the bounding box after transformation
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Debug box transformation
|
||||
- Verify box coordinates after spatial transform
|
||||
- Understand final box positioning
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
boxFromPatches: {
|
||||
showTransformedBox: true
|
||||
}
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `locator.debug.boxFromPatches.showBB` {#locator-debug-boxfrompatches-showbb}
|
||||
|
||||
**What it shows**: Displays the final bounding box around detected barcode region
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Verify final bounding box is correct
|
||||
- Debug box calculation from patches
|
||||
- Optimize localization accuracy
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
boxFromPatches: {
|
||||
showBB: true
|
||||
}
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
### Performance Analysis Flags {#performance-analysis-flags}
|
||||
|
||||
#### `decoder.debug.showFrequency` {#decoder-debug-showfrequency}
|
||||
|
||||
**What it shows**: Displays frequency data from the barcode scanline
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Analyze barcode signal quality
|
||||
- Debug decoding issues at the signal level
|
||||
- Understand why certain barcodes fail to decode
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
debug: {
|
||||
showFrequency: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
#### `decoder.debug.showPattern` {#decoder-debug-showpattern}
|
||||
|
||||
**What it shows**: Displays the pattern data extracted from the barcode
|
||||
|
||||
**When to use**:
|
||||
|
||||
- Analyze pattern recognition issues
|
||||
- Debug specific barcode format problems
|
||||
- Understand pattern extraction process
|
||||
|
||||
**How to enable**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
debug: {
|
||||
showPattern: true
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
## Common Debugging Scenarios {#common-debugging-scenarios}
|
||||
|
||||
### "No barcodes detected" {#no-barcodes-detected}
|
||||
|
||||
Enable console logging first, then add visual overlays if needed:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
target: document.querySelector('#scanner'),
|
||||
debug: {
|
||||
showImageDetails: true
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader"],
|
||||
debug: {
|
||||
printReaderInfo: true,
|
||||
drawBoundingBox: true, // Visual: see if barcode is located
|
||||
drawScanline: true // Visual: see scan path
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
debug: {
|
||||
showPatchSize: true,
|
||||
showImageDetails: true,
|
||||
showFoundPatches: true // Visual: see candidate patches
|
||||
}
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error("Init error:", err);
|
||||
return;
|
||||
}
|
||||
console.log("Starting Quagga...");
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
Check console for:
|
||||
|
||||
- ✅ Readers registered correctly?
|
||||
- ✅ Images loading?
|
||||
- ✅ Patch size reasonable (not 0x0)?
|
||||
- ✅ Canvas contexts created?
|
||||
|
||||
Check canvas overlay for:
|
||||
|
||||
- ✅ Are patches being detected? (green highlights)
|
||||
- ✅ Is a bounding box drawn?
|
||||
- ✅ Is the scanline visible and passing through the barcode?
|
||||
|
||||
### "Camera not working" {#camera-not-working}
|
||||
|
||||
Enable input stream debugging:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
debug: {
|
||||
showImageDetails: true // Shows camera/canvas setup
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
### "Wrong barcode type detected" {#wrong-barcode-type}
|
||||
|
||||
Enable reader info to verify configuration:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: [
|
||||
"code_128_reader", // Did you enable the right readers?
|
||||
"ean_reader"
|
||||
],
|
||||
debug: {
|
||||
printReaderInfo: true // Shows which readers are active
|
||||
}
|
||||
},
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
### "Deep-dive localization debugging" {#deep-dive-debugging}
|
||||
|
||||
For advanced debugging of the localization algorithm, enable all visual overlays:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
locator: {
|
||||
debug: {
|
||||
showCanvas: true,
|
||||
showPatches: true,
|
||||
showFoundPatches: true,
|
||||
showSkeleton: true,
|
||||
showLabels: true,
|
||||
showPatchLabels: true,
|
||||
showRemainingPatchLabels: true,
|
||||
boxFromPatches: {
|
||||
showTransformed: true,
|
||||
showTransformedBox: true,
|
||||
showBB: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
This will display every step of the localization process visually on the canvas. Use this to:
|
||||
|
||||
- Understand the complete localization pipeline
|
||||
- Optimize patch size and thresholds
|
||||
- Debug complex barcode positioning issues
|
||||
- Learn how the algorithm works
|
||||
|
||||
**Warning**: Enabling all visual overlays may impact performance and make the canvas cluttered. Enable only what you need.
|
||||
|
||||
## Performance Impact {#performance-impact}
|
||||
|
||||
Debug flags have varying performance impacts:
|
||||
|
||||
**Console logging flags** (minimal impact):
|
||||
|
||||
- `showImageDetails`, `printReaderInfo`, `showPatchSize` - negligible overhead
|
||||
- Only execute when enabled and only in development builds
|
||||
|
||||
**Visual canvas overlays** (moderate to high impact):
|
||||
|
||||
- Drawing operations on canvas can slow down real-time detection
|
||||
- More overlays = more draw calls = slower performance
|
||||
- Consider disabling in production or using only for development/debugging
|
||||
|
||||
**Performance analysis flags** (high impact):
|
||||
|
||||
- `showFrequency`, `showPattern` - can generate large amounts of data
|
||||
- Best used sparingly when diagnosing specific decoding issues
|
||||
|
||||
**Recommendation**: Enable only the flags you need. Disable all visual overlays for production.
|
||||
|
||||
## Disabling Debug Output {#disabling-debug-output}
|
||||
|
||||
To turn off all debug output, either:
|
||||
|
||||
**Option 1**: Remove debug properties entirely
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
// debug property removed
|
||||
},
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
**Option 2**: Set flags to `false`
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
debug: {
|
||||
showImageDetails: false
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
debug: {
|
||||
printReaderInfo: false,
|
||||
drawBoundingBox: false,
|
||||
drawScanline: false,
|
||||
showFrequency: false,
|
||||
showPattern: false
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
debug: {
|
||||
showCanvas: false,
|
||||
showPatches: false,
|
||||
showFoundPatches: false,
|
||||
showSkeleton: false,
|
||||
showLabels: false,
|
||||
showPatchLabels: false,
|
||||
showRemainingPatchLabels: false,
|
||||
showPatchSize: false,
|
||||
showImageDetails: false,
|
||||
boxFromPatches: {
|
||||
showTransformed: false,
|
||||
showTransformedBox: false,
|
||||
showBB: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Using in Node.js {#using-in-nodejs}
|
||||
|
||||
Debug flags work in Node.js too! Output goes to `console.log`:
|
||||
|
||||
```javascript
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
|
||||
Quagga.decodeSingle({
|
||||
src: './barcode.jpg',
|
||||
decoder: {
|
||||
readers: ['code_128_reader'],
|
||||
debug: {
|
||||
printReaderInfo: true // Shows reader registration
|
||||
}
|
||||
}
|
||||
}, (result) => {
|
||||
// Check console for debug output
|
||||
if (result) {
|
||||
console.log('Decoded:', result.codeResult.code);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Development vs Production {#development-vs-production}
|
||||
|
||||
**Development builds** (`dist/quagga.js` or when using webpack dev server):
|
||||
|
||||
- Debug flags work
|
||||
- Console output visible
|
||||
- Larger file size
|
||||
|
||||
**Production builds** (`dist/quagga.min.js`):
|
||||
|
||||
- Debug flags are stripped out (no-op)
|
||||
- No console output
|
||||
- Smaller file size
|
||||
|
||||
To enable debug output in production:
|
||||
|
||||
1. Use the development build (`quagga.js` instead of `quagga.min.js`)
|
||||
2. Set `ENV.development = true` before importing Quagga
|
||||
|
||||
## Summary {#summary}
|
||||
|
||||
Quagga2 provides **19 debug flags** organized into three categories:
|
||||
|
||||
**Console logging** (4 flags) - Minimal performance impact:
|
||||
|
||||
- `inputStream.debug.showImageDetails` - Frame grabber and image loading
|
||||
- `decoder.debug.printReaderInfo` - Reader registration
|
||||
- `locator.debug.showPatchSize` - Patch dimensions
|
||||
- `locator.debug.showImageDetails` - Canvas initialization
|
||||
|
||||
**Visual canvas overlays** (13 flags) - Moderate to high performance impact:
|
||||
|
||||
- `decoder.debug.drawBoundingBox` - Barcode location box
|
||||
- `decoder.debug.drawScanline` - Scan path
|
||||
- `locator.debug.showCanvas` - Locator's internal canvas
|
||||
- `locator.debug.showPatches` - All extracted patches
|
||||
- `locator.debug.showFoundPatches` - Candidate patches
|
||||
- `locator.debug.showSkeleton` - Skeleton structure
|
||||
- `locator.debug.showLabels` - Component labels
|
||||
- `locator.debug.showPatchLabels` - Patch labels
|
||||
- `locator.debug.showRemainingPatchLabels` - Post-filter labels
|
||||
- `locator.debug.boxFromPatches.showTransformed` - Transformed coordinates
|
||||
- `locator.debug.boxFromPatches.showTransformedBox` - Transformed box
|
||||
- `locator.debug.boxFromPatches.showBB` - Final bounding box
|
||||
|
||||
**Performance analysis** (2 flags) - High impact:
|
||||
|
||||
- `decoder.debug.showFrequency` - Signal frequency data
|
||||
- `decoder.debug.showPattern` - Pattern extraction data
|
||||
|
||||
**Debugging strategy**: Start with console logging flags, then add visual overlays as needed. Disable all flags in production.
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Configuration Reference](../reference/configuration.md) - Complete config documentation
|
||||
- [Optimize Performance](optimize-performance.md) - Speed up barcode detection
|
||||
- [Handle Difficult Barcodes](handle-difficult-barcodes.md) - Improve detection accuracy
|
||||
|
||||
## Changelog {#changelog}
|
||||
|
||||
**v1.8.4+** (November 2025): Debug flags introduced to replace always-on console spam
|
||||
|
||||
Prior versions logged debug information unconditionally, making it difficult to debug application code. The new flag system provides fine-grained control over diagnostic output.
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Ask in [Gitter Chat](https://gitter.im/quaggaJS/Lobby) or [open an issue](https://github.com/ericblade/quagga2/issues).
|
||||
@@ -0,0 +1,254 @@
|
||||
# Working with Box Coordinates {#working-with-box-coordinates}
|
||||
|
||||
This guide explains how Quagga2's coordinate system works and how to properly use `box`, `boxes`, and `line` coordinates for overlay rendering, especially when using `inputStream.size` to scale processing.
|
||||
|
||||
## Understanding the Coordinate System {#understanding-coordinate-system}
|
||||
|
||||
Quagga2 returns `box`, `boxes`, and `line` coordinates in **processed canvas coordinates**, not original image/video coordinates. This is important to understand when:
|
||||
|
||||
- Drawing overlay boxes on a video element
|
||||
- Using `inputStream.size` to reduce processing resolution
|
||||
- Cropping detected barcode regions from the original image
|
||||
|
||||
### Key Concepts {#key-concepts}
|
||||
|
||||
| Term | Description |
|
||||
|------|-------------|
|
||||
| **Real Size** | The actual dimensions of the source image/video |
|
||||
| **Processed Size** | The scaled dimensions used for barcode detection (controlled by `inputStream.size`) |
|
||||
| **Canvas Size** | The dimensions of the processing canvas (typically matches processed size) |
|
||||
|
||||
### How Coordinates are Generated {#how-coordinates-generated}
|
||||
|
||||
1. **Image Scaling**: When `inputStream.size` is set, the image is scaled so the longest side equals that value
|
||||
2. **Localization**: Barcode regions are found in the scaled image
|
||||
3. **Box Coordinates**: Returned coordinates are relative to the scaled/processed image
|
||||
4. **halfSample Adjustment**: If `halfSample: true`, coordinates are automatically scaled 2x
|
||||
|
||||
## Converting Coordinates to Original Image Space {#converting-coordinates}
|
||||
|
||||
When you need to draw boxes on the original video/image (not the processed canvas), you must scale the coordinates.
|
||||
|
||||
### For Live Video Streams {#live-video-streams}
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
if (!result.box) return;
|
||||
|
||||
// Get the video element
|
||||
const video = document.querySelector('video');
|
||||
const videoWidth = video.videoWidth; // Real video dimensions
|
||||
const videoHeight = video.videoHeight;
|
||||
|
||||
// Get processed dimensions from Quagga
|
||||
const canvas = Quagga.canvas.dom.image;
|
||||
const processedWidth = canvas.width;
|
||||
const processedHeight = canvas.height;
|
||||
|
||||
// Calculate scale factors
|
||||
const scaleX = videoWidth / processedWidth;
|
||||
const scaleY = videoHeight / processedHeight;
|
||||
|
||||
// Convert box coordinates to video space
|
||||
const scaledBox = result.box.map(function(point) {
|
||||
return [
|
||||
point[0] * scaleX,
|
||||
point[1] * scaleY
|
||||
];
|
||||
});
|
||||
|
||||
// Now use scaledBox for drawing on video overlay
|
||||
drawBoxOnVideo(scaledBox);
|
||||
});
|
||||
```
|
||||
|
||||
### For Static Images with decodeSingle {#static-images-decodesingle}
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: './barcode.jpg',
|
||||
inputStream: {
|
||||
size: 800 // Process at 800px max dimension
|
||||
},
|
||||
// ... other config
|
||||
}, function(result) {
|
||||
if (!result || !result.box) return;
|
||||
|
||||
// Load original image to get real dimensions
|
||||
const img = new Image();
|
||||
img.onload = function() {
|
||||
const realWidth = img.naturalWidth;
|
||||
const realHeight = img.naturalHeight;
|
||||
|
||||
// Calculate what the processed size was
|
||||
const aspectRatio = realWidth / realHeight;
|
||||
let processedWidth, processedHeight;
|
||||
|
||||
if (aspectRatio > 1) {
|
||||
// Landscape: width is the longest side
|
||||
processedWidth = 800;
|
||||
processedHeight = Math.floor(800 / aspectRatio);
|
||||
} else {
|
||||
// Portrait: height is the longest side
|
||||
processedHeight = 800;
|
||||
processedWidth = Math.floor(800 * aspectRatio);
|
||||
}
|
||||
|
||||
// Calculate scale factors
|
||||
const scaleX = realWidth / processedWidth;
|
||||
const scaleY = realHeight / processedHeight;
|
||||
|
||||
// Convert coordinates
|
||||
const scaledBox = result.box.map(function(point) {
|
||||
return [
|
||||
point[0] * scaleX,
|
||||
point[1] * scaleY
|
||||
];
|
||||
});
|
||||
|
||||
// Use scaledBox for original image operations
|
||||
cropBarcodeFromOriginal(scaledBox);
|
||||
};
|
||||
img.src = './barcode.jpg';
|
||||
});
|
||||
```
|
||||
|
||||
## Complete Example: Drawing Boxes on Live Video {#complete-example}
|
||||
|
||||
Here's a complete example showing how to draw accurate bounding boxes on a live video stream:
|
||||
|
||||
```javascript
|
||||
// Initialize Quagga with reduced processing size for performance
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
name: "Live",
|
||||
type: "LiveStream",
|
||||
target: document.querySelector('#scanner-container'),
|
||||
constraints: {
|
||||
width: 1280,
|
||||
height: 720,
|
||||
facingMode: "environment"
|
||||
},
|
||||
size: 640 // Process at 640px for better performance
|
||||
},
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
halfSample: true
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
// Handle detections with coordinate scaling
|
||||
Quagga.onDetected(function(result) {
|
||||
const video = document.querySelector('video');
|
||||
const overlay = document.querySelector('#overlay-canvas');
|
||||
const ctx = overlay.getContext('2d');
|
||||
|
||||
// Match overlay to video size
|
||||
overlay.width = video.videoWidth;
|
||||
overlay.height = video.videoHeight;
|
||||
|
||||
// Get processed canvas size
|
||||
const processedCanvas = Quagga.canvas.dom.image;
|
||||
|
||||
// Calculate scale factors
|
||||
const scaleX = video.videoWidth / processedCanvas.width;
|
||||
const scaleY = video.videoHeight / processedCanvas.height;
|
||||
|
||||
// Clear previous drawings
|
||||
ctx.clearRect(0, 0, overlay.width, overlay.height);
|
||||
|
||||
// Draw all detected boxes
|
||||
if (result.boxes) {
|
||||
result.boxes.forEach(function(box) {
|
||||
drawScaledBox(ctx, box, scaleX, scaleY, '#00ff00');
|
||||
});
|
||||
}
|
||||
|
||||
// Highlight the successfully decoded box
|
||||
if (result.box) {
|
||||
drawScaledBox(ctx, result.box, scaleX, scaleY, '#ff0000');
|
||||
}
|
||||
});
|
||||
|
||||
function drawScaledBox(ctx, box, scaleX, scaleY, color) {
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
|
||||
// Scale and draw each point
|
||||
const scaledPoints = box.map(p => [p[0] * scaleX, p[1] * scaleY]);
|
||||
|
||||
ctx.moveTo(scaledPoints[0][0], scaledPoints[0][1]);
|
||||
for (let i = 1; i < scaledPoints.length; i++) {
|
||||
ctx.lineTo(scaledPoints[i][0], scaledPoints[i][1]);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
```
|
||||
|
||||
## When Coordinates Don't Need Scaling {#no-scaling-needed}
|
||||
|
||||
If you're drawing on Quagga's own overlay canvas (`Quagga.canvas.dom.overlay`), coordinates are already in the correct space:
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
const ctx = Quagga.canvas.ctx.overlay;
|
||||
const canvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// No scaling needed - coordinates match the overlay canvas
|
||||
if (result.box) {
|
||||
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, ctx, {
|
||||
color: "#00ff00",
|
||||
lineWidth: 2
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Common Pitfalls {#common-pitfalls}
|
||||
|
||||
### 1. Forgetting halfSample Adjustment {#forgetting-halfsample}
|
||||
|
||||
If you're manually calculating processed size, remember that `halfSample: true` doesn't affect the returned coordinates (they're already adjusted).
|
||||
|
||||
### 2. Using Wrong Canvas Reference {#wrong-canvas-reference}
|
||||
|
||||
```javascript
|
||||
// ❌ Wrong - using overlay canvas for scale calculation
|
||||
const wrongWidth = Quagga.canvas.dom.overlay.width;
|
||||
|
||||
// ✅ Correct - using image canvas for scale calculation
|
||||
const correctWidth = Quagga.canvas.dom.image.width;
|
||||
```
|
||||
|
||||
### 3. Assuming Square Pixels {#assuming-square-pixels}
|
||||
|
||||
Always calculate scaleX and scaleY separately, as aspect ratios may differ:
|
||||
|
||||
```javascript
|
||||
// ❌ Wrong - using single scale factor
|
||||
const scale = videoWidth / canvasWidth;
|
||||
|
||||
// ✅ Correct - separate scale factors
|
||||
const scaleX = videoWidth / canvasWidth;
|
||||
const scaleY = videoHeight / canvasHeight;
|
||||
```
|
||||
|
||||
## Performance Tips {#performance-tips}
|
||||
|
||||
1. **Use smaller `inputStream.size`** (e.g., 640-800) for live video to reduce CPU usage
|
||||
2. **Cache scale factors** - recalculate only when video dimensions change
|
||||
3. **Use requestAnimationFrame** for smooth overlay rendering
|
||||
4. **Consider using Quagga's built-in overlay** when possible to avoid manual scaling
|
||||
118
quagga2/quagga2-1.12.1/docs/index.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Quagga2 Documentation
|
||||
|
||||
|
||||
Welcome to the Quagga2 documentation! Quagga2 is a JavaScript barcode scanner library supporting real-time location (localization) and decoding of various barcode types in both browser and Node.js environments.
|
||||
|
||||

|
||||
[](https://gitter.im/quaggaJS/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
## Quick Links
|
||||
|
||||
- [GitHub Repository](https://github.com/ericblade/quagga2)
|
||||
- [Changelog](https://github.com/ericblade/quagga2/releases)
|
||||
- [npm Package](https://www.npmjs.com/package/@ericblade/quagga2)
|
||||
- [Live Examples](examples/)
|
||||
|
||||
## Getting Started
|
||||
|
||||
New to Quagga2? Start here:
|
||||
|
||||
- **[Installation & Quick Start](getting-started.md)** - Get up and running in minutes
|
||||
|
||||
---
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
This documentation follows the [Divio Documentation System](https://documentation.divio.com/), organizing content into four types based on what you need:
|
||||
|
||||
### 📚 [Tutorials](tutorials/) - *Learning-oriented*
|
||||
|
||||
Step-by-step lessons to help you learn by doing. Perfect if you're just getting started.
|
||||
|
||||
- [Your First Barcode Scan](tutorials/first-scan.md) - Scan barcodes with your camera
|
||||
- [Decoding Static Images](tutorials/static-image.md) - Process image files
|
||||
- [Using with React](tutorials/react-integration.md) - React integration guide
|
||||
- [Using with Node.js](tutorials/node-usage.md) - Server-side barcode scanning
|
||||
|
||||
### 🛠️ [How-To Guides](how-to-guides/) - *Task-oriented*
|
||||
|
||||
Practical guides for accomplishing specific goals. Use these when you have a problem to solve.
|
||||
|
||||
- [Configure Barcode Readers](how-to-guides/configure-readers.md) - Select which barcode types to scan
|
||||
- [Optimize Performance](how-to-guides/optimize-performance.md) - Improve speed and accuracy
|
||||
- [Handle Difficult Barcodes](how-to-guides/handle-difficult-barcodes.md) - Deal with poor lighting, rotation, etc.
|
||||
- [Use Debug Flags](how-to-guides/use-debug-flags.md) - Enable diagnostic output
|
||||
- [Create External Readers](how-to-guides/external-readers.md) - Build custom barcode decoder plugins
|
||||
|
||||
### 📖 [Reference](reference/) - *Information-oriented*
|
||||
|
||||
Technical descriptions and API documentation. Look here when you need precise details.
|
||||
|
||||
- [API Documentation](reference/api.md) - Complete API reference
|
||||
- [Configuration Options](reference/configuration.md) - All config parameters explained
|
||||
- [Camera Access API](reference/camera-access.md) - Camera control methods
|
||||
- [Supported Barcode Types](reference/readers.md) - Available decoders
|
||||
- [Browser Support](reference/browser-support.md) - Compatibility information
|
||||
- [Dependencies](reference/dependencies.md) - Package dependencies explained
|
||||
|
||||
### 💡 [Explanation](explanation/) - *Understanding-oriented*
|
||||
|
||||
Background, context, and deeper understanding. Read these to learn *why* things work the way they do.
|
||||
|
||||
- [How Barcode Localization Works](explanation/how-barcode-localization-works.md) - The algorithm behind barcode detection
|
||||
- [Algorithm Overview](explanation/algorithm-overview.md) - Image processing pipeline
|
||||
- [Architecture](explanation/architecture.md) - Code structure and design decisions
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Contributing Guide](contributing.md) - How to contribute to Quagga2
|
||||
- [Gitter Chat](https://gitter.im/quaggaJS/Lobby) - Ask questions and get help
|
||||
|
||||
---
|
||||
|
||||
## Framework Integration
|
||||
|
||||
Using Quagga2 with a specific framework?
|
||||
|
||||
- **React**: [quagga2-react-example](https://github.com/ericblade/quagga2-react-example/) and [quagga2-redux-middleware](https://github.com/ericblade/quagga2-redux-middleware/)
|
||||
- **Angular**: [ngx-barcode-scanner](https://github.com/julienboulay/ngx-barcode-scanner) or [mobile-scanning-demo](https://github.com/classycodeoss/mobile-scanning-demo)
|
||||
- **Vue 2**: [vue-quagga-2](https://github.com/DevinNorgarb/vue-quagga-2)
|
||||
- **Vue 3**: [vue3-quagga-2](https://github.com/nick-0101/vue3-quagga-2)
|
||||
- **ThingWorx**: [ThingworxBarcodeScannerWidget](https://github.com/ptc-iot-sharing/ThingworxBarcodeScannerWidget)
|
||||
|
||||
---
|
||||
|
||||
## External Readers and Other Related Projects {#external-readers}
|
||||
|
||||
- **DataMatrix Reader**: [hackathi/quagga2-reader-datamatrix](https://github.com/hackathi/quagga2-reader-datamatrix)
|
||||
- **QR Code Reader**: [ericblade/quagga2-qr-code-reader](https://github.com/ericblade/quagga2-qr-code-reader)
|
||||
- **PDFBarcodeJS**: [rexshijaku/PDFBarcodeJS](https://github.com/rexshijaku/PDFBarcodeJS)
|
||||
|
||||
---
|
||||
|
||||
## Who Uses Quagga2? {#who-uses}
|
||||
Quagga2 is used by developers worldwide in various applications, including inventory management, retail point-of-sale systems, library systems, and mobile scanning apps. If you're using Quagga2 in your project, let us know!
|
||||
|
||||
- **Internet Archive Open Library**: [internetarchive/openlibrary](https://github.com/internetarchive/openlibrary)
|
||||
- **Grocy**: [grocy/grocy](https://github.com/grocy/grocy)
|
||||
- **German Federal Agency For Technical Relief**: [mziech/thw-inventory](https://github.com/mziech/thw-inventory)
|
||||
- **Food Coop Shop**: [foodcoopshop/foodcoopshop](https://github.com/foodcoopshop/foodcoopshop)
|
||||
- **Rintagi**: [Rintagi/Low-Code-Development-Platform](https://github.com/Rintagi/Low-Code-Development-Platform)
|
||||
- **LINE**: [line/line-api-use-case-smart-retail](https://github.com/line/line-api-use-case-smart-retail)
|
||||
- **Veganify**: [frontendnetwork/veganify](https://github.com/frontendnetwork/veganify)
|
||||
- **Appsemble**: [Appsemble](https://gitlab.com/appsemble/appsemble)
|
||||
- **Intake24**: [intake24/intake24](https://github.com/intake24/intake24)
|
||||
- **FridgeToPlate**: [COS301-SE-2023/FridgeToPlate](https://github.com/COS301-SE-2023/FridgeToPlate)
|
||||
- **Nutri-Scanner**: [kishan9535/Nutri-Scanner](https://github.com/kishan9535/Nutri-Scanner)
|
||||
- **Musclog**: [blopa/musclog-app](https://github.com/blopa/musclog-app)
|
||||
|
||||
|
||||
---
|
||||
|
||||
## About This Documentation
|
||||
|
||||
This documentation is maintained alongside the Quagga2 codebase. If you find errors or have suggestions for improvement, please [open an issue](https://github.com/ericblade/quagga2/issues) or submit a pull request.
|
||||
|
||||
**Last Updated**: November 2025
|
||||
758
quagga2/quagga2-1.12.1/docs/reference/api.md
Normal file
@@ -0,0 +1,758 @@
|
||||
# API Documentation {#api-documentation}
|
||||
|
||||
Complete reference for all Quagga2 methods, callbacks, and events.
|
||||
|
||||
## Core Methods {#core-methods}
|
||||
|
||||
### `Quagga.init(config, callback)` {#quagga-init}
|
||||
|
||||
Initializes the library with the given configuration and requests camera access if using live stream mode.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `config` (Object) - Configuration object. See [Configuration Reference](configuration.md) for complete details.
|
||||
- `callback` (Function) - Called when initialization completes: `callback(err)`
|
||||
- `err` (Error | null) - Error object if initialization failed, `null` on success
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example - Live Camera**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
target: document.querySelector('#scanner') // Or '#scanner'
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error("Initialization failed:", err);
|
||||
return;
|
||||
}
|
||||
console.log("Initialization successful, ready to start");
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
**Example - Static Images**:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "ImageStream",
|
||||
src: "/path/to/images/*.jpg",
|
||||
target: document.querySelector('#scanner')
|
||||
},
|
||||
decoder: {
|
||||
readers: ["ean_reader", "code_128_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
**Error Handling**:
|
||||
|
||||
The callback receives an `err` parameter if initialization fails. Common causes:
|
||||
|
||||
- User denies camera permission
|
||||
- No camera device found
|
||||
- Browser doesn't support required APIs
|
||||
- Invalid configuration parameters
|
||||
|
||||
Always check for errors before calling `start()`:
|
||||
|
||||
**Target Element**:
|
||||
|
||||
If no `target` is specified, Quagga looks for an element matching the CSS selector `#interactive.viewport` (for backwards compatibility).
|
||||
|
||||
### `Quagga.start()` {#quagga-start}
|
||||
|
||||
Starts the video stream and begins locating and decoding barcodes.
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
Quagga.init(config, function(err) {
|
||||
if (!err) {
|
||||
Quagga.start();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
- `Quagga.init()` must have completed successfully
|
||||
- For live stream: Camera permission must be granted
|
||||
|
||||
**Note**: Call this in the `init()` callback after checking for errors.
|
||||
|
||||
### `Quagga.stop()` {#quagga-stop}
|
||||
|
||||
Stops the decoder from processing images and disconnects the camera if one was requested.
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Returns**: `Promise<void>` - Resolves when cleanup is complete
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// Stop scanning
|
||||
await Quagga.stop();
|
||||
console.log("Scanner stopped");
|
||||
|
||||
// Or with .then()
|
||||
Quagga.stop().then(() => {
|
||||
console.log("Scanner stopped");
|
||||
});
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
|
||||
- Stops processing new frames
|
||||
- If using live camera: disconnects and releases camera
|
||||
- Does not remove event listeners (use `offDetected()` / `offProcessed()` for that)
|
||||
- Returns a Promise that resolves when camera release is complete
|
||||
|
||||
### `Quagga.pause()` {#quagga-pause}
|
||||
|
||||
Pauses frame processing without stopping the camera or releasing resources.
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// Pause scanning temporarily
|
||||
Quagga.pause();
|
||||
|
||||
// Later, resume scanning
|
||||
Quagga.start();
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
|
||||
- Stops processing new frames (no more `onProcessed` or `onDetected` callbacks)
|
||||
- **Does not stop the camera** - the video stream continues running
|
||||
- **Does not release resources** - the camera remains connected
|
||||
- Can be resumed by calling `Quagga.start()`
|
||||
|
||||
**Use Cases**:
|
||||
|
||||
- Temporarily pause scanning while showing a modal or dialog
|
||||
- Reduce CPU usage when the scanner is not visible
|
||||
- Pause after detecting a barcode to allow user confirmation before resuming
|
||||
|
||||
**Difference from `stop()`**:
|
||||
|
||||
| Aspect | `pause()` | `stop()` |
|
||||
|--------|-----------|----------|
|
||||
| Frame processing | Stops | Stops |
|
||||
| Camera stream | **Continues** | Disconnects |
|
||||
| Resources | **Retained** | Released |
|
||||
| Resume with | `start()` | `init()` + `start()` |
|
||||
|
||||
> **Note**: Since `pause()` keeps the camera running, the user's camera indicator light will remain on. If you want to fully release the camera, use `stop()` instead.
|
||||
|
||||
### `Quagga.onDetected(callback)` {#quagga-ondetected}
|
||||
|
||||
Registers a callback that is triggered when a barcode is successfully located and decoded.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `callback` (Function) - Handler function: `callback(data)`
|
||||
- `data` (Object) - Result object containing decoded barcode information
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
const code = result.codeResult.code;
|
||||
const format = result.codeResult.format;
|
||||
|
||||
console.log(`Detected ${format} barcode: ${code}`);
|
||||
|
||||
// Process the barcode
|
||||
processBarcode(code);
|
||||
});
|
||||
```
|
||||
|
||||
**Multiple Handlers**:
|
||||
|
||||
You can register multiple handlers - all will be called:
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(handler1);
|
||||
Quagga.onDetected(handler2); // Both execute on detection
|
||||
```
|
||||
|
||||
### `Quagga.onProcessed(callback)` {#quagga-onprocessed}
|
||||
|
||||
Registers a callback that is called for each processed frame, regardless of detection success.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `callback` (Function) - Handler function: `callback(data)`
|
||||
- `data` (Object) - Processing result with detailed information
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
Quagga.onProcessed(function(result) {
|
||||
const drawingCtx = Quagga.canvas.ctx.overlay;
|
||||
const drawingCanvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
if (result) {
|
||||
// Draw boxes and lines for visualization
|
||||
if (result.boxes) {
|
||||
drawingCtx.clearRect(0, 0,
|
||||
drawingCanvas.width, drawingCanvas.height);
|
||||
|
||||
result.boxes.forEach(function(box) {
|
||||
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1},
|
||||
drawingCtx, {color: "blue", lineWidth: 2});
|
||||
});
|
||||
}
|
||||
|
||||
if (result.box) {
|
||||
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1},
|
||||
drawingCtx, {color: "green", lineWidth: 2});
|
||||
}
|
||||
|
||||
if (result.codeResult && result.codeResult.code) {
|
||||
// Successfully decoded
|
||||
console.log("Code detected:", result.codeResult.code);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Use Cases**:
|
||||
|
||||
- Custom visualization of detection process
|
||||
- Counting processed frames
|
||||
- Performance monitoring
|
||||
- Drawing custom overlays
|
||||
|
||||
### `Quagga.drawScannerArea()` {#quagga-drawscannerarea}
|
||||
|
||||
Manually draws the scanner area overlay on the overlay canvas using the configured `inputStream.area`.
|
||||
|
||||
**Parameters**: None (uses the area configuration from `Quagga.init()`)
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Behavior**:
|
||||
|
||||
- Only draws when `locate` is `false` and `inputStream.area` is configured with styling
|
||||
- Uses the actual adjusted scanning area (after patch alignment) to accurately match where barcodes will be detected
|
||||
- Automatically accounts for the area offset and dimensions
|
||||
- Skips drawing if no styling is provided (no `borderColor`, `borderWidth`, or `backgroundColor`)
|
||||
- Requires `canvas.createOverlay: true` so the overlay canvas exists
|
||||
- Drawing occurs automatically every processed frame when conditions are met
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// Configure area during init
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
area: {
|
||||
top: "30%",
|
||||
right: "10%",
|
||||
bottom: "30%",
|
||||
left: "10%",
|
||||
borderColor: "#0F0",
|
||||
borderWidth: 2,
|
||||
backgroundColor: "rgba(255,0,0,0.15)"
|
||||
}
|
||||
},
|
||||
locate: false
|
||||
});
|
||||
|
||||
// Later, manually redraw if you've cleared the overlay
|
||||
Quagga.drawScannerArea();
|
||||
```
|
||||
|
||||
### `Quagga.offDetected(handler)` {#quagga-offdetected}
|
||||
|
||||
Removes a previously registered `onDetected` handler.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `handler` (Function, optional) - Specific handler to remove. If omitted, **all** handlers are removed.
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
function myHandler(result) {
|
||||
console.log(result.codeResult.code);
|
||||
}
|
||||
|
||||
Quagga.onDetected(myHandler);
|
||||
|
||||
// Later: remove specific handler
|
||||
Quagga.offDetected(myHandler);
|
||||
|
||||
// Or remove all handlers
|
||||
Quagga.offDetected();
|
||||
```
|
||||
|
||||
### `Quagga.offProcessed(handler)` {#quagga-offprocessed}
|
||||
|
||||
Removes a previously registered `onProcessed` handler.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `handler` (Function, optional) - Specific handler to remove. If omitted, **all** handlers are removed.
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
function processHandler(result) {
|
||||
// Process frame
|
||||
}
|
||||
|
||||
Quagga.onProcessed(processHandler);
|
||||
|
||||
// Remove specific handler
|
||||
Quagga.offProcessed(processHandler);
|
||||
|
||||
// Or remove all handlers
|
||||
Quagga.offProcessed();
|
||||
```
|
||||
|
||||
### `Quagga.decodeSingle(config, callback)` {#quagga-decodesingle}
|
||||
|
||||
Decodes a single image without using `getUserMedia`. Useful for processing uploaded images or static images.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `config` (Object) - Configuration object (subset of full config)
|
||||
- `callback` (Function) - Result handler: `callback(result)`
|
||||
- `result` (Object) - Same format as `onDetected` callback
|
||||
|
||||
**Returns**: `void`
|
||||
|
||||
**Important - Default Scaling**: `decodeSingle` has a built-in default of `inputStream.size: 800`. This means images are **automatically scaled to 800px** on their longest side (both larger images scaled down AND smaller images scaled up). The result's `box`, `boxes`, and `line` coordinates are returned in this scaled coordinate space, not the original image dimensions. To use the original image dimensions without scaling, set `inputStream.size` to `0`.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: '/images/barcode.jpg', // Or data URL
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader"]
|
||||
},
|
||||
locate: true // Try to locate barcode in image
|
||||
// Note: inputStream.size defaults to 800; images are scaled to 800px (up or down)
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log("Detected:", result.codeResult.code);
|
||||
console.log("Format:", result.codeResult.format);
|
||||
} else {
|
||||
console.log("No barcode detected");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Using Data URLs**:
|
||||
|
||||
```javascript
|
||||
// From file input
|
||||
document.querySelector('#file-input').addEventListener('change', function(e) {
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function(event) {
|
||||
Quagga.decodeSingle({
|
||||
src: event.target.result, // Data URL
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
// Default size: 800 applies - image scaled if larger
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
alert("Barcode: " + result.codeResult.code);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
```
|
||||
|
||||
**Node.js Usage**:
|
||||
|
||||
```javascript
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
|
||||
Quagga.decodeSingle({
|
||||
src: "./barcode.jpg",
|
||||
inputStream: {
|
||||
size: 800 // This is actually the default; shown explicitly here
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log("Code:", result.codeResult.code);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Result Object {#result-object}
|
||||
|
||||
The result object passed to `onDetected`, `onProcessed`, and `decodeSingle` callbacks contains detailed information about the detection and decoding process.
|
||||
|
||||
### Complete Result Structure {#result-structure}
|
||||
|
||||
```javascript
|
||||
{
|
||||
codeResult: {
|
||||
code: "FANAVF1461710", // The decoded barcode string
|
||||
format: "code_128", // Barcode format
|
||||
start: 355, // Start position
|
||||
end: 26, // End position
|
||||
codeset: 100, // Code 128 specific
|
||||
startInfo: {
|
||||
error: 1.0,
|
||||
code: 104,
|
||||
start: 21,
|
||||
end: 41
|
||||
},
|
||||
decodedCodes: [ // Individual code segments
|
||||
{ code: 104, start: 21, end: 41 },
|
||||
// ... more segments
|
||||
{ error: 0.88, code: 106, start: 328, end: 350 }
|
||||
],
|
||||
endInfo: {
|
||||
error: 0.88,
|
||||
code: 106,
|
||||
start: 328,
|
||||
end: 350
|
||||
},
|
||||
direction: -1 // Scan direction
|
||||
},
|
||||
line: [ // Scan line coordinates
|
||||
{ x: 25.97, y: 360.56 },
|
||||
{ x: 401.92, y: 70.88 }
|
||||
],
|
||||
angle: -0.657, // Rotation angle in radians
|
||||
pattern: [0, 0, 1, 1, ...], // Bar pattern (0=space, 1=bar)
|
||||
box: [ // Primary bounding box (4 corners)
|
||||
[77.41, 410.93], // Top-left
|
||||
[0.05, 310.54], // Top-right
|
||||
[360.16, 33.06], // Bottom-right
|
||||
[437.51, 133.45] // Bottom-left
|
||||
],
|
||||
boxes: [ // All detected boxes
|
||||
[/* box 1 */],
|
||||
[/* box 2 */],
|
||||
// ...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Result Properties {#result-properties}
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `codeResult` | Object | Decoded barcode information (may be `undefined` if detection failed) |
|
||||
| `codeResult.code` | String | The decoded barcode value |
|
||||
| `codeResult.format` | String | Barcode format (e.g., "code_128", "ean_13") |
|
||||
| `codeResult.start` | Number | Start position in pattern |
|
||||
| `codeResult.end` | Number | End position in pattern |
|
||||
| `codeResult.direction` | Number | Scan direction (1 or -1) |
|
||||
| `codeResult.supplement` | Object | (Optional) Supplement barcode data for EAN-13/UPC-A with EAN-2 or EAN-5 extensions |
|
||||
| `codeResult.supplement.code` | String | The decoded supplement value (2 or 5 digits) |
|
||||
| `codeResult.supplement.format` | String | Supplement format: "ean_2" or "ean_5" |
|
||||
| `line` | Array | Two points defining the scan line |
|
||||
| `angle` | Number | Barcode rotation angle (radians) |
|
||||
| `pattern` | Array | Binary pattern (0=space, 1=bar) |
|
||||
| `box` | Array | Bounding box coordinates (4 corner points) |
|
||||
| `boxes` | Array | All candidate boxes found during localization. When `locate` is `false`, this contains a single box representing the actual adjusted scanning area (after patch alignment) |
|
||||
|
||||
> **Note: `boxes` with `locate: false`**
|
||||
>
|
||||
> When `locate` is `false` and an `inputStream.area` is configured, `result.boxes` contains a single box representing the actual scanning area dimensions. This box reflects the adjusted dimensions after patch alignment (which can differ slightly from the percentage-based area due to rounding to patch size multiples). Use these coordinates if you need to know the exact scanning rectangle:
|
||||
>
|
||||
> ```javascript
|
||||
> Quagga.onProcessed(function(result) {
|
||||
> if (result.boxes && result.boxes.length > 0) {
|
||||
> // When locate=false, boxes[0] is the actual scanning area
|
||||
> const scanArea = result.boxes[0];
|
||||
> console.log("Scanning area corners:", scanArea);
|
||||
> }
|
||||
> });
|
||||
> ```
|
||||
|
||||
> **Important: Coordinate System**
|
||||
>
|
||||
> The `box`, `boxes`, and `line` coordinates are returned in **processed canvas coordinates**, not original image/video coordinates. If you're using `inputStream.size` to scale the processing resolution (e.g., for performance), you'll need to scale these coordinates to match your original video/image dimensions.
|
||||
>
|
||||
> ```javascript
|
||||
> // Scale coordinates to original video size
|
||||
> const scaleX = video.videoWidth / Quagga.canvas.dom.image.width;
|
||||
> const scaleY = video.videoHeight / Quagga.canvas.dom.image.height;
|
||||
> const scaledBox = result.box.map(p => [p[0] * scaleX, p[1] * scaleY]);
|
||||
> ```
|
||||
>
|
||||
> See [Working with Box Coordinates](../how-to-guides/working-with-coordinates.md) for complete examples.
|
||||
|
||||
### Checking for Successful Detection {#checking-detection}
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
// Always check if codeResult exists
|
||||
if (result && result.codeResult && result.codeResult.code) {
|
||||
console.log("Detected:", result.codeResult.code);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Using Multiple Barcode Detection {#multiple-barcode-detection}
|
||||
|
||||
When `decoder.multiple` is `true`, results are returned as an array:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ["code_128_reader"],
|
||||
multiple: true
|
||||
}
|
||||
});
|
||||
|
||||
Quagga.onDetected(function(result) {
|
||||
// result is an array of result objects
|
||||
result.forEach(function(item) {
|
||||
if (item.codeResult) {
|
||||
console.log("Code:", item.codeResult.code);
|
||||
console.log("Box:", item.box);
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Canvas Access {#canvas-access}
|
||||
|
||||
Quagga automatically creates and manages two canvas elements for visualization. These are positioned over the video/image stream and sized to match the processing dimensions.
|
||||
|
||||
### Canvas Structure {#canvas-structure}
|
||||
|
||||
```javascript
|
||||
Quagga.canvas = {
|
||||
dom: {
|
||||
image: HTMLCanvasElement, // Canvas for processed image data
|
||||
overlay: HTMLCanvasElement | null // Transparent canvas for drawing overlays
|
||||
},
|
||||
ctx: {
|
||||
image: CanvasRenderingContext2D, // Context for image canvas
|
||||
overlay: CanvasRenderingContext2D | null // Context for overlay canvas
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
> **Note**: The overlay canvas can be `null` if `canvas.createOverlay` is set to `false` in the configuration. See [Canvas Configuration](configuration.md#canvas-configuration) for details.
|
||||
|
||||
### Overlay Canvas {#overlay-canvas}
|
||||
|
||||
The **overlay canvas** (`Quagga.canvas.dom.overlay`) is a transparent canvas element positioned over the video stream. It's automatically created when Quagga initializes (unless `canvas.createOverlay` is `false`) and is designed for drawing bounding boxes, scan lines, and other visual feedback.
|
||||
|
||||
**Key characteristics:**
|
||||
- Has CSS class `drawingBuffer`
|
||||
- Sized to match the processed image dimensions (`inputStream.size`)
|
||||
- Positioned absolutely over the video/image element
|
||||
- Automatically appended to the viewport container
|
||||
- Coordinates match the processed image space (no scaling needed)
|
||||
- Can be disabled via `canvas.createOverlay: false` for performance
|
||||
|
||||
**Accessing the overlay:**
|
||||
```javascript
|
||||
const overlay = Quagga.canvas.dom.overlay;
|
||||
const overlayCtx = Quagga.canvas.ctx.overlay;
|
||||
|
||||
// Always check if overlay exists before using
|
||||
if (overlayCtx && overlay) {
|
||||
// Clear overlay
|
||||
overlayCtx.clearRect(0, 0, overlay.width, overlay.height);
|
||||
|
||||
// Draw custom shapes
|
||||
overlayCtx.strokeStyle = "red";
|
||||
overlayCtx.lineWidth = 3;
|
||||
overlayCtx.strokeRect(10, 10, 100, 100);
|
||||
}
|
||||
```
|
||||
|
||||
### Image Canvas {#image-canvas}
|
||||
|
||||
The **image canvas** (`Quagga.canvas.dom.image`) contains the processed grayscale image data used for barcode detection. This is primarily for internal use and debugging.
|
||||
|
||||
**Key characteristics:**
|
||||
- Has CSS class `imgBuffer`
|
||||
- Contains the grayscale/processed image data
|
||||
- Useful for debugging locator issues
|
||||
|
||||
### When to Use Each Canvas {#when-to-use-canvas}
|
||||
|
||||
| Use Case | Canvas to Use |
|
||||
|----------|---------------|
|
||||
| Drawing bounding boxes | `overlay` |
|
||||
| Highlighting detected barcodes | `overlay` |
|
||||
| Custom scan line visualization | `overlay` |
|
||||
| Debugging image processing | `image` |
|
||||
| Checking processed resolution | Either (they have same dimensions) |
|
||||
|
||||
### Important: Coordinate System {#canvas-coordinate-system}
|
||||
|
||||
When drawing on the overlay canvas, use `result.box` and `result.boxes` coordinates directly - **no scaling is needed**. These coordinates are already in the overlay canvas's coordinate space.
|
||||
|
||||
```javascript
|
||||
Quagga.onProcessed(function(result) {
|
||||
const ctx = Quagga.canvas.ctx.overlay;
|
||||
const canvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
// Clear previous drawings
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw box directly - coordinates already match the overlay canvas
|
||||
if (result && result.box) {
|
||||
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, ctx, {
|
||||
color: "green", lineWidth: 2
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: Scaling is only needed when drawing on a **different** canvas (like a custom overlay on the original video element). See [Working with Box Coordinates](../how-to-guides/working-with-coordinates.md) for details.
|
||||
|
||||
### CSS Styling {#canvas-css-styling}
|
||||
|
||||
The overlay canvas can be styled with CSS for positioning:
|
||||
|
||||
```css
|
||||
/* Default positioning (handled automatically by Quagga) */
|
||||
canvas.drawingBuffer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* Ensure proper stacking */
|
||||
#scanner-container {
|
||||
position: relative;
|
||||
}
|
||||
```
|
||||
|
||||
## ImageDebug Helper {#imagedebug-helper}
|
||||
|
||||
Quagga provides a helper for drawing debug visualizations:
|
||||
|
||||
```javascript
|
||||
// Draw a path (array of points)
|
||||
Quagga.ImageDebug.drawPath(points, offset, ctx, options);
|
||||
|
||||
// Example
|
||||
Quagga.ImageDebug.drawPath(
|
||||
result.box,
|
||||
{ x: 0, y: 1 },
|
||||
overlayCtx,
|
||||
{ color: "green", lineWidth: 2 }
|
||||
);
|
||||
```
|
||||
|
||||
## Complete Example {#complete-example}
|
||||
|
||||
```javascript
|
||||
// Initialize
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
target: document.querySelector('#scanner'),
|
||||
constraints: {
|
||||
width: 640,
|
||||
height: 480,
|
||||
facingMode: "environment"
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start scanning
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
// Handle detections
|
||||
Quagga.onDetected(function(result) {
|
||||
console.log("Barcode detected:", result.codeResult.code);
|
||||
|
||||
// Stop after first detection
|
||||
Quagga.stop();
|
||||
|
||||
// Cleanup
|
||||
Quagga.offDetected();
|
||||
Quagga.offProcessed();
|
||||
});
|
||||
|
||||
// Visualize processing
|
||||
Quagga.onProcessed(function(result) {
|
||||
const ctx = Quagga.canvas.ctx.overlay;
|
||||
const canvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
if (result && result.box) {
|
||||
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, ctx, {
|
||||
color: "green",
|
||||
lineWidth: 2
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Stop button
|
||||
document.querySelector('#stop').addEventListener('click', function() {
|
||||
Quagga.stop();
|
||||
Quagga.offDetected();
|
||||
Quagga.offProcessed();
|
||||
});
|
||||
```
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Configuration Reference](configuration.md) - Complete configuration options
|
||||
- [CameraAccess API](camera-access.md) - Camera control methods
|
||||
- [Supported Barcode Types](readers.md) - Available barcode readers
|
||||
- [Getting Started](../getting-started.md) - Basic usage examples
|
||||
|
||||
---
|
||||
|
||||
[← Back to Reference](index.md)
|
||||
109
quagga2/quagga2-1.12.1/docs/reference/browser-support.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Browser Support {#browser-support}
|
||||
|
||||
Quagga2 makes use of many modern Web APIs which are not implemented by all browsers yet. This page details browser compatibility and required APIs.
|
||||
|
||||
## Operating Modes {#operating-modes}
|
||||
|
||||
Quagga2 operates in two modes:
|
||||
|
||||
1. **Analyzing static images** - Process existing image files
|
||||
2. **Using a camera** - Decode images from a live video stream
|
||||
|
||||
The latter requires the MediaDevices API for camera access.
|
||||
|
||||
## Browser Compatibility {#browser-compatibility}
|
||||
|
||||
You can track the compatibility of the used Web APIs for each mode:
|
||||
|
||||
- [Static Images](http://caniuse.com/#feat=canvas,typedarrays,bloburls,blobbuilder)
|
||||
- [Live Stream](http://caniuse.com/#feat=canvas,typedarrays,bloburls,blobbuilder,stream)
|
||||
|
||||
### Static Image Mode {#static-image-mode}
|
||||
|
||||
The following APIs must be supported by your browser:
|
||||
|
||||
- [Canvas](http://caniuse.com/#feat=canvas)
|
||||
- [Typed Arrays](http://caniuse.com/#feat=typedarrays)
|
||||
- [Blob URLs](http://caniuse.com/#feat=bloburls)
|
||||
- [Blob Builder](http://caniuse.com/#feat=blobbuilder)
|
||||
|
||||
### Live Stream Mode {#live-stream-mode}
|
||||
|
||||
In addition to the APIs required for static images:
|
||||
|
||||
- [MediaDevices API](http://caniuse.com/#feat=stream) - Required for camera access
|
||||
|
||||
## Secure Origins Required {#secure-origins}
|
||||
|
||||
**Important**: Accessing `getUserMedia` requires a secure origin in most browsers:
|
||||
|
||||
- `http://` can **only** be used on `localhost`
|
||||
- All other hostnames **must** be served via `https://`
|
||||
|
||||
This is a browser security requirement. Read more in the [Chrome M47 WebRTC Release Notes](https://groups.google.com/forum/#!topic/discuss-webrtc/sq5CVmY69sc).
|
||||
|
||||
## Feature Detection {#feature-detection}
|
||||
|
||||
### Detecting getUserMedia Support {#detecting-getusermedia}
|
||||
|
||||
Every browser implements the `mediaDevices.getUserMedia` API differently. It's highly recommended to include [webrtc-adapter](https://github.com/webrtc/adapter) in your project for cross-browser compatibility.
|
||||
|
||||
**How to test browser capabilities:**
|
||||
|
||||
```javascript
|
||||
if (navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {
|
||||
// Safe to use getUserMedia
|
||||
console.log('Camera access is supported');
|
||||
} else {
|
||||
// Camera access not available
|
||||
console.log('Camera access is NOT supported');
|
||||
}
|
||||
```
|
||||
|
||||
### Browser Support Table {#browser-support-table}
|
||||
|
||||
The above condition evaluates as follows:
|
||||
|
||||
| Browser | Result | Notes |
|
||||
|---------------|---------|-------|
|
||||
| Chrome | `true` | Full support |
|
||||
| Firefox | `true` | Full support |
|
||||
| Edge | `true` | Full support |
|
||||
| Safari iOS | `true` | Requires HTTPS |
|
||||
| IE 11 | `false` | Not supported |
|
||||
| Safari Desktop| `true` | macOS 11+ |
|
||||
|
||||
## Known Issues {#known-issues}
|
||||
|
||||
### iOS Torch/Flash {#ios-torch-flash}
|
||||
|
||||
Torch (flash) control via `CameraAccess.enableTorch()` and `CameraAccess.disableTorch()` does **not work** on iOS devices running version 16.4 and earlier. Support on later versions may vary.
|
||||
|
||||
### Safari Limitations {#safari-limitations}
|
||||
|
||||
- Older Safari versions may require user interaction before camera access
|
||||
- Some older iOS versions have limited WebRTC support
|
||||
|
||||
### Internet Explorer {#internet-explorer}
|
||||
|
||||
Internet Explorer 11 and below do not support the MediaDevices API and cannot use live camera features. Static image decoding may work with polyfills, but this is not officially supported.
|
||||
|
||||
## Recommendations {#recommendations}
|
||||
|
||||
For best compatibility:
|
||||
|
||||
1. **Use HTTPS** - Required for camera access on all non-localhost domains
|
||||
2. **Include webrtc-adapter** - Normalizes browser differences
|
||||
3. **Feature detect** - Check for API support before attempting to use camera
|
||||
4. **Provide fallbacks** - Offer file upload as alternative to camera access
|
||||
5. **Test thoroughly** - Browser behavior varies, especially on mobile
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Configuration Reference](configuration.md) - How to configure Quagga2
|
||||
- [Camera Access API](camera-access.md) - Camera control methods
|
||||
- [Getting Started](../getting-started.md) - Installation and setup
|
||||
|
||||
---
|
||||
|
||||
[← Back to Reference](index.md)
|
||||
376
quagga2/quagga2-1.12.1/docs/reference/camera-access.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# CameraAccess API {#cameraaccess-api}
|
||||
|
||||
Quagga2 exposes a `CameraAccess` API for direct control of camera functionality. This API provides shortcuts for commonly used camera operations.
|
||||
|
||||
**Access**: `Quagga.CameraAccess`
|
||||
|
||||
## Overview {#overview}
|
||||
|
||||
The CameraAccess API allows you to:
|
||||
|
||||
- Request and release camera access
|
||||
- Enumerate available video devices
|
||||
- Control camera torch (flash)
|
||||
- Get information about active video streams and tracks
|
||||
|
||||
All methods return Promises for async operation handling.
|
||||
|
||||
## Methods {#methods}
|
||||
|
||||
### `CameraAccess.request(videoElement, constraints)` {#cameraaccess-request}
|
||||
|
||||
Initializes the camera and starts playback.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `videoElement` (HTMLVideoElement | null) - Video element to display camera stream. If `null`, camera initializes but remains invisible.
|
||||
- `constraints` (MediaTrackConstraints, optional) - Camera selection and configuration constraints.
|
||||
|
||||
**Returns**: `Promise<void>` - Resolves when camera is ready, rejects on error.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
const video = document.querySelector('#camera-video');
|
||||
|
||||
// Request camera with default constraints
|
||||
await Quagga.CameraAccess.request(video);
|
||||
|
||||
// Request specific camera
|
||||
await Quagga.CameraAccess.request(video, {
|
||||
facingMode: 'environment', // Back camera on mobile
|
||||
width: { ideal: 1280 },
|
||||
height: { ideal: 720 }
|
||||
});
|
||||
|
||||
// Request camera by device ID
|
||||
const deviceId = 'abc123...';
|
||||
await Quagga.CameraAccess.request(video, { deviceId });
|
||||
|
||||
// Initialize camera without displaying (for probing)
|
||||
await Quagga.CameraAccess.request(null);
|
||||
```
|
||||
|
||||
**Use cases**:
|
||||
|
||||
- Start camera before Quagga initialization
|
||||
- Probe camera availability and permissions
|
||||
- Initialize camera without displaying video
|
||||
|
||||
### `CameraAccess.release()` {#cameraaccess-release}
|
||||
|
||||
Stops the video stream and releases all camera resources.
|
||||
|
||||
**Returns**: `Promise<void>` - Resolves when all tracks are stopped and resources released.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// Stop camera
|
||||
await Quagga.CameraAccess.release();
|
||||
console.log('Camera released');
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
|
||||
1. Pauses the video element
|
||||
2. Stops all tracks in the media stream
|
||||
3. Releases camera for use by other applications
|
||||
|
||||
**Note**: Always call `release()` when finished with the camera to free system resources.
|
||||
|
||||
### `CameraAccess.enumerateVideoDevices(constraints?)` {#cameraaccess-enumeratevideodevices}
|
||||
|
||||
Lists all available video input devices (cameras), optionally filtered by constraints.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `constraints` (MediaTrackConstraints, optional) - Constraints to filter devices. When provided, only devices that can satisfy the given constraints will be returned.
|
||||
|
||||
**Returns**: `Promise<MediaDeviceInfo[]>` - Array of video device information.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// Get all video devices
|
||||
const devices = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
|
||||
devices.forEach(device => {
|
||||
console.log('Device:', device.label);
|
||||
console.log('Device ID:', device.deviceId);
|
||||
console.log('Group ID:', device.groupId);
|
||||
});
|
||||
|
||||
// Example output:
|
||||
// Device: Front Camera
|
||||
// Device ID: abc123...
|
||||
// Device: Back Camera
|
||||
// Device ID: def456...
|
||||
```
|
||||
|
||||
**Filtering devices with constraints**:
|
||||
|
||||
```javascript
|
||||
// Get only devices that support a minimum resolution
|
||||
const hdDevices = await Quagga.CameraAccess.enumerateVideoDevices({
|
||||
width: { min: 1280 },
|
||||
height: { min: 720 }
|
||||
});
|
||||
|
||||
// Get only back-facing cameras
|
||||
const backCameras = await Quagga.CameraAccess.enumerateVideoDevices({
|
||||
facingMode: 'environment'
|
||||
});
|
||||
|
||||
// Eliminate wide-angle only cameras by specifying aspect ratio
|
||||
const standardCameras = await Quagga.CameraAccess.enumerateVideoDevices({
|
||||
aspectRatio: { ideal: 1.777 } // 16:9
|
||||
});
|
||||
```
|
||||
|
||||
**Use cases**:
|
||||
|
||||
- Build camera selector UI
|
||||
- Detect available cameras before initialization
|
||||
- Check for front/back camera availability on mobile
|
||||
- Filter out cameras that don't meet quality requirements
|
||||
- Eliminate wide-angle cameras that may not be suitable for barcode scanning
|
||||
|
||||
**Note**: Device labels may be empty strings until camera permission is granted. When using constraints, the method will request temporary access to each device to test if it satisfies the constraints.
|
||||
|
||||
### `CameraAccess.getActiveStreamLabel()` {#cameraaccess-getactivestreamlabel}
|
||||
|
||||
Gets the label of the currently active video track.
|
||||
|
||||
**Returns**: `string` - Label of active video track (e.g., "Back Camera", "USB Camera").
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
const label = Quagga.CameraAccess.getActiveStreamLabel();
|
||||
console.log('Using camera:', label);
|
||||
// Output: "Using camera: Back Camera"
|
||||
```
|
||||
|
||||
**Use cases**:
|
||||
|
||||
- Display which camera is currently active
|
||||
- Verify correct camera is being used
|
||||
- Logging and debugging
|
||||
|
||||
### `CameraAccess.getActiveStream()` {#cameraaccess-getactivestream}
|
||||
|
||||
Gets the complete MediaStream object for the currently active video.
|
||||
|
||||
**Returns**: `MediaStream | null` - The active MediaStream object, or `null` if no camera is active.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
const stream = Quagga.CameraAccess.getActiveStream();
|
||||
|
||||
if (stream) {
|
||||
console.log('Stream ID:', stream.id);
|
||||
console.log('Stream active:', stream.active);
|
||||
console.log('Video tracks:', stream.getVideoTracks().length);
|
||||
console.log('Audio tracks:', stream.getAudioTracks().length);
|
||||
|
||||
// Clone the stream
|
||||
const clonedStream = stream.clone();
|
||||
}
|
||||
|
||||
// Pass stream to WebRTC peer connection
|
||||
if (stream?.active) {
|
||||
peerConnection.addStream(stream);
|
||||
}
|
||||
```
|
||||
|
||||
**Use cases**:
|
||||
|
||||
- Pass the stream to WebRTC peer connections
|
||||
- Clone the stream for multiple consumers
|
||||
- Check if the stream is still active via `stream.active`
|
||||
- Access the stream ID
|
||||
- Work with all tracks (video and audio) in the stream
|
||||
|
||||
**Note**: For accessing just the video track, use `getActiveTrack()` instead.
|
||||
|
||||
### `CameraAccess.getActiveTrack()` {#cameraaccess-getactivetrack}
|
||||
|
||||
Gets the MediaStreamTrack for the currently active video.
|
||||
|
||||
**Returns**: `MediaStreamTrack | null` - Active video track object, or `null` if no camera is active.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
const track = Quagga.CameraAccess.getActiveTrack();
|
||||
|
||||
console.log('Track state:', track.readyState);
|
||||
console.log('Track settings:', track.getSettings());
|
||||
console.log('Track capabilities:', track.getCapabilities());
|
||||
|
||||
// Get current resolution
|
||||
const settings = track.getSettings();
|
||||
console.log(`Resolution: ${settings.width}x${settings.height}`);
|
||||
```
|
||||
|
||||
**Use cases**:
|
||||
|
||||
- Access advanced track capabilities
|
||||
- Monitor track state
|
||||
- Apply additional constraints
|
||||
- Access camera capabilities (zoom, focus, etc.)
|
||||
|
||||
### `CameraAccess.enableTorch()` {#cameraaccess-enabletorch}
|
||||
|
||||
Turns on the camera torch (flash).
|
||||
|
||||
**Returns**: `Promise<void>` - Resolves when torch is enabled, rejects on error.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
await Quagga.CameraAccess.enableTorch();
|
||||
console.log('Torch enabled');
|
||||
} catch (error) {
|
||||
console.error('Failed to enable torch:', error);
|
||||
}
|
||||
```
|
||||
|
||||
**Browser Support**:
|
||||
|
||||
- ✅ Chrome (Android)
|
||||
- ✅ Chrome (Desktop with supported cameras)
|
||||
- ❌ Safari iOS 16.4 and earlier
|
||||
- ⚠️ Safari iOS later versions - may or may not work
|
||||
|
||||
**Requirements**:
|
||||
|
||||
- Camera must support torch capability
|
||||
- Camera must be actively streaming
|
||||
- Browser must support torch constraint
|
||||
|
||||
**Note**: Always wrap in try-catch as not all devices support torch.
|
||||
|
||||
### `CameraAccess.disableTorch()` {#cameraaccess-disabletorch}
|
||||
|
||||
Turns off the camera torch (flash).
|
||||
|
||||
**Returns**: `Promise<void>` - Resolves when torch is disabled, rejects on error.
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
await Quagga.CameraAccess.disableTorch();
|
||||
console.log('Torch disabled');
|
||||
} catch (error) {
|
||||
console.error('Failed to disable torch:', error);
|
||||
}
|
||||
```
|
||||
|
||||
**Browser Support**: Same as `enableTorch()`.
|
||||
|
||||
## Complete Example {#complete-example}
|
||||
|
||||
```javascript
|
||||
// Enumerate cameras and let user choose
|
||||
const devices = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
const backCamera = devices.find(d => d.label.includes('back'));
|
||||
|
||||
// Initialize camera
|
||||
const video = document.querySelector('#video');
|
||||
await Quagga.CameraAccess.request(video, {
|
||||
deviceId: backCamera.deviceId
|
||||
});
|
||||
|
||||
console.log('Active camera:', Quagga.CameraAccess.getActiveStreamLabel());
|
||||
|
||||
// Enable torch for better scanning in dark environments
|
||||
try {
|
||||
await Quagga.CameraAccess.enableTorch();
|
||||
} catch (error) {
|
||||
console.log('Torch not available');
|
||||
}
|
||||
|
||||
// ... use camera for scanning ...
|
||||
|
||||
// Cleanup
|
||||
await Quagga.CameraAccess.disableTorch();
|
||||
await Quagga.CameraAccess.release();
|
||||
```
|
||||
|
||||
## Torch Control in Live Scanning {#torch-control}
|
||||
|
||||
For torch control during live scanning, you may want to provide a toggle button:
|
||||
|
||||
```javascript
|
||||
let torchEnabled = false;
|
||||
|
||||
document.querySelector('#torch-toggle').addEventListener('click', async () => {
|
||||
try {
|
||||
if (torchEnabled) {
|
||||
await Quagga.CameraAccess.disableTorch();
|
||||
torchEnabled = false;
|
||||
} else {
|
||||
await Quagga.CameraAccess.enableTorch();
|
||||
torchEnabled = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Torch control failed:', error);
|
||||
alert('Torch not available on this device');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Advanced Camera Control {#advanced-camera-control}
|
||||
|
||||
For advanced camera control (zoom, focus, etc.), use the MediaStreamTrack API:
|
||||
|
||||
```javascript
|
||||
const track = Quagga.CameraAccess.getActiveTrack();
|
||||
const capabilities = track.getCapabilities();
|
||||
|
||||
// Check if zoom is supported
|
||||
if (capabilities.zoom) {
|
||||
console.log('Zoom range:', capabilities.zoom.min, '-', capabilities.zoom.max);
|
||||
|
||||
// Apply zoom
|
||||
await track.applyConstraints({
|
||||
advanced: [{ zoom: 2.0 }]
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Read more: [MediaStreamTrack Capabilities](https://www.oberhofer.co/mediastreamtrack-and-its-capabilities)
|
||||
|
||||
## Error Handling {#error-handling}
|
||||
|
||||
Always handle errors when using CameraAccess methods:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
await Quagga.CameraAccess.request(video);
|
||||
} catch (error) {
|
||||
if (error.name === 'NotAllowedError') {
|
||||
console.error('Camera permission denied');
|
||||
} else if (error.name === 'NotFoundError') {
|
||||
console.error('No camera found');
|
||||
} else {
|
||||
console.error('Camera error:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Browser Support](browser-support.md) - Camera compatibility information
|
||||
- [Configuration Reference](configuration.md) - Camera configuration in Quagga.init()
|
||||
- [API Documentation](api.md) - Main Quagga API methods
|
||||
- [Tips & Tricks](../how-to-guides/tips-and-tricks.md) - Camera optimization tips
|
||||
|
||||
---
|
||||
|
||||
[← Back to Reference](index.md)
|
||||
1009
quagga2/quagga2-1.12.1/docs/reference/configuration.md
Normal file
295
quagga2/quagga2-1.12.1/docs/reference/dependencies.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Quagga2 Dependencies
|
||||
|
||||
This document explains the dependency structure of Quagga2 and clarifies which packages are runtime code dependencies versus build/test tools.
|
||||
|
||||
## Background
|
||||
|
||||
Quagga2 bundles all its code with Webpack, producing standalone browser and Node.js builds. As a result, **all packages are listed as `devDependencies`** in `package.json` because consumers never directly install them - they only use the pre-built bundles in `dist/` and `lib/`.
|
||||
|
||||
However, this makes it unclear which packages are actual code dependencies (bundled into the final output) versus which are just build/test tools. This document clarifies that distinction.
|
||||
|
||||
---
|
||||
|
||||
## Runtime Code Dependencies
|
||||
|
||||
These packages contain code that is **imported by the source code** and **bundled into the final output**:
|
||||
|
||||
### Core Libraries
|
||||
|
||||
- **`gl-matrix`** (^3.4.4)
|
||||
- **Purpose**: High-performance vector and matrix math operations
|
||||
- **Usage**: Used throughout the codebase for geometric calculations
|
||||
- **Files**:
|
||||
- `src/quagga/quagga.ts` - vec2 operations for bounding boxes
|
||||
- `src/quagga/initBuffers.ts` - vec2 for buffer initialization
|
||||
- `src/locator/barcode_locator.js` - vec2, mat2 for barcode location
|
||||
- `src/common/image_wrapper.ts` - vec2 for image transforms
|
||||
- `src/common/cvutils/ImageRef.ts` - vec2, vec3 for computer vision
|
||||
- `src/common/cluster.js` - vec2 for clustering algorithms
|
||||
|
||||
- **`lodash`** (^4.17.21)
|
||||
- **Purpose**: Utility functions for object manipulation
|
||||
- **Usage**: Primarily `merge()` for config merging, `pick()` for object selection
|
||||
- **Files**:
|
||||
- `src/quagga.js` - merge() for configuration
|
||||
- `src/QuaggaStatic.ts` - merge() for configuration
|
||||
- `src/reader/ean_reader.ts` - merge() for config defaults
|
||||
- `src/reader/i2of5_reader.ts` - merge() for config defaults
|
||||
- `src/input/camera_access.ts` - pick() for MediaTrackConstraints
|
||||
- `src/locator/test/barcode_locator.spec.ts` - merge() in tests
|
||||
|
||||
### Image Processing
|
||||
|
||||
- **`ndarray`** (^1.0.19)
|
||||
- **Purpose**: N-dimensional array manipulation
|
||||
- **Usage**: Core data structure for image data processing
|
||||
- **Files**:
|
||||
- `src/input/input_stream/input_stream_base.ts` - NdArray type definitions
|
||||
- `src/input/input_stream/input_stream.ts` - NdArray for frame data
|
||||
- `src/input/frame_grabber.ts` - Ndarray for frame manipulation
|
||||
- `src/vendor.d.ts` - Type definitions
|
||||
|
||||
- **`ndarray-linear-interpolate`** (^1.0.0)
|
||||
- **Purpose**: Bilinear interpolation for ndarray data
|
||||
- **Usage**: Image scaling and transformations
|
||||
- **Files**:
|
||||
- `src/input/frame_grabber.ts` - `d2()` method for 2D interpolation
|
||||
|
||||
- **`ndarray-pixels`** (^5.0.1)
|
||||
- **Purpose**: Convert between image formats and ndarray
|
||||
- **Usage**: Loading image data from various sources
|
||||
- **Files**:
|
||||
- `src/input/input_stream/input_stream.ts` - `getPixels()` for image loading
|
||||
|
||||
### Polyfills (Deprecated)
|
||||
|
||||
- **`@babel/polyfill`** (^7.12.1)
|
||||
- **Status**: ⚠️ **DEPRECATED** by Babel team
|
||||
- **Purpose**: Legacy polyfill for ES6+ features
|
||||
- **Current Usage**: Not directly imported in source code
|
||||
- **Recommendation**: Should be removed in favor of `core-js` + `regenerator-runtime` or Babel's automatic polyfill injection
|
||||
- **Migration Path**: Use `@babel/preset-env` with `useBuiltIns: 'usage'` and explicit `core-js@3`
|
||||
|
||||
---
|
||||
|
||||
## Build & Development Tools
|
||||
|
||||
These packages are **only used during build/development** and are **not bundled into the final output**:
|
||||
|
||||
### TypeScript Toolchain
|
||||
|
||||
- **`typescript`** (^5.9.3) - TypeScript compiler
|
||||
- **`@types/*`** packages - Type definitions for TypeScript
|
||||
- `@types/chai`, `@types/gl-vec2`, `@types/lodash`, `@types/mocha`, `@types/node`, `@types/sinon`, `@types/sinon-chai`
|
||||
|
||||
### Webpack & Bundling
|
||||
|
||||
- **`webpack`** (^4.44.2) - Module bundler (used to create `dist/` and `lib/` outputs)
|
||||
- **`webpack-cli`** (^3.3.12) - Webpack command-line interface
|
||||
- **`babel-loader`** (^8.2.5) - Webpack loader for Babel transpilation
|
||||
- **`source-map-loader`** (^1.1.1) - Webpack loader for source maps
|
||||
|
||||
### Babel Transpilation
|
||||
|
||||
- **`@babel/core`** (^7.28.5) - Babel compiler core
|
||||
- **`@babel/preset-env`** (^7.28.5) - Smart transpilation based on target environments
|
||||
- **`@babel/preset-typescript`** (^7.28.5) - TypeScript support in Babel
|
||||
- **`@babel/plugin-*`** - Various syntax plugins:
|
||||
- `@babel/plugin-proposal-class-properties`
|
||||
- `@babel/plugin-proposal-nullish-coalescing-operator`
|
||||
- `@babel/plugin-proposal-object-rest-spread`
|
||||
- `@babel/plugin-proposal-optional-chaining`
|
||||
- `@babel/plugin-transform-runtime`
|
||||
- **`@babel/runtime`** (^7.28.4) - Babel runtime helpers
|
||||
- **`babel-plugin-add-module-exports`** (^1.0.4) - CommonJS module.exports handling
|
||||
- **`babel-plugin-istanbul`** (^7.0.1) - Code coverage instrumentation
|
||||
|
||||
### Testing
|
||||
|
||||
- **`mocha`** (^5.2.0) - Test framework
|
||||
- **`chai`** (^4.3.10) - Assertion library
|
||||
- **`sinon`** (^21.0.0) - Test spies, stubs, and mocks
|
||||
- **`sinon-chai`** (^3.7.0) - Sinon assertions for Chai
|
||||
- **`ts-mocha`** (^11.1.0) - TypeScript support for Mocha
|
||||
- **`ts-node`** (^10.9.2) - TypeScript execution for Node.js
|
||||
- **`cypress`** (^13.1.0) - End-to-end browser testing
|
||||
- **`@cypress/webpack-preprocessor`** (6.0.0) - Webpack integration for Cypress
|
||||
- **`@cypress/code-coverage`** (^3.12.4) - Code coverage for Cypress tests
|
||||
- **`nyc`** (^17.1.0) - Code coverage tool (Istanbul wrapper)
|
||||
|
||||
### Linting & Code Quality
|
||||
|
||||
- **`eslint`** (^8.57.1) - JavaScript/TypeScript linter
|
||||
- **`@typescript-eslint/eslint-plugin`** (^7.18.0) - TypeScript-specific ESLint rules
|
||||
- **`@typescript-eslint/parser`** (^7.18.0) - TypeScript parser for ESLint
|
||||
- **`eslint-config-airbnb-base`** (^15.0.0) - Airbnb JavaScript style guide
|
||||
- **`eslint-config-airbnb-typescript`** (^18.0.0) - Airbnb style for TypeScript
|
||||
- **`eslint-config-airbnb-typescript-base`** (^6.0.1) - Base Airbnb TypeScript config
|
||||
- **`eslint-plugin-import`** (^2.32.0) - Import/export validation
|
||||
- **`eslint-plugin-jsx-a11y`** (^6.10.2) - Accessibility linting
|
||||
- **`eslint-plugin-typescript-sort-keys`** (^3.3.0) - Enforce sorted object keys
|
||||
|
||||
### Utilities
|
||||
|
||||
- **`core-js`** (^3.46.0) - Modern JavaScript polyfills (used by Babel)
|
||||
- **`cross-env`** (^10.1.0) - Cross-platform environment variable setting
|
||||
|
||||
---
|
||||
|
||||
## Optional Dependencies
|
||||
|
||||
- **`fsevents`** (2.3.3)
|
||||
- **Platform**: macOS only
|
||||
- **Purpose**: Native file watching for better performance
|
||||
- **Usage**: Automatically used by Webpack/build tools on macOS
|
||||
|
||||
---
|
||||
|
||||
## Overrides
|
||||
|
||||
The `overrides` field forces specific versions of transitive dependencies:
|
||||
|
||||
```json
|
||||
"overrides": {
|
||||
"@cypress/request": "^3.0.9"
|
||||
}
|
||||
```
|
||||
|
||||
- **Purpose**: Security fix for `form-data` vulnerability (CVE in versions < 2.5.4)
|
||||
- **Details**: Cypress 13.1.0 bundles `@cypress/request@3.0.0` which depends on vulnerable `form-data@2.3.3`. This override forces `@cypress/request@^3.0.9` which uses safe `form-data@~4.0.4`.
|
||||
|
||||
---
|
||||
|
||||
## Bundle Size Impact
|
||||
|
||||
When evaluating dependencies, consider their impact on bundle size:
|
||||
|
||||
| Package | Approximate Size | Bundled? |
|
||||
|---------|-----------------|----------|
|
||||
| `gl-matrix` | ~50 KB (minified) | ✅ Yes |
|
||||
| `lodash` | ~4 KB (only `merge` + `pick`) | ✅ Yes (tree-shaken) |
|
||||
| `ndarray` | ~5 KB | ✅ Yes |
|
||||
| `ndarray-linear-interpolate` | ~2 KB | ✅ Yes |
|
||||
| `ndarray-pixels` | ~10 KB | ✅ Yes (browser) |
|
||||
| `webpack` | ~1.5 MB | ❌ No (dev only) |
|
||||
| `typescript` | ~50 MB | ❌ No (dev only) |
|
||||
|
||||
---
|
||||
|
||||
## Adding New Dependencies
|
||||
|
||||
When adding a new dependency, consider:
|
||||
|
||||
1. **Is it a runtime dependency?**
|
||||
- Will the code be `import`ed in `src/` files?
|
||||
- Will it be bundled into `dist/` or `lib/` output?
|
||||
- → Add to `devDependencies` (all deps go here due to bundling)
|
||||
- → Document it in the "Runtime Code Dependencies" section above
|
||||
|
||||
2. **Is it a build/test tool?**
|
||||
- Is it only used by Webpack, Babel, ESLint, Mocha, etc.?
|
||||
- → Add to `devDependencies`
|
||||
- → Document it in the "Build & Development Tools" section above
|
||||
|
||||
3. **Bundle size impact?**
|
||||
- Run `npm run build` and check the size change in `dist/quagga.min.js`
|
||||
- Consider tree-shaking (does the library support ES modules?)
|
||||
- Look for lighter alternatives if the package is large
|
||||
|
||||
4. **Browser compatibility?**
|
||||
- Does the package work in browsers?
|
||||
- Does it require Node.js-specific APIs (`fs`, `path`, etc.)?
|
||||
- → Check if it's already shimmed in `configs/webpack.config.js` (e.g., `node: { fs: 'empty' }`)
|
||||
|
||||
---
|
||||
|
||||
## Version Constraints
|
||||
|
||||
### Pinned Versions
|
||||
|
||||
Some packages are pinned to specific versions due to compatibility issues:
|
||||
|
||||
- **`mocha@^5.2.0`** - Pinned to v5 because newer versions have breaking changes
|
||||
- **`chai@^4.3.10`** - Pinned to v4 because v5+ and v6+ are ESM-only, incompatible with CommonJS tests
|
||||
- **`sinon-chai@^3.7.0`** - Pinned to match `chai@4.x` compatibility
|
||||
- **`webpack@^4.44.2`** - Pinned to v4 because v5 requires significant config migration
|
||||
- **`cypress@^13.1.0`** - Pinned to v13 for stability
|
||||
|
||||
These are configured in `.ncurc.json` to prevent accidental upgrades via `npm-check-updates`.
|
||||
|
||||
### Upgrade Policy
|
||||
|
||||
- **TypeScript ecosystem** (`typescript`, `@typescript-eslint/*`, `ts-*`): Keep up-to-date
|
||||
- **Babel ecosystem** (`@babel/*`): Keep up-to-date for security and features
|
||||
- **Testing tools** (`mocha`, `chai`, `sinon`): Upgrade cautiously, test thoroughly
|
||||
- **Webpack & bundlers**: Major version upgrades require careful migration planning
|
||||
- **Runtime dependencies** (`gl-matrix`, `lodash`, `ndarray*`): Keep up-to-date unless breaking changes occur
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Known Issues
|
||||
|
||||
1. **`@babel/polyfill` is deprecated** - Should migrate to `core-js@3` + `regenerator-runtime`
|
||||
2. **Old `mocha` version** - v5.2.0 is from 2018, may have unpatched vulnerabilities
|
||||
3. **Webpack 4** - No longer receives updates, consider upgrading to Webpack 5
|
||||
|
||||
### Monitoring
|
||||
|
||||
- Run `npm audit` regularly to check for vulnerabilities
|
||||
- Use `npm run check-updates` to see available updates
|
||||
- Check GitHub Dependabot alerts
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Why are runtime dependencies in `devDependencies` instead of `dependencies`?**
|
||||
|
||||
A: Quagga2 is a **bundled library**. Consumers install the package and use the pre-built files (`dist/quagga.min.js` or `lib/quagga.js`), not the source code. They never run `npm install` on Quagga2's dependencies. Therefore, from npm's perspective, all packages are development dependencies (used during build), not runtime dependencies (used after install).
|
||||
|
||||
**Q: How can I tell if a package is actually used in the code?**
|
||||
|
||||
A: Search the `src/` directory:
|
||||
```bash
|
||||
# Search for imports
|
||||
grep -r "from 'package-name'" src/
|
||||
grep -r 'from "package-name"' src/
|
||||
grep -r "require('package-name')" src/
|
||||
```
|
||||
|
||||
**Q: What's the difference between `optionalDependencies` and `devDependencies`?**
|
||||
|
||||
A: `optionalDependencies` are packages that enhance functionality if available but aren't required (like `fsevents` for macOS file watching). `devDependencies` are required for development but not for using the published package.
|
||||
|
||||
**Q: Can I remove `@babel/polyfill`?**
|
||||
|
||||
A: Yes, but carefully. It's deprecated and not directly imported anymore. Remove it from `package.json` and verify that `@babel/preset-env` is configured to polyfill features automatically via `core-js@3`. Test thoroughly in older browsers (IE11, older Safari) after removal.
|
||||
|
||||
**Q: Why can't I upgrade `chai` to version 5 or 6?**
|
||||
|
||||
A: `chai@5+` and `chai@6+` are ESM-only (ES modules). Quagga2's tests use CommonJS (`require()`), and `mocha@5` doesn't support ESM. Upgrading `chai` requires also upgrading `mocha` to v9.1.0+ and migrating all test files to ESM syntax.
|
||||
|
||||
---
|
||||
|
||||
## Related Files
|
||||
|
||||
- **`package.json`** - Dependency declarations
|
||||
- **`.ncurc.json`** - npm-check-updates configuration (blocks unsafe auto-upgrades)
|
||||
- **`configs/webpack.config.js`** - Build configuration showing which dependencies are bundled
|
||||
- **`configs/webpack.node.config.js`** - Node.js-specific build configuration
|
||||
- **`CHANGELOG.md`** - Version history and dependency changes
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Notes
|
||||
|
||||
This document was created in November 2025 following the TypeScript 5.9.3 upgrade. It should be updated whenever:
|
||||
|
||||
- A new dependency is added or removed
|
||||
- A major version upgrade changes behavior
|
||||
- Security vulnerabilities are discovered and patched
|
||||
- Build tooling changes significantly
|
||||
|
||||
Last updated: 2025-11-16
|
||||
74
quagga2/quagga2-1.12.1/docs/reference/index.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Reference Documentation
|
||||
|
||||
Precise technical descriptions of Quagga2's API, configuration, and capabilities. Consult these when you need exact details about how something works.
|
||||
|
||||
## Core API
|
||||
|
||||
### [API Documentation](api.md)
|
||||
|
||||
Complete reference for all Quagga2 methods, callbacks, and events.
|
||||
|
||||
### [Configuration Options](configuration.md)
|
||||
|
||||
Detailed documentation of every configuration parameter and its effects.
|
||||
|
||||
### [Camera Access API](camera-access.md)
|
||||
|
||||
Methods for controlling camera access, torch/flash, and device enumeration.
|
||||
|
||||
## Barcode Support
|
||||
|
||||
### [Supported Barcode Types](readers.md)
|
||||
|
||||
List of all supported barcode formats with characteristics and use cases.
|
||||
|
||||
## Compatibility
|
||||
|
||||
### [Browser Support](browser-support.md)
|
||||
|
||||
Browser compatibility matrix and required Web APIs.
|
||||
|
||||
## Development
|
||||
|
||||
### [Dependencies](dependencies.md)
|
||||
|
||||
Explanation of all package dependencies - which are bundled vs. dev-only.
|
||||
|
||||
## Quick Lookup
|
||||
|
||||
| Need | See |
|
||||
|------|-----|
|
||||
| Method signatures | [API Documentation](api.md) |
|
||||
| Config parameter details | [Configuration Options](configuration.md) |
|
||||
| Which browsers work | [Browser Support](browser-support.md) |
|
||||
| Which barcodes supported | [Supported Barcode Types](readers.md) |
|
||||
| Camera control | [Camera Access API](camera-access.md) |
|
||||
| Package versions | [Dependencies](dependencies.md) |
|
||||
|
||||
## Reading Reference Docs
|
||||
|
||||
Reference documentation is:
|
||||
|
||||
- **Information-oriented** - Focuses on describing *what exists*
|
||||
- **Accurate** - Every detail matters, kept up-to-date with code
|
||||
- **Complete** - Covers all features, even obscure ones
|
||||
- **Structured** - Organized for lookup, not linear reading
|
||||
|
||||
Don't read reference docs cover-to-cover. Instead:
|
||||
|
||||
1. **Look up** what you need when you need it
|
||||
2. **Verify** assumptions about how things work
|
||||
3. **Discover** features you didn't know existed
|
||||
4. **Confirm** exact method signatures or parameter types
|
||||
|
||||
## Need Context?
|
||||
|
||||
Reference docs tell you *what* and *how*, but not *why*:
|
||||
|
||||
- For **why** something works a certain way → See [Explanation](../explanation/)
|
||||
- For **how to use** something in practice → See [How-To Guides](../how-to-guides/)
|
||||
- For **learning** from scratch → See [Tutorials](../tutorials/)
|
||||
|
||||
---
|
||||
|
||||
[← Back to Documentation Home](../index.md)
|
||||
326
quagga2/quagga2-1.12.1/docs/reference/readers.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# Supported Barcode Types {#supported-barcode-types}
|
||||
|
||||
Quagga2 supports a wide variety of 1D barcode formats. This page lists all available barcode readers and how to configure them.
|
||||
|
||||
## Available Readers {#available-readers}
|
||||
|
||||
Quagga2 includes built-in readers for the following barcode formats:
|
||||
|
||||
| Reader Name | Barcode Format | Common Uses |
|
||||
|-------------|----------------|-------------|
|
||||
| `code_128_reader` | [Code 128](https://en.wikipedia.org/wiki/Code_128), [GS1-128](https://en.wikipedia.org/wiki/GS1-128) | General purpose, shipping, packaging, supply chain |
|
||||
| `ean_reader` | [EAN-13](https://en.wikipedia.org/wiki/International_Article_Number) | Retail products worldwide |
|
||||
| `ean_8_reader` | [EAN-8](https://en.wikipedia.org/wiki/EAN-8) | Small retail products |
|
||||
| `code_39_reader` | [Code 39](https://en.wikipedia.org/wiki/Code_39) | Automotive, defense, healthcare |
|
||||
| `code_39_vin_reader` | Code 39 VIN | Vehicle Identification Numbers |
|
||||
| `codabar_reader` | [Codabar](https://en.wikipedia.org/wiki/Codabar) | Libraries, blood banks, logistics |
|
||||
| `upc_reader` | [UPC-A](https://en.wikipedia.org/wiki/Universal_Product_Code) | Retail products (North America) |
|
||||
| `upc_e_reader` | [UPC-E](https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E) | Small retail products |
|
||||
| `i2of5_reader` | [Interleaved 2 of 5](https://en.wikipedia.org/wiki/Interleaved_2_of_5) | Warehouse, distribution |
|
||||
| `2of5_reader` | [Standard 2 of 5](https://en.wikipedia.org/wiki/Two-out-of-five_code) | Industrial, airline tickets |
|
||||
| `code_93_reader` | [Code 93](https://en.wikipedia.org/wiki/Code_93) | Logistics, retail |
|
||||
| `code_32_reader` | [Code 32](https://en.wikipedia.org/wiki/Pharmacode#Code_32) | Italian pharmaceuticals |
|
||||
| `pharmacode_reader` | [Pharmacode](https://en.wikipedia.org/wiki/Pharmacode) | Pharmaceutical packaging |
|
||||
|
||||
## Basic Configuration {#basic-configuration}
|
||||
|
||||
Specify which barcode types to detect in the `decoder.readers` array:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ["code_128_reader"] // Only detect Code 128
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
### Multiple Readers {#multiple-readers}
|
||||
|
||||
You can enable multiple readers to detect different barcode types:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: [
|
||||
"code_128_reader",
|
||||
"ean_reader",
|
||||
"upc_reader"
|
||||
]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Important Considerations {#important-considerations}
|
||||
|
||||
### Reader Priority and Order {#reader-priority-and-order}
|
||||
|
||||
**Readers are processed in the exact order they appear in the `readers` array.** The first reader to successfully decode the barcode wins - subsequent readers are not tried.
|
||||
|
||||
This allows you to prioritize certain formats over others when multiple formats might match the same barcode pattern:
|
||||
|
||||
```javascript
|
||||
decoder: {
|
||||
// EAN-13 checked first, then UPC formats
|
||||
readers: ['ean_reader', 'upc_reader', 'upc_e_reader']
|
||||
}
|
||||
```
|
||||
|
||||
**Why order matters:**
|
||||
|
||||
- Readers are processed sequentially, not in parallel
|
||||
- Some readers may return false positives for other formats
|
||||
- Example: EAN-13 and UPC-A/UPC-E share similar patterns and can clash
|
||||
- The first successful decode is returned immediately
|
||||
|
||||
**Best practice**: List your most commonly expected barcode types first for best accuracy and performance.
|
||||
|
||||
### Don't Enable All Readers {#dont-enable-all-readers}
|
||||
|
||||
**Why not enable all readers by default?**
|
||||
|
||||
- More readers = more processing time
|
||||
- Increased chance of false positives
|
||||
- Some formats overlap and can interfere
|
||||
|
||||
**Best practice**: Only enable the barcode formats you actually need to scan.
|
||||
|
||||
### Format Conflicts {#format-conflicts}
|
||||
|
||||
Some barcode formats are subsets or extensions of others:
|
||||
|
||||
- **UPC-A** is a subset of **EAN-13**
|
||||
- **EAN-8** is shorter version of **EAN-13**
|
||||
- **Code 39** and **Code 39 VIN** share similar patterns
|
||||
|
||||
Be careful when enabling multiple related formats together.
|
||||
|
||||
## GS1-128 Barcodes {#gs1-128-barcodes}
|
||||
|
||||
[GS1-128](https://en.wikipedia.org/wiki/GS1-128) (formerly known as EAN-128 or UCC-128) is a subset of Code 128 used extensively in supply chain and logistics. It uses special FNC1 (Function Code 1) characters to separate variable-length data fields called Application Identifiers (AIs).
|
||||
|
||||
### How GS1-128 Works {#gs1-128-how-it-works}
|
||||
|
||||
GS1-128 barcodes encode structured data using standardized Application Identifiers. For example:
|
||||
- **AI 01** = GTIN (Global Trade Item Number)
|
||||
- **AI 10** = Batch/Lot Number
|
||||
- **AI 17** = Expiration Date
|
||||
- **AI 21** = Serial Number
|
||||
|
||||
The FNC1 character acts as a field separator between variable-length AIs, allowing decoders to know where one field ends and another begins.
|
||||
|
||||
### FNC1 Character Handling {#fnc1-character-handling}
|
||||
|
||||
When the `code_128_reader` decodes a GS1-128 barcode, FNC1 characters are represented as ASCII 29 (Group Separator, `\x1D` or `\u001d`). This follows the GS1 standard for representing FNC1 in decoded data.
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: 'gs1-128-barcode.jpg',
|
||||
decoder: {
|
||||
readers: ['code_128_reader']
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
const code = result.codeResult.code;
|
||||
// FNC1 characters appear as ASCII 29 (Group Separator)
|
||||
const GS = String.fromCharCode(29); // '\x1D'
|
||||
|
||||
// Split on Group Separator to get individual AI fields
|
||||
const fields = code.split(GS);
|
||||
console.log('Fields:', fields);
|
||||
// Example output: ["", "01034531200000111719050810ABCD1234", ...]
|
||||
|
||||
// Or check for GS1-128 format (starts with FNC1)
|
||||
if (code.startsWith(GS)) {
|
||||
console.log('This is a GS1-128 barcode');
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Parsing GS1-128 Data {#parsing-gs1-128-data}
|
||||
|
||||
Once decoded, you can parse the GS1-128 data using the Application Identifier structure:
|
||||
|
||||
```javascript
|
||||
function parseGS1(code) {
|
||||
const GS = String.fromCharCode(29);
|
||||
// Remove leading FNC1 if present
|
||||
const data = code.startsWith(GS) ? code.substring(1) : code;
|
||||
|
||||
// Split by FNC1 separator
|
||||
const segments = data.split(GS);
|
||||
|
||||
// Parse each segment for its AI
|
||||
// (A full implementation would use a complete AI table)
|
||||
return segments;
|
||||
}
|
||||
```
|
||||
|
||||
For full GS1 parsing, consider using a dedicated library like [gs1-barcode-parser](https://www.npmjs.com/package/gs1-barcode-parser) after decoding with Quagga2.
|
||||
|
||||
## EAN Extensions {#ean-extensions}
|
||||
|
||||
### EAN-2 and EAN-5 Supplements {#ean-supplements}
|
||||
|
||||
The EAN and UPC barcode formats support a supplement format, adding an additional 2 or 5 digits beyond the main barcode, EAN-2 and EAN-5, respectively. They are typically used for:
|
||||
- **Magazines and periodicals**: The main barcode identifies the publication, while the supplement denotes issue numbers or publication dates
|
||||
- **Books with ISBN**: The 5-digit supplement often encodes the suggested retail price
|
||||
|
||||
By default, `ean_reader` does not read and decode these extensions, you must explicitly enable support for them, if you are looking for them. Since UPC-A is a subset of EAN-13 -- UPC-A codes are EAN-13 codes that begin with a 0 -- supplement support configured on `ean_reader` also works for UPC-A codes.
|
||||
|
||||
To enable supplement decoding:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: [{
|
||||
format: "ean_reader",
|
||||
config: {
|
||||
supplements: [
|
||||
'ean_5_reader', // 5-digit supplement
|
||||
'ean_2_reader' // 2-digit supplement
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Supplement Result Structure {#supplement-result-structure}
|
||||
|
||||
When a barcode with a supplement is decoded, the result includes a `supplement` property:
|
||||
|
||||
```javascript
|
||||
{
|
||||
codeResult: {
|
||||
code: "419871600890101", // Combined: main barcode + supplement
|
||||
format: "ean_13", // Main barcode format
|
||||
supplement: {
|
||||
code: "01", // Supplement digits only
|
||||
format: "ean_2" // "ean_2" or "ean_5"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The main `codeResult.code` contains the full combined value, while `codeResult.supplement` provides the supplement details separately.
|
||||
|
||||
### Important Notes About Supplements {#supplements-notes}
|
||||
|
||||
**Supplement order matters**: The reader stops when it finds the first matching supplement. List `ean_5_reader` before `ean_2_reader` if you want to prioritize 5-digit extensions.
|
||||
|
||||
**Cannot read regular EAN-13 with supplements enabled**: If you configure supplements, that reader instance can **only** decode EAN codes **with** supplements. To read both:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: [
|
||||
"ean_reader", // Regular EAN-13 without supplements
|
||||
{
|
||||
format: "ean_reader",
|
||||
config: {
|
||||
supplements: ['ean_5_reader', 'ean_2_reader']
|
||||
}
|
||||
} // EAN-13 with supplements
|
||||
]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This configuration creates two separate reader instances.
|
||||
|
||||
## External Readers {#external-readers}
|
||||
|
||||
Quagga2 supports external reader modules for additional barcode formats not built into the core library.
|
||||
|
||||
### QR Code Reader {#qr-code-reader}
|
||||
|
||||
For QR code support, see [quagga2-reader-qr](https://github.com/ericblade/quagga2-reader-qr).
|
||||
|
||||
External readers extend Quagga2's capabilities beyond 1D barcodes:
|
||||
|
||||
```javascript
|
||||
import Quagga from '@ericblade/quagga2';
|
||||
import QRReader from 'quagga2-reader-qr';
|
||||
|
||||
// Register external reader
|
||||
Quagga.registerReader('qr', QRReader);
|
||||
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
readers: ['qr'] // Use external QR reader
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### External Reader Priority {#external-reader-priority}
|
||||
|
||||
External readers follow the **same priority rules** as built-in readers. Once registered with `Quagga.registerReader()`, an external reader can be placed anywhere in the `readers` array, and its position determines when it attempts to decode relative to other readers:
|
||||
|
||||
```javascript
|
||||
// Register external reader first
|
||||
Quagga.registerReader('my_custom_reader', MyCustomReader);
|
||||
|
||||
// Use in config - position determines priority
|
||||
Quagga.init({
|
||||
decoder: {
|
||||
// External reader tried first, then built-in readers
|
||||
readers: ['my_custom_reader', 'ean_reader', 'code_128_reader']
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
- External readers must be registered via `registerReader()` before use
|
||||
- Their position in `readers` array determines decode priority
|
||||
- There is no automatic "internal first, external second" ordering
|
||||
- External readers interleave freely with built-in readers
|
||||
|
||||
### Creating Custom Readers {#creating-custom-readers}
|
||||
|
||||
You can create your own barcode reader implementations by extending the `BarcodeReader` prototype exported by Quagga2. See [How-To: Create External Readers](../how-to-guides/external-readers.md) for details.
|
||||
|
||||
## Reader Performance {#reader-performance}
|
||||
|
||||
Different readers have different performance characteristics:
|
||||
|
||||
**Fastest readers**:
|
||||
|
||||
- `code_128_reader` - Optimized, widely used
|
||||
- `ean_reader` - Fast and reliable
|
||||
|
||||
**Slower readers**:
|
||||
|
||||
- `code_39_reader` - More complex pattern
|
||||
- `i2of5_reader` - Requires more validation
|
||||
|
||||
**Resource intensive**:
|
||||
|
||||
- Multiple readers enabled simultaneously
|
||||
- Readers with supplements configured
|
||||
|
||||
## Validation {#validation}
|
||||
|
||||
Some barcode formats include check digits for validation:
|
||||
|
||||
- **EAN-13/EAN-8**: Built-in check digit
|
||||
- **Code 128**: Built-in check digit
|
||||
- **UPC-A/UPC-E**: Built-in check digit
|
||||
|
||||
For additional validation in your application, consider using [barcode-validator](https://github.com/ericblade/barcode-validator).
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Configuration Reference](configuration.md) - Complete config options
|
||||
- [API Documentation](api.md) - How to use Quagga2 methods
|
||||
- [Tips & Tricks](../how-to-guides/tips-and-tricks.md) - Handling false positives
|
||||
|
||||
---
|
||||
|
||||
[← Back to Reference](index.md)
|
||||
141
quagga2/quagga2-1.12.1/docs/tutorials/first-scan.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Your First Barcode Scan {#first-scan}
|
||||
|
||||
This tutorial walks you through creating a simple barcode scanner using your device's camera.
|
||||
|
||||
## Prerequisites {#prerequisites}
|
||||
|
||||
- A web browser that supports camera access (Chrome, Firefox, Safari, Edge)
|
||||
- A device with a camera
|
||||
- Basic knowledge of HTML and JavaScript
|
||||
|
||||
## Step 1: Set Up the HTML {#step-1-html}
|
||||
|
||||
Create an `index.html` file:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Barcode Scanner</title>
|
||||
<style>
|
||||
#scanner-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
}
|
||||
#scanner-container video {
|
||||
width: 100%;
|
||||
}
|
||||
#result {
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Barcode Scanner</h1>
|
||||
<div id="scanner-container"></div>
|
||||
<div id="result">Scan a barcode...</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Step 2: Initialize Quagga {#step-2-initialize}
|
||||
|
||||
Create an `app.js` file:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
target: document.querySelector('#scanner-container'),
|
||||
constraints: {
|
||||
facingMode: "environment" // Use back camera
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader", "upc_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error("Failed to initialize:", err);
|
||||
document.querySelector('#result').textContent = "Error: " + err.message;
|
||||
return;
|
||||
}
|
||||
console.log("Scanner ready");
|
||||
Quagga.start();
|
||||
});
|
||||
```
|
||||
|
||||
## Step 3: Handle Detections {#step-3-handle-detections}
|
||||
|
||||
Add detection handling to `app.js`:
|
||||
|
||||
```javascript
|
||||
Quagga.onDetected(function(result) {
|
||||
const code = result.codeResult.code;
|
||||
const format = result.codeResult.format;
|
||||
|
||||
document.querySelector('#result').textContent =
|
||||
`Found ${format}: ${code}`;
|
||||
|
||||
console.log("Barcode detected:", code);
|
||||
});
|
||||
```
|
||||
|
||||
## Step 4: Run It {#step-4-run}
|
||||
|
||||
1. Serve the files using a local web server (required for camera access)
|
||||
2. Open the page in your browser
|
||||
3. Allow camera access when prompted
|
||||
4. Point the camera at a barcode
|
||||
|
||||
**Note**: Camera access requires HTTPS on non-localhost domains.
|
||||
|
||||
## Complete Code {#complete-code}
|
||||
|
||||
Here's the complete `app.js`:
|
||||
|
||||
```javascript
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
target: document.querySelector('#scanner-container'),
|
||||
constraints: {
|
||||
facingMode: "environment"
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader", "upc_reader"]
|
||||
}
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
console.error("Failed to initialize:", err);
|
||||
document.querySelector('#result').textContent = "Error: " + err.message;
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
Quagga.onDetected(function(result) {
|
||||
const code = result.codeResult.code;
|
||||
const format = result.codeResult.format;
|
||||
document.querySelector('#result').textContent = `Found ${format}: ${code}`;
|
||||
});
|
||||
```
|
||||
|
||||
## Next Steps {#next-steps}
|
||||
|
||||
- [Static Image Scanning](static-image.md) - Decode images without camera
|
||||
- [Configuration Reference](../reference/configuration.md) - Customize behavior
|
||||
- [Tips and Tricks](../how-to-guides/tips-and-tricks.md) - Improve results
|
||||
|
||||
---
|
||||
|
||||
[← Back to Tutorials](index.md)
|
||||
77
quagga2/quagga2-1.12.1/docs/tutorials/index.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Tutorials
|
||||
|
||||
Step-by-step tutorials to help you learn Quagga2 by building real examples. These are learning-oriented guides designed to help beginners get hands-on experience.
|
||||
|
||||
## Available Tutorials
|
||||
|
||||
### [Your First Barcode Scan](first-scan.md) ⭐ Start Here
|
||||
Build a complete barcode scanner web app from scratch. You'll learn how to:
|
||||
- Set up HTML structure
|
||||
- Initialize Quagga2
|
||||
- Handle camera permissions
|
||||
- Process detected barcodes
|
||||
- Display results to users
|
||||
|
||||
**Time**: 15 minutes | **Difficulty**: Beginner
|
||||
|
||||
### [Decoding Static Images](static-image.md)
|
||||
Learn to decode barcodes from image files instead of camera feeds. Perfect for:
|
||||
- Photo upload features
|
||||
- Batch processing
|
||||
- Server-side Node.js applications
|
||||
- Testing without a camera
|
||||
|
||||
**Time**: 10 minutes | **Difficulty**: Beginner
|
||||
|
||||
### [Using with React](react-integration.md)
|
||||
Integrate Quagga2 into a React application. Covers:
|
||||
- Component lifecycle management
|
||||
- Handling cleanup
|
||||
- State management
|
||||
- Common pitfalls and solutions
|
||||
|
||||
**Time**: 20 minutes | **Difficulty**: Intermediate
|
||||
|
||||
### [Using with Node.js](node-usage.md)
|
||||
Use Quagga2 in server-side Node.js applications. Learn to:
|
||||
- Process images from filesystem
|
||||
- Handle image buffers
|
||||
- Batch decode multiple images
|
||||
- Build CLI tools
|
||||
|
||||
**Time**: 15 minutes | **Difficulty**: Intermediate
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting these tutorials, you should have:
|
||||
|
||||
- Basic HTML/JavaScript knowledge
|
||||
- A text editor
|
||||
- A web server (or ability to run `npx serve`)
|
||||
- A device with a camera (for live scanning tutorials)
|
||||
|
||||
## Tutorial Approach
|
||||
|
||||
These tutorials follow a **learning-by-doing** approach:
|
||||
|
||||
1. **Complete examples** - Each tutorial provides full working code
|
||||
2. **Progressive complexity** - Start simple, add features incrementally
|
||||
3. **Hands-on practice** - Type the code yourself to build muscle memory
|
||||
4. **Real-world scenarios** - Build things you'll actually use
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you get stuck:
|
||||
|
||||
- Check the [API Reference](../reference/api.md) for method details
|
||||
- Review the [Configuration Guide](../reference/configuration.md)
|
||||
- Ask in [Gitter Chat](https://gitter.im/quaggaJS/Lobby)
|
||||
- Search [GitHub Issues](https://github.com/ericblade/quagga2/issues)
|
||||
|
||||
## Contributing
|
||||
|
||||
Found a bug in a tutorial or have an idea for a new one? [Open an issue](https://github.com/ericblade/quagga2/issues) or submit a pull request!
|
||||
|
||||
---
|
||||
|
||||
**Next**: Start with [Your First Barcode Scan](first-scan.md) →
|
||||
182
quagga2/quagga2-1.12.1/docs/tutorials/node-usage.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Using Quagga2 in Node.js {#node-usage}
|
||||
|
||||
This tutorial covers server-side barcode scanning with Quagga2 in Node.js.
|
||||
|
||||
## Installation {#installation}
|
||||
|
||||
```bash
|
||||
npm install @ericblade/quagga2
|
||||
```
|
||||
|
||||
## Basic Usage {#basic-usage}
|
||||
|
||||
```javascript
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
|
||||
Quagga.decodeSingle({
|
||||
src: './barcode.jpg',
|
||||
decoder: {
|
||||
readers: ['code_128_reader', 'ean_reader']
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log('Barcode:', result.codeResult.code);
|
||||
console.log('Format:', result.codeResult.format);
|
||||
} else {
|
||||
console.log('No barcode found');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## With Promises {#with-promises}
|
||||
|
||||
Wrap in a Promise for async/await:
|
||||
|
||||
```javascript
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
|
||||
function decodeBarcode(imagePath, readers = ['code_128_reader']) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Quagga.decodeSingle({
|
||||
src: imagePath,
|
||||
decoder: { readers }
|
||||
}, (result) => {
|
||||
if (result && result.codeResult) {
|
||||
resolve(result.codeResult);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const result = await decodeBarcode('./barcode.jpg');
|
||||
if (result) {
|
||||
console.log(`Found ${result.format}: ${result.code}`);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
## Express API Example {#express-example}
|
||||
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const multer = require('multer');
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
|
||||
const app = express();
|
||||
const upload = multer({ dest: 'uploads/' });
|
||||
|
||||
app.post('/scan', upload.single('image'), (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: 'No image provided' });
|
||||
}
|
||||
|
||||
Quagga.decodeSingle({
|
||||
src: req.file.path,
|
||||
decoder: {
|
||||
readers: ['code_128_reader', 'ean_reader', 'upc_reader']
|
||||
}
|
||||
}, (result) => {
|
||||
if (result && result.codeResult) {
|
||||
res.json({
|
||||
code: result.codeResult.code,
|
||||
format: result.codeResult.format
|
||||
});
|
||||
} else {
|
||||
res.json({ code: null, error: 'No barcode found' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Barcode API running on port 3000');
|
||||
});
|
||||
```
|
||||
|
||||
## Batch Processing {#batch-processing}
|
||||
|
||||
Process multiple images:
|
||||
|
||||
```javascript
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
async function decodeImage(imagePath) {
|
||||
return new Promise((resolve) => {
|
||||
Quagga.decodeSingle({
|
||||
src: imagePath,
|
||||
decoder: { readers: ['code_128_reader', 'ean_reader'] }
|
||||
}, (result) => {
|
||||
resolve({
|
||||
file: path.basename(imagePath),
|
||||
code: result?.codeResult?.code || null,
|
||||
format: result?.codeResult?.format || null
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function processDirectory(dir) {
|
||||
const files = fs.readdirSync(dir)
|
||||
.filter(f => /\.(jpg|jpeg|png)$/i.test(f));
|
||||
|
||||
const results = [];
|
||||
for (const file of files) {
|
||||
const result = await decodeImage(path.join(dir, file));
|
||||
results.push(result);
|
||||
console.log(`${result.file}: ${result.code || 'No barcode'}`);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
processDirectory('./images');
|
||||
```
|
||||
|
||||
## Configuration Tips {#configuration-tips}
|
||||
|
||||
### Image Size {#image-size}
|
||||
|
||||
Control processing resolution:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: './large-image.jpg',
|
||||
inputStream: {
|
||||
size: 1280 // Scale to max 1280px
|
||||
},
|
||||
decoder: { readers: ['ean_reader'] }
|
||||
}, callback);
|
||||
```
|
||||
|
||||
### Locator Settings {#locator-settings}
|
||||
|
||||
For difficult images:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: './image.jpg',
|
||||
locate: true,
|
||||
locator: {
|
||||
patchSize: 'small',
|
||||
halfSample: false
|
||||
},
|
||||
decoder: { readers: ['code_128_reader'] }
|
||||
}, callback);
|
||||
```
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Static Image Scanning](static-image.md) - Browser-side image decoding
|
||||
- [API Reference](../reference/api.md#quagga-decodesingle) - `decodeSingle()` details
|
||||
- [Configuration Reference](../reference/configuration.md) - All options
|
||||
|
||||
---
|
||||
|
||||
[← Back to Tutorials](index.md)
|
||||
176
quagga2/quagga2-1.12.1/docs/tutorials/react-integration.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Using Quagga2 with React {#react-integration}
|
||||
|
||||
This tutorial shows how to integrate Quagga2 into a React application.
|
||||
|
||||
## Installation {#installation}
|
||||
|
||||
```bash
|
||||
npm install @ericblade/quagga2
|
||||
```
|
||||
|
||||
## Basic Component {#basic-component}
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useRef, useCallback } from 'react';
|
||||
import Quagga from '@ericblade/quagga2';
|
||||
|
||||
function BarcodeScanner({ onDetected }) {
|
||||
const scannerRef = useRef(null);
|
||||
|
||||
const handleDetected = useCallback((result) => {
|
||||
if (result.codeResult) {
|
||||
onDetected(result.codeResult.code);
|
||||
}
|
||||
}, [onDetected]);
|
||||
|
||||
useEffect(() => {
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: 'LiveStream',
|
||||
target: scannerRef.current,
|
||||
constraints: {
|
||||
facingMode: 'environment'
|
||||
}
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_128_reader', 'ean_reader']
|
||||
}
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.error('Failed to initialize:', err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
Quagga.onDetected(handleDetected);
|
||||
|
||||
return () => {
|
||||
Quagga.offDetected(handleDetected);
|
||||
Quagga.stop();
|
||||
};
|
||||
}, [handleDetected]);
|
||||
|
||||
return <div ref={scannerRef} style={{ width: '100%' }} />;
|
||||
}
|
||||
|
||||
export default BarcodeScanner;
|
||||
```
|
||||
|
||||
## Usage {#usage}
|
||||
|
||||
```jsx
|
||||
import BarcodeScanner from './BarcodeScanner';
|
||||
|
||||
function App() {
|
||||
const handleScan = (code) => {
|
||||
console.log('Scanned:', code);
|
||||
alert(`Barcode: ${code}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Scan a Barcode</h1>
|
||||
<BarcodeScanner onDetected={handleScan} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## With Hooks {#with-hooks}
|
||||
|
||||
Create a reusable hook:
|
||||
|
||||
```jsx
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import Quagga from '@ericblade/quagga2';
|
||||
|
||||
export function useQuagga(config = {}) {
|
||||
const scannerRef = useRef(null);
|
||||
const [scanning, setScanning] = useState(false);
|
||||
const [result, setResult] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!scannerRef.current) return;
|
||||
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: 'LiveStream',
|
||||
target: scannerRef.current,
|
||||
...config.inputStream
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_128_reader'],
|
||||
...config.decoder
|
||||
},
|
||||
...config
|
||||
}, (err) => {
|
||||
if (!err) {
|
||||
Quagga.start();
|
||||
setScanning(true);
|
||||
}
|
||||
});
|
||||
|
||||
Quagga.onDetected((data) => {
|
||||
setResult(data.codeResult);
|
||||
});
|
||||
|
||||
return () => {
|
||||
Quagga.stop();
|
||||
setScanning(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { scannerRef, scanning, result };
|
||||
}
|
||||
```
|
||||
|
||||
## Stop on Detection {#stop-on-detection}
|
||||
|
||||
```jsx
|
||||
function SingleScanComponent({ onComplete }) {
|
||||
const scannerRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
type: 'LiveStream',
|
||||
target: scannerRef.current
|
||||
},
|
||||
decoder: {
|
||||
readers: ['ean_reader']
|
||||
}
|
||||
}, (err) => {
|
||||
if (!err) Quagga.start();
|
||||
});
|
||||
|
||||
const handleDetected = (result) => {
|
||||
Quagga.stop();
|
||||
onComplete(result.codeResult.code);
|
||||
};
|
||||
|
||||
Quagga.onDetected(handleDetected);
|
||||
|
||||
return () => {
|
||||
Quagga.offDetected(handleDetected);
|
||||
Quagga.stop();
|
||||
};
|
||||
}, [onComplete]);
|
||||
|
||||
return <div ref={scannerRef} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Resources {#resources}
|
||||
|
||||
- [quagga2-react-example](https://github.com/ericblade/quagga2-react-example/) - Complete example
|
||||
- [quagga2-redux-middleware](https://github.com/ericblade/quagga2-redux-middleware/) - Redux integration
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [API Reference](../reference/api.md) - Full API documentation
|
||||
- [Configuration Reference](../reference/configuration.md) - All options
|
||||
|
||||
---
|
||||
|
||||
[← Back to Tutorials](index.md)
|
||||
130
quagga2/quagga2-1.12.1/docs/tutorials/static-image.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Decoding Static Images {#static-image}
|
||||
|
||||
This tutorial shows how to decode barcodes from image files instead of a live camera feed.
|
||||
|
||||
## Basic Usage {#basic-usage}
|
||||
|
||||
Use `Quagga.decodeSingle()` to decode a single image:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: '/path/to/barcode.jpg',
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader"]
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log("Barcode:", result.codeResult.code);
|
||||
} else {
|
||||
console.log("No barcode found");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## From File Input {#from-file-input}
|
||||
|
||||
Allow users to upload images:
|
||||
|
||||
### HTML
|
||||
|
||||
```html
|
||||
<input type="file" id="file-input" accept="image/*">
|
||||
<div id="result"></div>
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
```javascript
|
||||
document.querySelector('#file-input').addEventListener('change', function(e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
Quagga.decodeSingle({
|
||||
src: event.target.result,
|
||||
decoder: {
|
||||
readers: ["code_128_reader", "ean_reader", "upc_reader"]
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
document.querySelector('#result').textContent =
|
||||
"Found: " + result.codeResult.code;
|
||||
} else {
|
||||
document.querySelector('#result').textContent = "No barcode found";
|
||||
}
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
```
|
||||
|
||||
## Controlling Image Size {#controlling-image-size}
|
||||
|
||||
By default, `decodeSingle()` scales images to 800px. Adjust with `inputStream.size`:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: '/path/to/image.jpg',
|
||||
inputStream: {
|
||||
size: 1280 // Process at higher resolution
|
||||
},
|
||||
decoder: {
|
||||
readers: ["ean_reader"]
|
||||
}
|
||||
}, callback);
|
||||
```
|
||||
|
||||
Set `size: 0` to use original image dimensions.
|
||||
|
||||
## With Localization {#with-localization}
|
||||
|
||||
Enable `locate: true` (default) to find barcodes anywhere in the image:
|
||||
|
||||
```javascript
|
||||
Quagga.decodeSingle({
|
||||
src: '/path/to/image.jpg',
|
||||
locate: true,
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
halfSample: true
|
||||
},
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log("Found:", result.codeResult.code);
|
||||
console.log("Location:", result.box); // Bounding box
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Node.js Usage {#nodejs-usage}
|
||||
|
||||
Quagga2 works in Node.js:
|
||||
|
||||
```javascript
|
||||
const Quagga = require('@ericblade/quagga2').default;
|
||||
|
||||
Quagga.decodeSingle({
|
||||
src: './barcode.jpg',
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
}
|
||||
}, function(result) {
|
||||
if (result && result.codeResult) {
|
||||
console.log("Barcode:", result.codeResult.code);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Related {#related}
|
||||
|
||||
- [Your First Scan](first-scan.md) - Live camera scanning
|
||||
- [Node.js Usage](node-usage.md) - Server-side scanning
|
||||
- [API Reference](../reference/api.md#quagga-decodesingle) - `decodeSingle()` details
|
||||
|
||||
---
|
||||
|
||||
[← Back to Tutorials](index.md)
|
||||