README: Zählen-Abschnitte auf Aufnahme statt Dauerbetrieb aktualisiert

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) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 09:37:10 +02:00
co-authored by Claude Opus 5
parent 4b6bb065b1
commit 6688efc546
+102 -45
View File
@@ -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 Funktion nutzt ausschließlich ihre eigene Quelle; anders als früher weicht die
App nicht mehr automatisch auf Texterkennung aus, wenn kein Code gefunden App nicht mehr automatisch auf Texterkennung aus, wenn kein Code gefunden
wird. Die vierte Funktion, „Zählen", gehört nicht zum eigentlichen Sortieren: wird. Die vierte Funktion, „Zählen", gehört nicht zum eigentlichen Sortieren:
Sie bucht nichts, sondern zeigt beim Auslegen von Kleinteilen (z. B. Sie bucht nichts, sondern ermittelt auf Knopfdruck beim Auslegen von
Festplattenschrauben) fortlaufend deren Anzahl an. 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 Zwei Module gelten als zusammengehörig, wenn sie denselben Barcode-Inhalt
tragen — ein Barcode ist exakt gelesen, das ist der zuverlässigste Maßstab. 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 ausschließlich in `src/scan-modes.js` — es gibt keine zweite Stelle, an der
das steht: 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 | | **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 (Codesuche) | nein | | **QR-Code** | QRCode, MicroQRCode, RMQRCode, DataMatrix, Aztec, PDF417 | ja | nein |
| **Text erkennen** | keine Codes | nein | ja, auf Knopfdruck | | **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 „QR-Code" heißt auf Wunsch des Auftraggebers „QR-Code", liest aber bewusst
nicht nur QR-Varianten, sondern auch DataMatrix, Aztec und PDF417. Der 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 `src/scan-modes.js`), und der Name ist Absicht — es ist der Wunsch des
Auftraggebers für seinen Sprachgebrauch. Auftraggebers für seinen Sprachgebrauch.
„Modul scannen" erscheint nur in „Text erkennen" — in „Strichcode" und Eine einzige Aufnahme-Schaltfläche bedient beide Knopfdruck-Funktionen —
QR-Code" sucht die App ohnehin laufend, ein Knopf ohne Texterkennung hätte Modul scannen" in „Text erkennen", „Zählen" bzw. (nach der ersten
dort nichts zu tun. Der Zielrahmen hebt sich in „Strichcode" und „QR-Code" Aufnahme seit Betreten der Funktion) „Nochmal zählen" in „Zählen" — statt
hervor, sobald ein passender Code im Bild ist; in „Text erkennen" bleibt er 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 schlicht, weil dort nichts laufend erkannt wird. Trotzdem ist der Rahmen
auch dort wirksam: „Modul scannen" liest genau den Bildausschnitt innerhalb auch dort wirksam: Sowohl „Modul scannen" als auch „Zählen" lesen genau den
des Rahmens, in voller Kameraauflösung, nicht mehr das gesamte, Bildausschnitt innerhalb des Rahmens, in voller Kameraauflösung, nicht das
heruntergerechnete Kamerabild (`grabFrameRegion` in `src/camera.js` gesamte, heruntergerechnete Kamerabild (`grabFrameRegion` in
derselbe Ausschnitt, den auch die laufende Barcode-Suche benutzt). Ein `src/camera.js` derselbe Ausschnitt, den auch die laufende Barcode-Suche
Wechsel der Funktion während des Sortierens (z. B. bei gemischter Ware) benutzt). Ein Wechsel der Funktion während des Sortierens (z. B. bei
gilt sofort: laufende Suche startet oder stoppt, der Knopf erscheint oder gemischter Ware) gilt sofort: laufende Suche startet oder stoppt, die
verschwindet. Schaltfläche erscheint, verschwindet oder ändert ihren Wortlaut.
Liegt beim Start eine gesicherte Sitzung vor, erscheint zuerst die Frage Liegt beim Start eine gesicherte Sitzung vor, erscheint zuerst die Frage
nach dem Fortsetzen (siehe Punkt 6 unten) und erst danach die Funktionswahl 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 `src/ui/result-overlay.js`). Der Rohtext jedes Eintrags lässt sich später
auch in der Sitzungsliste nachlesen. auch in der Sitzungsliste nachlesen.
4. **Zählen** (ausschließlich in Funktion „Zählen"). Reine Anzeige, ohne 4. **Zählen** (ausschließlich in Funktion „Zählen"). Reine Anzeige, ohne
jeden Bezug zur Sitzung: Solange die Kamera läuft, wird der Zielrahmen jeden Bezug zur Sitzung — und, anders als die drei übrigen Funktionen mit
mehrmals pro Sekunde ausgewertet und die Anzahl der darin gefundenen laufender Erkennung, auf **Aufnahme statt Dauerbetrieb**: Eine Zählung ist
Fundstücke groß im Bild eingeblendet (`countObjects` in eine Schätzung, kein eindeutiger Treffer wie ein Barcode; sie mehrmals pro
`src/count-objects.js`, aufgerufen über denselben Zeitgeber/dieselbe Sekunde neu anzuzeigen macht sie nicht genauer, nur unruhig (am
Überlappungssperre wie die Barcode-Dauersuche — siehe Nutzergerät sprang die Anzeige im Dauerbetrieb zwischen etwa 3 und 60, an
`continuousSearchLoop` in `src/main.js`). Es gibt in dieser Funktion einem Standfoto derselben Teile lieferte dasselbe Verfahren stabil 18).
keinen „Modul scannen"-Knopf, keine Treffer-Rückmeldung und keine Antippen der Schaltfläche „Zählen“/„Nochmal zählen“ nimmt deshalb binnen
Stapel-Zuweisung; die Zahl wird nirgends gespeichert, nicht einmal rund einer Sekunde fünf Bilder aus dem Zielrahmen des laufenden
flüchtig für die Dauer der Sitzung. 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`), Kein Bilderkennungsmodell: Der **Otsu-Schwellwert** (`threshold.js`),
den die Texterkennung weiterhin nutzt, taugt für „Zählen" nicht — er den die Texterkennung weiterhin nutzt, taugt für „Zählen" nicht — er
@@ -266,7 +294,7 @@ korrekt funktioniert.
npm test 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 die reinen Module ohne Browser-Zugriff: Teilenummer-Decoder, Spec-Normalisierung
und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert, jetzt in und toleranter Vergleich, OCR-Bildaufbereitung (Otsu-Schwellwert, jetzt in
`threshold.js`) und -Feldextraktion, Stapel-Zuweisung, die Erkennungs-Pipeline `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, (`count-objects.js` — leeres Bild, einzelne und mehrere getrennte Flächen,
verworfenes Rauschen, Hochrechnung berührender Teile ohne ein nur leicht verworfenes Rauschen, Hochrechnung berührender Teile ohne ein nur leicht
größeres Einzelteil zu verdoppeln, helle wie dunkle Objekte, ein Bild ohne 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 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 manuell geprüft (siehe „Am Gerät noch zu prüfen" unten) — `node --test` kennt
kein DOM. kein DOM.
@@ -350,15 +382,16 @@ worauf du bei der Veröffentlichung achten solltest.
| `src/ocr-extract.js` | OCR-Rohtext → 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/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/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-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: 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-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/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/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/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/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/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/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/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/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 | | `.vch/deploy.yaml` | Bau- und Startbefehl sowie Gesundheitspfad für die Hosting-Umgebung |
`spec`, `pn-decoder`, `ocr-extract`, `session`, `pipeline`, `storage`, `spec`, `pn-decoder`, `ocr-extract`, `session`, `pipeline`, `storage`,
`scan-modes`, `scan-recognition`, `threshold` und `count-objects` sind reine `scan-modes`, `scan-recognition`, `threshold`, `count-objects` und
Funktionen ohne Browser-Zugriff (kein `window`, `document` oder `count-capture` sind reine Funktionen ohne Browser-Zugriff (kein `window`,
`localStorage` direkt) und deshalb vollständig mit `node:test` prüfbar. `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 `camera.js`, `barcode.js`, `ocr.js` und `src/ui/` brauchen einen echten
Browser und werden nur manuell geprüft. Browser und werden nur manuell geprüft.
@@ -586,18 +620,41 @@ werden:
dass die Vorbereitung tatsächlich nur einmal läuft)? In Funktion „Text dass die Vorbereitung tatsächlich nur einmal läuft)? In Funktion „Text
erkennen" darf dieses Laden dagegen gar nicht erst anlaufen, da dort nie erkennen" darf dieses Laden dagegen gar nicht erst anlaufen, da dort nie
`decodeBarcodes` aufgerufen wird. `decodeBarcodes` aufgerufen wird.
**Zählen (neu)** **Zählen (Aufnahme statt Dauerbetrieb)**
- „Zählen" wählen, Kamera auf ausgelegte Kleinteile richten (z. B. silberne - „Zählen" wählen, Kamera auf ausgelegte Kleinteile richten (z. B. silberne
Festplattenschrauben auf dunklem Untergrund): Eine große Zahl erscheint im Festplattenschrauben auf dunklem Untergrund): Es erscheint **keine**
Bild und aktualisiert sich laufend, ohne dass etwas angetippt werden muss. Zahl und **keine** fortlaufende Aktualisierung, sondern eine gut
Kein „Modul scannen"-Knopf, keine Treffer-Rückmeldung, kein roter Dialog, erreichbare Schaltfläche mit der Aufschrift „Zählen". Kein roter Dialog,
keine Stapel-Zuweisung — der Zielrahmen bleibt sichtbar und zeigt weiterhin, keine Treffer-Rückmeldung, keine Stapel-Zuweisung — der Zielrahmen bleibt
was gezählt wird. sichtbar und zeigt, was beim nächsten Antippen gezählt wird.
- Die angezeigte Zahl aus normalem Bedienabstand (Armlänge) lesbar prüfen. - „Zählen" antippen: Für rund eine Sekunde zeigt die Statuszeile „zähle …“,
- Ein paar Teile wegnehmen bzw. hinzufügen, während die Kamera läuft: Die danach erscheint eine große Zahl im Bild und bleibt stehen — sie
Zahl folgt ohne spürbare Verzögerung. 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 - 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 - Gegenprobe der Grenzen (siehe „Grenzen" oben): Teile eng aneinanderlegen
(Hochrechnung), Teile stapeln (nur oberste Schicht zählt), einseitige (Hochrechnung), Teile stapeln (nur oberste Schicht zählt), einseitige
Beleuchtung, geringer Kontrast zum Untergrund — beobachten, wo die Zahl Beleuchtung, geringer Kontrast zum Untergrund — beobachten, wo die Zahl