Spaces:
Sleeping
Sleeping
Implement strict QC suite, restore German umlauts, isolate ds labels, and fix formula completeness
74bf0c3 verified Download ocr_schema.py from MasterSayn/ocr-app-private: direct link, hf CLI and curl.
- Browser
- Download file 14.4 kB
-
https://huggingface.co/spaces/MasterSayn/ocr-app-private/resolve/main/ocr_schema.py
- Command line
-
hf download hf://spaces/MasterSayn/ocr-app-private/ocr_schema.py
-
curl -L -o ocr_schema.py https://huggingface.co/spaces/MasterSayn/ocr-app-private/resolve/main/ocr_schema.py
14.4 kB
| """ | |
| Pydantic Schemas & Universal System Prompt for Math & Handwriting OCR Pipeline V2.2. | |
| Implements: | |
| 1. Enhanced Regex Auto-Repair for UnicodeMath Subscripts & Fraction Symmetry | |
| 2. Two-Pass Table Extraction Blueprint (Phase 1: Region Detection, Phase 2: Cropped Markdown Extraction) | |
| 3. Micro-spatial Denominator Isolation against fraction collisions | |
| 4. Truth-doctrine with epistemic Escape Hatch '[UNLESERLICH]' for handwriting | |
| 5. Symmetry enforcement for rational terms (fractions) | |
| """ | |
| import re | |
| from pydantic import BaseModel, Field, field_validator | |
| def repair_unicodemath_subscripts(text_content: str) -> str: | |
| """ | |
| Syntaktischer Auto-Repair Mechanismus für UnicodeMath. | |
| Isoliert fälschlicherweise absorbierte Fließtexte aus ungeschlossenen Subskripten, | |
| stellt die mathematische Klammerhierarchie und die Zählersymmetrie deterministisch wieder her. | |
| Entfernt Phantom-Zeichen wie '= F' in Symbol-Legenden. | |
| """ | |
| if not text_content: | |
| return "" | |
| s = str(text_content) | |
| # 0. Phantom '= F' Bereinigung in Symbol-Legenden | |
| # Fall 1: '= F <symbol> <beschreibung>' z.B. '= F w Lagerverdrehung' -> 'w = Lagerverdrehung' | |
| s = re.sub(r'=\s*F\s+([a-zA-Z_]\w*)\s+([A-ZÄÖÜ])', r'\1 = \2', s) | |
| # Fall 2: '... = F ...' z.B. 'δ = F Verschiebung' -> 'δ = Verschiebung' | |
| s = re.sub(r'=\s*F\s+', r'= ', s) | |
| # Fall 3: '= F' am Zeilenende oder vor Einheiten/Klammern | |
| s = re.sub(r'=\s*F(?=[\s\n\[\]]|$)', r'=', s) | |
| # 0.1 Pseudo-Indizes / Dummy-Indizes an unbestimmten Integralen und Summen bereinigen: | |
| s = re.sub(r'∫_\([lsjwLSJW]\)\s*', r'∫ ', s) | |
| s = re.sub(r'∑_\([lsjwLSJW]\)\s*', r'∑ ', s) | |
| s = re.sub(r'∫_[lsjwLSJW](?=[^\w]|$)\s*', r'∫ ', s) | |
| s = re.sub(r'∑_[lsjwLSJW](?=[^\w]|$)\s*', r'∑ ', s) | |
| # 0.2 Deutsche OCR-Umlaut- und Tippfehler-Korrekturen: | |
| s = re.sub(r'\bFlchentrgheitsmoment\b', 'Flächenträgheitsmoment', s) | |
| s = re.sub(r'\bQuerschnittsflche\b', 'Querschnittsfläche', s) | |
| s = re.sub(r'\bWrmeausdehnungszahl\b', 'Wärmeausdehnungszahl', s) | |
| s = re.sub(r'\bberzhligen\b', 'Überzähligen', s) | |
| s = re.sub(r'\bBalkenstbe\b', 'Balkenstäbe', s) | |
| s = re.sub(r'\bFachwerkstbe\b', 'Fachwerkstäbe', s) | |
| s = re.sub(r'\bStablngennderung\b', 'Stablängenänderung', s) | |
| s = re.sub(r'\bFlche\b', 'Fläche', s) | |
| s = re.sub(r'\bflche\b', 'fläche', s) | |
| s = re.sub(r'\bWrme\b', 'Wärme', s) | |
| s = re.sub(r'\bwrme\b', 'wärme', s) | |
| # 0.3 Denominator & ds Absorbed Label Isolation (z.B. 'dsngleichf. t' -> 'ds'): | |
| s = re.sub(r'ds\s*(?:u?ngleichf\.?\s*t?|lagerversch\.?|lagerverdreh\.?|stabl[äa]ngen[äa]nderung|biegung|dehnung|schub)', r'ds', s, flags=re.IGNORECASE) | |
| # 0.4 Integraltafel: Zeilenspaltung für verschmolzene Tabellenzeilen: | |
| if '|' in s: | |
| s = re.sub(r'(\s*\|\s*)(?=\|\s*(?:[1-9]|1[0-2])\s*\|)', r'\1\n', s) | |
| # 0.5 Deduplizierung von P ... Position τ_max(x) aus Abschnitt 4: | |
| if '4 Biegeverformungen' in s or 'Differentialgleichungen' in s: | |
| s = re.sub(r'\s*P\s*\.\.\.\s*Position\s*τ_?(?:\(max\)|max)?\(x\)', '', s) | |
| # 1. Typo-Fix für geschlucktes S vor (chubspannung: | |
| s = re.sub(r'\(chubspannung:', r'Schubspannung:', s) | |
| # 2. Datum/Banner-Abschirmung: δ_(Stand: 12. Juli 2024) -> δ (Stand: 12. Juli 2024) | |
| s = re.sub(r'_\((Stand|Datum|Seite|Quelle)', r' (\1', s) | |
| # 3. Doppelpunkt unmittelbar nach Index in ungeschlossenen Subskripten: τ_(i: -> τ_(i) : | |
| s = re.sub(r'_\(([a-zA-Z0-9_]{1,5}?):', r'_(\1) :', s) | |
| # 4. Geschlossene Subskripte mit absorbiertem Wort: _(maxQuerschnitt) -> _(max) Querschnitt | |
| s = re.sub(r'_\(([a-z0-9]{1,4}?)([A-ZÄÖÜ][a-zäöüß]+|versch|verdreh|steg|länge|höhe|[a-zäöüß]{4,})\)', r'_(\1) \2', s) | |
| # 5. Ungeschlossene Subskripte mit absorbiertem Wort: _(maxQuerschnitt -> _(max) Querschnitt | |
| s = re.sub(r'_\(([a-z0-9]{1,4}?)([A-ZÄÖÜ][a-zäöüß]+|versch|verdreh|steg|länge|höhe|[a-zäöüß]{4,})', r'_(\1) \2', s) | |
| # 6. Subskripte ohne Klammern mit absorbiertem Wort: N_iversch. -> N_(i) versch. | |
| s = re.sub(r'_([a-z0-9]{1,2}?)(versch|verdreh|steg|länge|höhe|[A-ZÄÖÜ][a-zäöüß]{3,}|:)', r'_(\1) \2', s) | |
| # 7. Subskripte gefolgt von Leerzeichen und deutschem Wort: _(m von der... -> _(m) von der... | |
| s = re.sub(r'_\(([a-z0-9_]{1,4})\s+(von|der|die|das|und|oder|in|an|für|bei|linear)', r'_(\1) \2', s) | |
| # 8. Kreisring Subskripte für außen / innen: R_(a) ußen -> R_(a,außen), R_(i) nnen -> R_(i,innen) | |
| s = re.sub(r'R_\(([a-z0-9_]+)\)\s*(?:u[ßs]en|ussen|uen)', r'R_(\1,außen)', s) | |
| s = re.sub(r'R_\(([a-z0-9_]+)\)\s*(?:nnen|innen)', r'R_(\1,innen)', s) | |
| s = re.sub(r'R_([a-z0-9_]+)\s*(?:u[ßs]en|ussen|uen)', r'R_(\1,außen)', s) | |
| s = re.sub(r'R_([a-z0-9_]+)\s*(?:nnen|innen)', r'R_(\1,innen)', s) | |
| # 9. Globale Hierarchie-Validierung (offene Klammern am Blockende schließen) | |
| open_c = s.count('(') | |
| close_c = s.count(')') | |
| if open_c > close_c: | |
| s = re.sub(r'(_\([^)\s]+)(?=[,\s;:]|$)', r'\1)', s) | |
| # 10. Symmetrie-Reparatur für ungeschlossene Zähler: | |
| # z.B. (2α - α^(2) - γ^(2)/(6) -> (2α - α^(2) - γ^(2))/(6) | |
| i = 0 | |
| while i < len(s): | |
| if s[i] == '/': | |
| j = i - 1 | |
| paren_depth = 0 | |
| while j >= 0: | |
| if s[j] == ')': | |
| paren_depth += 1 | |
| elif s[j] == '(': | |
| paren_depth -= 1 | |
| if paren_depth < 0: | |
| s = s[:i] + ')' + s[i:] | |
| i += 1 | |
| break | |
| elif s[j] in ['=', '∫', '∑', '\n', ';'] and paren_depth <= 0: | |
| break | |
| j -= 1 | |
| i += 1 | |
| return s | |
| class OCRBlock(BaseModel): | |
| box_2d: list[int] = Field( | |
| description="Bounding Box zwingend als [ymin, xmin, ymax, xmax] mit Werten normalisiert von 0 bis 1000." | |
| ) | |
| text_content: str = Field( | |
| description="Erkannter Text oder Formel in UnicodeMath (kein LaTeX). Echte Unicode-Zeichen nutzen." | |
| ) | |
| block_type: str = Field( | |
| default="text", | |
| description="Kategorie des Inhalts: 'text', 'math', 'handwriting', 'table'." | |
| ) | |
| def auto_repair_text(cls, v: str) -> str: | |
| return repair_unicodemath_subscripts(v) | |
| def text(self) -> str: | |
| return self.text_content | |
| class PageResult(BaseModel): | |
| blocks: list[OCRBlock] = Field( | |
| description="Liste aller erkannten Inhaltsblöcke auf der Seite." | |
| ) | |
| SYSTEM_PROMPT_UNIVERSAL_V2 = """Du bist eine hochpräzise OCR- und Document-Understanding-KI für wissenschaftliche Dokumente, mathematische Skripte und Notizen. | |
| Deine Aufgabe ist es, absolut alle textlichen, mathematischen und handschriftlichen Inhalte aus dem Bild vollständig und fehlerfrei zu extrahieren. | |
| STRIKTE REGELN FÜR DIE EXTRAKTION: | |
| 1. BOUNDING BOXEN ('box_2d'): | |
| - Erfasse jeden semantischen Block, jede Textzeile, jede Formelzeile und jede Tabelle mit einer eigenen, eng anliegenden Bounding Box. | |
| - Format: [ymin, xmin, ymax, xmax] mit ganzzahligen Werten normalisiert von 0 bis 1000. | |
| - Keine leeren Ränder, keine Zusammenfassung separater Spalten in eine einzige Riesen-Box. | |
| 2. MATHEMATIK & FORMELN ('math'): | |
| - Konvertiere JEDE mathematische Formel strikt in lineares UnicodeMath (KEIN LaTeX, kein '\\sum', kein '\\int', kein '\\frac'). | |
| - Nutze echte Unicode-Zeichen: ∫, ∑, ∏, √, ϑ, φ, α, β, γ, δ, ε, κ, λ, μ, ν, π, ρ, σ, τ, ω, Δ, ∞, ·, ±, ≤, ≥, ≠, ≈. | |
| - Brüche werden als '(Zähler)/(Nenner)' formatiert, z.B. '(M_k · M_i)/(EI)'. | |
| - SYMMETRIE-PFLICHT FÜR BRÜCHE: | |
| Jeder mathematische Zähler, der mit einer öffnenden Klammer '(' beginnt, MUSS zwingend mit einer schließenden Klammer ')' beendet werden, bevor das Divisionszeichen '/' folgt. Prüfe deine Brüche immer auf balancierte Klammern (z. B. '(a + b)/(c)' anstelle von '(a + b/(c)'). | |
| - SPATIAL ISOLATION / NENNER-ISOLATION BEI BRÜCHEN: | |
| Wenn eine Bruchlinie (Fraction Bar) erkannt wird, führe eine zwingende mikro-räumliche Isolation durch: | |
| 1. Lokalisiere den Zähler exakt über der Bruchlinie. | |
| 2. Identifiziere als Nenner AUSSCHLIESSLICH die direkt an die Unterkante der Bruchlinie angrenzenden Zeichen (typischerweise Variablen wie EI, GA, EA, h, 2A_m, 6). | |
| 3. Es ist STRENGSTENS VERBOTEN, Textfragmente, Beschriftungen oder Legenden (z.B. 'Biegung', 'Dehnung', 'Schub', 'Lagerverdrehung', 'Lagerverschiebung'), die mit vertikalem Abstand unterhalb des eigentlichen Nenners stehen, in die Bruch-Syntax aufzunehmen! Die mathematische Integrität der Formel hat absolute Priorität vor dem umliegenden Text. | |
| - SUMMEN & INTEGRALE (KEINE KÜNSTLICHEN DUMMY-INDIZES): | |
| Unbestimmte Integrale oder Summen ohne explizit im Dokument gedruckte Grenzen oder Indizes werden IMMER als einfaches '∫' bzw. '∑' ohne Subskript geschrieben. | |
| Es ist STRENGSTENS VERBOTEN, künstliche Dummy-Indizes oder Bereichsbuchstaben (wie '_(l)', '_(s)', '_(j)', '_(w)') zu erfinden, wenn diese nicht wortwörtlich und explizit im Dokument gedruckt sind! | |
| Nur tatsächlich vorhandene, gedruckte mathematische Grenzen oder Laufvariablen werden als Subskript/Superskript transkribiert (z.B. '∫_0^L' oder '∑_(k=1)^n'). | |
| - DEUTSCHE UMLAUTE: Erhalte ausnahmslos alle deutschen Umlaute (ä, ö, ü, ß). Schreibe z.B. 'Flächenträgheitsmoment' (niemals 'Flchentrgheitsmoment'), 'Querschnittsfläche', 'Wärmeausdehnungszahl', 'Überzähligen', 'Balkenstäbe'. | |
| - GESTAPELTE LINKE SEITEN & GLEICHUNGEN: | |
| Stehen auf der linken Seite einer Gleichung mehrere alternative Ausdrücke untereinander (z.B. mit 'oder' oder geschweifter Klammer), erfasse die GESAMTE Gleichung inklusive aller linken Bedingungen und der rechten Seite in EINER EINZIGEN Bounding Box! Format: '(1,0 kN · δ_(ik) oder 1,0 kNm · φ_(ik)) = ...'. Erstelle NIEMALS eine isolierte Box, die nur mit '=' beginnt! | |
| - VOLLSTÄNDIGKEIT LANGER FORMELN & ARBEITSGLEICHUNG BALKENSTÄBE: | |
| Die Arbeitsgleichung Balkenstäbe besteht zwingend aus ACHT (8) Summanden über die gesamte Seitenbreite: | |
| '(1,0 kN · δ_(ik) oder 1,0 kNm · φ_(ik)) = ∫ (M_k · M_i)/(EI) ds + ∫ (N_k · N_i)/(EA) ds + ∫ (κ · V_k · V_i)/(GA) ds + ∫ N_i · α_T · T ds + ∫ M_i · α_T · (ΔT)/(h) ds - ∑ C_(i,l) · Δc_(kl) - ∑ M_(i,w) · φ_(k,w) - ∑ S_(i,s) · s_(k,s)' | |
| Die Bounding Box MUSS von xmin bis zum rechten Seitenrand xmax reichen und direkt unter der Überschrift 'Arbeitsgleichung Balkenstäbe' positioniert sein! | |
| Es ist STRENGSTENS VERBOTEN, nach Term 5 abzubrechen oder die Beschriftungen 'Biegung', 'Dehnung', 'Schub', 'gleichf. t', 'ungleichf. t', 'Lagerversch.', 'Lagerverdreh.', 'Stablängenänderung' in den Formeltext zu ziehen! | |
| - SYMBOL-LEGENDEN & KEIN PHANTOM '= F': | |
| In Legenden und Abkürzungslisten (z.B. 'δ = Verschiebung [m]'): Trenne Symbol, Gleichheitszeichen und Erläuterung sauber mit '='. Schreibe NIEMALS ein erfundenes oder hineingedeutetes '= F' vor die Beschreibung! | |
| - NEBENEINANDERSTEHENDE FORMELN: | |
| Stehen zwei oder mehr Formeln horizontal nebeneinander (z. B. TTM links und Schubspannung rechts), erstelle für JEDE Formel eine EIGENE separate Bounding Box! Fasse nebeneinanderstehende Formeln NIEMALS in eine einzige Box zusammen. | |
| 3. HANDSCHRIFT ('handwriting') & EPISTEMISCHE UNSICHERHEIT: | |
| - Erfasse JEDE handschriftliche Notiz vollständig – unabhängig von der Schreibfarbe (schwarz, bleistiftgrau, blau, rot, lila, grün oder weiß auf dunklem Grund). | |
| - Klassifiziere alle handschriftlichen Elemente mit block_type='handwriting'. | |
| - ABSOLUTES HALLUZINATIONS-VERBOT & ESCAPE-HATCH [UNLESERLICH]: | |
| Handschriftliche Notizen unterliegen einer strikten Wahrheits-Doktrin: | |
| 1. Transkribiere AUSSCHLIESSLICH Grapheme und Wörter, die aufgrund ihrer visuellen Bildinformation zweifelsfrei lesbar sind. | |
| 2. Wenn ein handgeschriebenes Wort, eine Zahl oder ein Symbol unleserlich, verwischt oder stark überlagert ist, ist es DIR STRENGSTENS UNTERSAGT, auf Basis des mathematischen Kontexts fehlende Variablen zu 'erraten' oder zu interpolieren. | |
| 3. Jedes unleserliche Element MUSS zwingend durch das dedizierte Tag '[UNLESERLICH]' ersetzt werden. In wissenschaftlichen Dokumenten ist Informationsauslassung der Falschinformation strikt vorzuziehen. | |
| 4. TABELLEN & WERTEÜBERSICHTEN ('table'): | |
| - Fasse jede Tabelle (z. B. Schubverteilung κ, Rechteckquerschnitt-Werte h/b | α | β, Torsionstabellen) als EINE zusammenhängende Bounding Box mit block_type='table' zusammen! | |
| - Formatiere kleine und mittlere Wertetabellen IMMER direkt als saubere Markdown-Tabelle mit Spaltentrennern ('| Spalte 1 | Spalte 2 | ... |') und Trennzeile ('|---|---|'). Erstelle NIEMALS Dutzende winzige isolierte Einzelboxen für einzelne Zahlenwerte innerhalb derselben Wertetabelle! | |
| - RIESIGE INTEGRALTAFELN (ab 50+ Zellen / dichte Formelgitter): Fasse den gesamten Tabellenbereich als EINE Bounding Box mit block_type='table' und text_content='[TABELLE FOLGT]' zusammen, damit die tabellarische Feinstrukturierung im fokussierten zweiten Durchlauf erfolgt. | |
| - SKIZZEN & DIAGRAMME: Grafische Zeichnungen, Kurvenskizzen, Momentenverläufe und Querschnitts-Illustrationen dürfen NICHT als Pseudo-Matrizen '■(...)' oder ASCII-Art transkribiert werden. Transkribiere in Tabellen ausschließlich tatsächlich vorhandenen Text, Zahlen, Formeln und Achsenbeschriftungen. | |
| 5. TEXTBLÖCKE ('text'): | |
| - Erhalte logische Absätze und Zeilenumbrüche. | |
| - Zweispaltige Übersichten (wie Übersicht griechische Buchstaben): | |
| Das Alphabet besteht zwingend aus 24 Buchstaben in 2 Spalten: | |
| Spalte 1: A (Alpha), B (Beta), Γ (Gamma), Δ (Delta), E (Epsilon), Z (Zeta), H (Eta), Θ (Theta), I (Iota), K (Kappa), Λ (Lambda), M (My). | |
| Spalte 2: N (Ny), Ξ (Xi), O (Omikron), Π (Pi), P (Rho), Σ (Sigma), T (Tau), Y (Ypsilon), Φ (Phi), X (Chi), Ψ (Psi), Ω (Omega). | |
| Erstelle für JEDEN Eintrag eine EIGENE separate Bounding Box. Lese zwingend zuerst alle 12 Einträge der linken Spalte (bis einschließlich M, μ My) und danach alle 12 Einträge der rechten Spalte (bis einschließlich Ω, ω Omega) vollständig aus, bevor nachfolgende Abschnitte ('4 Biegeverformungen') begonnen werden! | |
| """ | |