diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md index 0090291..f40a754 100644 --- a/BEDIENUNGSANLEITUNG.md +++ b/BEDIENUNGSANLEITUNG.md @@ -4,17 +4,32 @@ Dieses Programm ersetzt Formelbilder in den Markdown-Dateien von epub2md und pdf LaTeX-Formeln. Dadurch passen die Formeln beim Neubau als EPUB oder PDF in Größe und Schrift zum Text. Es liest jede Formel zuerst mit dem Formel-OCR **pix2text** und ersetzt sie nur, wenn das Bildmodell -**Qwen** dieselbe Formel liest (bei Uneinigkeit darf das zweite Bildmodell Gemma pix2text bestätigen) und -das gerenderte LaTeX dem Bild grob ähnelt. Eingesetzt wird die Lesart von pix2text, ohne ihre -überflüssigen Leerzeichen. Alles läuft lokal. Bei Zweifeln fragt es dich in einer -Review-Seite im Browser. Ohne deine Zustimmung bleibt die Formel ein Bild. +**Qwen** dieselbe Formel liest (bei Uneinigkeit darf das zweite Bildmodell **Gemma** pix2text bestätigen) +und das gerenderte LaTeX dem Bild grob ähnelt. Eingesetzt wird die Lesart von pix2text. Alles läuft lokal. +Bei Zweifeln fragt es dich in einer Review-Seite im Browser; ohne deine Zustimmung bleibt die Formel ein +Bild. „Dieselbe Formel“ heißt: jedes Zeichen stimmt und steht an der richtigen Stelle. Abstände, Pfeillängen, -Schriftgröße, kursiv/aufrecht und Fettdruck dürfen abweichen. +Schriftgröße, kursiv/aufrecht und Fettdruck dürfen abweichen. Wie das intern geprüft wird, steht in +[FUNKTIONSWEISE.md](FUNKTIONSWEISE.md). --- -## 1. Einrichtung (einmalig) +## Inhalt + +1. [Installation](#1-installation) +2. [Modellserver starten und beenden](#2-modellserver-starten-und-beenden) +3. [Ein Buch bearbeiten](#3-ein-buch-bearbeiten) +4. [Einzelne Schritte und Optionen](#4-einzelne-schritte-und-optionen) +5. [Konfiguration](#5-konfiguration) +6. [Betrieb](#6-betrieb) +7. [Werkzeuge zum Messen und Einstellen](#7-werkzeuge-zum-messen-und-einstellen) +8. [Fehlersuche](#8-fehlersuche) +9. [Häufige Fragen](#9-häufige-fragen) + +--- + +## 1. Installation ```bash sudo apt install texlive-latex-extra texlive-fonts-extra texlive-science pandoc dvipng @@ -23,40 +38,14 @@ pip install -r requirements.txt python3 formula_img2latex.py --version ``` -### Die beiden lokalen Bildmodelle starten - -Zwei Vision-Sprachmodelle lesen jede Formel, je eines pro Grafikkarte (je ~18–21 GB). In zwei Terminals: - -```bash -# Modell A: Qwen3-VL auf GPU 1, Port 8011 -M=~/endeavour_home/lm_studio/models/lmstudio-community/Qwen3-VL-30B-A3B-Instruct-GGUF -CUDA_VISIBLE_DEVICES=1 ~/.local/bin/llama-server \ - -m $M/Qwen3-VL-30B-A3B-Instruct-Q4_K_M.gguf --mmproj $M/mmproj-Qwen3-VL-30B-A3B-Instruct-F16.gguf \ - --host 127.0.0.1 --port 8011 -ngl 99 -c 32768 --parallel 8 - -# Modell B: Gemma 4 auf GPU 2, Port 8012 -M=~/nvme2n1p7_home/huggingface/models/gemma4 -CUDA_VISIBLE_DEVICES=2 ~/.local/bin/llama-server \ - -m $M/gemma-4-26B-A4B-it-Q4_0.gguf --mmproj $M/mmproj-gemma-4-26B-A4B-it-BF16.gguf \ - --host 127.0.0.1 --port 8012 -ngl 99 -c 16384 --parallel 4 -``` - -Qwen läuft mit 8 Plätzen zu je 4096 Tokens (zusammen 32768) und belegt rund 22,6 GB; `run` stellt bis zu -8 Anfragen gleichzeitig. Gemma braucht auf GPU 2 rund 17,5 GB. Laufen dort Chatterbox-TTS und der STT-Server, reicht der Speicher -nicht (Gemma bricht beim ersten Bild mit „out of memory“ ab); dann vorher stoppen und danach wieder starten: - -```bash -systemctl --user stop chatterbox-tts.service open-notebook-stt.service -systemctl --user start chatterbox-tts.service open-notebook-stt.service # nach der Arbeit -``` - -pix2text läuft zusätzlich auf der ersten sichtbaren GPU (unter 1 GB). Beenden mit Strg+C in beiden -Terminals. +- **TeX Live** setzt die Formeln für den Bildabgleich (pdflatex, Schriften `lmodern`, `newtx`, `newpx`, + `stix2`, `eulervm`, `txfonts`). +- **Pandoc** prüft, dass jede Formel später als MathML gesetzt werden kann. +- **dvipng** braucht nur `calibrate --synthetic`. ### pix2text (Formel-OCR) -pix2text läuft ohne Server im Programm selbst, auf der ersten sichtbaren Grafikkarte (sonst auf der CPU, -dann ~5× langsamer). Einmalig: +pix2text läuft ohne Server im Programm selbst. Einmalig: ```bash pip install "pix2text[multilingual]" @@ -64,24 +53,64 @@ pip install --no-deps pix2tex munch # nur für post_p pip uninstall -y onnxruntime && pip install onnxruntime-gpu # für die GPU ``` -Das Modell (~300 MB) wird beim ersten Lauf nach `~/.pix2text` geladen. Ohne Angabe nimmt pix2text die -Grafikkarte mit dem meisten freien Speicher und liest die Formeln gebündelt. Gerät festlegen mit -`FORMULA_P2T_DEVICE=cpu` oder `cuda:N` (`N` zählt innerhalb von `CUDA_VISIBLE_DEVICES`). +`pix2tex` wird ohne Abhängigkeiten installiert, weil es sonst eine alte `timm`-Version erzwingen würde. +Das Modell (~300 MB) wird beim ersten Lauf nach `~/.pix2text` geladen. pix2text nimmt die sichtbare +Grafikkarte mit dem meisten freien Speicher (unter 1 GB) und liest die Formeln gebündelt; ohne +`onnxruntime-gpu` läuft es auf der CPU, dann etwa 5× langsamer. -Gemma (Port 8012) wird nur für Formeln gefragt, bei denen pix2text und Qwen uneinig sind. Läuft Gemma -nicht, bleiben diese Formeln im Review. +### Modelle für die Bildmodelle -### Frühere Regel mit Cloud (optional) +| Modell | Datei | Ort | +|---|---|---| +| Qwen3-VL-30B-A3B-Instruct | `Qwen3-VL-30B-A3B-Instruct-Q4_K_M.gguf` + `mmproj-Qwen3-VL-30B-A3B-Instruct-F16.gguf` | `~/endeavour_home/lm_studio/models/lmstudio-community/Qwen3-VL-30B-A3B-Instruct-GGUF/` | +| Gemma-4-26B-A4B-it | `gemma-4-26B-A4B-it-Q4_0.gguf` + `mmproj-gemma-4-26B-A4B-it-BF16.gguf` (ggml-org, zusammen 15,8 GB) | `~/nvme2n1p7_home/huggingface/models/gemma4/` | -Bis Version 0.2.0 kam die Bestätigung von **GLM-4.6V** über OpenRouter (Qwen = Gemma = GLM). Diese Regel -ist weiter wählbar: im Arbeitsordner in `workspace.json` unter `settings` den Eintrag -`"consensus_rule": "vlm3"` setzen; dann muss `OPENROUTER_API_KEY` gesetzt sein. +Gemma neu laden: + +```bash +cd ~/nvme2n1p7_home/huggingface/models/gemma4 +hf download ggml-org/gemma-4-26B-A4B-it-GGUF gemma-4-26B-A4B-it-Q4_0.gguf \ + mmproj-gemma-4-26B-A4B-it-BF16.gguf --local-dir . +``` + +Optional, nur für den Strukturvergleich (aus): `pip install sympy lark`. --- -## 2. Ein Buch bearbeiten +## 2. Modellserver starten und beenden -### Schritt 1: Buch wie gewohnt umwandeln +Zwei Bildmodelle laufen als llama.cpp-Server, je eines pro Grafikkarte. In zwei Terminals: + +```bash +# Qwen3-VL auf GPU 1, Port 8011: 8 Plätze zu je 4096 Tokens, rund 22,6 GB +M=~/endeavour_home/lm_studio/models/lmstudio-community/Qwen3-VL-30B-A3B-Instruct-GGUF +CUDA_VISIBLE_DEVICES=1 ~/.local/bin/llama-server \ + -m $M/Qwen3-VL-30B-A3B-Instruct-Q4_K_M.gguf --mmproj $M/mmproj-Qwen3-VL-30B-A3B-Instruct-F16.gguf \ + --host 127.0.0.1 --port 8011 -ngl 99 -c 32768 --parallel 8 + +# Gemma 4 auf GPU 2, Port 8012: rund 17,5 GB +M=~/nvme2n1p7_home/huggingface/models/gemma4 +CUDA_VISIBLE_DEVICES=2 ~/.local/bin/llama-server \ + -m $M/gemma-4-26B-A4B-it-Q4_0.gguf --mmproj $M/mmproj-gemma-4-26B-A4B-it-BF16.gguf \ + --host 127.0.0.1 --port 8012 -ngl 99 -c 16384 --parallel 4 +``` + +Laufen auf GPU 2 Chatterbox-TTS und der STT-Server, reicht der Speicher für Gemma nicht. Dann vorher +stoppen und nach der Arbeit wieder starten: + +```bash +systemctl --user stop chatterbox-tts.service open-notebook-stt.service +systemctl --user start chatterbox-tts.service open-notebook-stt.service # nach der Arbeit +``` + +Gemma wird nur für Formeln gefragt, bei denen pix2text und Qwen uneinig sind. Läuft Gemma nicht, meldet +`run` das einmal, und diese Formeln bleiben im Review. Beenden: Strg+C in beiden Terminals. + +--- + +## 3. Ein Buch bearbeiten + +### Schritt 1: Buch umwandeln ```bash cd ~/Python_Programs/epub2md @@ -90,7 +119,7 @@ python3 epub2md.py buch.epub output/buch Bei PDFs mit pdf2md immer `--images` (oder `--epub`) angeben, sonst gibt es keine Formelbilder. Für Mathebücher **`--formulas`** verwenden: Dann findet pdf2md jede Formel auf der Seite, auch im Fließtext und -in Merkkästen, und schneidet sie als Bild aus (braucht pix2text, siehe oben): +in Merkkästen, und schneidet sie als Bild aus (braucht pix2text): ```bash cd ~/Python_Programs/pdf2md @@ -106,10 +135,11 @@ cd ~/Python_Programs/formula_img2latex python3 formula_img2latex.py run ~/Python_Programs/epub2md/output/buch ``` -Bei pdf2md zusätzlich die Original-PDF angeben. Dann schneidet das Programm jede Formel scharf aus dem PDF: +Bei pdf2md die Markdown-Datei angeben und die Original-PDF dazu; Box-Bilder werden dann scharf aus dem PDF +neu ausgeschnitten: ```bash -python3 formula_img2latex.py run Output/buch.md --pdf buch.pdf +python3 formula_img2latex.py run ~/Python_Programs/pdf2md/Output/buch/buch.md --pdf buch.pdf ``` Das Programm arbeitet in Paketen zu 50 Formeln und meldet nach jedem Paket den Stand und die Zeiten je @@ -119,7 +149,7 @@ Stufe (die Stufen laufen teils gleichzeitig, daher ist die Summe größer als di 100/940 verarbeitet: automatisch ersetzt 78 (78%), Review 20, bleibt Bild/keine Formel 2 [Paket 35 s; pix2text 3 s, Qwen 30 s, Prüfung 14 s, Gemma 21 s] ``` -Am Ende steht eine Übersicht je Status, beim Gehrke-Brückenkurs zum Beispiel: +Am Ende steht eine Übersicht je Status, zum Beispiel: ```text auto_accepted 942 ← Lesart bestätigt, wird ersetzt @@ -128,15 +158,7 @@ Am Ende steht eine Übersicht je Status, beim Gehrke-Brückenkurs zum Beispiel: pending 539 ← Abbildungen/Symbole, nicht geprüft ``` -Dauer: etwa 0,8 s je Formel, also 35–50 Sekunden je Paket (Corral mit `--formulas`: 940 Formeln in -13 Minuten, das ganze Buch mit 9239 Formeln rund 2 Stunden). Am längsten braucht die Bestätigung durch Qwen. -Der Scan liest beim ersten Mal alle Bilder ein (Corral: 6 Minuten); beim Fortsetzen entfällt er, solange -sich das Markdown nicht geändert hat. -Abbrechen (Strg+C) und erneut starten ist kein Problem: Bereits gelesene Formeln werden nicht noch einmal -gefragt, der Lauf setzt fort. `--chunk 100` ändert die Paketgröße, `--limit 200` verarbeitet nur die ersten -200 Bilder. - -Neben dem Buchordner entsteht ein Arbeitsordner `buch.formulas/`. Er enthält alle Ergebnisse. Lösch ihn nicht, +Neben dem Buchordner entsteht ein Arbeitsordner `buch.formulas/` mit allen Ergebnissen. Lösch ihn nicht, solange du am Buch arbeitest. ### Schritt 3: Zweifelsfälle im Browser entscheiden @@ -152,12 +174,10 @@ Der Browser öffnet `http://127.0.0.1:8765/`. Für jede Formel siehst du: stimmt etwas nicht (Exponent, Vorzeichen, Klammer). - **Diff:** schwarz = beide gleich, rot = nur im Original, blau = nur im LaTeX, gelb = deutliche Abweichung. - **LaTeX:** Text im Eingabefeld (die Lesart von pix2text), darunter die Lesarten der Modelle zum - Anklicken. Oben steht der Grund für das Review, z. B. „Modelle uneinig“ mit den abweichenden Lesarten. - -Schaltflächen: + Anklicken. Oben steht der Grund für das Review, z. B. „pix2text und Qwen uneinig“. | Schaltfläche | Wirkung | -| --- | --- | +|---|---| | LaTeX prüfen | rendert dein (korrigiertes) LaTeX neu und zeigt Overlay und Messwerte | | LaTeX übernehmen | die Formel wird beim nächsten `apply` ersetzt | | Bild behalten | die Formel bleibt ein Bild | @@ -165,7 +185,8 @@ Schaltflächen: | Zurücksetzen | Entscheidung zurücknehmen | Jede Entscheidung wird sofort gespeichert. Du kannst jederzeit aufhören (Strg+C im Terminal) und später -weitermachen. `--all` zeigt auch die automatisch angenommenen Formeln. +weitermachen. `--all` zeigt auch die automatisch angenommenen Formeln, `--port` ändert den Port, +`--no-browser` öffnet keinen Browser. ### Schritt 4: Formeln ins Markdown schreiben @@ -176,31 +197,21 @@ python3 formula_img2latex.py apply ~/Python_Programs/epub2md/output/buch Aus `![…](../media/images/0042-…jpg)` wird `$L = \{-\sqrt{2};\sqrt{2}\}$` im Text bzw. `$$…$$` als eigener Absatz. Die Bilddateien bleiben liegen. Hat die Formelerkennung bei einer Formel im Text ein Satzzeichen -mit ausgeschnitten, steht es danach als normaler Text hinter der Formel (`$a^{x}$,`). +mit ausgeschnitten, steht es danach als normaler Text hinter der Formel (`$a^{x}$,`). In deutschen +Büchern werden Dezimalzahlen zu `3{,}6`; eine Gleichungsnummer steht als Text hinter der Formel. -Bei pdf2md-Büchern baut `pdf2md.py --from-md` das EPUB neu, mit allen Formeln als MathML: - -```bash -cd ~/Python_Programs/pdf2md && python3 pdf2md.py --from-md Output/buch/buch.md -``` - -**Danach die Gesamtdatei neu bauen**, sie enthält sonst noch die Bilder: +**Danach das Buch neu bauen:** ```bash +# epub2md: Gesamtdatei neu, dann z. B. EPUB mit MathML cd ~/Python_Programs/epub2md python3 epub2md_merge.py output/buch --force -``` - -Für EPUB und PDF mit Formeln gelten die Pandoc-Befehle aus der epub2md-Anleitung mit `--mathml`, z. B.: - -```bash -cd ~/Python_Programs/epub2md/output/buch -P=~/Python_Programs/epub2md/pandoc +cd output/buch && P=~/Python_Programs/epub2md/pandoc pandoc buch.md --toc --mathml --lua-filter $P/callouts.lua -o buch.epub -``` -In deutschen Büchern schreibt `apply` Dezimalzahlen als `3{,}6`, sonst setzt LaTeX „3, 6“. Eine -Gleichungsnummer steht als Text hinter der Formel, wie bei epub2md. +# pdf2md: EPUB mit allen Formeln als MathML +cd ~/Python_Programs/pdf2md && python3 pdf2md.py --from-md Output/buch/buch.md +``` ### Rückgängig machen @@ -208,64 +219,173 @@ Gleichungsnummer steht als Text hinter der Formel, wie bei epub2md. python3 formula_img2latex.py revert ~/Python_Programs/epub2md/output/buch ``` -Das stellt die Kapiteldateien wie vor dem ersten `apply` wieder her. Hast du eine Datei seitdem von Hand -geändert, wird sie übersprungen. `--force` setzt sie trotzdem zurück, dann ist die Handänderung weg. +Das stellt die Markdown-Dateien wie vor dem ersten `apply` wieder her. Hast du eine Datei seitdem von Hand +geändert, wird sie übersprungen; `--force` setzt sie trotzdem zurück, dann ist die Handänderung weg. --- -## 3. Einzelne Schritte statt `run` +## 4. Einzelne Schritte und Optionen ```bash -python3 formula_img2latex.py scan AUSGABE # Bilder finden -python3 formula_img2latex.py recognize AUSGABE --engine p2t # pix2text (zuerst) -python3 formula_img2latex.py recognize AUSGABE --engine local # Qwen (bestätigt) +python3 formula_img2latex.py scan AUSGABE # Bilder finden (immer neu) +python3 formula_img2latex.py recognize AUSGABE --engine p2t # pix2text +python3 formula_img2latex.py recognize AUSGABE --engine local # Qwen python3 formula_img2latex.py verify AUSGABE # prüfen und entscheiden -python3 formula_img2latex.py recognize AUSGABE --engine local2 --only-unresolved # Gemma (Stichwahl) +python3 formula_img2latex.py recognize AUSGABE --engine local2 --only-unresolved # Gemma python3 formula_img2latex.py verify AUSGABE -python3 formula_img2latex.py status AUSGABE +python3 formula_img2latex.py status AUSGABE # Zählung je Status (--json) ``` -Nützliche Optionen: +| Option | Befehle | Wirkung | +|---|---|---| +| `--limit 50` | run, recognize | nur die ersten 50 Bilder (zum Ausprobieren) | +| `--chunk 100` | run | Paketgröße (Standard 50) | +| `--pdf BUCH.pdf` | run, scan | pdf2md: Original-PDF für scharfe Neuausschnitte | +| `--preset cm` | run, verify | Schrift für den Bildabgleich fest vorgeben (`cm`, `times`, `txfonts`, `palatino`, `stix`, `euler`); Standard: automatisch je Buch | +| `--include-figures` | run, recognize, verify | auch Bilder versuchen, die wie Abbildungen aussehen | +| `--model NAME`, `--url URL` | run, recognize | anderes Modell bzw. anderer Server | +| `--workers N` | run, recognize, verify | parallele Anfragen bzw. Prüfprozesse | +| `--redo` | verify | bereits geprüfte Formeln neu prüfen | +| `--workspace ORDNER` | alle außer calibrate | Arbeitsordner an anderem Ort | +| `--quiet`, `-q` | alle | nur Fehler ausgeben | -| Option | Wirkung | -| --- | --- | -| `--limit 50` | nur die ersten 50 Bilder (zum Ausprobieren) | -| `--preset cm` | Schrift fest vorgeben (`cm`, `times`, `txfonts`, `palatino`, `stix`, `euler`); Standard: automatisch | -| `--include-figures` | auch Bilder versuchen, die wie Abbildungen aussehen | -| `--model NAME` | anderes Modell (lokal: Servername, Cloud: OpenRouter-ID) | -| `--workspace ORDNER` | Arbeitsordner an anderem Ort | -| `--redo` (verify) | bereits geprüfte Formeln neu prüfen, z. B. nach einem Programm-Update | +Alle Optionen: `python3 formula_img2latex.py BEFEHL --help`. --- -## 4. Häufige Fragen +## 5. Konfiguration + +### Umgebungsvariablen + +| Variable | Standard | Wirkung | +|---|---|---| +| `FORMULA_LOCAL_URL`, `FORMULA_LOCAL_MODEL` | `http://127.0.0.1:8011/v1`, Modell des Servers | Qwen-Server | +| `FORMULA_LOCAL2_URL`, `FORMULA_LOCAL2_MODEL` | `http://127.0.0.1:8012/v1`, Modell des Servers | Gemma-Server | +| `FORMULA_LOCAL_API_KEY`, `FORMULA_LOCAL2_API_KEY` | – | falls der Server einen Schlüssel verlangt | +| `FORMULA_P2T_DEVICE` | `auto` | pix2text: `auto`, `cpu` oder `cuda:N` (`N` zählt innerhalb von `CUDA_VISIBLE_DEVICES`) | +| `FORMULA_P2T_MODEL` | `mfr-1.5` | pix2text-Modell | +| `FORMULA_CLOUD_URL`, `FORMULA_CLOUD_MODEL` | `https://openrouter.ai/api/v1`, `z-ai/glm-4.6v` | nur Regel `vlm3` | +| `OPENROUTER_API_KEY` | – | nur Regel `vlm3` | +| `FORMULA_LOCAL_THINKING`, `FORMULA_LOCAL2_THINKING`, `FORMULA_CLOUD_THINKING` | `0` | `1` schaltet das „Nachdenken“ der Modelle wieder ein | +| `FORMULA_STRUCTURAL` | `0` | `1` schaltet den Strukturvergleich mit sympy ein | + +### Einstellungen je Buch (`workspace.json`) + +Im Arbeitsordner unter `settings`, zum Beispiel: + +```json +{ + "settings": { + "consensus_rule": "p2t", + "consensus_thresholds": {"veto_ncc": 0.65, "bold_relevant": 0} + } +} +``` + +| Schlüssel | Werte | Wirkung | +|---|---|---| +| `consensus_rule` | `p2t` (Standard), `vlm3` | Regel `vlm3`: Qwen = Gemma, bestätigt von GLM-4.6V über OpenRouter (bis 0.2.0) | +| `decide` | `consensus` (Standard), `pixel` | `pixel`: alter Pixel-Entscheider (bis 0.1.0) | +| `consensus_thresholds` | siehe [FUNKTIONSWEISE.md, Schwellen](FUNKTIONSWEISE.md#schwellen-settingsconsensus_thresholds-in-workspacejson) | Veto-Schwelle, Fettdruck, Schiedsrichter, PDF-Textebene … | +| `preset` | wird beim ersten Prüfen gesetzt | Schrift für den Bildabgleich | + +Nach einer Änderung `verify` aufrufen; die Modelle werden nicht neu gefragt. + +--- + +## 6. Betrieb + +**Dauer:** etwa 0,8 s je Formel, also 35–50 Sekunden je Paket (Corral mit `--formulas`: 940 Formeln in +13 Minuten, das ganze Buch mit 9239 Formeln rund 2 Stunden). Am längsten braucht die Bestätigung durch +Qwen. Der Scan liest beim ersten Mal alle Bilder ein (Corral: 6 Minuten); beim Fortsetzen entfällt er, +solange sich das Markdown nicht geändert hat. + +**Abbrechen und fortsetzen:** Strg+C und erneut `run` ist kein Problem. Bereits gelesene Formeln werden +nicht noch einmal gefragt; fehlgeschlagene Anfragen werden wiederholt. + +**Neu umgewandeltes Buch:** Nach `epub2md --force` erst `scan`, dann `apply`. Die Entscheidungen hängen am +Bildinhalt und bleiben erhalten. + +**Arbeitsordner:** `.formulas/` neben der Ausgabe. Er enthält die Datensätze, Ausschnitte, +Vergleichsbilder und die Sicherung von `apply`. Aufbau: [FUNKTIONSWEISE.md, Arbeitsordner](FUNKTIONSWEISE.md#3-arbeitsordner-und-datensätze). + +**Datenschutz:** Mit der Standardregel verlässt nichts den Rechner. Die Regel `vlm3` schickt Formelbilder, +bei denen Qwen und Gemma einig sind, samt eines kurzen Textausschnitts um die Formel (höchstens 240 +Zeichen) an OpenRouter; beim Gehrke-Brückenkurs waren das 967 Anfragen für zusammen rund 0,15–0,20 $. + +--- + +## 7. Werkzeuge zum Messen und Einstellen + +Die Werkzeuge schreiben nach `vergleich/` bzw. in den Arbeitsordner; beides wird nicht ins Git übernommen +(Buchausschnitte). + +| Werkzeug | Zweck | +|---|---| +| `tools/vergleich.py AUSGABE` | feste Stichprobe von 50 Formeln eines Buchs, verarbeitet wie `run`; Seite `vergleich//index.html` mit Original, gerenderten Lesarten, Entscheidung und Bildabgleich | +| `tools/buch_bericht.py AUSGABE` | Ergebnisseite eines ganzen Buchs nach `run`, filterbar; liegt in `/bericht/index.html` | +| `tools/regeltest.py EPUB [--n 50] [--reuse …] [--refresh local,local2]` | Standardregel an Formeln mit bekanntem TeX, wie `run`; Seite mit wahrer Formel, allen Lesarten und Urteil | +| `tools/zeittest.py EPUB` | Zeit und Trefferquote je Erkenner, seriell an 15 Formeln mit bekanntem TeX | +| `tools/vergleich_bericht.py --regeltest VORHER:NACHHER` | Vorher/Nachher-Seite zweier Läufe auf denselben Lesarten | +| `tools/konsens_bericht.py BERICHT.json EPUB` | Übersichtsseite zu `calibrate --consensus --report` | +| `formula_img2latex.py calibrate --consensus --engines local,local2,cloud,p2t --report X.json EPUB` | alle Regeln an Formeln mit bekanntem TeX messen; `--reuse X.json` rechnet ohne Modellabfragen neu, `--synthetic` erzeugt zusätzlich Gehrke-ähnliche Nachbildungen | + +Formeln mit bekanntem TeX liefern Springer-EPUBs, die das TeX jeder Formel im alt-Text tragen (z. B. +Göllmann). + +--- + +## 8. Fehlersuche + +| Meldung oder Beobachtung | Ursache | Abhilfe | +|---|---|---| +| `Lokaler Modellserver unter http://127.0.0.1:8011/v1 nicht erreichbar` | Qwen-Server läuft nicht | Server starten ([2](#2-modellserver-starten-und-beenden)) | +| `Stichwahl (Gemma) nicht möglich, strittige Formeln bleiben im Review` | Gemma-Server läuft nicht | Gemma starten und `run` erneut aufrufen | +| Gemma bricht beim ersten Bild mit „out of memory“ ab | GPU 2 ist belegt | Chatterbox-TTS und STT stoppen ([2](#2-modellserver-starten-und-beenden)) | +| `pix2text ist nicht installiert` / `pix2tex ist nicht installiert` | Pakete fehlen | Befehle aus [1](#pix2text-formel-ocr) ausführen | +| `onnxruntime ohne CUDA – pip install onnxruntime-gpu` | nur die CPU-Version von onnxruntime | `pip uninstall -y onnxruntime && pip install onnxruntime-gpu` | +| pix2text sehr langsam | läuft auf der CPU | `onnxruntime-gpu` installieren oder `FORMULA_P2T_DEVICE=cuda:0` | +| `pdflatex nicht gefunden` / `pandoc nicht gefunden` | TeX Live bzw. Pandoc fehlen | `sudo apt install texlive-latex-extra pandoc` | +| `Kein Arbeitsordner gefunden … (zuerst 'scan' ausführen)` | `verify`, `apply` usw. vor dem ersten `scan`/`run` | `scan` oder `run` ausführen | +| `… ist weder eine epub2md-Ausgabe … noch eine pdf2md-Ausgabe` | falscher Pfad | epub2md-Ordner bzw. pdf2md-`.md`-Datei angeben | +| `Arbeitsordner existiert und ist kein formula_img2latex-Ordner` | Zielordner von `--workspace` belegt | anderen Ordner wählen | +| `Bildverweis nicht mehr gefunden: datei.md:123` | Markdown wurde neu erzeugt oder von Hand geändert | `scan` ausführen | +| `revert` überspringt Dateien | seit `apply` von Hand geändert | prüfen, dann `revert --force` | +| `OPENROUTER_API_KEY ist nicht gesetzt` | Regel `vlm3` oder `--engine cloud` ohne Schlüssel | Schlüssel setzen oder Regel `p2t` verwenden | +| Formel bleibt in EPUB/PDF als TeX-Text stehen | Pandoc konnte sie nicht umsetzen (sollte die Pandoc-Prüfung verhindern) | Formel im Review korrigieren, Fall melden | + +**Rückgabecodes:** + +| Code | Bedeutung | +|---|---| +| 0 | Erfolg | +| 1 | Bedienfehler: Parameter, Eingabe oder Arbeitsordner nicht lesbar oder belegt | +| 2 | Eingabe ist keine epub2md- oder pdf2md-Ausgabe | +| 3 | Externes Werkzeug fehlt oder versagt: pdflatex, pandoc, Modellserver, pix2text | +| 4 | Unerwarteter interner Fehler (Traceback wird ausgegeben) | + +--- + +## 9. Häufige Fragen **Das Buch wurde mit epub2md `--force` neu umgewandelt. Ist meine Arbeit weg?** Nein. Die Entscheidungen hängen am Inhalt der Bilder, nicht an Dateinamen. `scan`, dann `apply`. **Warum landet ein Teil im Review?** Ersetzt wird nur, was pix2text liest und Qwen (oder bei Uneinigkeit Gemma) bestätigt. Sind Qwen und Gemma -gemeinsam anderer Meinung als pix2text, entscheidest du. Häufige Gründe für Review: ein Modell liest ein -Zeichen anders (oft pix2text: `\nu` statt fettem v, Wörter wie „fir“ statt „für“), sehr kleine oder -unscharfe Bilder, mehrzeilige Rechnungen mit anderen Zeilenumbrüchen oder `\underbrace`. Verschiedene -Rahmen derselben Zeilen (`aligned`, `cases`, `&`) gelten seit 0.4.0 als gleich. Im Review stehen die Lesarten +gemeinsam anderer Meinung als pix2text, entscheidest du. Häufige Gründe: ein Modell liest ein Zeichen anders +(oft pix2text: `\nu` statt fettem v, Wörter wie „fir“ statt „für“), sehr kleine oder unscharfe Bilder, +mehrzeilige Rechnungen mit anderen Zeilenumbrüchen oder `\underbrace`. Im Review stehen die Lesarten nebeneinander; oft genügt ein Blick und „LaTeX übernehmen“. **Wie sicher ist „automatisch ersetzt“?** -Gemessen an 300 Formeln mit bekannter Lösung: keine einzige falsche Formel wurde angenommen (Fehlerquote -sehr wahrscheinlich unter 2 %). Am Gehrke-Brückenkurs wurden 62 automatisch ersetzte Formeln von Hand -geprüft, alle korrekt. Fettdruck wird bewusst nicht geprüft: Ein fetter Vektor **v** kann als normales v -übernommen werden. +An einigen hundert Formeln mit bekannter Lösung wurde keine falsche Formel angenommen (Fehlerquote sehr +wahrscheinlich unter 2 %). Fettdruck wird bewusst nicht geprüft: Ein fetter Vektor **v** kann als normales +v übernommen werden. Messungen: [FUNKTIONSWEISE.md](FUNKTIONSWEISE.md#12-messungen). **Ich will erst ein paar Formeln ansehen, bevor das ganze Buch läuft.** -`python3 tools/vergleich.py AUSGABE` verarbeitet eine feste Stichprobe von 50 Formeln genau wie `run` -und schreibt `vergleich//index.html`: Originalbild, die gerenderten Lesarten aller Modelle, die -Entscheidung mit Grund und den Bildabgleich. Der Ordner `vergleich/` wird nicht ins Git übernommen. +`python3 tools/vergleich.py AUSGABE` verarbeitet eine feste Stichprobe von 50 Formeln genau wie `run` und +schreibt `vergleich//index.html`. **Geht die blaue Hinterlegung mancher Formeln verloren?** Ja. Das Bild hatte die Farbe eingebaut, LaTeX-Formeln haben keinen Hintergrund. - -**Werden Buchinhalte ins Internet geschickt?** -Nein, mit der Standardregel bleibt alles lokal. Nur die frühere Regel `vlm3` schickt die Formelbilder, bei -denen Qwen und Gemma einig sind, samt eines kurzen Textausschnitts um die Formel (höchstens 240 Zeichen) -an OpenRouter/GLM. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b87e85..7155f16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,136 +1,151 @@ -# Changelog +# Änderungsprotokoll -## Unveröffentlicht +Alle nennenswerten Änderungen an formula_img2latex. Format nach +[Keep a Changelog](https://keepachangelog.com/de/1.1.0/), Versionen nach +[Semantic Versioning](https://semver.org/lang/de/) (vor 1.0: Nebenversion = möglicher Bruch). +Messergebnisse im Einzelnen: [FUNKTIONSWEISE.md, Messungen](FUNKTIONSWEISE.md#12-messungen). + +## [Unreleased] Alle Formeln eines PDF-Buchs, auch im Fließtext, und etwa dreimal schneller. -- **pdf2md --formulas** (Nachbarprojekt): Die Formelerkennung von pix2text (MFD) findet jede Formel auf der - Seite, eingebettet oder abgesetzt, auch in Merkkästen. Sie wird zeichengenau aus dem Text geschnitten - (300 dpi) und mit Seite, Rechteck und PDF-Text in `formulas-*.json` vermerkt. "Formel"-Boxen mit Text - werden zu Text, solche mit Kurven zu Grafiken. formula_img2latex liest die Liste - (`sources/pdf2md.listed`). Corral, 10 % der 9239 Formeln: **79 % automatisch** (im Text 83 %). -- **Eingebettete Formeln:** Ein Satzzeichen am Ende wird vor dem Vergleich abgetrennt und als Text hinter - die Formel gesetzt (`final_suffix`). -- **Normalform:** `\underset{…}{\lim}` = `\lim_{…}`, Gleichungsnummer am Ende = `\tag`, `\textbf` als - Fettdruck, `\cfrac` = `\frac` (pix2text schreibt `\dfrac`), wörtliches `\n` aus Modellantworten entfällt. -- **Tempo** (Pilot 869 Formeln: 70 → ~24 min): - - Markdown-Verweise und Ausschnitte nur einmal lesen (`crops/`); - - pix2text gebündelt nach Pixelbudget, auf der freiesten GPU, einmal geladen; - - Lesen und Prüfen überlappen, die Prüfprozesse starten per `forkserver`; - - Qwen mit 8 parallelen Anfragen (`llama-server --parallel 8 -c 32768`); - - pdflatex beim Entscheiden nur, wenn gleiche Bilder möglich sind; - - kein neuer Scan beim Fortsetzen; - - je Paket nur dessen Datensätze laden; - - Zeiten je Stufe in der Statuszeile. -- **Bildprüfung:** Originale höchstens 1600 px breit, über 2,5 MP nicht, Lesarten mit mehr als 2,5-fach - falschen Proportionen sofort abgelehnt. Vorher hielten einzelne Formeln den Lauf über 10 Minuten auf. -- **Gemessen und verworfen:** - - PDF-Textebene als Bestätigung: 12 von 319 widersprachen Qwen, fehlende Punkte und Indizes; Schalter - `pdftext` bleibt aus. - - pix2texts eigene Sicherheit: Fehler auch bei 1,0. - - Qwen3-VL-8B: nicht schneller als das MoE-30B-Modell (3B aktiv), etwas schlechter. -- **Offen:** Formeln in Tabellen. +### Neu +- **Formeln im Fließtext** über `pdf2md --formulas` (Nachbarprojekt): Die Formelerkennung von pix2text (MFD) + findet jede Formel auf der Seite, auch in Merkkästen; pdf2md schneidet sie zeichengenau aus (300 dpi) und + vermerkt Seite, Rechteck und PDF-Text in `formulas-*.json`. formula_img2latex liest die Liste + (`sources/pdf2md.listed`). Corral, Stichprobe 10 % von 9239 Formeln: 79 % automatisch (im Text 83 %). +- Ein Satzzeichen am Ende einer Formel im Text wird vor dem Vergleich abgetrennt und als Text hinter die + Formel gesetzt (`final_suffix`). +- Normalform: `\underset{…}{\lim}` = `\lim_{…}`, Gleichungsnummer am Ende = `\tag`, `\textbf` als Fettdruck, + `\cfrac` = `\frac`, wörtliches `\n` aus Modellantworten entfällt. +- Schalter `pdftext` (aus): Die PDF-Textebene einer flachen Formel darf pix2text bestätigen. +- `LICENSE` (MIT) und `FUNKTIONSWEISE.md` (Aufbau, Datenfluss, Designentscheidungen, Grenzen). -## 0.4.0 – 2026-10-09 +### Geändert +- **Tempo** (Pilot 869 Formeln: 70 → ~24 Minuten): Markdown-Verweise und Ausschnitte nur einmal lesen + (`crops/`); pix2text gebündelt nach Pixelbudget auf der freiesten GPU, einmal geladen; Lesen und Prüfen + überlappen, die Prüfprozesse starten per `forkserver`; Qwen mit 8 parallelen Anfragen; pdflatex beim + Entscheiden nur, wenn gleiche Bilder möglich sind; kein neuer Scan beim Fortsetzen; je Paket nur dessen + Datensätze laden; Zeiten je Stufe in der Statuszeile. +- Bildprüfung: Originale höchstens 1600 px breit, über 2,5 MP nicht verglichen, Lesarten mit mehr als + 2,5-fach falschen Proportionen sofort abgelehnt (einzelne Formeln hielten den Lauf über 10 Minuten auf). +- Dokumentation nach dem Projektstandard gegliedert: README knapp mit Abschnitt „Documentation“, + Bedienungsanleitung mit Konfiguration, Betrieb und Fehlersuche, Hintergrund und Messungen in + `FUNKTIONSWEISE.md`. +- Hilfetexte der Kommandozeile beschreiben die Regel `p2t` statt des Pixelvergleichs; Cloud-Standard + `z-ai/glm-4.6v` statt `google/gemini-2.5-flash`. + +### Behoben +- `requirements.txt` enthält `requests` (von den Modell-Engines benötigt). + +Gemessen und verworfen: PDF-Textebene als Bestätigung (12 von 319 widersprachen Qwen), pix2texts eigene +Sicherheit (Fehler auch bei 1,0), Qwen3-VL-8B (nicht schneller, etwas schlechter). + +### Beim Update zu tun +- `pip install -r requirements.txt`. +- Qwen-Server mit `--parallel 8 -c 32768` neu starten (BEDIENUNGSANLEITUNG, Abschnitt 2). +- Für Formeln im Fließtext: aktuelles pdf2md verwenden und das Buch mit `--formulas` neu umwandeln. +- Bestehende Arbeitsordner bleiben gültig; Lesarten werden nicht neu gefragt. + +## [0.4.0] – 2026-10-09 Bessere Normalform: Lesarten, die sich nur in der Schreibweise unterscheiden, gelten als gleich. -Göllmann (400 Formeln mit bekannter Lösung, gleiche Lesarten): **+17 automatisch, weiterhin 0 falsch**. +Göllmann (400 Formeln mit bekannter Lösung, gleiche Lesarten): +17 automatisch, weiterhin 0 falsch. -- **Mehrzeilige Formeln:** gleich bei gleichen Zeilen mit gleichen Zeichen, egal in welchem Rahmen +### Neu +- **Mehrzeilige Formeln** sind gleich bei gleichen Zeilen mit gleichen Zeichen, egal in welchem Rahmen (`aligned`/`gathered`/`split` mit `&` oder nur `\\`; `cases` = `\left\{array\right.` = `\left\{aligned\right.`); eine leere letzte Zeile zählt nicht. In Matrizen trennt `&` weiter Spalten. -- **Argumente nach Argumentzahl:** Eine Gruppe ist nur Argument, wenn der Befehl noch eines erwartet - (`t^{2}\,{\sqrt{2}}` = `t^2\sqrt{2}`, `\Delta{x_{n}}` = `\Delta x_n`). -- **Weitere Gleichsetzungen:** `\cdot\cdot\cdot` und `...` = `\dots`, `\root3\of{x}` = `\sqrt[3]{x}`, +- Weitere Gleichsetzungen: `\cdot\cdot\cdot` und `...` = `\dots`, `\root3\of{x}` = `\sqrt[3]{x}`, `\left.`/`\right.` entfallen. -- **Messung genauer:** Der Wahrheitsvergleich nutzt dieselbe Normalform; richtige mehrzeilige Lesarten - zählten bisher als falsch (pix2text allein richtig: Band 2 jetzt 45/50 und 47/50). -- **Ergebnis:** Band 1 89 % / 88 % automatisch (bisher 86 % / 85 %), Band 2 44 und 45 von 50 (bisher 39 - und 43), 0 falsch. -- **Strukturvergleich mit sympy** (`structure.py`, neu, optional, aus): gleicher unausgewerteter - Ausdrucksbaum, nichts umgerechnet; Relationen nie an sympy, Zeichen durch Platzhalter geschützt. - Brachte vor der neuen Normalform +1 Formel, danach nichts – `structural` = 1 bzw. - `FORMULA_STRUCTURAL=1` schaltet ihn ein (`pip install sympy lark`). -- **Bild-Schiedsrichter** (`verify.image_arbiter`, gemessen, aus): bei „Qwen = Gemma gegen pix2text“ - entscheidet der Bildbeleg. +11 Formeln ab Schwelle 8, aber der gemeinsame Fehler `\mathcal{E}_r` - bekam Beleg 7,2 – zu knapp. Einstellungen `arbiter`, `arbiter_margin`. -- **Prompt-Stilvorgabe** getestet und verworfen (Band 2: 44/44 statt 44/45; die Modelle schrieben nach - `\\` ein wörtliches `\n`). Prompt-Version bleibt 2, gespeicherte Lesarten bleiben gültig. -- **Werkzeuge:** `tools/vergleich_bericht.py` (Vorher/Nachher-Seite zweier Läufe), `regeltest.py - --refresh` (Engines trotz `--reuse` neu fragen) und `--arbiter`; `calibrate` misst den - Schiedsrichter (Werte je Fall, Schwellen) und listet nur strukturell gefundene Übereinstimmungen; - `tools/buch_bericht.py` (Ergebnisseite eines ganzen Buchs: jede Formel mit Original, gesetztem - LaTeX, Status, Begründung und allen Lesarten, filterbar). -- **Erstes ganzes PDF-Buch** (Corral, *Elementary Calculus*, 346 Seiten, pdf2md): 1150 Ausschnitte, - **429 automatisch ersetzt (37 %)**, 633 Review, 61 keine Formel. Gefundene und behobene Probleme: - - pix2text schreibt hier Brüche als `\cfrac`: in der Normalform = `\frac`, beim Bereinigen zu - `\dfrac` (Pandoc kennt `\cfrac` nicht und ließ die Formel als TeX-Text stehen). - - Qwen und Gemma schreiben Zeilenumbrüche als wörtliches `\n` (`\begin{aligned}\n…`): wird zu - Leerraum (`tex.fix_literal_newlines`), echte Befehle wie `\nu`, `\neq` bleiben. - - 600-dpi-Ausschnitte bis 3800 × 2700 Pixel: Qwen lehnte sie ab („400 Bad Request“, Kontext voll), - eine ganzseitige Abbildung (33 MP) brachte den Bildvergleich zum Absturz. Jetzt: Bilder für die - Modelle höchstens 1600 Pixel lang, Bildvergleich nur bis 4 MP (größte übernommene Formel: 1,7 MP). - - Fehlgeschlagene Modellanfragen werden beim nächsten Lauf wiederholt (zählten als „schon gefragt“). - - Göllmann danach unverändert: 0 falsch. - - Grenze: pdf2md schneidet in diesem Buch auch Merkkästen mit Text, Tabellen und ganze - Herleitungsblöcke als „Formel“ aus; sie bilden den Großteil des Reviews. +- **Strukturvergleich mit sympy** (`structure.py`, optional, aus): gleicher unausgewerteter + Ausdrucksbaum. Einschalten mit `structural` = 1 bzw. `FORMULA_STRUCTURAL=1`. +- **Bild-Schiedsrichter** (`verify.image_arbiter`, aus): bei „Qwen = Gemma gegen pix2text“ entscheidet + der Bildbeleg. Einstellungen `arbiter`, `arbiter_margin`. +- Werkzeuge: `tools/vergleich_bericht.py` (Vorher/Nachher-Seite), `tools/buch_bericht.py` + (Ergebnisseite eines ganzen Buchs), `regeltest.py --refresh` und `--arbiter`. -## 0.3.0 – 2026-10-09 +### Geändert +- Eine Klammergruppe ist nur Argument, wenn der Befehl noch eines erwartet (`t^{2}\,{\sqrt{2}}` = + `t^2\sqrt{2}`, `\Delta{x_{n}}` = `\Delta x_n`). +- Der Wahrheitsvergleich der Messung nutzt dieselbe Normalform (richtige mehrzeilige Lesarten zählten + bisher als falsch). +- Bilder für die Modelle höchstens 1600 Pixel lang. -Neue Standardregel `p2t`: **pix2text liest zuerst, Qwen bestätigt, Gemma nur bei Uneinigkeit**; GLM (Cloud) -wird nicht mehr benutzt. +### Behoben +- pix2texts `\cfrac` wird beim Bereinigen zu `\dfrac` (Pandoc kennt `\cfrac` nicht und ließ die Formel als + TeX-Text stehen). +- Wörtliches `\n` in Antworten von Qwen und Gemma wird zu Leerraum (`tex.fix_literal_newlines`). +- Sehr große Ausschnitte: Qwen lehnte sie ab („400 Bad Request“), eine ganzseitige Abbildung brachte den + Bildvergleich zum Absturz. +- Fehlgeschlagene Modellanfragen werden beim nächsten Lauf wiederholt (zählten als „schon gefragt“). +Gemessen und verworfen: Strukturvergleich (nach der neuen Normalform kein Gewinn), Bild-Schiedsrichter +(der gemeinsame Fehler `\mathcal{E}_r` bekam Beleg 7,2), Prompt-Stilvorgabe (keine Verbesserung). +Erstes ganzes PDF-Buch: Corral, *Elementary Calculus*, 1150 Ausschnitte, 429 automatisch (37 %). + +### Beim Update zu tun +- Nichts Pflichtiges. Prompt-Version bleibt 2, gespeicherte Lesarten bleiben gültig; `run` rechnet die + Entscheidungen mit der neuen Normalform neu. +- Optional für den Strukturvergleich: `pip install sympy lark`. + +## [0.3.0] – 2026-10-09 + +Neue Standardregel `p2t`: pix2text liest zuerst, Qwen bestätigt, Gemma nur bei Uneinigkeit; GLM (Cloud) wird +nicht mehr benutzt. Göllmann Band 1: 86 % / 85 % automatisch, 0 falsch (bisher 78 % / 70 %). + +### Neu - **pix2text** (Formel-OCR MFR 1.5, ONNX) als Engine `p2t`, im Prozess, auf der GPU mit `onnxruntime-gpu` - (`FORMULA_P2T_DEVICE`: `auto`, `cpu`, `cuda:N`). Allein am häufigsten richtig (Göllmann Band 1: 90 % / - 85 %) und am schnellsten (0,1 s/Formel auf der GPU, Qwen 0,7 s, Gemma 1,8 s). -- **Regel:** übernommen wird immer pix2texts Lesart (trifft den Fettdruck am besten), wenn Qwen sie - bestätigt oder – nur bei Uneinigkeit gefragt – Gemma. Qwen und Gemma gemeinsam gegen pix2text gehen - an einen Menschen (so kamen an Band 1 die gemeinsamen Fehler der beiden Bildmodelle durch). Der - Schlusspunkt muss bestätigt sein. Bild-Veto und Pandoc wie bisher, geprüft auf pix2texts Lesart. - Die alte Regel bleibt als `"consensus_rule": "vlm3"` in `workspace.json` wählbar. -- **pix2text-Ausgabe bereinigt:** Leerzeichen mit `post_process` aus pix2tex (installiert mit - `--no-deps`), Fettschreibweisen `\mathrm{\boldmath~ x ~}` / `\mathrm{\bf~ r o t}` → - `\boldsymbol{x}` / `\mathbf{rot}`. An 400 Lesarten geprüft: gleiches gesetztes Bild. -- **Normalform:** `\operatorname{l i m}` = `\lim`, `\ddot{u}` in Text = ü, `^{\prime}` = `'`, - abschließendes `\\` vor `\end`, `\begin{array}[t]`, `\mathop{…}`. -- **calibrate:** beliebig viele Engines (`--engines`), Regel-Tabelle, `--reuse` rechnet aus einem - früheren Bericht ohne Modellabfragen; wahre Formeln aus dem alt-Text werden bereinigt - (einzeiliges `aligned`, `\mathop{}\!\mathrm{d}`, upgreek). -- **Werkzeuge:** `tools/zeittest.py` (Zeit und Trefferquote je Tool), `tools/regeltest.py` (Standardregel - an Formeln mit bekanntem TeX, Seite zum Mitbeurteilen; `--reuse`, `--exclude`). -- **Messung:** Göllmann Band 1 (2 × 150) 86 % / 85 % automatisch, 0 falsch (bisher 78 % / 70 %); - Band 2, zwei frische Stichproben zu 50: 78 % und 86 % automatisch, 0 falsch. + (`FORMULA_P2T_DEVICE`: `auto`, `cpu`, `cuda:N`). +- **Regel `p2t`:** übernommen wird pix2texts Lesart, wenn Qwen sie bestätigt oder – nur bei Uneinigkeit + gefragt – Gemma. Qwen und Gemma gemeinsam gegen pix2text gehen an einen Menschen. +- `calibrate`: beliebig viele Engines (`--engines`), Regel-Tabelle, `--reuse` ohne Modellabfragen. +- Werkzeuge: `tools/zeittest.py`, `tools/regeltest.py`. -## 0.2.0 – 2026-10-07 +### Geändert +- pix2text-Ausgabe bereinigt: Leerzeichen mit `post_process` aus pix2tex, Fettschreibweisen + `\mathrm{\boldmath~ x ~}` → `\boldsymbol{x}`. An 400 Lesarten geprüft: gleiches gesetztes Bild. +- Normalform: `\operatorname{l i m}` = `\lim`, `\ddot{u}` in Text = ü, `^{\prime}` = `'`, abschließendes + `\\` vor `\end`, `\begin{array}[t]`, `\mathop{…}`. -Entscheider ist jetzt der **Konsens dreier Bildmodelle** statt eines Pixelvergleichs. +### Beim Update zu tun +- pix2text installieren: `pip install "pix2text[multilingual]"`, `pip install --no-deps pix2tex munch`, + für die GPU `pip uninstall -y onnxruntime && pip install onnxruntime-gpu`. +- Bestehende Arbeitsordner: `run` erneut aufrufen; pix2text liest nach, Qwen-Lesarten bleiben gültig. +- Wer die Regel mit GLM behalten will: `"consensus_rule": "vlm3"` in `workspace.json`. -- **Annahmeregel:** jedes Zeichen richtig und an der richtigen Stelle; Abstände, Längen, Schriftgröße, - kursiv/aufrecht und Fettdruck egal. -- **Konsens:** Qwen3-VL-30B-A3B (lokal) = Gemma-4-26B-A4B (lokal) = GLM-4.6V (OpenRouter, andere - Modellfamilie); Pandoc muss die Formel umsetzen; grobes Bild-Veto (elastischer Vergleich, - Ähnlichkeit ≥ 0,65). Bei 2:1 entscheidet ein Mensch. -- **`consensus.py`:** Normalform auf Baumbasis (Abstände, `\left`/`\right`, freistehende Klammern, - `\vec`/`\overrightarrow`, `\frac`/`\dfrac`, `\iff`/`\Leftrightarrow`, Matrix-Umgebungen, TeX-Umlaute, - `\atop`/`\substack`, kursiv/aufrecht, fett/normal) und Render-Gleichheit als Rückfallebene. -- **Engines:** zweite lokale Engine `local2`; Token-Wahrscheinlichkeiten aus Logprobs; Nachdenken - abgeschaltet (Gemma ~10× schneller, GLM besser und ~25× billiger); Antworten auf 800 Tokens begrenzt; - Prompt fragt ausdrücklich nach Fettdruck; Prompt-Version und Nachdenk-Einstellung im Cache-Schlüssel. -- **`run`:** in Paketen zu 50 mit Statuszeile; beide lokalen Modelle parallel; GLM nur für Einige; - geprüft wird nur die übernommene Lesart; Prüfergebnisse mit Verfahrensversion. -- **Bildvergleich:** elastische Ausrichtung (DTW über Spalten und Zeilen, Dehnen kostet proportional zur - Tinte); layouttreue Prüfvarianten und rauschnormierter Kontrast im Pixel-Modus (`decide=pixel`). -- **apply:** deutsches Dezimalkomma `3{,}6`. -- **Behoben:** Gleichungsnummern wurden aus alten, verworfenen Lesarten übernommen (erfundene „(3.7)“); - sie kommen jetzt nur aus der übernommenen Lesart. Gleichzeitige Speicherungen zweier Engines sind - abgesichert. Verify stürzte bei Formeln ohne aktuelle Lesart ab. -- **Werkzeuge:** `calibrate --consensus`, `tools/vergleich.py` (50er-Stichprobe mit Bildern), - `tools/konsens_bericht.py`; Ausgaben in `vergleich/` (nicht versioniert). -- **Messung** (2 × 150 Formeln mit bekanntem TeX): 63–68 % automatisch, 0 falsch. - Gehrke-Brückenkurs: 942 von 1505 automatisch (63 %), 62 von 62 geprüften korrekt. +## [0.2.0] – 2026-10-07 -## 0.1.0 – 2026-10-07 +Entscheider ist der Konsens dreier Bildmodelle statt eines Pixelvergleichs. +Gehrke-Brückenkurs: 942 von 1505 automatisch (63 %), 62 von 62 geprüften korrekt. -Erste Fassung: scan, recognize (ein Vision-Modell), verify mit Pixelvergleich und Kontrasttest gegen -Verwechslungsvarianten, Review-Galerie, apply/revert, calibrate. Sicher (0 Falsch-Annahmen), aber an -echten Büchern nur 1–2 % automatisch. +### Neu +- **Konsens:** Qwen3-VL-30B-A3B (lokal) = Gemma-4-26B-A4B (lokal) = GLM-4.6V (OpenRouter); Pandoc muss die + Formel umsetzen; grobes Bild-Veto (elastischer Vergleich, Ähnlichkeit ≥ 0,65). +- `consensus.py`: Normalform auf Baumbasis und Render-Gleichheit als Rückfallebene. +- Zweite lokale Engine `local2`; Token-Wahrscheinlichkeiten aus Logprobs. +- `run` in Paketen zu 50 mit Statuszeile; `calibrate --consensus`; `tools/vergleich.py`, + `tools/konsens_bericht.py`. +- `apply`: deutsches Dezimalkomma `3{,}6`. + +### Geändert +- Nachdenken der Modelle abgeschaltet (Gemma ~10× schneller, GLM besser und ~25× billiger); Antworten auf + 800 Tokens begrenzt; Prompt fragt ausdrücklich nach Fettdruck. +- Elastischer Bildvergleich (DTW über Spalten und Zeilen, Dehnen kostet proportional zur Tinte). +- Geprüft wird nur die übernommene Lesart; Prüfergebnisse tragen eine Verfahrensversion. + +### Behoben +- Gleichungsnummern wurden aus alten, verworfenen Lesarten übernommen (erfundene „(3.7)“). +- Gleichzeitiges Speichern zweier Engines; Absturz bei Formeln ohne aktuelle Lesart. + +### Beim Update zu tun +- Zwei llama.cpp-Server starten (Qwen auf Port 8011, Gemma auf Port 8012) und Gemma laden + (BEDIENUNGSANLEITUNG, Abschnitte 1 und 2); `OPENROUTER_API_KEY` setzen. +- Arbeitsordner aus 0.1.0: Standard ist jetzt der Konsens; den alten Entscheider behält + `"decide": "pixel"` in `workspace.json`. + +## [0.1.0] – 2026-10-07 + +Erstveröffentlichung: scan, recognize (ein Bildmodell), verify mit Pixelvergleich und Kontrasttest gegen +Verwechslungsvarianten, Review-Galerie, apply/revert, calibrate. Sicher (0 Falsch-Annahmen), aber an echten +Büchern nur 1–2 % automatisch. diff --git a/FUNKTIONSWEISE.md b/FUNKTIONSWEISE.md new file mode 100644 index 0000000..819a1d3 --- /dev/null +++ b/FUNKTIONSWEISE.md @@ -0,0 +1,432 @@ +# formula_img2latex – Funktionsweise + +Dieses Dokument erklärt, **wie formula_img2latex innen arbeitet und warum es so gebaut ist**. Für die +Benutzung siehe [BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md), für Änderungen je Version +[CHANGELOG.md](CHANGELOG.md). + +Stand: Version 0.4.0 und unveröffentlichte Änderungen (siehe CHANGELOG, Abschnitt `[Unreleased]`) + +--- + +## Inhaltsverzeichnis + +1. [Überblick](#1-überblick) +2. [Module](#2-module) +3. [Arbeitsordner und Datensätze](#3-arbeitsordner-und-datensätze) +4. [Ablauf von `run`](#4-ablauf-von-run) +5. [Die Erkenner](#5-die-erkenner) +6. [Entscheidungsregel](#6-entscheidungsregel) +7. [Wann zwei Lesarten dieselbe Formel sind](#7-wann-zwei-lesarten-dieselbe-formel-sind) +8. [Bild-Veto: der elastische Bildvergleich](#8-bild-veto-der-elastische-bildvergleich) +9. [apply und revert](#9-apply-und-revert) +10. [Quellen: epub2md und pdf2md](#10-quellen-epub2md-und-pdf2md) +11. [Sicherheit](#11-sicherheit) +12. [Messungen](#12-messungen) +13. [Designentscheidungen](#13-designentscheidungen) +14. [Grenzen](#14-grenzen) + +--- + +## 1. Überblick + +Ein Formelbild wird nur durch LaTeX ersetzt, wenn **zwei verschiedenartige Erkenner dieselbe Formel lesen** +und ein grober Bildabgleich nicht widerspricht. Die Annahmeregel des Projekts: + +> Jedes Zeichen muss stimmen und an der richtigen Stelle relativ zu den anderen stehen. Abstände, +> Pfeil- und Bruchstrichlängen, Schriftgröße, kursiv/aufrecht und Fettdruck dürfen abweichen. + +```mermaid +flowchart TB + A["Markdown von
epub2md oder pdf2md
mit Formelbildern"] --> S["scan
Bildverweise finden,
Formel oder Abbildung?"] + S --> R1["pix2text
(Formel-OCR)
liest jede Formel"] + R1 --> R2["Qwen3-VL
(Bildmodell)
liest jede Formel"] + R2 --> V["Bild-Veto
pix2texts Lesart rendern
und elastisch vergleichen"] + V --> D{"Entscheidung
pix2text = Qwen?"} + D -->|"uneinig"| G["Gemma 4
(zweites Bildmodell)
Stichwahl"] + G --> D2{"Gemma = pix2text?"} + D -->|"einig, Veto und
Pandoc bestanden"| OK["auto_accepted"] + D2 -->|"ja"| OK + D2 -->|"nein"| RV["needs_review
Review-Galerie"] + RV -->|"Mensch übernimmt
oder korrigiert"| OK + OK --> AP["apply
Bildverweis im Markdown
→ LaTeX-Formel"] +``` + +Übernommen wird immer die Lesart von pix2text; Qwen und Gemma bestätigen nur. Was nicht bestätigt ist, +bleibt Bild, bis ein Mensch entscheidet. + +--- + +## 2. Module + +| Modul | Aufgabe | Wichtigste Namen | +|---|---|---| +| `cli.py` | Unterbefehle, Rückgabecodes | `main`, `build_parser` | +| `pipeline.py` | scan, recognize, verify, Entscheidung, `run` in Paketen, apply/revert | `scan`, `recognize`, `verify_all`, `decide_all_consensus`, `run_consensus`, `apply`, `revert` | +| `decide.py` | Annahmeregeln und Schwellen | `decide_pair_tiebreak` (Regel `p2t`), `decide_consensus` (Regel `vlm3`), `decide` (Pixel-Modus), `CONSENSUS_DEFAULTS` | +| `consensus.py` | Sagen zwei Lesarten dasselbe? | `canonical`, `same_reading`, `render_equal` | +| `structure.py` | Strukturvergleich mit sympy (optional, aus) | `structural_equal` | +| `engines/p2t.py` | pix2text (MFR 1.5, ONNX) im Prozess, gebündelt, CPU oder GPU | `Pix2TextEngine`, `tidy_p2t`, `resolve_device` | +| `engines/vlm.py` | Bildmodelle über die OpenAI-API (llama.cpp, OpenRouter), Logprobs | `VisionEngine`, `PROMPT`, `PROMPT_VERSION`, `prepare_image`, `parse_answer` | +| `verify.py` | Bildprüfung einer Lesart (Veto bzw. Pixel-Entscheider) | `verify`, `image_arbiter` | +| `compare.py` | Ausrichtung, elastische Verzerrung, Kennzahlen, Kontrast, Diff-Bilder | `compare`, `elastic_warp`, `contrast`, `diff_image` | +| `render.py` | pdflatex-Batch, Schrift-Presets, Rasterung | `render_many`, `PRESETS` | +| `confusables.py` | Verwechslungsvarianten (nur Pixel-Modus) | `variants`, `EQUIVALENT` | +| `tex.py` | TeX-Hilfen: Begrenzer, Sicherheitsprüfung, Gleichungsnummern | `unsafe_reason`, `split_tag`, `prepare_for_render`, `text_to_tex` | +| `imageio.py` | Originale laden: PNG/GIF/JPG/SVG, Transparenz, getönter Hintergrund | `load_ink`, `gray_to_ink`, `color_stats` | +| `classify.py` | Vorfilter: Formel, unklar oder Abbildung | `classify` | +| `checks.py` | Pandoc/MathML-Prüfung | `pandoc_mathml_ok` | +| `sources/` | Quellen: Markdown-Scan, Begrenzer, Dezimalkomma; epub2md, pdf2md | `open_source`, `scan_markdown`, `Epub2MdSource`, `Pdf2MdSource` | +| `pdfcrop.py` | Box-Bild auf der PDF-Seite finden, 600-dpi-Neuausschnitt | `PdfCropper` | +| `store.py` | Arbeitsordner, ein JSON-Datensatz je Formel | `Workspace`, `Formula`, `STATUSES` | +| `calibrate.py` | Messung an Formelbildern mit bekanntem TeX | `calibrate`, `calibrate_consensus` | +| `review/` | lokaler Review-Server und Seite | `serve` | +| `tools/` | Mess- und Berichtswerkzeuge (nicht Teil des Pakets) | `vergleich.py`, `regeltest.py`, `zeittest.py`, `buch_bericht.py`, `konsens_bericht.py`, `vergleich_bericht.py` | + +--- + +## 3. Arbeitsordner und Datensätze + +```text +.formulas/ +├── workspace.json # Quelle, Schrift, Einstellungen, letzter Scan, apply-Zustand +├── formulas/f--.json # ein Datensatz je Bild und Darstellung (d = abgesetzt, i = im Text) +├── crops/.png # Original je Formel, einmal ausgeschnitten (PDF-Quellen) +├── review/-{orig,render,rigid,diff}.png # Bilder des Bildabgleichs +├── bericht/ # Ergebnisseite (tools/buch_bericht.py) +└── backup/ # Markdown-Dateien vor dem ersten apply +``` + +Der Arbeitsordner liegt **neben** der Ausgabe, weil `epub2md --force` die Ausgabe vollständig ersetzt. + +Ein Datensatz (`store.Formula`) hält: Bildpfad und SHA-256, Darstellung (abgesetzt oder im Text), alle +Fundstellen im Markdown (Datei, Zeile, genauer Bildverweis, Textausschnitt), die Vorklassifikation, **alle +Lesarten** (je Engine, Modell, Prompt-Version und Nachdenk-Einstellung, mit Zeit und Token-Wahrscheinlichkeiten), +das Ergebnis des Bildabgleichs, Status, Begründung und das endgültige LaTeX (`final_tex`, dazu +`final_suffix` für ein abgetrenntes Satzzeichen). + +**Schlüssel und Zwischenspeicher:** + +| Was | Schlüssel | Wirkung | +|---|---|---| +| Datensatz | SHA-256 des Bildes + Darstellung | ein erneuter epub2md-Lauf verliert keine Entscheidung | +| Lesart | Engine, Modell, `PROMPT_VERSION`, Nachdenken | dieselbe Frage wird nie zweimal gestellt; fehlgeschlagene Anfragen werden wiederholt | +| Bildabgleich | `VERIFY_VERSION` + geprüfte Lesart | nach einer Änderung am Vergleich wird neu gerechnet | +| Scan | Größe und Zeitstempel aller Markdown-Dateien | beim Fortsetzen entfällt der Scan, solange sich das Markdown nicht ändert | + +**Status:** `pending`, `auto_accepted`, `needs_review`, `not_formula`, `rejected_keep_image`, +`render_failed`, `no_candidate`; nach dem Review `accepted_by_human`, `corrected_by_human`, +`kept_by_human`. `apply` schreibt `auto_accepted`, `accepted_by_human` und `corrected_by_human`. + +--- + +## 4. Ablauf von `run` + +`run` verarbeitet die Formeln in **Paketen zu 50** und meldet nach jedem Paket eine Statuszeile mit Zeiten +je Stufe. Beide Stufen laufen gleichzeitig: Während die CPU Paket n gegen die Bilder prüft, lesen +pix2text und Qwen auf der GPU schon Paket n+1. + +```mermaid +flowchart TB + subgraph GPU["GPU: Lesen (Paket n+1)"] + direction TB + P["pix2text
gebündelt nach
Pixelbudget"] --> Q["Qwen
bis 8 Anfragen
gleichzeitig"] + end + subgraph CPU["CPU: Prüfen (Paket n)"] + direction TB + V1["Bild-Veto
Prozesspool"] --> E1["Entscheidung"] + E1 --> T{"Stichwahl
nötig?"} + T -->|"ja"| G["Gemma
(zweite GPU)"] + G --> E2["Entscheidung"] + T -->|"nein"| Z["Statuszeile"] + E2 --> Z + end +``` + +- **Scan** (`pipeline.scan`): findet jeden Bildverweis außerhalb von Codeblöcken, fasst gleiche Bilder + zusammen, klassifiziert (Farbe der Tinte, Graustufen, Höhe, Stellung im Text) und speichert. Abbildungen + (`figure`) werden nicht gelesen, unklare (`maybe`) schon. +- **Lesen:** pix2text liest jede Formel, Qwen ebenfalls. Gemma nur, wenn pix2text und Qwen uneinig sind. +- **Prüfen** (`verify_all`): rendert nur die Lesart, die übernommen werden kann (pix2texts), und vergleicht + sie mit dem Original. Die Prozesse starten per `forkserver`, weil der Lesefaden gleichzeitig in + CUDA/pix2text arbeitet und ein `fork` mitten darin halb gehaltene Sperren erbt. +- **Entscheiden** (`decide_all_consensus`): siehe [6](#6-entscheidungsregel). + +Ein abgebrochener Lauf setzt beim nächsten `run` fort: Lesarten und Prüfergebnisse liegen im +Arbeitsordner. + +--- + +## 5. Die Erkenner + +| Engine | Modell | Art | Rolle (Regel `p2t`) | +|---|---|---|---| +| `p2t` | pix2text MFR 1.5 (ONNX) | Formel-OCR, im Prozess | liest zuerst, **seine Lesart wird übernommen** | +| `local` | Qwen3-VL-30B-A3B-Instruct (llama.cpp, Port 8011) | Bildmodell (MoE, ~3B aktiv) | bestätigt | +| `local2` | Gemma-4-26B-A4B-it (llama.cpp, Port 8012) | Bildmodell | Stichwahl bei Uneinigkeit | +| `cloud` | GLM-4.6V über OpenRouter | Bildmodell | nur Regel `vlm3` | + +**pix2text** (`engines/p2t.py`) läuft auf der sichtbaren GPU mit dem meisten freien Speicher (sonst CPU) und +liest gebündelt. Seine Ausgabe wird bereinigt (`tidy_p2t`): überflüssige Leerzeichen entfernt +`post_process` aus pix2tex (`\frac{\alpha} {3 6 0}` → `\frac{\alpha}{360}`), Fettschreibweisen wie +`\mathrm{\boldmath~ x ~}` werden zu `\boldsymbol{x}`, `\cfrac` zu `\dfrac` (Pandoc kennt `\cfrac` nicht), +eine Gleichungsnummer nach großem Abstand zu `\tag{…}`. + +**Bildmodelle** (`engines/vlm.py`) bekommen das Bild vorbereitet (`prepare_image`): auf Weiß gelegt, +kleine Formeln auf mindestens 96 px Höhe vergrößert, große auf höchstens 1600 px Kantenlänge verkleinert. +Der Prompt verlangt eine buchstabengetreue Abschrift (ausdrücklich mit Fettdruck), `\tag{}` für +Gleichungsnummern und eine JSON-Antwort; ein kurzer Textausschnitt um die Formel wird zur Orientierung +mitgegeben. Weitere Festlegungen: + +- **Nachdenken aus:** Gemma 4 wird ~10× schneller, GLM liest ohne besser und billiger. +- **Antwortlänge** höchstens 800 Tokens: lange mehrzeilige Formeln brauchen ~400, Endlosschleifen werden + gekappt. +- **Token-Wahrscheinlichkeiten** (Logprobs) des LaTeX-Teils werden gespeichert. Die Selbstauskunft + („confidence“) wird erfragt, entscheidet aber nicht. +- **Robuste Auswertung:** JSON mit einfachen Backslashes (`\frac` → Seitenvorschub + `rac`) wird erkannt + und wörtlich neu gelesen; wörtliches `\n` in Antworten wird zu Leerraum. + +--- + +## 6. Entscheidungsregel + +### Regel `p2t` (Standard, `decide.decide_pair_tiebreak`) + +Lesarten: Qwen (0), pix2text (1), Gemma (2, nur bei Bedarf). + +```mermaid +flowchart TB + S["Qwen: keine Formel
(und Gemma auch)?"] -->|"ja"| NF["not_formula"] + S -->|"nein"| A{"pix2text = Qwen?"} + A -->|"ja"| C["Prüfungen"] + A -->|"nein"| B{"Gemma gefragt?"} + B -->|"nein"| W["needs_review:
Stichwahl fehlt
→ Gemma fragen"] + B -->|"ja"| G{"Gemma = pix2text?"} + G -->|"ja"| C + G -->|"nein, aber
Gemma = Qwen"| H["needs_review:
Mensch entscheidet"] + G -->|"alle uneinig"| H + C --> K{"Pandoc ok und
Ähnlichkeit ≥ 0,65?"} + K -->|"ja"| OK["auto_accepted
(pix2texts Lesart)"] + K -->|"nein"| R["needs_review"] +``` + +- Übernommen wird **immer pix2texts Lesart**: Sie trifft den Fettdruck am besten. +- **Qwen und Gemma gemeinsam gegen pix2text** gehen an einen Menschen. In der Messung waren genau das die + Fehler, die beide Bildmodelle teilen (`\mathcal{E}_r` statt `\varepsilon_r`, ein erfundenes `\tilde{z}`). + Der Bild-Schiedsrichter (`arbiter`, siehe [12](#12-messungen)) könnte diese Fälle entscheiden; er ist aus. +- **Satzzeichen:** Bei abgesetzten Formeln muss auch der Schlusspunkt bestätigt sein. Bei Formeln im Text + wird ein Satzzeichen am Ende vor dem Vergleich abgetrennt und als Text hinter die Formel gesetzt + (`$a^{x}$,`), denn es gehört zum Satz. +- **PDF-Textebene** (`pdftext`, aus): Bei `pdf2md --formulas` könnte der PDF-Text einer „flachen“ Formel + (eine Grundlinie, eine Größe) Qwen ersetzen. Gemessen fehlten dort in 12 von 319 Fällen Punkte, Striche + oder Indizes. + +### Regel `vlm3` (bis 0.2.0, `decide.decide_consensus`) + +Qwen = Gemma, bestätigt von GLM-4.6V aus einer anderen Modellfamilie; eine 2:1-Mehrheit reicht nicht. +Übernommen wird Qwens Lesart; der bestätigenden Lesart darf der Schlusspunkt fehlen. Wählbar mit +`"consensus_rule": "vlm3"` in `workspace.json`. + +### Pixel-Modus (bis 0.1.0, `decide.decide`) + +Eine Lesart wird gerendert und muss jede Verwechslungsvariante (`confusables.variants`: andere Ziffer, +hoch statt tief, `,`/`;`, ähnliche Buchstaben …) im Bild deutlich schlagen. Sicher, aber an kleinen +JPEG-Formeln zu streng. Wählbar mit `"decide": "pixel"`. + +### Schwellen (`settings.consensus_thresholds` in `workspace.json`) + +| Schlüssel | Standard | Bedeutung | +|---|---|---| +| `veto_ncc` | 0,65 | Mindestähnlichkeit nach elastischer Ausrichtung | +| `veto_scale_dev` | 0 (aus) | erlaubte Abweichung der Schriftgröße vom Buchmittel | +| `bold_relevant` | 0 | 1: fett und normal gelten als verschiedene Zeichen | +| `require_third` | 1 | Regel `vlm3`: dritte Lesart muss zustimmen | +| `min_p_mean`, `min_p_min` | 0 | Mindest-Token-Wahrscheinlichkeiten (Mittel, kleinste) | +| `structural` | 0 | Strukturvergleich mit sympy | +| `arbiter`, `arbiter_margin` | 0, 5 | Bild-Schiedsrichter bei „Qwen = Gemma gegen pix2text“ | +| `pdftext` | 0 | PDF-Textebene darf pix2text bestätigen | + +--- + +## 7. Wann zwei Lesarten dieselbe Formel sind + +`consensus.same_reading` prüft in dieser Reihenfolge: + +1. **Normalform** (`canonical`): Beide Lesarten werden in Tokens zerlegt und vereinheitlicht. + - **Entfällt:** Abstände (`\,`, `\quad` …), `\left`/`\right`/`\big`, `\displaystyle`, aufrecht/kursiv + (`\mathrm`, `\text`, `\operatorname` behalten nur ihren Inhalt), standardmäßig auch Fettdruck. + - **Synonyme:** `\vec` = `\overrightarrow`, `\frac` = `\dfrac` = `\cfrac`, `\le` = `\leq`, + `\iff` = `\Leftrightarrow`, `\cdots` = `\dots`, `\root3\of{x}` = `\sqrt[3]{x}`, + `\underset{…}{\lim}` = `\lim_{…}`, TeX-Umlaute (`f{\"u}r` = `für`), `\atop` = `\substack`. + - **Umgebungen:** `pmatrix` = `\left(\begin{matrix}…\right)`, `array` = `matrix`. Mehrzeilige Formeln + sind gleich, wenn sie dieselben Zeilen mit denselben Zeichen haben, egal ob `aligned`, `gathered`, + `split` oder nur `\\`; `cases` = `\left\{\begin{array}…\right.`. In Matrizen trennt `&` weiter Spalten. + - **Klammergruppen:** Eine Gruppe ist nur dann Argument, wenn der Befehl davor noch eines erwartet + (`\frac` 2, `\sqrt` 1, `\Delta` 0). So wird `t^{2}\,{\sqrt{2}}` = `t^2\sqrt{2}`, aber + `\frac{a}{b+c}` ≠ `\frac{a}{b}+c`. + - Eine Gleichungsnummer ist Teil der Normalform: Alle Lesarten müssen dieselbe tragen. +2. **Strukturvergleich** (`structure.py`, nur mit `structural` = 1): gleicher *unausgewerteter* sympy-Baum + (`\frac{a}{b}` = `a/b`). Nichts wird umgerechnet (`a+b` ≠ `b+a`), Relationen gehen nie an sympy, jedes + Zeichen ist durch einen Platzhalter geschützt. +3. **Render-Gleichheit** (`render_equal`): Beide Lesarten werden mit pdflatex gesetzt und Pixel gegen Pixel + verglichen (höchstens 0,2 % abweichende Tintenpixel). Das ist ein Vergleich **Render gegen Render**, + exakt und rauschfrei. Haben die Normalformen verschiedene Buchstaben oder Ziffern, entfällt dieser + Schritt. + +--- + +## 8. Bild-Veto: der elastische Bildvergleich + +Das Veto fängt Fehler ab, die mehrere Erkenner gemeinsam machen (weggelassener Term, mitgelesene +Gleichungsnummer, Halluzination). Es entscheidet nicht über Kleinigkeiten. + +1. **Rendern** (`render.py`): pdflatex mit dem Paket `preview`, viele Formeln je Lauf. Formeln mit + TeX-Fehlern werden über die Zeilennummern im Log erkannt und herausgenommen, der Rest neu übersetzt. + Die Schrift wählt `choose_preset` je Buch an einer Stichprobe: `cm`, `times`, `txfonts`, `palatino`, + `stix` oder `euler`. +2. **Normalisieren** (`imageio.gray_to_ink`): Transparenz auf Weiß, helle Schrift auf dunklem Grund wird + umgekehrt, die Tinte zwischen Papierton und dunkelstem Pixel gestreckt (ein getönter Kasten wird so zu + Papier), JPEG-Rauschen unter einer Schwelle entfernt, zugeschnitten. Breite Originale werden auf + 1600 px verkleinert, Ausschnitte über 2,5 MP nicht verglichen. +3. **Ausrichten** (`compare.py`): gleichmäßig skalieren (nie strecken), auf Viertelpixel verschieben. + Weichen die Proportionen mehr als 2,5-fach ab, gibt es keinen Vergleich. +4. **Elastisch verzerren** (`elastic_warp`): dynamische Zeitverzerrung (DTW) über Spalten, dann Zeilen. + Jede Originalspalte schiebt den Kandidaten um 0–3 Spalten weiter. Dehnen kostet proportional zur Tinte + der Spalte, übersprungene Tinte ihren vollen Wert. Abstände und Pfeilschäfte dürfen sich so ändern, + Zeichenformen praktisch nicht, und kein Zeichen kann verschwinden. +5. **Messen:** Korrelation (NCC) nach der Verzerrung; das Veto verlangt ≥ 0,65. + +Die Bilder (Original, starr und elastisch ausgerichtetes Render, farbiges Diff) landen in `review/` und +erscheinen in der Review-Galerie. + +--- + +## 9. apply und revert + +- **Sicherung:** Das erste `apply` kopiert jede betroffene Markdown-Datei nach `backup/`. Jedes `apply` + baut aus dieser Sicherung neu auf, ist also wiederholbar. Hat sich eine Datei seitdem geändert + (neuer epub2md-Lauf), gilt sie als neues Original. +- **Ersetzen:** Der genaue Bildverweis in der gespeicherten Zeile wird ersetzt: abgesetzt durch `$$…$$`, + im Text durch `$…$` (epub2md `--math tex`: `\[…\]` bzw. `\(…\)`). +- **Gleichungsnummer:** Sie kommt nur aus der übernommenen Lesart und steht bei abgesetzten Formeln als + `\qquad\text{(3.7)}` hinter der Formel, wie bei epub2md. +- **Deutsche Bücher:** Dezimalzahlen werden zu `3{,}6`, sonst setzt LaTeX „3, 6“. +- **revert** spielt die Sicherung zurück, außer bei Dateien, die seit `apply` von Hand geändert wurden + (mit `--force` auch diese). + +--- + +## 10. Quellen: epub2md und pdf2md + +| Quelle | Erkennung | Original für den Vergleich | +|---|---|---| +| epub2md-Ordner | `conversion-report.json` (`tool: epub2md`); Warnungen `formula-image` markieren sichere Formeln | das extrahierte Bild | +| pdf2md (Box-Bilder) | Bildname `--`; mit `--pdf` per Template-Matching auf der Seite gefunden | 600-dpi-Neuausschnitt aus dem Vektor-PDF (`pdfcrop.py`), einmal in `crops/` | +| pdf2md `--formulas` | Bildname `--f.png`, Liste `formulas-*.json` mit Seite, Rechteck, Art und PDF-Text | das Bild selbst (300 dpi aus dem Vektor-PDF) | + +Mit `pdf2md --formulas` findet die Formelerkennung von pix2text (MFD) jede Formel auf der Seite, auch im +Fließtext und in Merkkästen; pdf2md schneidet sie zeichengenau aus dem Text. Ob ein Box-Bild eine Formel +ist, verraten bei Box-Bildern die Schriften im Ausschnitt (CMMI, CMSY, EURM …). + +--- + +## 11. Sicherheit + +- **Lesarten sind unsicher:** Sie kommen von Modellen. Vor pdflatex lehnt `tex.unsafe_reason` Befehle ab, + die Dateien lesen oder schreiben, Code ausführen oder Makros umdefinieren (`\input`, `\write`, `\def`, + `\catcode`, `\csname` …), außerdem `^^`-Zeichencodes, Begrenzer und unausgeglichene Klammern. +- **pdflatex** läuft mit `-no-shell-escape`, `openout_any=p`, `openin_any=p` und Zeitlimit. +- **Review-Server** bindet nur an `127.0.0.1`; jede Entscheidung muss ein Zufallstoken der Seite + mitschicken, damit fremde Webseiten im selben Browser nichts entscheiden können. +- **Daten nach außen:** Mit der Standardregel verlässt nichts den Rechner. Nur die Regel `vlm3` schickt + Formelbilder samt Textausschnitt an OpenRouter. + +--- + +## 12. Messungen + +Gemessen wird an Formelbildern mit **bekanntem TeX**: Springer-EPUBs (Göllmann) tragen das TeX jeder Formel +im alt-Text. Dazu kommen Nachbildungen im Stil von Gehrke (LaTeX + dvipng mit Bitmap-Schriften, ~102 dpi, +JPEG). Werkzeuge: `calibrate --consensus`, `tools/regeltest.py`, `tools/zeittest.py`. + +### Regeln im Vergleich (Göllmann Band 1, je 150 echte und 150 nachgebildete Formeln, Stand 0.3.0) + +| Regel | echt: automatisch / falsch | Gehrke-Stil: automatisch / falsch | +|---|---|---| +| Qwen = Gemma | 73 % / 1 | 69 % / 2 | +| Qwen = Gemma = Gemini 2.5 Flash | 67 % / 0 | 57 % / 1 | +| Qwen = Gemma = GLM-4.6V (Regel `vlm3`) | 78 % / 0 | 70 % / 0 | +| pix2text = Qwen | 81 % / 0 | 76 % / 0 | +| **pix2text zuerst, bereinigt, Stichwahl Gemma** (Regel `p2t`) | **86 % / 0** | **85 % / 0** | +| + Stichwahl auch Qwen = Gemma gegen pix2text | 92 % / 1 | 91 % / 2 | + +Mit der mehrzeiligen Normalform (0.4.0), gleiche Lesarten: **89 % / 0** und **88 % / 0**; Göllmann Band 2 +(zwei Stichproben zu 50): 44 und 45 automatisch, 0 falsch. + +Allein richtig (Stand 0.3.0): pix2text 90 % / 85 %, Qwen 86 % / 84 %, Gemma 86 % / 80 %, GLM 86 % / 83 %. +Zeit je Formel seriell: pix2text 0,10 s (GPU) bzw. 0,51 s (CPU), Qwen 0,72 s, GLM 1,19 s, Gemma 1,77 s. + +### Ganze Bücher (ohne bekannte Lösung) + +| Buch | Version | Formelbilder | automatisch | Review | Prüfung von Hand | +|---|---|---|---|---|---| +| Gehrke, Brückenkurs Mathematik (EPUB) | 0.2.0, Regel `vlm3` | 1505 | 942 (63 %) | 478 | 62 von 62 korrekt | +| Corral, *Elementary Calculus* (PDF, Box-Bilder) | 0.4.0 | 1150 | 429 (37 %) | 633 | – | +| Corral mit `pdf2md --formulas`, Stichprobe 10 % | unveröffentlicht | 940 | 747 (79 %) | 174 | – | + +Bei Gehrke ergab Pandoc `--mathml` nach `apply` für 945 von 945 Formeln MathML. Bei Corral (Box-Bilder) +bestand das Review überwiegend aus Ausschnitten, die keine einzelne Formel waren (Merkkästen mit Text, +Tabellen, Herleitungen); `--formulas` behebt das weitgehend. + +### Gemessen und verworfen + +- **Selbstauskunft** der Modelle („95 % sicher“) und **pix2texts eigene Sicherheit**: Fehler auch bei 1,0. +- **Token-Wahrscheinlichkeiten** als Schwelle: neben der Bestätigung kein Gewinn, nur weniger Abdeckung. +- **Gemini als dritte Lesart:** mit Gemma verwandt, ließ einen gemeinsamen Fehler durch. +- **GLM mit Nachdenken:** 79 % statt 85 % allein richtig, ~25× mehr Tokens. +- **Bild-Schiedsrichter** (`verify.image_arbiter`) bei „Qwen = Gemma gegen pix2text“: In 16 Fällen hatte + Qwen 14-mal recht, aber der gemeinsame Fehler `\mathcal{E}_r` bekam Beleg 7,2 – zu knapp für eine + Schwelle von 8. +- **Strukturvergleich mit sympy:** nach der mehrzeiligen Normalform kein zusätzlicher Treffer. +- **Prompt-Stilvorgabe** (feste Schreibweisen): keine Verbesserung, die Modelle schrieben wörtliches `\n`. +- **PDF-Textebene als Bestätigung:** 12 von 319 widersprachen Qwen, mit fehlenden Punkten und Indizes. +- **Qwen3-VL-8B statt 30B-A3B:** nicht schneller (das MoE-Modell rechnet nur ~3B je Token), etwas schlechter. +- **Schriftgrößen-Prüfung im Veto:** nur Fehlalarme. + +„0 falsch“ bei einigen hundert Formeln bedeutet eine Fehlerquote sehr wahrscheinlich unter ~2 %, nicht +null. Die Kalibrierung lief ohne den Textausschnitt, den `run` mitgibt. + +--- + +## 13. Designentscheidungen + +| Entscheidung | Verworfene Alternativen | Grund | +|---|---|---| +| **Eigenes Nachbearbeitungs-Werkzeug** | Option in epub2md/pdf2md | Die Konverter bleiben schlank und offline; torch, Modelle und GPU-Server gehören nicht hinein. | +| **Erkenner entscheiden, Bild nur Veto** | Pixelvergleich als Entscheider (0.1.0) | Bei 15 px hohen JPEG-Formeln mit Bitmap-Hinting trennte der Pixelvergleich richtig und falsch zu schlecht: 1–2 % automatisch. | +| **Zwei verschiedenartige Erkenner** | ein Modell mit Selbstauskunft; zwei Modelle einer Familie | Selbstauskünfte sind wertlos; verwandte Modelle machen dieselben Fehler (Gemini/Gemma). pix2text (Formel-OCR) und Qwen (Bildmodell) irren verschieden. | +| **pix2texts Lesart übernehmen** | Lesart des Bildmodells | pix2text liest allein am häufigsten richtig und trifft den Fettdruck am besten. | +| **Qwen + Gemma gegen pix2text → Mensch** | 2:1-Mehrheit | Genau diese Fälle waren die gemeinsamen Fehler der Bildmodelle (+6 % Abdeckung, aber Fehler). | +| **Normalform statt String-Vergleich** | Zeichenkette; sympy-Auswertung | Die Erkenner schreiben dieselbe Formel verschieden; sympy rechnet um und verwischt Unterschiede (`v=(3,4)ᵀ` und `\nu=(3,4)ᵀ` werden beide `False`). | +| **Render gegen Render als Rückfallebene** | Render gegen Original | Zwei Renders sind exakt vergleichbar; das Original ist verrauscht und anders gesetzt. | +| **Elastischer Vergleich mit Tintenkosten** | starre Ausrichtung; freie Verzerrung | Starr scheitert an anderen Abständen und Pfeillängen; frei könnte ein falsches Zeichen passend biegen. | +| **Fettdruck zählt nicht** | Fettdruck als eigenes Zeichen | Vorgabe des Nutzers; alle Bildmodelle lesen Fettdruck schlecht. | +| **Nachdenken der Modelle aus** | Standard der Server | Schneller, billiger, bei GLM sogar genauer. | +| **Arbeitsordner neben der Ausgabe, Schlüssel = Bildinhalt** | Daten in der Ausgabe; Dateinamen als Schlüssel | `epub2md --force` ersetzt die Ausgabe; Dateinamen ändern sich, Bildinhalte nicht. | +| **apply aus Sicherung** | Ersetzen im laufenden Markdown | Wiederholbar, rückgängig zu machen, neue Konverterläufe werden erkannt. | +| **Pakete zu 50, Lesen und Prüfen überlappt** | alles nacheinander | Statuszeilen für den Nutzer; die GPUs warten nicht auf die CPU (Pilot 869 Formeln: 70 → ~24 Minuten). | +| **Lokale Modelle, Cloud optional** | Cloud als Standard | Buchinhalte bleiben auf dem Rechner; die lokale Regel misst besser als die mit GLM. | +| **MIT-Lizenz** | GPL | Einfache Wiederverwendung. | + +--- + +## 14. Grenzen + +| Grenze | Auswirkung | Mögliche Abhilfe | +|---|---|---| +| Mehrzeilige Rechnungen mit verschiedenen Zeilenumbrüchen, `\underbrace`, fehlenden Integrationsgrenzen | landen im Review | Normalform erweitern | +| Fettdruck wird nicht geprüft | ein fetter Vektor kann als normales v übernommen werden | `bold_relevant` = 1 schickt solche Fälle ins Review | +| Formeln auf farbig hinterlegten Kästen | verlieren beim Ersetzen die Hinterlegung | – | +| Formeln in Tabellen | erfasst `pdf2md --formulas` noch nicht | Erkennung in Tabellenzellen | +| Riesenausschnitte (Grafiken, ganze Herleitungen) | ohne Bildvergleich abgelehnt (über 2,5 MP, Proportionen über 2,5-fach) | bessere Trennung in pdf2md | +| Messbasis | einige hundert Formeln eines Verlags; Textausschnitt nicht kalibriert | weitere Bücher mit bekannter Lösung | +| Laufzeit | ~0,8 s je Formel mit zwei RTX 3090 | – | diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0c1ff7f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Dieter Schlüter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 92ca467..8a8a0fd 100644 --- a/README.md +++ b/README.md @@ -3,357 +3,56 @@ Nachbearbeitungs-Werkzeug für die Markdown-Ausgaben von [epub2md](https://kitux.de/forgejo/dschlueter/epub2md) und [pdf2md](https://kitux.de/forgejo/dschlueter/pdf2md): Formeln, die nur als Bild vorliegen, werden durch -LaTeX ersetzt – **aber nur, wenn zwei unabhängige Erkenner dieselbe Formel lesen** (zuerst das Formel-OCR -pix2text, bestätigt vom Bildmodell Qwen; bei Uneinigkeit darf Gemma pix2text bestätigen) und ein grober -Bildabgleich nicht widerspricht. Alles andere geht in eine Review-Galerie oder bleibt Bild. +LaTeX ersetzt. Beim Neubau als EPUB oder PDF passen sie dann in Größe und Schrift zum Text. -Für PDF-Bücher findet `pdf2md --formulas` jede Formel auf der Seite, auch im Fließtext und in Merkkästen, -und schneidet sie als Bild aus; formula_img2latex übersetzt sie dann ebenso (siehe [Quellen](#quellen)). +- **Nur bestätigte Formeln:** Das Formel-OCR **pix2text** liest jede Formel, das Bildmodell **Qwen3-VL** + muss dieselbe Formel lesen (bei Uneinigkeit darf **Gemma 4** pix2text bestätigen), und ein grober + Bildabgleich darf nicht widersprechen. Alles andere geht in eine Review-Galerie oder bleibt Bild. +- **Annahmeregel:** jedes Zeichen richtig und an der richtigen Stelle; Abstände, Längen, Schriftgröße, + kursiv/aufrecht und Fettdruck dürfen abweichen. +- **Lokal:** pix2text im Prozess, Qwen und Gemma über llama.cpp; keine Buchinhalte verlassen den Rechner. +- **Wiederholbar und umkehrbar:** Ergebnisse liegen in einem Arbeitsordner neben der Ausgabe, `apply` + schreibt aus einer Sicherung, `revert` stellt den alten Stand wieder her. +- **PDF-Bücher:** Mit `pdf2md --formulas` werden auch Formeln im Fließtext und in Merkkästen erfasst. -Schritt-für-Schritt-Anleitung: [BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md). Änderungen je Version: -[CHANGELOG.md](CHANGELOG.md). Hintergrund und ursprüngliche Planung: `Idee/`. +## Schnellstart -## Annahmeregel - -> Jedes Zeichen muss stimmen und an der richtigen Stelle relativ zu den anderen stehen. Abstände, -> Pfeil- und Bruchstrichlängen, Schriftgröße, kursiv/aufrecht und Fettdruck dürfen abweichen. - -Eine Formel wird automatisch ersetzt (Regel `p2t`, Standard), wenn - -1. **pix2text** (Formel-OCR MFR 1.5, lokal, GPU) sie zuerst liest und **Qwen3-VL-30B-A3B** (Bildmodell, - lokal, llama.cpp) dieselbe Formel liest **oder**, wenn die beiden uneinig sind, **Gemma-4-26B-A4B** - (lokal) die Lesart von pix2text bestätigt. Übernommen wird immer **pix2texts Lesart** – sie trifft den - Fettdruck am besten. Ihre überflüssigen Leerzeichen entfernt vorher `post_process` aus pix2tex (LaTeX-OCR): - `\frac{\alpha} {3 6 0}` → `\frac{\alpha}{360}`; am gesetzten Bild ändert das nichts, geprüft an - 400 Lesarten. Auch der Schlusspunkt muss bestätigt sein, -2. Pandoc sie nach MathML umsetzen kann (sonst bliebe sie im EPUB/PDF roher TeX-Text) und -3. das gerenderte LaTeX dem Original grob ähnelt (Bild-Veto, elastischer Vergleich, Ähnlichkeit ≥ 0,65). - -Sind Qwen und Gemma gemeinsam gegen pix2text einig, entscheidet ein Mensch: Genau das sind die Fehler, -die beide Bildmodelle teilen (`\mathcal{E}_r` statt `\varepsilon_r`, erfundenes `\tilde{z}`). - -Die frühere Regel `vlm3` (Qwen = Gemma, bestätigt von **GLM-4.6V** über OpenRouter) bleibt wählbar: -`"consensus_rule": "vlm3"` unter `settings` in `workspace.json` des Arbeitsordners. - -„Dieselbe Formel“ prüft `consensus.py`: gleiche Normalform (Abstände, `\left`/`\right`, überflüssige -Klammern, `\vec`/`\overrightarrow`, `\frac`/`\dfrac`, `\iff`/`\Leftrightarrow`, `pmatrix`/`\left(\begin{matrix}`, -TeX-Umlaute, `\atop`/`\substack`, `\cdot\cdot\cdot`/`\cdots`, `\root3\of{x}`/`\sqrt[3]{x}`, kursiv/aufrecht, -fett/normal …) oder – als Rückfallebene – zeichengleiches Rendering der beiden Lesarten (Render gegen -Render, nicht gegen das verrauschte Original). Eine Gleichungsnummer muss in allen Lesarten -übereinstimmen und wird als Text hinter die Formel gesetzt. - -**Mehrzeilige Formeln** (seit 0.4.0): gleich, wenn sie dieselben Zeilen mit denselben Zeichen haben, -egal in welchem Rahmen. `aligned`/`gathered`/`split` und ihr `&` fallen weg (`a\\b` = -`\begin{aligned}a&\\b\end{aligned}`), eine leere letzte Zeile zählt nicht; `cases`, -`\left\{\begin{array}{ll}…\right.` und pix2texts `\left\{\begin{aligned}…\right.` sind dasselbe. -Matrizen bleiben Matrizen: Dort trennt `&` Spalten (`1&2` ≠ `12`). Ob eine Gruppe ein Argument ist, -entscheidet die Argumentzahl des Befehls (`\frac` 2, `\sqrt` 1, `\Delta` 0), nicht ihre Stellung: -`t^{2}\,{\sqrt{2}}` = `t^2\sqrt{2}`, `\Delta{x_{n}}` = `\Delta x_n`. - -**Gleiche Struktur** (`structure.py`, sympy, **optional, standardmäßig aus**): Beide Lesarten ergeben -denselben *unausgewerteten* Ausdrucksbaum (`\frac{a}{b}` = `a/b`, `a\cdot b` = `ab`); nichts wird -umgerechnet oder umgestellt (`a+b` ≠ `b+a`, `2x=4` ≠ `x=2`, `a-b` ≠ `a+(-b)`). Relationen gehen nie an -sympy (es wertet `v=(3,4)ᵀ` und `\nu=(3,4)ᵀ` beide zu `False` aus), jedes Zeichen wird durch einen -Platzhalter geschützt (sympy macht aus `\varepsilon` und `\epsilon` ein Symbol), sichtbare Klammern -zählen, Ungedecktes fällt auf den Render-Vergleich zurück. Gemessen: vor der mehrzeiligen Normalform -+1 Formel bei 400, danach **nichts mehr** – deshalb aus. Einschalten: `structural` = 1 oder -`FORMULA_STRUCTURAL=1`; braucht `pip install sympy lark`. - -Sind sich die Modelle uneinig oder widerspricht das Veto, entscheidet ein Mensch im Review. - -## Messung - -### Regel `p2t` (Standard) - -Göllmann Band 1, 150 echte Bilder und 150 Nachbildungen im Gehrke-Stil, Lesarten aller Modelle: - -| Regel | echt: automatisch / falsch | Gehrke-Stil: automatisch / falsch | -| --- | --- | --- | -| pix2text = Qwen | 81 % / 0 | 76 % / 0 | -| + Stichwahl: Gemma bestätigt pix2text, Qwens Lesart (erste Fassung) | 85 % / 0 | 80 % / 0 | -| **pix2text zuerst und übernommen, bereinigt, Stichwahl Gemma** (Standard) | **86 % / 0** | **85 % / 0** | -| + Stichwahl auch Qwen = Gemma gegen pix2text | 92 % / 1 | 91 % / 2 | -| Qwen = Gemma = GLM (Regel `vlm3`) | 78 % / 0 | 70 % / 0 | - -Ab 0.4.0 mit mehrzeiliger Normalform, gleiche Lesarten (`--reuse`): **89 % / 0** (134) und **88 % / 0** -(132); Band 2 (`tools/regeltest.py`, zwei Stichproben zu 50): **44 und 45** automatisch statt 39 und 43, -**0 falsch**, 6 bzw. 5 Review. Zusammen +17 Formeln bei 400. Die Tabelle und die folgenden Absätze -zeigen den Stand 0.3.0. - -Allein richtig: pix2text 90 % / 85 %, Qwen 86 % / 84 %, Gemma 86 % / 80 %, GLM 86 % / 83 %. -Zeit je Formel (seriell, 15 Formeln): pix2text 0,10 s (GPU) bzw. 0,51 s (CPU), Qwen 0,72 s, GLM 1,19 s, -Gemma 1,77 s. Gegenprobe an Göllmann Band 2 (`tools/regeltest.py`, zwei getrennte Stichproben zu 50 -Formeln): 39 bzw. 43 automatisch (78 % bzw. 86 %), **0 falsch**, 11 bzw. 7 Review; Gemma wurde für 14 -bzw. 9 Formeln gefragt. Lesen je Formel im Mittel 2,3 bzw. 1,5 s (alle Tools nacheinander, ohne Bildprüfung). -pix2text ist nicht unfehlbar: In den Reviews standen auch Fälle, in denen nur pix2text sich verlesen hatte -(fehlende Wurzelexponenten, zerfallene Vektoren, `\nu` statt fettem v) – deshalb bleibt die Bestätigung -durch Qwen Pflicht. - -**Bild-Schiedsrichter** (0.4.0, gemessen, **aus**): Bei „Qwen = Gemma gegen pix2text“ misst -`verify.image_arbiter`, wie deutlich das Original Qwens Lesart zeigt (gleiches Kontrastmaß wie der -Variantentest). In 16 solchen Fällen (Band 1 und 2) hatte Qwen 14-mal recht, mit Belegen von 3 bis 50. -Aber: Beim gemeinsamen Fehler `\mathcal{E}_r` statt `\varepsilon_r` sprach das Bild mit 7,2 sogar für -die falsche Lesart. Ab Schwelle 8 wären es +11 Formeln bei 0 falsch – der Abstand ist bei zwei -Gegenbeispielen nicht belastbar. Einschalten: `arbiter` = 1, `arbiter_margin` (5). - -**Prompt-Stilvorgabe** (0.4.0, gemessen, verworfen): feste Schreibweisen für `aligned`, `pmatrix`, -`cases`, `\dots`, `\text`. Band 2: 44 und 44 statt 44 und 45 – die Normalform gleicht die Schreibweisen -schon aus, und mit der Zeilenvorgabe schrieben beide Modelle nach `\\` ein wörtliches `\n`. - -### Regel `vlm3` (Standard bis 0.2.0) - -An Formelbildern mit bekanntem TeX (Göllmann, Springer: TeX im alt-Text), je 150 echte Bilder und 150 -Nachbildungen im Stil von Gehrke (LaTeX + dvipng mit Bitmap-Schriften, ~102 dpi, JPEG): - -| Regel | echt: automatisch / davon falsch | Gehrke-Stil: automatisch / davon falsch | -| --- | --- | --- | -| Qwen = Gemma | 73 % / 1 | 69 % / 2 | -| Qwen = Gemma = Gemini 2.5 Flash | 67 % / 0 | 57 % / 1 | -| **Qwen = Gemma = GLM-4.6V** ohne Nachdenken (Standard) | **68 % / 0** | **63 % / 0** | - -- Fehler, bei denen zwei Modelle gemeinsam irren, gibt es: `\mathcal{E}_r` statt `\varepsilon_r`, ein - erfundenes `\tilde{z}`, `x^{f}` statt `x'`. Die dritte Lesart aus einer **anderen Modellfamilie** fängt - sie ab; Gemini (mit Gemma verwandt) machte einen davon mit. -- Selbstauskünfte der Modelle („95 % sicher“) sind wertlos. Token-Wahrscheinlichkeiten (Logprobs) werden - gespeichert, brachten aber neben der dritten Lesart keinen Gewinn (Schwellen bleiben einstellbar). -- „Nachdenken“ (Reasoning) ist für alle Modelle abgeschaltet: Gemma 4 wird ~10× schneller, GLM-4.6V liest - ohne Nachdenken sogar besser (85 % statt 79 % allein richtig) und braucht ~25× weniger Tokens. -- Fettdruck lesen alle Modelle schlecht (fette Vektoren werden zu normalen Buchstaben); nach Vorgabe zählt - er nicht. Mit `bold_relevant=1` würden diese Fälle ins Review gehen. -- Das Bild-Veto fing den einzigen Fall ab, in dem alle drei Modelle gemeinsam irrten (Ähnlichkeit - 0,5–0,6); richtige Lesarten lagen bei ≥ 0,66. Daher die Schwelle 0,65. Eine Prüfung der Schriftgröße - (Pixel pro Punkt gegen das Buchmittel) erzeugte nur Fehlalarme und ist abgeschaltet. -- Die Messung lief **ohne** Textausschnitt um die Formel; `run` gibt den Modellen einen kurzen - Textausschnitt (≤ 240 Zeichen) mit. Die Stichproben am Buch (62 von 62 korrekt) sprechen nicht gegen - den Kontext, gemessen ist er aber nicht. - -Gehrke, Brückenkurs Mathematik (EPUB, 1505 Formelbilder): siehe [Stand](#stand). - -## Ablauf - -```text -scan Bildverweise in chapters/*.md (bzw. pdf2md-Markdown) finden, Formel/Abbildung vorklassifizieren -run in Paketen zu 50: pix2text, dann Qwen lesen, prüfen, Gemma nur für Uneinige, prüfen; - nach jedem Paket eine Statuszeile mit Zeiten je Stufe (Regel vlm3: Qwen + Gemma, GLM nur - für Einige). Die GPUs lesen schon das nächste Paket, während die CPU das vorige prüft. -review Zweifelsfälle im Browser: Original / Render / Overlay / Diff, LaTeX korrigieren und neu prüfen -apply angenommene Formeln ins Markdown schreiben (aus einer Sicherung, idempotent) -revert Markdown-Dateien auf den Stand vor apply zurücksetzen -recognize einzelne Engine (local, local2, cloud, p2t) laufen lassen -verify prüfen und entscheiden (ohne neue Modellabfragen) -status Zählung je Status -calibrate Messung an Formelbildern mit bekanntem TeX (--consensus: Konsensregel) -``` - -```bash -python3 formula_img2latex.py run output/buch # epub2md-Ausgabeordner -python3 formula_img2latex.py review output/buch -python3 formula_img2latex.py apply output/buch -python3 epub2md_merge.py output/buch --force # Gesamtdatei danach neu bauen -``` - -PDF-Buch mit allen Formeln, auch im Fließtext: - -```bash -cd ~/Python_Programs/pdf2md -python3 pdf2md.py --formulas --epub buch.pdf Output/buch/buch.md # Formeln als Bilder + Liste -cd ~/Python_Programs/formula_img2latex -python3 formula_img2latex.py run ~/Python_Programs/pdf2md/Output/buch/buch.md --pdf buch.pdf -python3 formula_img2latex.py apply ~/Python_Programs/pdf2md/Output/buch/buch.md -cd ~/Python_Programs/pdf2md && python3 pdf2md.py --from-md Output/buch/buch.md # EPUB mit MathML -``` - -Status je Formel: `auto_accepted`, `needs_review`, `rejected_keep_image`, `render_failed`, `no_candidate`, -`not_formula`, nach Review `accepted_by_human`, `corrected_by_human`, `kept_by_human`. - -## Modelle und Server - -| Engine | Standard | Konfiguration | -| --- | --- | --- | -| `local` | llama.cpp `http://127.0.0.1:8011/v1` (Qwen3-VL-30B-A3B-Instruct Q4_K_M + mmproj), 8 parallele Anfragen | `FORMULA_LOCAL_URL`, `FORMULA_LOCAL_MODEL` | -| `local2` | llama.cpp `http://127.0.0.1:8012/v1` (Gemma-4-26B-A4B-it Q4_0 + mmproj) | `FORMULA_LOCAL2_URL`, `FORMULA_LOCAL2_MODEL` | -| `cloud` | OpenRouter, `z-ai/glm-4.6v` (nur Regel `vlm3`) | `OPENROUTER_API_KEY`, `FORMULA_CLOUD_MODEL` | -| `p2t` | pix2text MFR 1.5 (ONNX) im Prozess, die sichtbare GPU mit dem meisten freien Speicher, sonst CPU; gebündelt nach Pixelbudget | `FORMULA_P2T_DEVICE` (`auto`, `cpu`, `cuda:N`), `FORMULA_P2T_MODEL` | - -pix2text auf der GPU braucht `onnxruntime-gpu` statt `onnxruntime` (cuDNN kommt aus den nvidia-Paketen -von torch). Für `post_process` wird `pix2tex` ohne seine Abhängigkeiten installiert -(`pip install --no-deps pix2tex munch`), weil es sonst `timm==0.5.4` erzwingen würde. `cuda:N` zählt innerhalb von `CUDA_VISIBLE_DEVICES`. - -`FORMULA__THINKING=1` schaltet das Nachdenken wieder ein. Antworten sind auf 800 Tokens begrenzt -(lange mehrzeilige Formeln brauchen ~400; einzelne Antworten von Gemma liefen sonst in -Wiederholungsschleifen bis 165 s). Jede Lesart wird mit Modell, Prompt-Version und Nachdenk-Einstellung -gespeichert; dieselbe Frage wird nie zweimal gestellt, ein abgebrochener Lauf setzt dort fort. - -Qwen-Server mit `--parallel 8 -c 32768` (je Platz 4096 Tokens) starten; `run` stellt bis zu 8 Anfragen -gleichzeitig (BEDIENUNGSANLEITUNG). Gemessen und verworfen: Qwen3-VL-8B statt 30B-A3B ist nicht schneller -(das MoE-Modell rechnet je Token nur ~3B Parameter) und liest etwas schlechter (Göllmann 42/44 statt 44/45 -automatisch). - -Kosten der Cloud-Stufe (nur Regel `vlm3`): GLM liest nur Formeln, bei denen Qwen und Gemma einig sind. Gehrke: 967 -Anfragen, Ø 2,6 s, zusammen rund 0,15–0,20 $. - -## Bildvergleich (Veto) - -`compare.py` rendert die Lesart mit pdflatex (Schrift je Buch automatisch: `cm`, `times`, `txfonts`, -`palatino`, `stix`, `euler`), richtet sie auf das Original aus und verzerrt sie **elastisch** -(dynamische Zeitverzerrung über Spalten und Zeilen; Dehnen kostet proportional zur Tinte), damit -Abstände und Längen keine Rolle spielen. Die Ähnlichkeit danach ist das Veto. Geprüft wird nur -die Lesart, die übernommen werden kann: die von pix2text (Regel `vlm3`: die von Qwen). Bestätigende -Lesarten werden nie übernommen und brauchen keine Prüfung. Die Prüfergebnisse tragen eine -Versionsnummer des Verfahrens und werden nach Änderungen neu berechnet. Der alte Pixel-Entscheider mit -Verwechslungsvarianten und Kontrasttest bleibt als Modus `decide=pixel` erhalten. - -Sicherheit: Kandidaten kommen von Modellen und gelten als unsicher. Vor pdflatex werden Befehle wie -`\input`, `\write`, `\def`, `\catcode` abgelehnt; pdflatex läuft mit `-no-shell-escape`, -`openout_any=p`, Zeitlimit. - -## Arbeitsordner - -```text -.formulas/ # neben der Ausgabe, weil epub2md --force die Ausgabe ersetzt -├── workspace.json # Quelle, Schrift, Einstellungen, apply-Zustand -├── formulas/f--.json # je Bild und Darstellung: Lesarten, Prüfung, Entscheidung -├── crops/.png # Original je Formel, einmal ausgeschnitten (PDF-Quellen) -├── review/-{orig,render,rigid,diff}.png -├── bericht/ # Ergebnisseite (tools/buch_bericht.py) -└── backup/ # Markdown-Dateien vor dem ersten apply -``` - -Die Datensätze hängen am SHA-256 des Bildes: Ein erneuter epub2md-Lauf verliert keine Entscheidungen. - -Einstellungen in `workspace.json` → `settings.consensus_thresholds`: `require_third` (1), -`bold_relevant` (0), `structural` (0), `arbiter` (0), `arbiter_margin` (5), `veto_ncc` (0,65), -`veto_scale_dev` (0 = aus), `min_p_mean`/`min_p_min` (0), `pdftext` (0: die PDF-Textebene bestätigt nie; -gemessen: in 12 von 319 Fällen fehlten dort Punkte, Striche oder Indizes, die Qwen sah). - -## Quellen - -| Quelle | Erkennung | Begrenzer | -| --- | --- | --- | -| epub2md-Ordner | `conversion-report.json`; Warnungen `formula-image` markieren sichere Formeln | `--math auto`: `$…$`/`$$…$$`, `--math tex`: `\(…\)`/`\[…\]`; deutsche Bücher: `3{,}6` | -| pdf2md (`.md` + `_images/` oder `--split`-Ordner) | Bildname `--`; mit `--pdf` per Template-Matching auf der Seite gefunden und mit 600 dpi aus dem Vektor-PDF neu ausgeschnitten | `$…$`/`$$…$$` | -| pdf2md `--formulas` | Bildname `--f.png` (300 dpi), Liste `_images/formulas-*.json` mit Seite, Rechteck, Art (im Text/abgesetzt) und PDF-Text; das Bild wird direkt verwendet | `$…$`/`$$…$$` | - -Mit `pdf2md --formulas` findet die Formelerkennung von pix2text (MFD) jede Formel auf der Seite, auch im -Fließtext und in Merkkästen; pdf2md schneidet sie zeichengenau aus dem Text (statt Glyphensalat wie `x4n`). -Ein Satzzeichen am Ende einer eingebetteten Formel wird vor dem Vergleich abgetrennt und als Text hinter -die Formel gesetzt (`$a^{x}$,`). Codeblöcke werden nie verändert. Formeln in Tabellen erfasst die -Erkennung noch nicht. - -## Werkzeuge zum Einstellen - -- `tools/vergleich.py AUSGABE` – feste Stichprobe (50 Formeln), verarbeitet wie `run`, schreibt - `vergleich//index.html` mit Original, gerenderten Lesarten, Entscheidung und Veto-Overlay. -- `tools/konsens_bericht.py BERICHT.json EPUB` – Übersichtsseite zu `calibrate --consensus --report`. -- `tools/zeittest.py EPUB` – Zeit und Trefferquote je Tool, seriell an 15 Formeln mit bekanntem TeX. -- `tools/regeltest.py EPUB [--n 50] [--reuse ergebnis.json] [--refresh local,local2] [--arbiter 8]` – die - Standardregel an Formeln mit bekanntem TeX, wie `run`; Seite mit Original, wahrer Formel, allen - Lesarten und Entscheidung. `--refresh` fragt trotz `--reuse` diese Engines neu (Prompt-Test). -- `tools/buch_bericht.py AUSGABE` – Ergebnisseite eines ganzen Buchs nach `run`: jede Formel mit - Original, gesetztem LaTeX, Status, Begründung und allen Lesarten, filterbar; liegt in - `/bericht/index.html` (Buchausschnitte, nicht versioniert). -- `tools/vergleich_bericht.py --regeltest VORHER:NACHHER --kalibrierung VORHER.json:NACHHER.json` – - Vorher/Nachher-Seite zweier Läufe auf denselben Lesarten: Zahlen, jede geänderte Entscheidung und - jedes geänderte Urteil gegen die Wahrheit, mit Original und gerenderten Lesarten. -- `calibrate --consensus --engines local,local2,cloud,p2t --report X.json` misst alle Regeln; - `--reuse X.json` rechnet sie ohne Modellabfragen neu. -- `vergleich/` ist nicht versioniert (Ausschnitte aus Büchern). - -## Stand - -**Formeln im Fließtext** (unveröffentlicht, nach 0.4.0): Corral mit `pdf2md --formulas`: 9239 Formelbilder -(davon ~88 % eingebettet). Stichprobe 10 % (940): **747 automatisch (79 %)**, im Text 83 %, abgesetzte -Box-Bilder (Herleitungen, Kästen) nur ~29 %; 174 Review, 19 keine Formel; 13 Minuten, hochgerechnet ~2 h -für das ganze Buch. Beschleunigung am Pilot (869 Formeln): 70 → ~24 Minuten, Entscheidungen zu 863/876 -gleich (die Abweichungen kommen von den Modellen selbst). Qualität ist hier ohne bekannte Lösung – -vor dem Einsatz eine Stichprobe der übernommenen Formeln von Hand prüfen. - -Version 0.4.0 (Regel `p2t`, mehrzeilige Normalform) ist an Göllmann Band 1 und 2 gemessen (siehe -[Messung](#messung)), aber noch **nicht am ganzen Gehrke** gelaufen. - -**Erstes ganzes PDF-Buch** (0.4.0): Corral, *Elementary Calculus* (346 Seiten, TeX-PDF, pdf2md mit -600-dpi-Neuausschnitt): 1150 Ausschnitte → **429 automatisch ersetzt (37 %)**, 633 Review, 61 keine -Formel, 27 Abbildungen. EPUB (`pdf2md --from-md`, jetzt mit `--mathml`): 429 Formeln als MathML, kein -TeX-Rest. Laufzeit rund 4 Stunden (pix2text Ø 3,9 s, Qwen Ø 8,1 s, Gemma Ø 15 s je Anfrage – große -Ausschnitte). Das Review besteht überwiegend aus Ausschnitten, die keine einzelne Formel sind -(Merkkästen mit Text, Tabellen, Diagramme, ganze Herleitungen) – das muss pdf2md besser trennen. Die folgenden Zahlen stammen von Version 0.2.0 (Regel `vlm3`, mit GLM). - -Version 0.2.0 (Konsens), getestet am Gehrke, Brückenkurs Mathematik (EPUB, CM-Formeln als kleine JPGs): - -| | Anzahl | -| --- | --- | -| Formelbilder verarbeitet (sicher oder unklar klassifiziert) | 1505 | -| **automatisch ersetzt** | **942 (63 %)** | -| Review: Qwen und Gemma uneinig | 410 | -| Review: GLM weicht ab | 65 | -| Review: Bild-Veto / Pandoc | 2 / 1 | -| keine Formel (laut Modellen) | 85 | -| nicht geprüft (Abbildungen, Symbole) | 539 | - -- **Von Hand geprüft:** die 42 automatisch ersetzten Formeln der festen 50er-Stichprobe und 20 zufällig - gezogene weitere – **62 von 62 Zeichen für Zeichen korrekt** (Brüche, Wurzeln mit Index, `cases`, - Grenzwerte, Integrale, Vektorpfeile, Überstriche über mehreren Buchstaben). -- **apply** an einer Kopie: 946 Ersetzungen in 258 Kapiteldateien (manche Formeln kommen mehrfach vor). - `epub2md_merge.py` → 945 Formeln in der Gesamtdatei (eine stand in einer doppelten Fußnote, die das - Zusammenfügen entfernt). Pandoc `--mathml`: **945 von 945** als MathML, keine „Could not convert TeX - math“-Meldung. EPUB daraus in 10 s gebaut. -- **Laufzeit** mit beiden lokalen Modellen parallel (zwei RTX 3090) und GLM: ~5 Minuten je 50 Formeln, - das Buch also rund 2,5 Stunden. - -Grenzen und offene Punkte: - -- Mehrzeilige Rechnungen: Rahmen und `&` gleicht die Normalform seit 0.4.0 aus; verschiedene - Zeilenumbrüche, `\underbrace` und fehlende Integrationsgrenzen bleiben im Review. -- Formeln auf farbig hinterlegten Kästen verlieren beim Ersetzen die Hinterlegung. -- Fettdruck wird nicht geprüft (Vorgabe). -- Die Messung umfasst 2 × 150 Formeln eines Buches ohne Textausschnitt; „0 Fehler“ bedeutet eine - Fehlerquote sehr wahrscheinlich unter ~2 %. -- pdf2md-Bücher: am ganzen Corral getestet (ohne `--formulas` 0.4.0, mit `--formulas` 10 %); Formeln in - Tabellen fehlen noch. -- Einzelne Riesenausschnitte (Grafiken, dreizeilige Herleitungen mit völlig anderer Lesart) werden ohne - Bildvergleich abgelehnt: über 2,5 MP oder mehr als 2,5-fach falsche Proportionen; breite Originale werden - für den Vergleich auf 1600 px verkleinert. - -## Installation +Voraussetzungen und das Starten der Modellserver: [BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md). ```bash sudo apt install texlive-latex-extra texlive-fonts-extra texlive-science pandoc dvipng -pip install -r requirements.txt # pymupdf, numpy, scipy, scikit-image, opencv, Pillow, lxml, requests -pip install -r requirements-dev.txt # pytest +pip install -r requirements.txt +pip install "pix2text[multilingual]" && pip install --no-deps pix2tex munch + +python3 formula_img2latex.py run ~/Python_Programs/epub2md/output/buch # lesen, prüfen, entscheiden +python3 formula_img2latex.py review ~/Python_Programs/epub2md/output/buch # Zweifelsfälle im Browser +python3 formula_img2latex.py apply ~/Python_Programs/epub2md/output/buch # Formeln ins Markdown schreiben ``` -Modelle: siehe BEDIENUNGSANLEITUNG (llama-server starten). +## Stand -## Tests +Version 0.4.0 plus unveröffentlichte Änderungen (siehe [CHANGELOG.md](CHANGELOG.md)). An Formelbildern +mit bekannter Lösung (Göllmann, Springer) werden 88–89 % automatisch ersetzt, **keine davon falsch**. +Ganze Bücher: Gehrke-Brückenkurs (EPUB) 63 %, alle 62 von Hand geprüften korrekt; Corral mit +`pdf2md --formulas` 79 % (Stichprobe). Einzelheiten: [FUNKTIONSWEISE.md, Messungen](FUNKTIONSWEISE.md#12-messungen). + +## Documentation + +| Dokument | Inhalt | +|---|---| +| [BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md) | Installation, Modellserver, ein Buch bearbeiten, Konfiguration, Betrieb, Fehlersuche | +| [FUNKTIONSWEISE.md](FUNKTIONSWEISE.md) | Aufbau, Datenfluss, Entscheidungsregel, Normalform, Bildabgleich, Messungen, Designentscheidungen, Grenzen | +| [CHANGELOG.md](CHANGELOG.md) | Änderungen je Version und was beim Update zu tun ist | +| [LICENSE](LICENSE) | MIT | +| `Idee/` | ursprüngliche Problemstellung und Planung | + +## Entwicklung ```bash +pip install -r requirements-dev.txt python3 -m pytest -q # ca. 45 s; synthetische Bilder, kein Modellserver, keine Buchinhalte ``` -Rückgabecodes: `0` Erfolg, `1` Bedienfehler, `2` Eingabe ist keine epub2md-/pdf2md-Ausgabe, -`3` externes Werkzeug fehlt (pdflatex, pandoc, Modellserver), `4` interner Fehler. +## License -## Architektur - -```text -formula_img2latex/ -├── cli.py Unterbefehle, Exit-Codes -├── pipeline.py scan, recognize, verify, Entscheidung (Konsens/Pixel), run in Paketen, apply/revert -├── consensus.py Normalform, Render-Gleichheit: sagen zwei Lesarten dasselbe? -├── decide.py Annahmeregeln und Schwellen (Konsens- und Pixel-Modus) -├── engines/ Vision-Modelle über die OpenAI-API (llama.cpp, OpenRouter), Logprobs, Prompt-Version; -│ p2t.py: pix2text-Formel-OCR (ONNX, CPU/GPU) -├── compare.py Ausrichtung, elastische Verzerrung, Kennzahlen, Kontrasttest, Diff-Bilder -├── confusables.py Verwechslungsvarianten (Pixel-Modus) -├── verify.py Bildprüfung einer Lesart (Veto bzw. Pixel-Entscheider) -├── render.py pdflatex-Batch, Schrift-Presets, Rasterung -├── imageio.py Originale laden (PNG/GIF/JPG/SVG, Transparenz, Tönung) -├── sources/ epub2md.py, pdf2md.py, base.py (Markdown-Scan, Begrenzer, Dezimalkomma) -├── structure.py Strukturvergleich mit sympy (optional, aus) -├── pdfcrop.py Formelbild auf der PDF-Seite finden, 600-dpi-Neuausschnitt -├── classify.py Formel / unklar / Abbildung -├── checks.py Pandoc/MathML-Prüfung -├── calibrate.py Messung an Formelbildern mit bekanntem TeX -├── store.py Arbeitsordner, Datensätze -└── review/ lokaler Review-Server + Seite -``` +MIT, siehe [LICENSE](LICENSE). diff --git a/formula_img2latex/cli.py b/formula_img2latex/cli.py index ddfb14b..0964614 100644 --- a/formula_img2latex/cli.py +++ b/formula_img2latex/cli.py @@ -15,13 +15,14 @@ from .render import PRESETS DESCRIPTION = """\ Ersetzt Formelbilder in Markdown-Ausgaben von epub2md und pdf2md durch LaTeX – aber nur, wenn -das gerenderte LaTeX nachweislich so aussieht wie das Original. Zweifelsfälle gehen in eine -Review-Galerie, alles andere bleibt Bild. +zwei Erkenner dieselbe Formel lesen (pix2text, bestätigt von Qwen oder bei Uneinigkeit von Gemma) +und ein grober Bildabgleich nicht widerspricht. Zweifelsfälle gehen in eine Review-Galerie, +alles andere bleibt Bild. """ EPILOG = """\ Typischer Ablauf: - formula_img2latex.py run output/buch # scan, erkennen (lokal, dann Cloud), prüfen + formula_img2latex.py run output/buch # scan, lesen (pix2text, Qwen, Gemma), prüfen formula_img2latex.py review output/buch # Zweifelsfälle im Browser entscheiden formula_img2latex.py apply output/buch # geprüfte Formeln ins Markdown schreiben formula_img2latex.py revert output/buch # alles rückgängig machen @@ -56,10 +57,10 @@ def build_parser() -> argparse.ArgumentParser: sp.add_argument("--quiet", "-q", action="store_true", help="nur Fehler ausgeben") def recog_opts(sp: argparse.ArgumentParser) -> None: - sp.add_argument("--model", default=None, help="Modellname (Standard: local = Modell des Servers, " - "cloud = $FORMULA_CLOUD_MODEL oder google/gemini-2.5-flash)") + sp.add_argument("--model", default=None, help="Modellname (Standard: local/local2 = Modell des Servers, " + "cloud = $FORMULA_CLOUD_MODEL oder z-ai/glm-4.6v, p2t = mfr-1.5)") sp.add_argument("--url", default=None, help="API-Basis-URL (Standard: local http://127.0.0.1:8011/v1, " - "cloud https://openrouter.ai/api/v1)") + "local2 http://127.0.0.1:8012/v1, cloud https://openrouter.ai/api/v1)") sp.add_argument("--include-figures", action="store_true", help="auch Bilder versuchen, die wie Abbildungen aussehen") sp.add_argument("--limit", type=int, default=0, help="höchstens N Bilder (zum Ausprobieren)") @@ -70,7 +71,7 @@ def build_parser() -> argparse.ArgumentParser: sp.add_argument("--pdf", type=Path, default=None, help="pdf2md: Original-PDF, um Formeln scharf neu auszuschneiden") - sp = sub.add_parser("recognize", help="LaTeX-Kandidaten mit einem Vision-Modell erzeugen") + sp = sub.add_parser("recognize", help="Lesarten einer Engine erzeugen (p2t, local, local2, cloud)") common(sp) recog_opts(sp) sp.add_argument("--engine", choices=["local", "local2", "cloud", "p2t"], default="local") @@ -85,13 +86,13 @@ def build_parser() -> argparse.ArgumentParser: sp.add_argument("--include-figures", action="store_true") sp.add_argument("--redo", action="store_true", help="auch bereits geprüfte Formeln neu prüfen") - sp = sub.add_parser("run", help="scan + recognize (lokal, dann Cloud) + verify") + sp = sub.add_parser("run", help="scan + lesen + prüfen + entscheiden, in Paketen mit Statuszeile") common(sp) recog_opts(sp) sp.add_argument("--pdf", type=Path, default=None) - sp.add_argument("--cloud-model", default=None, help="Modell für die Cloud-Stufe") - sp.add_argument("--no-local", action="store_true", help="lokale Stufe überspringen") - sp.add_argument("--no-cloud", action="store_true", help="Cloud-Stufe überspringen") + sp.add_argument("--cloud-model", default=None, help="Regel vlm3: Modell für die Cloud-Stufe") + sp.add_argument("--no-local", action="store_true", help="nur Modus decide=pixel: lokale Stufe überspringen") + sp.add_argument("--no-cloud", action="store_true", help="Regel vlm3 bzw. decide=pixel: Cloud-Stufe überspringen") sp.add_argument("--preset", choices=["auto", *PRESETS], default="auto") sp.add_argument("--chunk", type=int, default=50, help="Paketgröße; nach jedem Paket eine Statuszeile (Standard 50)") diff --git a/requirements.txt b/requirements.txt index 9fbe016..bcd41ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,6 @@ scikit-image>=0.22 opencv-python-headless>=4.8 Pillow>=10.0 lxml>=5.0 +requests>=2.31 +# pix2text separat, siehe BEDIENUNGSANLEITUNG: pip install "pix2text[multilingual]"; pip install --no-deps pix2tex munch # optional, only for the structural comparison (FORMULA_STRUCTURAL=1): sympy>=1.13, lark>=1.1