From 6688efc546675cefe5bea261e04113f8a723e5e9 Mon Sep 17 00:00:00 2001 From: TanerUslu Date: Thu, 30 Jul 2026 09:37:10 +0200 Subject: [PATCH] =?UTF-8?q?README:=20Z=C3=A4hlen-Abschnitte=20auf=20Aufnah?= =?UTF-8?q?me=20statt=20Dauerbetrieb=20aktualisiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beschreibt die neue Aufnahme-Schaltfläche ("Zählen"/"Nochmal zählen"), die fünf Bilder je Aufnahme samt Median, die um die Einzelmessungen erweiterte Diagnosezeile und das Zurücksetzen beim Verlassen der Funktion. Aufbau- Tabelle, Testanzahl und der Prüfabschnitt "Am Gerät noch zu prüfen" folgen. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 147 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 38d1eed..48aa49c 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,9 @@ QR-Code, Text erkennen oder Zählen (siehe „Funktionsweise" unten). Jede Funktion nutzt ausschließlich ihre eigene Quelle; anders als früher weicht die App nicht mehr automatisch auf Texterkennung aus, wenn kein Code gefunden wird. Die vierte Funktion, „Zählen", gehört nicht zum eigentlichen Sortieren: -Sie bucht nichts, sondern zeigt beim Auslegen von Kleinteilen (z. B. -Festplattenschrauben) fortlaufend deren Anzahl an. +Sie bucht nichts, sondern ermittelt auf Knopfdruck beim Auslegen von +Kleinteilen (z. B. Festplattenschrauben) deren Anzahl und zeigt sie an, bis +der Nutzer erneut antippt. Zwei Module gelten als zusammengehörig, wenn sie denselben Barcode-Inhalt tragen — ein Barcode ist exakt gelesen, das ist der zuverlässigste Maßstab. @@ -65,12 +66,12 @@ keine Daten verlassen das Gerät. ausschließlich in `src/scan-modes.js` — es gibt keine zweite Stelle, an der das steht: - | Funktion | Liest | Laufende Suche/Zählung | Texterkennung | + | Funktion | Liest | Laufende Codesuche | Texterkennung | |---|---|---|---| - | **Strichcode** | Code128, Code39, Code93, ITF, EAN-13, EAN-8, UPC-A, UPC-E, Codabar | ja (Codesuche) | nein | - | **QR-Code** | QRCode, MicroQRCode, RMQRCode, DataMatrix, Aztec, PDF417 | ja (Codesuche) | nein | + | **Strichcode** | Code128, Code39, Code93, ITF, EAN-13, EAN-8, UPC-A, UPC-E, Codabar | ja | nein | + | **QR-Code** | QRCode, MicroQRCode, RMQRCode, DataMatrix, Aztec, PDF417 | ja | nein | | **Text erkennen** | keine Codes | nein | ja, auf Knopfdruck | - | **Zählen** | keine Codes | ja (Zählung, keine Buchung) | nein | + | **Zählen** | keine Codes | nein | nein, dafür Zählung auf Knopfdruck (keine Buchung) | „QR-Code" heißt auf Wunsch des Auftraggebers „QR-Code", liest aber bewusst nicht nur QR-Varianten, sondern auch DataMatrix, Aztec und PDF417. Der @@ -80,18 +81,24 @@ keine Daten verlassen das Gerät. `src/scan-modes.js`), und der Name ist Absicht — es ist der Wunsch des Auftraggebers für seinen Sprachgebrauch. - „Modul scannen" erscheint nur in „Text erkennen" — in „Strichcode" und - „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 + Eine einzige Aufnahme-Schaltfläche bedient beide Knopfdruck-Funktionen — + „Modul scannen" in „Text erkennen", „Zählen" bzw. (nach der ersten + Aufnahme seit Betreten der Funktion) „Nochmal zählen" in „Zählen" — statt + einer zweiten Schaltfläche daneben; nur Wortlaut und Rückruf unterscheiden + sich + (`setCaptureLabel` in `src/ui/scan-view.js`, verdrahtet in + `src/main.js`/`applyMode()`). In „Strichcode" und „QR-Code" bleibt sie + verborgen — dort sucht die App ohnehin laufend, ein Knopf hätte nichts zu + tun. Der Zielrahmen hebt sich in „Strichcode" und „QR-Code" hervor, sobald + ein passender Code im Bild ist; in „Text erkennen" und „Zählen" bleibt er 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. + auch dort wirksam: Sowohl „Modul scannen" als auch „Zählen" lesen genau den + Bildausschnitt innerhalb des Rahmens, in voller Kameraauflösung, nicht 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, die + Schaltfläche erscheint, verschwindet oder ändert ihren Wortlaut. Liegt beim Start eine gesicherte Sitzung vor, erscheint zuerst die Frage nach dem Fortsetzen (siehe Punkt 6 unten) und erst danach die Funktionswahl @@ -172,15 +179,36 @@ keine Daten verlassen das Gerät. `src/ui/result-overlay.js`). Der Rohtext jedes Eintrags lässt sich später auch in der Sitzungsliste nachlesen. 4. **Zählen** (ausschließlich in Funktion „Zählen"). Reine Anzeige, ohne - jeden Bezug zur Sitzung: Solange die Kamera läuft, wird der Zielrahmen - mehrmals pro Sekunde ausgewertet und die Anzahl der darin gefundenen - Fundstücke groß im Bild eingeblendet (`countObjects` in - `src/count-objects.js`, aufgerufen über denselben Zeitgeber/dieselbe - Überlappungssperre wie die Barcode-Dauersuche — siehe - `continuousSearchLoop` in `src/main.js`). Es gibt in dieser Funktion - keinen „Modul scannen"-Knopf, keine Treffer-Rückmeldung und keine - Stapel-Zuweisung; die Zahl wird nirgends gespeichert, nicht einmal - flüchtig für die Dauer der Sitzung. + jeden Bezug zur Sitzung — und, anders als die drei übrigen Funktionen mit + laufender Erkennung, auf **Aufnahme statt Dauerbetrieb**: Eine Zählung ist + eine Schätzung, kein eindeutiger Treffer wie ein Barcode; sie mehrmals pro + Sekunde neu anzuzeigen macht sie nicht genauer, nur unruhig (am + Nutzergerät sprang die Anzeige im Dauerbetrieb zwischen etwa 3 und 60, an + einem Standfoto derselben Teile lieferte dasselbe Verfahren stabil 18). + Antippen der Schaltfläche „Zählen“/„Nochmal zählen“ nimmt deshalb binnen + rund einer Sekunde fünf Bilder aus dem Zielrahmen des laufenden + Videobilds auf (`grabFrameRegion` in `src/camera.js` — bewusst nicht die + native Fotoaufnahme des Geräts, deren Bildausschnitt von der Vorschau + abweichen kann), zählt jedes einzeln (`countObjects` in + `src/count-objects.js`, unverändert) und zeigt als Ergebnis deren Median + (`combineCaptureCounts` in `src/count-capture.js`) — robust gegen ein + einzelnes verwackeltes oder mitten in eine Fokusregelung fallendes Bild, + ohne die frühere fortlaufende Glättung über die letzten neun Messungen + (`count-history.js`, entfallen: eine Aufnahme mit fünf Bildern leistet + dieselbe Robustheit bereits selbst). Während der rund einen Sekunde zeigt + die Statuszeile „zähle …“, damit der Nutzer die Kamera ruhig hält und den + Knopfdruck nicht für wirkungslos hält (`runCountCapture` in + `src/main.js`). Das Ergebnis bleibt danach stehen, bis der Nutzer erneut + antippt — verlässt er die Funktion oder wechselt zu einer anderen, werden + Ergebnis und Diagnosezeile zurückgesetzt. Es gibt in dieser Funktion keine + Treffer-Rückmeldung und keine Stapel-Zuweisung; die Zahl wird nirgends + gespeichert, nicht einmal flüchtig für die Dauer der Sitzung. Die + Diagnosezeile unter der großen Zahl (Antippen der Zahl blendet sie ein + oder aus) zeigt zusätzlich zu den übrigen Zwischenwerten (die sich auf die + *letzte* der fünf Einzelmessungen beziehen) die fünf Einzelmessungen + dieser Aufnahme selbst — daran erkennen Nutzer und Entwickler sofort, ob + das Ergebnis belastbar ist: Fünf Messungen von 17 bis 19 bedeuten etwas + anderes als fünf Messungen von 4 bis 50. Kein Bilderkennungsmodell: Der **Otsu-Schwellwert** (`threshold.js`), den die Texterkennung weiterhin nutzt, taugt für „Zählen" nicht — er @@ -266,7 +294,7 @@ korrekt funktioniert. npm test ``` -165 Tests, alle grün (Stand dieses Dokuments). Getestet werden ausschließlich +179 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, jetzt in `threshold.js`) und -Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline @@ -282,7 +310,11 @@ nie in „Text erkennen"/„Zählen"). Dazu die Zähl-Berechnung selbst (`count-objects.js` — leeres Bild, einzelne und mehrere getrennte Flächen, verworfenes Rauschen, Hochrechnung berührender Teile ohne ein nur leicht größeres Einzelteil zu verdoppeln, helle wie dunkle Objekte, ein Bild ohne -Bildpunkte). Kamera, Barcode-/OCR-Adapter selbst, die Auswahl-Oberfläche und +Bildpunkte). Dazu die reine Zusammenfassung der fünf Einzelmessungen einer +Zaehl-Aufnahme zu deren Median (`count-capture.js` — auch für den Fall +weniger als fünf verwertbarer Bilder, sowie eine leere Messreihe, die +regulär wirft statt stillschweigend `NaN` zu liefern). Kamera, +Barcode-/OCR-Adapter selbst, die Auswahl-Oberfläche und die Verdrahtung in `main.js` laufen nur im echten Browser und werden dort manuell geprüft (siehe „Am Gerät noch zu prüfen" unten) — `node --test` kennt kein DOM. @@ -350,15 +382,16 @@ worauf du bei der Veröffentlichung achten solltest. | `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`) — kennt keine Scan-Funktionen; liefert zusätzlich (additiv) den ungefilterten OCR-Rohtext (`rawText`), leer, wenn keine Texterkennung lief | -| `src/scan-modes.js` | Die vier Scan-Funktionen (Strichcode, QR-Code, Text erkennen, Zählen): Kennung, deutsche Beschriftung, gelesene Codearten, laufende Suche ja/nein, Texterkennung ja/nein, laufende Zählung 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, in „Zählen" liefern beide sofort leer (diese Funktion ruft `recognize()` im normalen Betrieb ohnehin nicht auf) | +| `src/scan-modes.js` | Die vier Scan-Funktionen (Strichcode, QR-Code, Text erkennen, Zählen): Kennung, deutsche Beschriftung, gelesene Codearten, laufende Codesuche ja/nein, Texterkennung ja/nein — einzige Quelle, sowohl für die Auswahl-Oberfläche als auch für `main.js`. „Zählen" läuft wie „Text erkennen" auf Knopfdruck (`continuousSearch: false`) und braucht dafür kein eigenes Merkmal mehr | +| `src/scan-recognition.js` | Baut die an `recognize()` übergebenen Adapter anhand der gewählten Funktion: liest eine Funktion keine Codearten (leeres `barcodeFormats`, bei „Text erkennen" und „Zählen"), liefert die Barcode-Dekodierung sofort eine leere Liste ohne `zxing-wasm` anzustoßen; nur bei `useOcr` läuft die echte Texterkennung, sonst liefert sie sofort leeren Text ohne Tesseract anzustoßen (in „Zählen" ruft `recognize()` im normalen Betrieb ohnehin nicht auf — siehe `count-capture.js`) | +| `src/count-capture.js` | Reine Berechnung für die Aufnahme in Funktion „Zählen": bildet aus den (bis zu fünf) Einzelmessungen einer Aufnahme (`countObjects()` je Bild) deren Median (`combineCaptureCounts`) — robust gegen ein einzelnes verwackeltes oder unscharfes Bild, bleibt auch bei weniger als fünf verwertbaren Bildern sinnvoll | | `src/threshold.js` | Otsu-Schwellwertbestimmung (Graustufen, Histogramm, Schwellwert) — reines Modul ohne Browser-Zugriff, aus `ocr.js` herausgezogen; gemeinsame Grundlage für `preprocess()` (ocr.js) und `countObjects()` (count-objects.js) | | `src/count-objects.js` | Reine Berechnung für Funktion „Zählen": zählt zusammenhängende Objektflächen in einem Bildausschnitt über örtlichen Kontrast (Normierung auf 800 Bildpunkte Breite, Glättung und örtlicher Hintergrund je über Kastenfilter/Summenbild, Maske aus geglättetem Bild vs. örtlichem Hintergrund ± Marge, beide Polaritäten, Connected-Component-Labeling mit eigener Arbeitsliste, Rauschfilter, Massstab relativ zur größten Fläche, Hochrechnung berührender Teile über den Flächen-Median) — nutzt `threshold.js`/Otsu bewusst **nicht** mehr (siehe „Funktionsweise" oben) | | `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, „Modul scannen" in „Text erkennen" **und** die laufende Zählung in „Zählen"), Bilddatei-Ersatzweg | +| `src/camera.js` | Kamerastart, Einzelbildaufnahme (herunterskaliert, für die Dateiauswahl), Rahmenausschnitt in voller Auflösung (`grabFrameRegion`, für die laufende Barcode-Suche, „Modul scannen" in „Text erkennen" **und** jedes der fünf Bilder einer Aufnahme in „Zählen"), 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, über `threshold.js`) 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), große Zählanzeige (nur in „Zählen" sichtbar), Stapel-Leiste, „Zuletzt"-Zeile, Ersatzweg-Schaltfläche bei fehlender Kamera | +| `src/ui/scan-view.js` | Scan-Ansicht: Kamera-Vorschau, antippbare Anzeige der gewählten Funktion (Wechsel), eine gemeinsame Aufnahme-Schaltfläche für beide Knopfdruck-Funktionen (sichtbar in „Text erkennen" und „Zählen", Wortlaut je nach Funktion und Zustand über `setCaptureLabel`), große Zählanzeige samt Diagnosezeile (nur in „Zählen" 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: vier große Flächen, eine je Funktion aus `src/scan-modes.js` | | `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 | @@ -371,9 +404,10 @@ worauf du bei der Veröffentlichung achten solltest. | `.vch/deploy.yaml` | Bau- und Startbefehl sowie Gesundheitspfad für die Hosting-Umgebung | `spec`, `pn-decoder`, `ocr-extract`, `session`, `pipeline`, `storage`, -`scan-modes`, `scan-recognition`, `threshold` und `count-objects` sind reine -Funktionen ohne Browser-Zugriff (kein `window`, `document` oder -`localStorage` direkt) und deshalb vollständig mit `node:test` prüfbar. +`scan-modes`, `scan-recognition`, `threshold`, `count-objects` und +`count-capture` sind reine Funktionen ohne Browser-Zugriff (kein `window`, +`document` oder `localStorage` direkt) und deshalb vollständig mit +`node:test` prüfbar. `camera.js`, `barcode.js`, `ocr.js` und `src/ui/` brauchen einen echten Browser und werden nur manuell geprüft. @@ -586,18 +620,41 @@ werden: dass die Vorbereitung tatsächlich nur einmal läuft)? In Funktion „Text erkennen" darf dieses Laden dagegen gar nicht erst anlaufen, da dort nie `decodeBarcodes` aufgerufen wird. -**Zählen (neu)** +**Zählen (Aufnahme statt Dauerbetrieb)** - „Zählen" wählen, Kamera auf ausgelegte Kleinteile richten (z. B. silberne - Festplattenschrauben auf dunklem Untergrund): Eine große Zahl erscheint im - Bild und aktualisiert sich laufend, ohne dass etwas angetippt werden muss. - Kein „Modul scannen"-Knopf, keine Treffer-Rückmeldung, kein roter Dialog, - keine Stapel-Zuweisung — der Zielrahmen bleibt sichtbar und zeigt weiterhin, - was gezählt wird. -- Die angezeigte Zahl aus normalem Bedienabstand (Armlänge) lesbar prüfen. -- Ein paar Teile wegnehmen bzw. hinzufügen, während die Kamera läuft: Die - Zahl folgt ohne spürbare Verzögerung. + Festplattenschrauben auf dunklem Untergrund): Es erscheint **keine** + Zahl und **keine** fortlaufende Aktualisierung, sondern eine gut + erreichbare Schaltfläche mit der Aufschrift „Zählen". Kein roter Dialog, + keine Treffer-Rückmeldung, keine Stapel-Zuweisung — der Zielrahmen bleibt + sichtbar und zeigt, was beim nächsten Antippen gezählt wird. +- „Zählen" antippen: Für rund eine Sekunde zeigt die Statuszeile „zähle …“, + danach erscheint eine große Zahl im Bild und bleibt stehen — sie + aktualisiert sich **nicht** von selbst weiter. Die Schaltfläche trägt jetzt + die Aufschrift „Nochmal zählen". +- Während der rund einen Sekunde Kamera und Teile ruhig halten (leichte + Bewegung zwischendurch simulieren) und danach prüfen, dass das Ergebnis + plausibel bleibt — die App nimmt in dieser Zeit fünf Bilder auf und zeigt + deren Median. +- Die Zahl antippen: Diagnosezeile blendet sich ein und zeigt (in dieser + Reihenfolge) `Roh: …`, `Messungen: …` (die fünf Einzelmessungen dieser + Aufnahme, z. B. `17, 18, 19, 18, 20`), `Bild: …`, `Flächen: …`, + `Größte Fläche: …`, `Typische Größe: …`, `Art: …`, `Dauer: …`. Nochmaliges + Antippen blendet sie wieder aus. Prüfen, dass die fünf Messungen tatsächlich + eng beieinanderliegen, wenn die Teile ruhig lagen, und spürbar streuen, + wenn während der Aufnahme bewegt wurde. +- „Nochmal zählen" antippen: neue Aufnahme, neues Ergebnis ersetzt das alte + vollständig (Zahl **und** Diagnosezeile). +- Zur Funktionswahl wechseln und zurück zu „Zählen": Zahl und Diagnosezeile + sind zurückgesetzt (nichts angezeigt), Schaltfläche zeigt wieder „Zählen" + statt „Nochmal zählen". Dasselbe beim Wechsel zu einer anderen Funktion und + zurück. - Zugänge zur Sitzungsliste und zum Funktionswechsel bleiben auch in dieser - Funktion erreichbar (Anzeige der Funktion antippen, Stapel-Leiste antippen). + Funktion erreichbar (Anzeige der Funktion antippen, Stapel-Leiste antippen) + — außer während die rund einssekündige Aufnahme selbst läuft, genau wie bei + einem laufenden Scan in „Text erkennen". +- Aufnahme antippen, bevor das Kamerabild bereit ist (z. B. gleich nach dem + Start): verständliche Meldung „Zählen nicht möglich — Kamerabild noch + nicht bereit" statt Absturz oder stiller Nichtreaktion. - Gegenprobe der Grenzen (siehe „Grenzen" oben): Teile eng aneinanderlegen (Hochrechnung), Teile stapeln (nur oberste Schicht zählt), einseitige Beleuchtung, geringer Kontrast zum Untergrund — beobachten, wo die Zahl