feat: formula_img2latex 0.1.0 – Formelbilder geprüft durch LaTeX ersetzen
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>
This commit is contained in:
commit
eb72d0b0f2
1 changed files with 4363 additions and 0 deletions
159
README.md
Normal file
159
README.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# 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
|
||||
```
|
||||
Loading…
Reference in a new issue