Fix pipeline: robust gegen unerwartete Adapter-Rueckgaben und mehrdeutige Barcodes
recognize() stuerzte ab, wenn decodeBarcodes() etwas anderes als eine Liste zurueckgab (null/undefined/Objekt), da die for...of-Schleife nicht iterierbare Werte nicht abfaengt. Nicht-Listen werden nun wie eine leere Liste behandelt, ebenso wird eine nicht-string-wertige runOcr()-Rueckgabe wie leerer Text behandelt. Ausserdem gewann bei mehreren verwertbaren Barcodes im selben Bild bisher ungeprueft der erste Treffer mit gruener Ampel. Jetzt werden alle verwertbaren Codes gesammelt: genau einer bleibt gruen wie bisher, mehrere untereinander vertraegliche werden zusammengefuehrt (spaeterer Treffer ueberschreibt kein bereits gesetztes Feld) und bleiben gruen, mehrere unvertraegliche ergeben rot mit leerem Spec statt eines geratenen Ergebnisses. Korrigiert ausserdem den irrefuehrenden Warnkommentar in pn-tables.js: ein falscher Tabelleneintrag faellt auf dem Barcode-Weg nicht automatisch auf, da dort keine Texterkennung zum Abgleich laeuft. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+46
-4
@@ -1,4 +1,4 @@
|
||||
import { emptySpec, isUsable } from './spec.js';
|
||||
import { emptySpec, isUsable, specsCompatible } from './spec.js';
|
||||
import { decodePartNumber } from './pn-decoder.js';
|
||||
import { extractFields } from './ocr-extract.js';
|
||||
|
||||
@@ -10,24 +10,66 @@ async function safely(fn, fallback) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuehrt mehrere untereinander vertraegliche Specs zusammen: ein spaeterer
|
||||
* Treffer fuellt nur Felder, die noch offen sind, ueberschreibt aber nie
|
||||
* ein bereits gesetztes Feld.
|
||||
*/
|
||||
function mergeCompatibleSpecs(specs) {
|
||||
const merged = { ...specs[0] };
|
||||
for (let i = 1; i < specs.length; i++) {
|
||||
for (const field of Object.keys(merged)) {
|
||||
if (merged[field] === null && specs[i][field] !== null) {
|
||||
merged[field] = specs[i][field];
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/** Prueft, ob alle Specs paarweise vertraeglich sind. */
|
||||
function allCompatible(specs) {
|
||||
for (let i = 0; i < specs.length; i++) {
|
||||
for (let j = i + 1; j < specs.length; j++) {
|
||||
if (!specsCompatible(specs[i], specs[j])) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Barcode zuerst, OCR nur als Rueckfallebene.
|
||||
* @returns {Promise<{spec: object, source: 'barcode'|'ocr'|'none', confidence: 'green'|'yellow'|'red'}>}
|
||||
*/
|
||||
export async function recognize(frame, deps) {
|
||||
const codes = await safely(() => deps.decodeBarcodes(frame), []);
|
||||
const decodedCodes = await safely(() => deps.decodeBarcodes(frame), []);
|
||||
const codes = Array.isArray(decodedCodes) ? decodedCodes : [];
|
||||
|
||||
let best = emptySpec();
|
||||
const usable = [];
|
||||
for (const code of codes) {
|
||||
const decoded = decodePartNumber(code);
|
||||
if (isUsable(decoded)) {
|
||||
return { spec: decoded, source: 'barcode', confidence: 'green' };
|
||||
usable.push(decoded);
|
||||
continue;
|
||||
}
|
||||
// Teilenummer merken, auch wenn das Schema unbekannt ist.
|
||||
if (best.partNumber === null) best = decoded;
|
||||
}
|
||||
|
||||
const text = await safely(() => deps.runOcr(frame), '');
|
||||
if (usable.length === 1) {
|
||||
return { spec: usable[0], source: 'barcode', confidence: 'green' };
|
||||
}
|
||||
if (usable.length > 1) {
|
||||
if (allCompatible(usable)) {
|
||||
return { spec: mergeCompatibleSpecs(usable), source: 'barcode', confidence: 'green' };
|
||||
}
|
||||
// Mehrdeutigkeit zwischen verwertbaren Barcodes: nicht raten, Nutzer entscheidet.
|
||||
return { spec: emptySpec(), source: 'none', confidence: 'red' };
|
||||
}
|
||||
|
||||
const ocrResult = await safely(() => deps.runOcr(frame), '');
|
||||
const text = typeof ocrResult === 'string' ? ocrResult : '';
|
||||
const fromOcr = extractFields(text);
|
||||
|
||||
// Der Barcode ist die exaktere Quelle: seine Teilenummer gewinnt.
|
||||
|
||||
Reference in New Issue
Block a user