From 87ba4392874cdc99e8d1b6d8008f2d7b59251274 Mon Sep 17 00:00:00 2001 From: TanerUslu Date: Wed, 29 Jul 2026 15:19:34 +0200 Subject: [PATCH] Doku: voller OCR-Rohtext in "Text erkennen" (Rahmen, Whitelist, Anzeige) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README auf den aktuellen Stand gebracht: Zielrahmen ist jetzt auch in "Text erkennen" wirksam (grabFrameRegion statt grabFrame), breitere Zeichen- Whitelist samt Abwägung, additiver OCR-Rohtext (rawText) in Pipeline, Speicherung und Anzeige, wartende statt automatisch ausblendende Rückmeldung in "Text erkennen". Datei-Tabelle, Abschnitt "Sitzungssicherung im Detail", "Grenzen" und die manuelle Geräte-Prüfliste entsprechend ergänzt; Testanzahl 144 → 154. --- README.md | 129 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 3453a11..4e4c14c 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,14 @@ keine Daten verlassen das Gerät. „QR-Code" sucht die App ohnehin laufend, ein Knopf ohne Texterkennung hätte dort nichts zu tun. Der Zielrahmen hebt sich in „Strichcode" und „QR-Code" hervor, sobald ein passender Code im Bild ist; in „Text erkennen" bleibt er - schlicht, weil dort nichts laufend erkannt wird. Ein Wechsel der Funktion - während des Sortierens (z. B. bei gemischter Ware) gilt sofort: laufende - Suche startet oder stoppt, der Knopf erscheint oder verschwindet. + schlicht, weil dort nichts laufend erkannt wird. Trotzdem ist der Rahmen + auch dort wirksam: „Modul scannen" liest genau den Bildausschnitt innerhalb + des Rahmens, in voller Kameraauflösung, nicht mehr das gesamte, + heruntergerechnete Kamerabild (`grabFrameRegion` in `src/camera.js` — + derselbe Ausschnitt, den auch die laufende Barcode-Suche benutzt). Ein + Wechsel der Funktion während des Sortierens (z. B. bei gemischter Ware) + gilt sofort: laufende Suche startet oder stoppt, der Knopf erscheint oder + verschwindet. Liegt beim Start eine gesicherte Sitzung vor, erscheint zuerst die Frage nach dem Fortsetzen (siehe Punkt 5 unten) und erst danach die Funktionswahl @@ -123,20 +128,41 @@ keine Daten verlassen das Gerät. 3. **Texterkennung** (ausschließlich in „Text erkennen", ausgelöst durch „Modul scannen"). Anders als früher ist das kein automatischer Rückfall vom Barcode-Weg mehr, sondern die einzige Quelle dieser Funktion — sie - liest ohnehin nie einen Barcode (siehe Punkt 0). Das Kamerabild wird dafür + liest ohnehin nie einen Barcode (siehe Punkt 0). Gelesen wird der + Bildausschnitt innerhalb des Zielrahmens, in voller Kameraauflösung + (`grabFrameRegion`, siehe Punkt 0) — nicht mehr das ganze, auf 1280 Pixel + heruntergerechnete Kamerabild. Das Bild wird dafür in Graustufen gewandelt und mit einem **Otsu-Schwellwert** in Schwarz/Weiß aufbereitet: Der Schwellwert wird aus der Helligkeitsverteilung des gesamten Bildes bestimmt, nicht aus einer festen Kontrastspreizung zwischen hellstem und dunkelstem Pixel. Das macht die Aufbereitung robust gegen einzelne Lichtreflexe auf glänzenden Metalletiketten, die eine reine - Min/Max-Spreizung leicht kippen würden. Das OCR-Ergebnis wird anschließend - gegen die bekannten Spec-Werte abgeglichen, was die typischen - Verwechslungen (0/O, 1/I, 8/B, …) auflöst. Da in dieser Funktion nie eine - per Barcode gelesene Teilenummer vorliegt, entscheiden hier ausschließlich - die aus dem erkannten Text abgeleiteten technischen Angaben (Kapazität, - Bauform, Rank, Geschwindigkeit) über die Stapelzuordnung - (`specsCompatible` in `src/spec.js`) — anders als in „Strichcode"/ - „QR-Code", wo meist die exakte Teilenummer trägt. + Min/Max-Spreizung leicht kippen würden. Die Zeichen-Whitelist von + Tesseract (`tessedit_char_whitelist` in `src/ocr.js`) lässt Groß- **und** + Kleinbuchstaben, Ziffern sowie die auf Etiketten üblichen Satz- und + Sonderzeichen zu — nicht mehr nur Großbuchstaben. Das OCR-Ergebnis wird + anschließend gegen die bekannten Spec-Werte abgeglichen, was die + typischen Verwechslungen (0/O, 1/I, 8/B, …) auflöst; das kann durch die + breitere Whitelist bei den Codeteilen (Kapazität, Rank, Geschwindigkeit, + Teilenummer) etwas an Genauigkeit kosten, weil Tesseract jetzt z. B. + zwischen „O“ und „o“ unterscheiden muss — eine bewusst in Kauf genommene + Abwägung, damit der vollständige Etikettentext lesbar wird. Da in dieser + Funktion nie eine per Barcode gelesene Teilenummer vorliegt, entscheiden + hier ausschließlich die aus dem erkannten Text abgeleiteten technischen + Angaben (Kapazität, Bauform, Rank, Geschwindigkeit) über die + Stapelzuordnung (`specsCompatible` in `src/spec.js`) — anders als in + „Strichcode"/„QR-Code", wo meist die exakte Teilenummer trägt. + + Der vollständige, ungefilterte Erkennungstext (nicht nur die daraus + verstandenen Felder) wird zusätzlich mitgeführt (`recognize` in + `src/pipeline.js` liefert ihn als `rawText` zurück) und je Eintrag + gespeichert — rein additiv, ohne jeden Einfluss auf die Stapelzuordnung + (siehe „Grenzen" unten). In Funktion „Text erkennen" bleibt die + Treffer-Rückmeldung deshalb stehen, bis der Nutzer sie wegtippt, und zeigt + den vollen erkannten Text an, statt sich nach gut einer Sekunde von selbst + auszublenden wie in den beiden Barcode-Funktionen (`showResult` in + `src/ui/result-overlay.js`). Der Rohtext jedes Eintrags lässt sich später + auch in der Sitzungsliste nachlesen. 4. **Stapel-Zuweisung.** Grün (Barcode) und Gelb (OCR) laufen ohne Eingabe durch; nur bei roter Konfidenz (nichts Eindeutiges erkannt, widersprüchliche Barcodes oder widersprüchliche unbekannte Barcodes ohne jede OCR-Kapazität) @@ -187,12 +213,15 @@ korrekt funktioniert. npm test ``` -144 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich +154 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich die reinen Module ohne Browser-Zugriff: Teilenummer-Decoder, Spec-Normalisierung und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert) und --Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline samt Zeitgrenzen -und Mehrdeutigkeitsbehandlung, die Sitzungssicherung samt Prüfung eines -wiederhergestellten Zustands, die drei Scan-Funktionen (`scan-modes.js` — +-Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline samt Zeitgrenzen, +Mehrdeutigkeitsbehandlung und dem additiven OCR-Rohtext (`rawText` wird nur +zurückgegeben, wenn die Texterkennung tatsächlich lief, und beeinflusst +nachweislich nie die Stapelzuordnung), die Sitzungssicherung samt Prüfung +eines wiederhergestellten Zustands (einschließlich `entry.rawText`), die drei +Scan-Funktionen (`scan-modes.js` — Codearten je Funktion, DataMatrix/Aztec/PDF417 in „QR-Code") sowie die je Funktion tatsächlich aufgerufenen Adapter (`scan-recognition.js` — Texterkennung läuft nachweislich nie in „Strichcode"/„QR-Code", Barcode-Dekodierung nachweislich @@ -263,19 +292,19 @@ worauf du bei der Veröffentlichung achten solltest. | `src/pn-decoder.js` | Teilenummer → Spec-Felder | | `src/ocr-extract.js` | OCR-Rohtext → Spec-Felder | | `src/session.js` | Stapel halten, vorschlagen, buchen, umsortieren, zurücknehmen — inklusive der aus den Einträgen abgeleiteten, je Stapel bekannten Barcode-Inhalte (`stack.codes`), die vor dem Vergleich der technischen Angaben über die Zuordnung entscheiden, sofern diese Angaben nicht widersprechen | -| `src/pipeline.js` | Barcode → Teilenummer → OCR → Ampelfarbe, inkl. Zeitgrenzen, Mehrdeutigkeitsbehandlung und Kurzschluss über einen bereits bekannten Barcode-Inhalt (`deps.isKnownCode`) — unverändert; kennt keine Scan-Funktionen | +| `src/pipeline.js` | Barcode → Teilenummer → OCR → Ampelfarbe, inkl. Zeitgrenzen, Mehrdeutigkeitsbehandlung und Kurzschluss über einen bereits bekannten Barcode-Inhalt (`deps.isKnownCode`) — kennt keine Scan-Funktionen; liefert zusätzlich (additiv) den ungefilterten OCR-Rohtext (`rawText`), leer, wenn keine Texterkennung lief | | `src/scan-modes.js` | Die drei Scan-Funktionen (Strichcode, QR-Code, Text erkennen): Kennung, deutsche Beschriftung, gelesene Codearten, laufende Suche ja/nein, Texterkennung ja/nein — einzige Quelle, sowohl für die Auswahl-Oberfläche als auch für `main.js` | | `src/scan-recognition.js` | Baut die an `recognize()` übergebenen Adapter anhand der gewählten Funktion: in „Strichcode"/„QR-Code" liefert die Texterkennung sofort leeren Text ohne Tesseract anzustoßen, in „Text erkennen" liefert die Barcode-Dekodierung sofort eine leere Liste ohne `zxing-wasm` anzustoßen | -| `src/storage.js` | Absturzschutz der laufenden Sitzung: sichert und lädt aus `localStorage`, verwirft beim Laden jeden in sich unstimmigen Zustand vollständig (siehe unten) | -| `src/camera.js` | Kamerastart, Einzelbildaufnahme, Bilddatei-Ersatzweg | +| `src/storage.js` | Absturzschutz der laufenden Sitzung: sichert und lädt aus `localStorage`, verwirft beim Laden jeden in sich unstimmigen Zustand vollständig (siehe unten); `entry.rawText` ist wie `entry.codes` optional und wird bei Fehlen als leerer Rohtext wiederhergestellt | +| `src/camera.js` | Kamerastart, Einzelbildaufnahme (herunterskaliert, für die Dateiauswahl), Rahmenausschnitt in voller Auflösung (`grabFrameRegion`, für die laufende Barcode-Suche **und** für „Modul scannen" in „Text erkennen"), Bilddatei-Ersatzweg | | `src/barcode.js` | Adapter zu `zxing-wasm`; `decodeBarcodes(imageData, formats)` bekommt die Codearten übergeben, ohne Angabe wie bisher (Code128 + DataMatrix) | -| `src/ocr.js` | Bildaufbereitung (Otsu) und Adapter zu `tesseract.js` | +| `src/ocr.js` | Bildaufbereitung (Otsu) und Adapter zu `tesseract.js`; Zeichen-Whitelist deckt Groß- und Kleinbuchstaben, Ziffern sowie übliche Etiketten-Sonderzeichen ab | | `src/ui/scan-view.js` | Scan-Ansicht: Kamera-Vorschau, antippbare Anzeige der gewählten Funktion (Wechsel), Scan-Knopf (nur in „Text erkennen" sichtbar), Stapel-Leiste, „Zuletzt"-Zeile, Ersatzweg-Schaltfläche bei fehlender Kamera | | `src/ui/mode-dialog.js` | Vollbild-Auswahl der Scan-Funktion vor dem Start und beim Wechsel: drei große Flächen, eine je Funktion aus `src/scan-modes.js` | -| `src/ui/result-overlay.js` | Kurze Treffer-Rückmeldung (grün/gelb), blendet sich nach kurzer Zeit selbst wieder aus | +| `src/ui/result-overlay.js` | Treffer-Rückmeldung (grün/gelb): blendet sich in „Strichcode"/„QR-Code" nach kurzer Zeit selbst aus; in „Text erkennen" bleibt sie stehen, zeigt den vollen OCR-Rohtext und wartet auf eine Eingabe, bevor sie schließt | | `src/ui/ambiguous-dialog.js` | Rot-Dialog bei roter Konfidenz oder mehrdeutiger Stapelzuordnung | | `src/ui/resume-dialog.js` | Dialog beim Start: gesicherte Sitzung fortsetzen oder verwerfen — läuft vor der Funktionswahl | -| `src/ui/session-list.js` | Sitzungsliste: Stapel-Übersicht, Umsortieren, Entfernen, Sitzung beenden | +| `src/ui/session-list.js` | Sitzungsliste: Stapel-Übersicht, Umsortieren, Entfernen, Sitzung beenden, Anzeige des vollständigen OCR-Rohtexts je Eintrag (sofern vorhanden) | | `src/ui/describe-spec.js` | Gemeinsame Kurzbeschreibung eines Specs (Kapazität, Rank, Geschwindigkeit, Bauform), wahlweise mit Platzhaltern für fehlende Felder; von Treffer-Rückmeldung, „Zuletzt"-Zeile und Sitzungsliste gemeinsam genutzt | | `src/main.js` | Verdrahtung aller Module zur lauffähigen App | | `src/styles.css` | Farbvariablen (Grün/Gelb/Rot der Ampel-Rückmeldung), Layout des Kamera-Vollbilds samt Zielrahmen, Stapel-Leiste und Aktionsknöpfe sowie die Overlays für Treffer-Rückmeldung, Rot-Dialog und Sitzungsliste | @@ -309,6 +338,14 @@ Zeichenketten); ein gesicherter Stand aus einer Fassung vor dieser Erweiterung ohne diese Felder gilt dagegen nicht als unstimmig, sondern wird beim Laden als Stapel bzw. Eintrag ohne bekannte Codes wiederhergestellt. +Nach demselben Muster ist `entry.rawText` (der volle OCR-Rohtext, siehe +„Funktionsweise" oben) optional: Ist er vorhanden, muss er eine Zeichenkette +sein; ein gesicherter Stand aus einer Fassung vor dieser Erweiterung ohne +dieses Feld gilt nicht als unstimmig und wird beim Laden als Eintrag ohne +Rohtext (leere Zeichenkette) wiederhergestellt. Anders als `stack.codes` wird +`entry.rawText` beim Laden nicht neu abgeleitet — er hängt an genau diesem +Eintrag und fließt in keine Stapel-Berechnung ein. + ## Grenzen - Keine Bestandsführung über Sitzungen hinweg, kein Export. Die Sicherung in @@ -346,6 +383,13 @@ als Stapel bzw. Eintrag ohne bekannte Codes wiederhergestellt. Validierung braucht deshalb echte Module, keine Code-Prüfung. - Farb- und Schriftgestaltung ist bewusst schlicht gehalten und kann nach dem ersten Einsatz am Tisch nachgezogen werden. +- Der volle OCR-Rohtext (`rawText`) dient ausschließlich der Anzeige für den + Nutzer (Treffer-Rückmeldung, Sitzungsliste). Die Stapelzuordnung stützt + sich bewusst weiterhin ausschließlich auf die daraus abgeleiteten, + verstandenen Spec-Felder — roher Erkennungstext schwankt zwischen + Aufnahmen (Zeilenumbrüche, zusätzlich erkannte Wörter, Groß-/ + Kleinschreibung, …) und würde als Gruppierungsmerkmal für praktisch jedes + Modul einen eigenen Stapel erzeugen. ## Herstellertabellen erweitern @@ -404,7 +448,11 @@ werden: halten: ebenfalls Treffer. - „Text erkennen" wählen: kein automatisches Hervorheben des Rahmens (er bleibt durchgehend schlicht), „Modul scannen"-Knopf sichtbar und löst die - Texterkennung aus. + Texterkennung aus. Der Rahmen ist dort trotzdem wirksam: nur der + Bildausschnitt innerhalb des Rahmens wird gelesen (nicht das ganze + Kamerabild) — mit Text außerhalb des Rahmens auf demselben Etikett prüfen, + dass er **nicht** erkannt wird, während Text innerhalb des Rahmens + vollständig erscheint. - Anzeige der gewählten Funktion in der Scan-Ansicht antippen: Auswahl erscheint erneut, nach der Wahl gilt der Wechsel sofort — laufende Suche startet/stoppt, Knopf erscheint/verschwindet, **ohne** die Seite neu zu @@ -418,7 +466,21 @@ werden: - In Funktion „Text erkennen" ein Etikett fotografieren und „Modul scannen" antippen: die Otsu-Aufbereitung liefert auf einem echten, glänzenden Etikettenfoto tatsächlich brauchbaren Text (bisher nur an synthetischen - Testbildern geprüft, nie an einem echten Foto). + Testbildern geprüft, nie an einem echten Foto). Prüfen, dass die + Rückmeldung dabei den **vollständigen** erkannten Text zeigt (Groß- und + Kleinbuchstaben, nicht nur die von der App verstandenen Felder wie + `32GB 2Rx4 PC4-2666`), stehen bleibt, bis sie weggetippt wird, und bei + langem Text lesbar bleibt (per Scrollen erreichbar, wächst nicht aus dem + Bildschirm heraus). Danach in der Sitzungsliste nachsehen, dass derselbe + Rohtext beim Eintrag zum Nachlesen erscheint. +- Da der Bildausschnitt für die Texterkennung jetzt deutlich größer ist als + das bisher übergebene, auf 1280 Pixel herunterskalierte Vollbild + (`grabFrameRegion` statt `grabFrame`, siehe „Funktionsweise"), dauert ein + einzelner Erkennungsversuch länger als vorher — beobachten, wie lange ein + normaler Scan auf einem durchschnittlichen Handy tatsächlich braucht, und + ob er dabei spürbar unter der 20-Sekunden-Zeitgrenze der Pipeline bleibt + (siehe dazu auch den folgenden Punkt zur Ladezeit des Tesseract-Arbeiters, + die zusätzlich in dieselbe Zeitgrenze fällt). - Ladezeit des WASM-Barcode-Moduls (`zxing-wasm`) beim allerersten Scan einer Sitzung in Funktion „Strichcode" oder „QR-Code" beobachten — bleibt sie deutlich unter der 10-Sekunden-Zeitgrenze der Pipeline, und braucht ein @@ -440,8 +502,11 @@ werden: Ladevorgang dauerhaft "verfügbar", springt nach einem erzwungenen Fehlschlag (z. B. Flugmodus beim ersten Laden) beim nächsten Erfolg wieder darauf zurück? -- Stichprobe, ob die feste Zeichen-Whitelist der Texterkennung auf echten - Etiketten keine tatsächlich benötigten Zeichen ausschließt. +- Stichprobe, ob die Zeichen-Whitelist der Texterkennung (Groß-/ + Kleinbuchstaben, Ziffern, übliche Satz-/Sonderzeichen) auf echten Etiketten + keine tatsächlich benötigten Zeichen ausschließt, und ob die Erkennung der + Codeteile (Kapazität, Rank, Geschwindigkeit, Teilenummer) durch die jetzt + mögliche Groß-/Kleinschreibung noch verlässlich genug bleibt. - Funktion „Strichcode"/„QR-Code": kein passender Code im Bild führt zu Rot-Dialog (statt Fehler oder Absturz) — hier ohne jeden OCR-Versuch. Funktion „Text erkennen": kein verwertbarer OCR-Text führt ebenfalls zu @@ -461,9 +526,15 @@ werden: es passend. - Stapel-Leiste (`A: 12`, `B: 4`) aus rund einem Meter Entfernung mit einer Hand lesbar. -- Grüne/gelbe Rückmeldung erscheint nach dem Scan und verschwindet nach - rund 1200 ms von selbst; bei zwei Scans deutlich unter 1200 ms Abstand - bleibt jeweils nur eine Rückmeldung sichtbar und die App bleibt bedienbar. +- In „Strichcode"/„QR-Code": Grüne/gelbe Rückmeldung erscheint nach dem Scan + und verschwindet nach rund 1200 ms von selbst; bei zwei Scans deutlich + unter 1200 ms Abstand bleibt jeweils nur eine Rückmeldung sichtbar und die + App bleibt bedienbar. +- In „Text erkennen": Rückmeldung bleibt stehen, bis sie über die + „weiter"-Schaltfläche (mindestens 56px hoch) weggetippt wird — kein + automatisches Ausblenden. Ein weiterer Scan, bevor die vorherige + Rückmeldung weggetippt wurde, ersetzt sie sofort durch die neue, ohne die + App dauerhaft zu sperren. - Alle Bedienflächen (Scan-, Rückgängig-, Stapel-Knöpfe) mit dem Daumen erreichbar, während die andere Hand das Modul hält. - Rückgängig-Knopf: Screenreader liest "Letzten Scan zurücknehmen" vor.