API Redesign
See original GitHub issueAs most of you have already noticed, the API of Quagga is neither intuitive nor easy to use. Within this issue I would like you to discuss with me ideas for a new API. The new design should be readable, easy to use and extensible for features to come.
I’m very interested in your suggestions and ideas, so please help me make this API better for everyone. Tell me your current pain-points and let’s discuss them in this thread. Based on your feedback and my time available, I’ll push a draft of the API in the coming days. Feel free to collaborate.
Requirements
- Work with instances rather than singletons
- Event based, in preparation for reactive streams
- Easy to use with RxJS
- Prefer promises over callbacks
- creating scanners using images or video in a consistent way
Proposal
As it is currently under implementation
Creating a default video-based scanner
Creates a scanner with default settings and the user’s camera:
Quagga
.fromVideo({constraints: {facingMode: "environment"}})
.addEventListener('detected', (result) => (console.log(result)));
Creating a default image-based scanner
Creates a scanner that is suitable for static images
Quagga
.fromImage('../test/fixtures/code_39/image-001.jpg', {size: 800})
.addEventListener('processed', (result) => (console.log(result)));
When scanning images, the consumer either expects a result, or an error (no result). Hence, returning a Promise would be handy:
Quagga
.fromImage('../test/fixtures/code_39/image-001.jpg', {size: 800})
.toPromise()
.then((result) => {
console.log(result);
}).catch((result) => {
console.log("not found!");
});
Passing configuration
In most cases, the default configuration is not sufficient and can be adjusted prior to creation:
const customScanner = Quagga
.decoder({readers: ['ean_reader']})
.locator({patchSize: 'medium'});
or pass in the entire configuration at once:
const customScanner = Quagga
.config({decoder: {readers: ['ean_reader']}, locator: {patchSize: 'medium'}});
Starting/Stopping
The general idea is, that everything should be as lazy as possible.
Consumer based
Meaning that the scanner should not start automatically without an event-listener attached. Therefore the processing is started/paused based on the state of the event-listeners. As long as there is a consumer attached, the scanner is active. It should automatically stop/pause when the last consumer is detached via removeEventListener(eventName, callback)
.
Manually
Another approach would be to start/stop the scanner manually by calling the appropriate methods provided.
RxJS
One reason for redesigning the whole API is the desired move towards a more reactive implementation. With RxJS one could simply write the following code to scan Code 128 and EAN-13 barcodes and react only to the results of interest:
// creating a stream listening on the "processed" events
const processStream = Quagga
.decoder({readers: ['ean_reader', 'code_128_reader']})
.fromVideo({constraints: {facingMode: "environment"}})
.observe('processed');
// create a stream only containing the label and format of a detected barcode
const detectedCodes = processStream
.filter(result => result && result.codeResult && result.codeResult.code)
.map(result => ({label: result.codeResult.code, format: result.codeResult.format});
// create a stream containing all boxes which are detected during scanning
const detecedBoxes = processStream
.filter(result => result.boxes && result.boxes.length > 0)
.map(result => result.boxes);
After creating these streams, one can listen to them:
detectedCodes.subscribe(code => console.log(code));
detecedBoxes.subscribe(boxes => draw(boxes));
Issue Analytics
- State:
- Created 7 years ago
- Reactions:5
- Comments:14 (4 by maintainers)
Top GitHub Comments
It seems @JauernigIT and I have a similar interest in getting the processed image but it seems the only way to use Quagga to get at it right now is using the
ResultCollector
to intercept the image data before it is massaged to publish as an event forQuagga.onDetected
. Have you considered changing the API for the results? Would there be a performance hit if the imageData was set to theonDetected
callback and not consumed?FYI @JauernigIT, I’ve replaced using
Quagga.onDetected
withQuagga.registerResultCollector({ addResult: (imageData, canvasSize, resultCode) => {...} })
.We have an app that needs to scan barcodes from 2 different devices in order to associate them in the database. I’m sure multiple quaggas is a rare use case.
We got it working with a wizard-style single form, and I was able to detach the event listeners for quagga and reattach when the user moves to the next step. I got the idea from one of your examples.