Nachbearbeitung für epub2md- und pdf2md-Ausgaben: scan → recognize (Vision-LLM lokal/OpenRouter) → verify → review → apply/revert. - Rendern mit pdflatex (Batch, Fehlerisolierung, unsichere Befehle gesperrt), Schrift-Presets cm/times/txfonts/palatino/stix/euler, automatische Wahl je Buch - Bildvergleich in Originalauflösung mit ¼-Pixel-Ausrichtung, Pixel/Punkt-Konsistenz - Kontrasttest gegen layouttreue Verwechslungsvarianten, Selbstkorrektur per Hill-Climbing - Pandoc/MathML-Prüfung vor jeder Annahme - pdf2md: Formelbild per Template-Matching auf der Seite finden, 600-dpi-Neuausschnitt - Review-Galerie auf localhost (Token-geschützt), idempotentes apply mit Sicherung, revert - calibrate: Messung an Formelbildern mit TeX im alt-Text (0/600 Falsch-Annahmen) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
159 lines
9.2 KiB
Markdown
159 lines
9.2 KiB
Markdown
# formula_img2latex
|
||
|
||
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 das gerenderte LaTeX nachweislich so aussieht wie das Original**.
|
||
Zweifelsfälle gehen in eine Review-Galerie, alles andere bleibt Bild.
|
||
|
||
Schritt-für-Schritt-Anleitung: [BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md). Hintergrund und
|
||
ursprüngliche Planung: `Idee/`.
|
||
|
||
> **Stand 0.1.0 (Prototyp).** Der Ablauf ist vollständig (scan → erkennen → prüfen → Review → apply/revert)
|
||
> und getestet. Die Annahmeschwellen sind noch nicht endgültig kalibriert; an echten Büchern landet
|
||
> derzeit der größte Teil im Review (siehe [Stand und Grenzen](#stand-und-grenzen)).
|
||
|
||
## Ablauf
|
||
|
||
```text
|
||
scan Bildverweise in chapters/*.md (bzw. pdf2md-Markdown) finden, Formel/Abbildung vorklassifizieren
|
||
recognize LaTeX-Kandidaten von einem Vision-Modell holen: lokal (llama.cpp) zuerst, Cloud (OpenRouter) nur für Ungelöste
|
||
verify Kandidat rendern (pdflatex), mit dem Original vergleichen, Verwechslungsvarianten prüfen, entscheiden
|
||
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
|
||
run scan + recognize lokal + verify + recognize Cloud (nur Ungelöste) + verify
|
||
calibrate Messung an Formelbildern mit bekanntem TeX (Springer-EPUBs: TeX im alt-Text)
|
||
```
|
||
|
||
```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
|
||
```
|
||
|
||
## Wie geprüft wird
|
||
|
||
Die OCR liefert nur einen **Kandidaten**. Entschieden wird über das Bild:
|
||
|
||
1. **Rendern** mit pdflatex (Paket `preview`, viele Formeln je Lauf, `-no-shell-escape`,
|
||
`openout_any=p`). Unsichere Befehle (`\input`, `\write`, `\def`, `\catcode`, …) werden vorher abgelehnt.
|
||
Die Schrift wird je Buch automatisch gewählt: `cm`, `times`, `txfonts`, `palatino`, `stix`, `euler`.
|
||
2. **Vergleichen** in der Auflösung des Originals: Tinte normalisieren (Transparenz, JPEG-Rauschen,
|
||
getönter Hintergrund), gleichmäßig skalieren (nie strecken), auf ¼ Pixel ausrichten.
|
||
Kennzahlen: Korrelation (NCC), SSIM, Abweichungsflecken, Größenabweichung, **Pixel pro Punkt**.
|
||
Pixel pro Punkt ist innerhalb eines Buches konstant; Abweichungen verraten falsche Stile
|
||
(`\frac` statt `\tfrac`, `\displaystyle`).
|
||
3. **Kontrasttest gegen Verwechslungsvarianten** (`confusables.py`). Erzeugt werden ~50–160 Varianten,
|
||
die sich durch je einen typischen Erkennungsfehler unterscheiden: andere Ziffer, hoch statt tief,
|
||
`,`/`;`, `-`/`+`/`=`, ähnliche Buchstaben, Groß/klein, `\mathcal`/`\mathbb`/…, fehlendes Vorzeichen, Strich
|
||
oder Akzent. Varianten werden **layouttreu** gerendert (`\mathrlap{neu}\mathbin{\phantom{alt}}`), damit
|
||
sich nur das eine Zeichen ändert. Verglichen wird nur dort, wo sich Kandidat und Variante unterscheiden
|
||
(Likelihood-Test). Der Kandidat muss **jede** unterscheidbare Variante mit Abstand schlagen.
|
||
4. **Selbstkorrektur:** Erklärt eine Variante das Bild deutlich besser, wird sie neuer Kandidat
|
||
(höchstens 3 Schritte). So werden einzelne OCR-Fehler ohne erneute Modellanfrage repariert.
|
||
5. **Pandoc-Prüfung:** Jede angenommene Formel muss mit `pandoc --mathml` umsetzbar sein, sonst
|
||
bliebe sie im späteren EPUB/PDF roher TeX-Text.
|
||
|
||
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`.
|
||
|
||
## Arbeitsordner
|
||
|
||
```text
|
||
<ausgabe>.formulas/ # neben der Ausgabe, weil epub2md --force die Ausgabe ersetzt
|
||
├── workspace.json # Quelle, Schrift, Pixel/Punkt, Schwellen, apply-Zustand
|
||
├── formulas/f-<sha12>-<d|i>.json # je Bild und Darstellung: Kandidaten, Prüfung, Entscheidung
|
||
├── review/<id>-{orig,render,diff}.png
|
||
└── backup/ # Markdown-Dateien vor dem ersten apply
|
||
```
|
||
|
||
Die Datensätze sind über den SHA-256 des Bildes verknüpft. Ein erneuter epub2md-Lauf mit `--force` verliert
|
||
deshalb keine Entscheidungen. `scan` und `apply` spielen sie ohne neue OCR wieder ein. Modellantworten
|
||
werden je Modell gespeichert; dasselbe Modell wird nie zweimal nach demselben Bild gefragt.
|
||
|
||
## Quellen
|
||
|
||
| Quelle | Erkennung | Begrenzer |
|
||
| --- | --- | --- |
|
||
| epub2md-Ordner | `conversion-report.json` (`tool: epub2md`); Warnungen `formula-image` markieren sichere Formeln | `--math auto`: `$…$`/`$$…$$`, `--math tex`: `\(…\)`/`\[…\]` |
|
||
| pdf2md-Datei `<name>.md` + `<name>_images/` oder `--split`-Ordner | Bildname `<pdf>-<seite>-<box>`; mit `--pdf` wird jede Formel per Template-Matching auf der Seite gefunden, mit 600 dpi aus dem Vektor-PDF neu ausgeschnitten und an Mathe-Schriften (CMMI, EURM, …) als Formel erkannt | `$…$`/`$$…$$` |
|
||
|
||
Abgesetzte Bildformeln gelten als Display-Mathematik, Bilder im Fließtext als Inline-Mathematik.
|
||
Codeblöcke werden nie verändert. Inline-Formeln, die pdf2md als Glyphensalat im Fließtext ausgibt,
|
||
sind nicht Teil dieser Version.
|
||
|
||
## Engines
|
||
|
||
Jede Engine spricht die OpenAI-Chat-API mit Bildteil:
|
||
|
||
| `--engine` | Standard | Konfiguration |
|
||
| --- | --- | --- |
|
||
| `local` | llama.cpp-Server `http://127.0.0.1:8011/v1`, Modell vom Server | `FORMULA_LOCAL_URL`, `FORMULA_LOCAL_MODEL`, `FORMULA_LOCAL_API_KEY` |
|
||
| `cloud` | OpenRouter, `google/gemini-2.5-flash` | `OPENROUTER_API_KEY`, `FORMULA_CLOUD_MODEL`, `FORMULA_CLOUD_URL` |
|
||
|
||
Getestet: **Qwen3-VL-30B-A3B-Instruct** (Q4_K_M + mmproj) auf einer RTX 3090 mit 0,2–0,5 s je Formel;
|
||
Gehrke Brückenkurs (1329 Bilder) in 9 Minuten.
|
||
|
||
## Stand und Grenzen
|
||
|
||
Messungen vom 07.10.2026 (Details: `calibrate`):
|
||
|
||
- **Synthetisch** (Computer Modern, ~1,4 px/pt, JPEG – wie Gehrke): Die korrekte Formel schlägt in
|
||
98 % der Fälle alle Verwechslungsvarianten; ein einzelner falscher Startkandidat wird meist repariert.
|
||
- **Gehrke Brückenkurs** (EPUB, ~1400 Formel-JPGs, CM): Die OCR ist gut. Abgelehnt oder ins Review
|
||
geschickt wird überwiegend wegen **typografischer** Abweichungen der OCR, z. B.
|
||
`\xrightarrow{…}` statt `\stackrel{…}{\longrightarrow}`, `\vec` statt `\overrightarrow`, Text neben statt
|
||
über `=`. Bei einer Pfeillänge verrutscht alles dahinter. Abhilfe in Arbeit: Korrekturrunde, in der das
|
||
Modell Original und eigenes Render nebeneinander sieht.
|
||
- **Springer-Bücher** (Liesen, Göllmann): Was im alt-Text kein TeX hat, sind meist komplexe mehrzeilige
|
||
Formeln eines fremden Satzsystems. Abstände und Klammergrößen weichen ab, der starre Vergleich bestätigt
|
||
sie nicht → Review.
|
||
- **PDF** (Hefferon, Euler-Schrift): Einfache Formeln passen mit `euler` sehr gut (NCC 0,96). Wo Ziffern
|
||
oder Operatornamen in anderen Schnitten gesetzt sind (serifenloses `sgn`), wird der Kontrasttest
|
||
unzuverlässig → Review.
|
||
- Formeln auf farbig hinterlegten Kästen verlieren beim Ersetzen die Hinterlegung.
|
||
|
||
Nächste Schritte: Schwellen mit `calibrate` an den layouttreuen Varianten neu festlegen; Korrekturrunde
|
||
mit Diff-Bild; abschnittsweiser Vergleich (Zeilen/Spalten getrennt ausrichten) für mehrzeilige Formeln
|
||
fremder Satzsysteme; Inline-Formeln in PDFs.
|
||
|
||
## Installation
|
||
|
||
```bash
|
||
sudo apt install texlive-latex-extra texlive-fonts-extra texlive-science pandoc # pdflatex, Schriften, Pandoc
|
||
pip install -r requirements.txt # pymupdf, numpy, scipy, scikit-image, opencv, Pillow, lxml, requests
|
||
pip install -r requirements-dev.txt # pytest
|
||
```
|
||
|
||
## Tests
|
||
|
||
```bash
|
||
python3 -m pytest -q # ca. 30 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.
|
||
|
||
## Architektur
|
||
|
||
```text
|
||
formula_img2latex/
|
||
├── cli.py Unterbefehle, Exit-Codes
|
||
├── pipeline.py scan, recognize, verify (Prozess-Pool), Entscheidung, apply/revert
|
||
├── sources/ epub2md.py, pdf2md.py, base.py (Markdown-Scan, Begrenzer)
|
||
├── pdfcrop.py Formelbild auf der PDF-Seite finden, 600-dpi-Neuausschnitt, Mathe-Schrift-Erkennung
|
||
├── classify.py Formel / unklar / Abbildung (Farbe der Tinte, Graustufen, Größe, Kontext)
|
||
├── engines/ Vision-Modelle über die OpenAI-API (llama.cpp, OpenRouter)
|
||
├── render.py pdflatex-Batch, Schrift-Presets, Rasterung
|
||
├── imageio.py Originale laden (PNG/GIF/JPG/SVG, Transparenz, Tönung)
|
||
├── compare.py Ausrichtung, Kennzahlen, Kontrasttest, Diff-Bilder
|
||
├── confusables.py Verwechslungsvarianten, layouttreue Prüfvarianten
|
||
├── verify.py Prüfung + Selbstkorrektur
|
||
├── decide.py Schwellen und Status
|
||
├── checks.py Pandoc/MathML-Prüfung
|
||
├── calibrate.py Messung an Formelbildern mit bekanntem TeX
|
||
├── store.py Arbeitsordner, Datensätze
|
||
└── review/ lokaler Review-Server + Seite
|
||
```
|