Image-to-Text
Transformers
Safetensors
Turkish
English
ocr
turkish
document-intelligence
vision-language-model
svtrv2
qwen2.5-vl
kvkk
pii-redaction
legal-nlp
ictihat
financial-verification
visual-redaction
Eval Results (legacy)
Instructions to use ersanbil/TurkceOCR with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ersanbil/TurkceOCR with Transformers:
# Use a pipeline as a high-level helper # Warning: Pipeline type "image-to-text" is no longer supported in transformers v5. # You must load the model directly (see below) or downgrade to v4.x with: # pip install "transformers<5.0.0" from transformers import pipeline pipe = pipeline("image-to-text", model="ersanbil/TurkceOCR")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ersanbil/TurkceOCR", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download turkceocr/data/pdf_and_table_engine.py from ersanbil/TurkceOCR: direct link, hf CLI and curl.
- Browser
- Download file 20.5 kB
-
https://huggingface.co/ersanbil/TurkceOCR/resolve/main/turkceocr/data/pdf_and_table_engine.py
- Command line
-
hf download hf://ersanbil/TurkceOCR/turkceocr/data/pdf_and_table_engine.py
-
curl -L -o pdf_and_table_engine.py https://huggingface.co/ersanbil/TurkceOCR/resolve/main/turkceocr/data/pdf_and_table_engine.py
20.5 kB
| """ | |
| TürkçeOCR — İleri Düzey Karmaşık Muhasebe Tablosu (Çoklu KDV %1/%10/%20, İskonto, | |
| Birleşik Hücre, Çizgisiz/Zebra/Izgara Stilleri) ve Born-Digital Türkçe PDF -> Bozulmuş | |
| İkiz (`BornDigitalPDFPairGenerator`) Üretim Motoru. | |
| Bu modül: | |
| 1. `ComplexTurkishTableRenderer`: | |
| - 5–7 sütunlu (`Sıra`, `Stok Kodu`, `Kalem / Hizmet Açıklaması`, `Miktar`, `İskonto`, | |
| `KDV Oranı (%1, %10, %20)`, `Tutar (TL)`) yoğun kurumsal tabloları, | |
| - `full_grid` (tam ızgara), `horizontal_only` (dikey çizgisiz modern ERP faturası) ve | |
| `zebra_striped` (satır dolgulu) olmak üzere 3 farklı görsel tablo stilinde çizer, | |
| - Alt kısımda **Çoklu KDV Matrah ve Tutar Dökümü Tablosu** oluşturur. | |
| 2. `BornDigitalPDFPairGenerator`: | |
| - Gerçek born-digital Türkçe PDF sayfalarını (eğer ortamda `pymupdf`/`fitz` veya `pypdf` | |
| mevcutsa doğrudan PDF'den, değilse çift sütunlu akademik/mevzuat PDF mizanpaj | |
| motorundan) yüksek çözünürlüklü görüntüye çevirir, | |
| - Metin bloklarının koordinatlarını (`bbox`) çıkarıp `TurkceDegrade` ile tam metin/tablo | |
| üzerine ıslak kaşe/mühür/imza basarak `(Bozulmuş_Görüntü, Temiz_Görüntü, Markdown, | |
| SURR_Kaşe_Altı_Metni, Token_Maskesi, Satır_Kırpımları)` ikizini üretir. | |
| """ | |
| from __future__ import annotations | |
| import io | |
| import random | |
| from dataclasses import dataclass, field | |
| from pathlib import Path | |
| from typing import Dict, List, Optional, Tuple | |
| from PIL import Image, ImageDraw | |
| from turkceocr.data.degradation import TurkceDegrade | |
| from turkceocr.data.stamp_signature import _load_turkish_font | |
| from turkceocr.data.synthetic_generator import ( | |
| AGGLUTINATIVE_HARD_LEXICON, | |
| SyntheticDocumentSample, | |
| TurkishDocumentGenerator, | |
| ) | |
| from turkceocr.postprocess.financial_solver import format_turkish_amount | |
| ERP_LINE_ITEMS_POOL = [ | |
| ("STK-0104", "Kurumsal Türkçe Görsel-Dil Modeli (VLM) Sunucu Lisansı", "Adet", 0.20), | |
| ("STK-0219", "Yüksek Çözünürlüklü Belge Tarama ve Arşivleme Hizmeti", "Ay", 0.20), | |
| ("GDA-0842", "Organik Iğdır Kayısısı ve Kuruyemiş Kumanya Paketi", "Koli", 0.01), | |
| ("TKS-0511", "Personel İş Güvenliği Koruyucu Tekstil Ekipmanı", "Takım", 0.10), | |
| ("DNS-0930", "Sözleşmelerimizdeki Mevzuat Uyum ve Mali Müşavirlik Hizmeti", "Saat", 0.20), | |
| ("EGT-0315", "Yapılandırılmış Veri Analitiği ve Kurumsal Eğitim Programı", "Gün", 0.10), | |
| ("TLM-0774", "Temel Gıda Toptan Un ve Bakliyat Sevkiyatı", "Çuval", 0.01), | |
| ] | |
| class ComplexTableItem: | |
| """Karmaşık muhasebe tablosundaki tek bir satır kalemi.""" | |
| index: int | |
| sku_code: str | |
| description: str | |
| quantity: int | |
| unit: str | |
| unit_price: float | |
| discount_rate: float # 0.00, 0.05, 0.10 | |
| vat_rate: float # 0.01, 0.10, 0.20 | |
| def gross_amount(self) -> float: | |
| return round(self.quantity * self.unit_price, 2) | |
| def discount_amount(self) -> float: | |
| return round(self.gross_amount * self.discount_rate, 2) | |
| def net_matrah(self) -> float: | |
| return round(self.gross_amount - self.discount_amount, 2) | |
| def vat_amount(self) -> float: | |
| return round(self.net_matrah * self.vat_rate, 2) | |
| class ComplexTurkishTableRenderer: | |
| """ | |
| `TEDS >= %96.5` (KPI-8) ve `CVKR >= %99.5` (KPI-10) hedefleri için çoklu KDV | |
| (`%1, %10, %20`), iskonto ve 3 farklı tablo çizgi topolojisi (`full_grid`, | |
| `horizontal_only`, `zebra_striped`) üreten yüksek entropili tablo motoru. | |
| """ | |
| def __init__(self, seed: Optional[int] = None) -> None: | |
| self.rng = random.Random(seed) | |
| self.doc_gen = TurkishDocumentGenerator(seed=seed) | |
| self.degrader = TurkceDegrade(seed=seed) | |
| def generate_multi_vat_erp_invoice( | |
| self, | |
| apply_degradation: bool = True, | |
| is_ood: bool = False, | |
| table_style: Optional[str] = None, | |
| ) -> SyntheticDocumentSample: | |
| w, h = (920, 1140) if is_ood else (880, 1080) | |
| clean_img = Image.new("RGB", (w, h), (254, 254, 252)) | |
| draw = ImageDraw.Draw(clean_img) | |
| style = table_style or self.rng.choice(["full_grid", "horizontal_only", "zebra_striped"]) | |
| font_h1 = _load_turkish_font(19, style="bold", rng=self.rng) | |
| font_bold = _load_turkish_font(13, style="bold", rng=self.rng) | |
| font_reg = _load_turkish_font(12, style="sans", rng=self.rng) | |
| font_mono = _load_turkish_font(12, style="mono", rng=self.rng) | |
| tckn = self.doc_gen.generate_valid_tckn() | |
| vkn = self.doc_gen.generate_valid_vkn() | |
| iban = self.doc_gen.generate_valid_tr_iban() | |
| num_rows = self.rng.randint(3, 5) | |
| sampled_specs = self.rng.sample(ERP_LINE_ITEMS_POOL, k=num_rows) | |
| items: List[ComplexTableItem] = [] | |
| for idx, (sku, desc, unit, vat_r) in enumerate(sampled_specs, start=1): | |
| qty = self.rng.randint(1, 12) | |
| u_price = round(self.rng.uniform(450.0, 4800.0), 2) | |
| disc_r = self.rng.choice([0.0, 0.05, 0.10]) if is_ood else 0.0 | |
| items.append( | |
| ComplexTableItem( | |
| index=idx, | |
| sku_code=sku, | |
| description=desc, | |
| quantity=qty, | |
| unit=unit, | |
| unit_price=u_price, | |
| discount_rate=disc_r, | |
| vat_rate=vat_r, | |
| ) | |
| ) | |
| total_matrah = round(sum(it.net_matrah for it in items), 2) | |
| total_kdv = round(sum(it.vat_amount for it in items), 2) | |
| grand_total = round(total_matrah + total_kdv, 2) | |
| header = ( | |
| "T.C. GELİR İDARESİ BAŞKANLIĞI — ÇOKLU KDV KURUMSAL ERP e-FATURA" | |
| if is_ood | |
| else "T.C. GELİR İDARESİ BAŞKANLIĞI — TİCARİ e-FATURA DETAY CETVELİ" | |
| ) | |
| seller = "SATICI: ANADOLU ENDÜSTRİYEL BİLİŞİM VE LOJİSTİK A.Ş." | |
| buyer = "ALICI: İSTANBUL ŞİŞLİ VE IĞDIR BÖLGE MÜDÜRLÜĞÜ" | |
| line_bboxes: List[Tuple[Tuple[int, int, int, int], str]] = [] | |
| draw.text((36, 30), header, fill=(15, 15, 15), font=font_h1) | |
| line_bboxes.append(((32, 26, w - 32, 60), header)) | |
| draw.line([(36, 64), (w - 36, 64)], fill=(30, 30, 30), width=2) | |
| draw.text((36, 80), seller, fill=(20, 20, 20), font=font_bold) | |
| draw.text((36, 104), f"VERGİ KİMLİK NO (VKN): {vkn} | BANKA IBAN: {iban}", fill=(20, 20, 20), font=font_reg) | |
| draw.text((36, 130), f"{buyer} | TCKN: {tckn}", fill=(20, 20, 20), font=font_reg) | |
| line_bboxes.append(((32, 100, w - 32, 126), f"VERGİ KİMLİK NO (VKN): {vkn} | BANKA IBAN: {iban}")) | |
| # Tablo sütun X koordinatları | |
| col_x = [36, 95, 185, 515, 605, 685, 765, w - 36] | |
| headers = ["Sıra", "Stok Kodu", "Kalem / Hizmet Açıklaması", "Miktar", "İskonto", "KDV", "Net Tutar (TL)"] | |
| table_top = 175 | |
| row_h = 38 | |
| table_bottom = table_top + (num_rows + 1) * row_h | |
| # Başlık arka planı | |
| draw.rectangle([col_x[0], table_top, col_x[-1], table_top + row_h], fill=(232, 236, 242)) | |
| for c_i, h_text in enumerate(headers): | |
| draw.text((col_x[c_i] + 6, table_top + 10), h_text, fill=(10, 10, 10), font=font_bold) | |
| md_table_rows = [ | |
| "| Sıra | Stok Kodu | Kalem / Hizmet Açıklaması | Miktar | İskonto | KDV | Net Tutar (TL) |", | |
| "| :---: | :---: | :--- | :---: | :---: | :---: | ---: |", | |
| ] | |
| for r_i, it in enumerate(items): | |
| y_r = table_top + (r_i + 1) * row_h | |
| if style == "zebra_striped" and r_i % 2 == 1: | |
| draw.rectangle([col_x[0], y_r, col_x[-1], y_r + row_h], fill=(246, 247, 250)) | |
| if style in ("full_grid", "horizontal_only"): | |
| draw.line([(col_x[0], y_r), (col_x[-1], y_r)], fill=(120, 120, 120), width=1) | |
| qty_str = f"{it.quantity} {it.unit}" | |
| disc_str = f"%{int(it.discount_rate * 100)}" | |
| vat_str = f"%{int(it.vat_rate * 100)}" | |
| net_str = format_turkish_amount(it.net_matrah) | |
| cells = [ | |
| str(it.index), | |
| it.sku_code, | |
| it.description[:42], | |
| qty_str, | |
| disc_str, | |
| vat_str, | |
| net_str, | |
| ] | |
| for c_i, cell_val in enumerate(cells): | |
| f_use = font_mono if c_i in (1, 3, 4, 5, 6) else font_reg | |
| draw.text((col_x[c_i] + 6, y_r + 10), cell_val, fill=(18, 18, 18), font=f_use) | |
| line_bboxes.append( | |
| ((col_x[0], y_r + 2, col_x[-1], y_r + row_h - 2), f"{it.sku_code} {it.description} {net_str}") | |
| ) | |
| md_table_rows.append( | |
| f"| {it.index} | {it.sku_code} | {it.description} | {qty_str} | {disc_str} | {vat_str} | {net_str} |" | |
| ) | |
| if style == "full_grid": | |
| draw.rectangle([col_x[0], table_top, col_x[-1], table_bottom], outline=(40, 40, 40), width=2) | |
| for cx in col_x[1:-1]: | |
| draw.line([(cx, table_top), (cx, table_bottom)], fill=(130, 130, 130), width=1) | |
| else: | |
| draw.line([(col_x[0], table_top), (col_x[-1], table_top)], fill=(30, 30, 30), width=2) | |
| draw.line([(col_x[0], table_top + row_h), (col_x[-1], table_top + row_h)], fill=(30, 30, 30), width=1) | |
| draw.line([(col_x[0], table_bottom), (col_x[-1], table_bottom)], fill=(30, 30, 30), width=2) | |
| # Kaşe altı hedef satırı (ortadaki tablo satırı) | |
| mid_item = items[len(items) // 2] | |
| underneath_text = mid_item.description | |
| target_bbox = (col_x[2], table_top + row_h, col_x[6], table_bottom) | |
| matrah_s = format_turkish_amount(total_matrah) | |
| kdv_s = format_turkish_amount(total_kdv) | |
| toplam_s = format_turkish_amount(grand_total) | |
| y_sum = table_bottom + 35 | |
| sum_x = w - 360 | |
| draw.text((sum_x, y_sum), f"MATRAH (ARA TOPLAM): {matrah_s} TL", fill=(15, 15, 15), font=font_reg) | |
| draw.text((sum_x, y_sum + 28), f"HESAPLANAN KDV: {kdv_s} TL", fill=(15, 15, 15), font=font_reg) | |
| draw.text((sum_x, y_sum + 56), f"GENEL TOPLAM: {toplam_s} TL", fill=(10, 10, 10), font=font_bold) | |
| line_bboxes.append(((sum_x - 4, y_sum + 52, w - 32, y_sum + 82), f"GENEL TOPLAM: {toplam_s} TL")) | |
| final_img = clean_img | |
| stamp_tags: List[str] = [] | |
| if apply_degradation: | |
| deg = self.degrader.degrade( | |
| clean_img, | |
| preset="corporate_stamped", | |
| target_text_bbox=target_bbox, | |
| ) | |
| final_img = deg.image | |
| stamp_tags = [s.xml_tag for s in deg.stamps_and_signatures] | |
| md_lines = [ | |
| f"# {header}", | |
| f"**{seller}**", | |
| f"VERGİ KİMLİK NO (VKN): <vkn>{vkn}</vkn>", | |
| f"BANKA IBAN: <iban>{iban}</iban>", | |
| f"**{buyer}**", | |
| f"T.C. KİMLİK NO (TCKN): <tckn>{tckn}</tckn>", | |
| "", | |
| *md_table_rows, | |
| "", | |
| f"- **MATRAH (ARA TOPLAM):** {matrah_s} TL", | |
| f"- **HESAPLANAN KDV:** {kdv_s} TL", | |
| f"- **GENEL TOPLAM:** {toplam_s} TL", | |
| ] | |
| if stamp_tags: | |
| md_lines.append("") | |
| md_lines.extend(stamp_tags) | |
| markdown = "\n".join(md_lines) | |
| plain = "\n".join( | |
| [header, seller, f"VKN: {vkn}", f"IBAN: {iban}", buyer, f"TCKN: {tckn}"] | |
| + [f"{it.sku_code} {it.description} {format_turkish_amount(it.net_matrah)}" for it in items] | |
| + [f"MATRAH: {matrah_s} TL", f"KDV: {kdv_s} TL", f"GENEL TOPLAM: {toplam_s} TL"] | |
| ) | |
| return SyntheticDocumentSample( | |
| doc_type="multi_vat_erp_invoice", | |
| image=final_img, | |
| clean_image=clean_img, | |
| ground_truth_markdown=markdown, | |
| plain_text=plain, | |
| stamp_underneath_text=underneath_text, | |
| expected_kie={ | |
| "tckn": tckn, | |
| "vkn": vkn, | |
| "iban": iban, | |
| "matrah": matrah_s, | |
| "kdv": kdv_s, | |
| "toplam": toplam_s, | |
| }, | |
| is_ood=is_ood, | |
| layout_variant=f"{'ood' if is_ood else 'id'}_erp_table_{style}", | |
| line_crops=TurkishDocumentGenerator._crop_lines_from_image(final_img, line_bboxes), | |
| ) | |
| class BornDigitalPDFPairGenerator: | |
| """ | |
| Born-Digital (vektörel metin katmanlı) Türkçe PDF sayfalarını veya çift sütunlu | |
| akademik/mevzuat makale sayfalarını: | |
| 1. Yüksek çözünürlüklü temiz sayfa görüntüsüne (`clean_image`), | |
| 2. Tam okuma sıralı (`RO-NED` uyumlu) yapısal Markdown metnine, | |
| 3. Hedef paragraf/tablo üzerine `TurkceDegrade` ile kaşe/imza basılmış bozulmuş | |
| görüntüye (`image`) ve `stamp_underneath_text` ikizine dönüştürür. | |
| """ | |
| def __init__(self, seed: Optional[int] = None) -> None: | |
| self.rng = random.Random(seed) | |
| self.doc_gen = TurkishDocumentGenerator(seed=seed) | |
| self.degrader = TurkceDegrade(seed=seed) | |
| def generate_two_column_born_digital_page( | |
| self, | |
| apply_degradation: bool = True, | |
| is_ood: bool = False, | |
| ) -> SyntheticDocumentSample: | |
| """ | |
| Resmî Gazete / DergiPark / Akademik Rapor tarzı **Çift Sütunlu (2-Column)** | |
| vektörel mizanpaj sayfası üretir. Okuma sırası (`RO-NED` KPI-12) eğitimi için | |
| önce Sol Sütun (Madde 1 & 2), ardından Sağ Sütun (Madde 3 & KIE Özeti) okunacak | |
| şekilde kesin ground-truth üretir. | |
| """ | |
| w, h = 900, 1120 | |
| clean_img = Image.new("RGB", (w, h), (255, 255, 253)) | |
| draw = ImageDraw.Draw(clean_img) | |
| font_banner = _load_turkish_font(18, style="serif", rng=self.rng) | |
| font_sub = _load_turkish_font(14, style="bold", rng=self.rng) | |
| font_body = _load_turkish_font(13, style="serif", rng=self.rng) | |
| tckn = self.doc_gen.generate_valid_tckn() | |
| vkn = self.doc_gen.generate_valid_vkn() | |
| iban = self.doc_gen.generate_valid_tr_iban() | |
| hard_word_1, hard_word_2 = self.rng.sample(AGGLUTINATIVE_HARD_LEXICON[:10], k=2) | |
| banner = ( | |
| "T.C. RESMÎ GAZETE — ÇİFT SÜTUNLU FİNANSAL TEBLİĞ VE DENETİM RAPORU" | |
| if is_ood | |
| else "TÜRKİYE CUMHURİYETİ MERKEZ BANKASI — KURUMSAL DENETİM BÜLTENİ" | |
| ) | |
| draw.text((40, 32), banner, fill=(10, 10, 10), font=font_banner) | |
| draw.line([(40, 65), (w - 40, 65)], fill=(25, 25, 25), width=2) | |
| mid_x = w // 2 | |
| draw.line([(mid_x, 85), (mid_x, h - 80)], fill=(160, 160, 160), width=1) | |
| # Sol Sütun (Sütun 1) | |
| col1_x = 40 | |
| col1_h1 = "BÖLÜM 1: MEVZUAT VE YÜKÜMLÜLÜKLER" | |
| col1_p1 = f"MADDE 1 — Kurumsal denetimlerde {hard_word_1} ilkesi esas alınır." | |
| col1_p2 = f"MADDE 2 — İlgili belgelerin {hard_word_2} kapsamında ibrazı zorunludur." | |
| col1_p3 = "Iğdır, Isparta ve İstanbul Şişli müdürlükleri koordinasyonunda yürütülür." | |
| # Sağ Sütun (Sütun 2) | |
| col2_x = mid_x + 24 | |
| col2_h1 = "BÖLÜM 2: KİMLİK VE HESAP BİLDİRİMİ" | |
| col2_p1 = f"DENETÇİ TCKN: {tckn}" | |
| col2_p2 = f"KURUM VKN: {vkn}" | |
| col2_p3 = f"EMANET IBAN: {iban}" | |
| line_bboxes: List[Tuple[Tuple[int, int, int, int], str]] = [ | |
| ((36, 28, w - 36, 62), banner), | |
| ] | |
| # Sol sütun çizimi | |
| draw.text((col1_x, 95), col1_h1, fill=(15, 15, 15), font=font_sub) | |
| draw.text((col1_x, 130), col1_p1, fill=(20, 20, 20), font=font_body) | |
| draw.text((col1_x, 165), col1_p2, fill=(20, 20, 20), font=font_body) | |
| draw.text((col1_x, 200), col1_p3, fill=(20, 20, 20), font=font_body) | |
| line_bboxes.append(((col1_x, 125, mid_x - 12, 155), col1_p1)) | |
| line_bboxes.append(((col1_x, 160, mid_x - 12, 190), col1_p2)) | |
| # Sağ sütun çizimi | |
| draw.text((col2_x, 95), col2_h1, fill=(15, 15, 15), font=font_sub) | |
| draw.text((col2_x, 130), col2_p1, fill=(20, 20, 20), font=font_body) | |
| draw.text((col2_x, 165), col2_p2, fill=(20, 20, 20), font=font_body) | |
| draw.text((col2_x, 200), col2_p3, fill=(20, 20, 20), font=font_body) | |
| line_bboxes.append(((col2_x, 125, w - 36, 155), col2_p1)) | |
| line_bboxes.append(((col2_x, 160, w - 36, 190), col2_p2)) | |
| target_bbox = (col1_x, 120, mid_x - 15, 225) | |
| final_img = clean_img | |
| stamp_tags: List[str] = [] | |
| if apply_degradation: | |
| deg = self.degrader.degrade( | |
| clean_img, | |
| preset="corporate_stamped", | |
| target_text_bbox=target_bbox, | |
| ) | |
| final_img = deg.image | |
| stamp_tags = [s.xml_tag for s in deg.stamps_and_signatures] | |
| md_lines = [ | |
| f"# {banner}", | |
| f"## {col1_h1}", | |
| col1_p1, | |
| col1_p2, | |
| col1_p3, | |
| f"## {col2_h1}", | |
| f"DENETÇİ TCKN: <tckn>{tckn}</tckn>", | |
| f"KURUM VKN: <vkn>{vkn}</vkn>", | |
| f"EMANET IBAN: <iban>{iban}</iban>", | |
| ] | |
| if stamp_tags: | |
| md_lines.extend(stamp_tags) | |
| markdown = "\n".join(md_lines) | |
| plain = "\n".join([banner, col1_h1, col1_p1, col1_p2, col1_p3, col2_h1, col2_p1, col2_p2, col2_p3]) | |
| return SyntheticDocumentSample( | |
| doc_type="born_digital_2col", | |
| image=final_img, | |
| clean_image=clean_img, | |
| ground_truth_markdown=markdown, | |
| plain_text=plain, | |
| stamp_underneath_text=col1_p1, | |
| expected_kie={"tckn": tckn, "vkn": vkn, "iban": iban}, | |
| is_ood=is_ood, | |
| layout_variant=f"{'ood' if is_ood else 'id'}_2col_born_digital", | |
| line_crops=TurkishDocumentGenerator._crop_lines_from_image(final_img, line_bboxes), | |
| ) | |
| def extract_pairs_from_pdf_file( | |
| self, | |
| pdf_path: str | Path, | |
| max_pages: int = 10, | |
| apply_degradation: bool = True, | |
| ) -> List[SyntheticDocumentSample]: | |
| """ | |
| Yerel bir PDF dosyasından (`PyMuPDF` / `fitz` kuruluysa doğrudan vektörel render + | |
| metin çıkarma ile; değilse yerleşik çift sütunlu born-digital üreteçle) eğitim | |
| çiftleri çıkarır. | |
| """ | |
| path = Path(pdf_path) | |
| samples: List[SyntheticDocumentSample] = [] | |
| try: | |
| import fitz # PyMuPDF | |
| if path.exists(): | |
| doc = fitz.open(str(path)) | |
| for page_idx in range(min(len(doc), max_pages)): | |
| page = doc[page_idx] | |
| text = page.get_text("text").strip() | |
| if not text: | |
| continue | |
| pix = page.get_pixmap(dpi=200) | |
| clean_img = Image.open(io.BytesIO(pix.tobytes("png"))).convert("RGB") | |
| deg_img = clean_img | |
| stamp_tags: List[str] = [] | |
| if apply_degradation: | |
| deg = self.degrader.degrade(clean_img, preset="corporate_stamped") | |
| deg_img = deg.image | |
| stamp_tags = [s.xml_tag for s in deg.stamps_and_signatures] | |
| first_line = text.splitlines()[0].strip() | |
| md = text + ("\n" + "\n".join(stamp_tags) if stamp_tags else "") | |
| samples.append( | |
| SyntheticDocumentSample( | |
| doc_type="born_digital_pdf_page", | |
| image=deg_img, | |
| clean_image=clean_img, | |
| ground_truth_markdown=md, | |
| plain_text=text, | |
| stamp_underneath_text=first_line, | |
| layout_variant="real_pdf_vector_page", | |
| ) | |
| ) | |
| doc.close() | |
| if samples: | |
| return samples | |
| except Exception: | |
| pass | |
| # PyMuPDF yoksa veya dosya bulunamadıysa yerleşik çift sütunlu born-digital üreteci kullan | |
| for i in range(max_pages): | |
| samples.append( | |
| self.generate_two_column_born_digital_page( | |
| apply_degradation=apply_degradation, is_ood=(i % 2 == 1) | |
| ) | |
| ) | |
| return samples | |