Barcode-Adapter nimmt Codearten entgegen, Adapter-Weiche je Funktion

decodeBarcodes(imageData, formats) bekommt die zu suchenden Codearten
uebergeben; ohne Angabe unveraendertes Verhalten (Code128 + DataMatrix).

buildRecognitionAdapters() baut daraus und aus dem echten OCR-Adapter
die an recognize() (pipeline.js, unveraendert) uebergebenen Adapter:
in Strichcode/QR-Code liefert die Texterkennung sofort leeren Text ohne
Tesseract anzustossen, in Text-erkennen liefert die Barcode-Dekodierung
sofort eine leere Liste ohne zxing-wasm anzustossen.
This commit is contained in:
2026-07-29 13:37:05 +02:00
parent 9ef038ca98
commit 2eb9d1ecf8
3 changed files with 124 additions and 3 deletions
+11 -3
View File
@@ -25,8 +25,14 @@ async function ensureReady() {
await readyPromise;
}
// Bisheriges Verhalten (Code-128 und DataMatrix), falls der Aufrufer keine
// eigene Liste von Codearten angibt - main.js reicht seit den Scan-Funktionen
// (siehe scan-modes.js) stets eine eigene Liste durch, aber ohne Angabe
// bleibt der Adapter unveraendert nutzbar (z. B. fuer kuenftige Aufrufer).
const DEFAULT_FORMATS = ['Code128', 'DataMatrix'];
/**
* Dekodiert Code-128 und DataMatrix aus einem Einzelbild.
* Dekodiert Barcodes aus einem Einzelbild.
* Barcode-Dekodierung ist exakt - deshalb ist sie die bevorzugte Quelle
* gegenueber der fehleranfaelligen Texterkennung.
*
@@ -35,12 +41,14 @@ async function ensureReady() {
* Ausgang und kein Fehler.
*
* @param {ImageData} imageData Einzelbild aus der Kamera.
* @param {string[]} [formats] Zu suchende Codearten (zxing-wasm-Formatnamen,
* siehe scan-modes.js). Ohne Angabe wie bisher: Code128 und DataMatrix.
* @returns {Promise<string[]>}
*/
export async function decodeBarcodes(imageData) {
export async function decodeBarcodes(imageData, formats = DEFAULT_FORMATS) {
await ensureReady();
const results = await readBarcodes(imageData, {
formats: ['Code128', 'DataMatrix'],
formats,
tryHarder: true,
maxNumberOfSymbols: 4,
});
+34
View File
@@ -0,0 +1,34 @@
// Baut die Adapter fuer recognize() (pipeline.js) anhand der gewaehlten
// Scan-Funktion (siehe scan-modes.js). Reines Modul ohne Browser-Zugriff -
// direkt unter Node pruefbar. pipeline.js selbst bleibt unveraendert: die
// Funktionen unterscheiden sich ausschliesslich darin, welche Adapter
// hineingereicht werden.
//
// Funktionen 1/2 (laufende Codesuche): echte Barcode-Dekodierung mit den zur
// Funktion gehoerenden Codearten; die Texterkennung liefert sofort leeren
// Text, ohne dass runOcr ueberhaupt aufgerufen wird.
// Funktion 3 (Text erkennen): echte Texterkennung; die Barcode-Dekodierung
// liefert sofort eine leere Liste, ohne dass decodeBarcodes ueberhaupt
// aufgerufen wird.
import { getScanMode } from './scan-modes.js';
/**
* @param {string} modeId Kennung aus scan-modes.js.
* @param {{decodeBarcodes: Function, runOcr: Function}} adapters echte,
* browser-gebundene Adapter (aus barcode.js / ocr.js).
* @returns {{decodeBarcodes: Function, runOcr: Function}} an recognize()
* durchzureichende Adapter, passend zur gewaehlten Funktion.
*/
export function buildRecognitionAdapters(modeId, adapters) {
const mode = getScanMode(modeId);
return {
decodeBarcodes: mode.useOcr
? async () => []
: (imageData) => adapters.decodeBarcodes(imageData, mode.barcodeFormats),
runOcr: mode.useOcr
? adapters.runOcr
: async () => '',
};
}
+79
View File
@@ -0,0 +1,79 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { buildRecognitionAdapters } from '../src/scan-recognition.js';
import { recognize } from '../src/pipeline.js';
test('Funktion 1: Texterkennung wird nicht aufgerufen, auch wenn kein Code gefunden wird', async () => {
let ocrCalled = false;
const adapters = buildRecognitionAdapters('barcode', {
decodeBarcodes: async () => [],
runOcr: async () => { ocrCalled = true; return 'irgendein Text'; },
});
const result = await recognize({}, adapters);
assert.equal(ocrCalled, false, 'runOcr darf in Funktion 1 nie aufgerufen werden');
assert.equal(result.confidence, 'red');
});
test('Funktion 2: Texterkennung wird nicht aufgerufen, auch wenn kein Code gefunden wird', async () => {
let ocrCalled = false;
const adapters = buildRecognitionAdapters('qrcode', {
decodeBarcodes: async () => [],
runOcr: async () => { ocrCalled = true; return 'irgendein Text'; },
});
const result = await recognize({}, adapters);
assert.equal(ocrCalled, false, 'runOcr darf in Funktion 2 nie aufgerufen werden');
assert.equal(result.confidence, 'red');
});
test('Funktion 3: Barcode-Dekodierung wird nicht aufgerufen', async () => {
let barcodeCalled = false;
const adapters = buildRecognitionAdapters('ocr', {
decodeBarcodes: async () => { barcodeCalled = true; return []; },
runOcr: async () => '64GB 4DRx4 PC4-2400T-LD1-11-MC0 M386A8K40BM1-CRC4Y 1908',
});
const result = await recognize({}, adapters);
assert.equal(barcodeCalled, false, 'decodeBarcodes darf in Funktion 3 nie aufgerufen werden');
assert.equal(result.source, 'ocr');
assert.equal(result.confidence, 'yellow');
});
test('Funktion 1: ein gelesener Strichcode fuehrt zu Gruen', async () => {
const adapters = buildRecognitionAdapters('barcode', {
decodeBarcodes: async () => ['M386A8K40BM1-CRC4Y'],
runOcr: async () => '',
});
const result = await recognize({}, adapters);
assert.equal(result.source, 'barcode');
assert.equal(result.confidence, 'green');
});
test('Funktion 3: kein brauchbarer Text fuehrt zu Rot, nicht zu Gruen', async () => {
const adapters = buildRecognitionAdapters('ocr', {
decodeBarcodes: async () => { throw new Error('darf nicht aufgerufen werden'); },
runOcr: async () => 'Made in Philippines',
});
const result = await recognize({}, adapters);
assert.equal(result.source, 'none');
assert.equal(result.confidence, 'red');
});
test('decodeBarcodes bekommt die Codearten der jeweiligen Funktion uebergeben', async () => {
let receivedFormats = null;
const adapters = buildRecognitionAdapters('qrcode', {
decodeBarcodes: async (_imageData, formats) => { receivedFormats = formats; return []; },
runOcr: async () => '',
});
await adapters.decodeBarcodes({});
assert.deepEqual(receivedFormats, ['QRCode', 'MicroQRCode', 'RMQRCode']);
});
test('Funktion 3 uebergibt keine Codearten, weil decodeBarcodes gar nicht aufgerufen wird', async () => {
let called = false;
const adapters = buildRecognitionAdapters('ocr', {
decodeBarcodes: async () => { called = true; return []; },
runOcr: async () => '',
});
const codes = await adapters.decodeBarcodes({});
assert.deepEqual(codes, []);
assert.equal(called, false);
});