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/cli.py from ersanbil/TurkceOCR: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/ersanbil/TurkceOCR/resolve/main/turkceocr/cli.py
- Command line
-
hf download hf://ersanbil/TurkceOCR/turkceocr/cli.py
-
curl -L -o cli.py https://huggingface.co/ersanbil/TurkceOCR/resolve/main/turkceocr/cli.py
14.3 kB
| """ | |
| TürkçeOCR (`turkceocr` / `turkceocr`) Komut Satırı Çıkarım ve Doğrulama Aracı (CLI). | |
| Desteklenen Modlar: | |
| 1. `--image <path>` veya `--pdf <path>`: | |
| PNG/JPG görüntülerini veya çok sayfalı PDF belgelerini (`PyMuPDF / fitz`) yükler, | |
| `HighResDiacriticTiler` ile yüksek çözünürlüklü yamalara böler, `TurkceOCRLine` | |
| ve `TurkceVerify` (TCKN/VKN/IBAN/KDV/İ-ı) hattından geçirir. | |
| 2. `--demo-doc {invoice,pos,handwritten,gazette,erp_table,born_digital_2col}`: | |
| Gerçekçi kaşeli/imzalı/termal Türkçe belge sentezler, satır kırpımlarını | |
| `TurkceOCRLine` ile okur ve `TurkceVerify` algoritmik sağlama onarımını uçtan uca çalıştırır. | |
| 3. `--markdown-text "..."` veya `--markdown-file <path>`: | |
| Ham VLM/OCR Markdown metnini doğrudan `TurkceOCRPipeline.postprocess_document_markdown` | |
| ve `TurkishFinancialSolver` hattından geçirerek onarır. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import io | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from typing import Any, Dict, List, Optional, Sequence | |
| from PIL import Image | |
| from turkceocr.data.pdf_and_table_engine import ( | |
| BornDigitalPDFPairGenerator, | |
| ComplexTurkishTableRenderer, | |
| ) | |
| from turkceocr.data.synthetic_generator import ( | |
| SyntheticDocumentSample, | |
| TurkishDocumentGenerator, | |
| ) | |
| from turkceocr.models.vl_model import SUPPORTED_VL_BACKBONES, TURKCEOCR_SPECIAL_TOKENS | |
| from turkceocr.pipeline import TurkceOCRPipeline | |
| from turkceocr.postprocess.entity_engine import UnifiedTurkishEntityEngine | |
| from turkceocr.postprocess.visual_redactor import VisualRedactor | |
| DEMO_DOC_TYPES = ( | |
| "invoice", | |
| "pos", | |
| "handwritten", | |
| "gazette", | |
| "erp_table", | |
| "born_digital_2col", | |
| ) | |
| def synthesize_demo_document( | |
| doc_type: str, | |
| apply_degradation: bool = True, | |
| is_ood: bool = False, | |
| seed: int = 2026, | |
| ) -> SyntheticDocumentSample: | |
| """Belirtilen tipte gerçekçi Türkçe belge sentezler.""" | |
| norm = doc_type.strip().lower() | |
| gen = TurkishDocumentGenerator(seed=seed) | |
| if norm in ("invoice", "corporate_invoice"): | |
| return gen.generate_corporate_invoice(apply_degradation=apply_degradation, is_ood=is_ood) | |
| if norm in ("pos", "thermal_pos_receipt"): | |
| return gen.generate_thermal_pos_receipt(apply_degradation=apply_degradation, is_ood=is_ood) | |
| if norm in ("handwritten", "mixed_handwritten_form"): | |
| return gen.generate_mixed_handwritten_form(apply_degradation=apply_degradation, is_ood=is_ood) | |
| if norm in ("gazette", "official_gazette"): | |
| return gen.generate_official_gazette_or_contract(apply_degradation=apply_degradation, is_ood=is_ood) | |
| if norm in ("erp_table", "multi_vat_erp_invoice"): | |
| table_eng = ComplexTurkishTableRenderer(seed=seed) | |
| return table_eng.generate_multi_vat_erp_invoice( | |
| apply_degradation=apply_degradation, | |
| is_ood=is_ood, | |
| table_style="zebra_striped", | |
| ) | |
| if norm in ("born_digital_2col", "two_column"): | |
| pdf_gen = BornDigitalPDFPairGenerator(seed=seed) | |
| return pdf_gen.generate_two_column_born_digital_page( | |
| apply_degradation=apply_degradation, | |
| is_ood=is_ood, | |
| ) | |
| raise ValueError( | |
| f"Desteklenmeyen demo belge tipi: {doc_type!r}. Geçerli tipler: {DEMO_DOC_TYPES}" | |
| ) | |
| def inject_realistic_ocr_corruptions(sample: SyntheticDocumentSample) -> str: | |
| """ | |
| Demo akışında `TurkceVerify` algoritmik onarımını göstermek için sentetik belgenin | |
| ham OCR çıktısına gerçekçi görsel karışıklıklar (0->O, İ->I, Ş->S) enjekte eder. | |
| """ | |
| corrupted = sample.ground_truth_markdown | |
| tckn = sample.expected_kie.get("tckn") | |
| if tckn and f"<tckn>{tckn}</tckn>" in corrupted: | |
| bad_tckn = tckn.replace("0", "O", 1) if "0" in tckn else (tckn[:-1] + "O") | |
| corrupted = corrupted.replace(f"<tckn>{tckn}</tckn>", f"<tckn>{bad_tckn}</tckn>", 1) | |
| vkn = sample.expected_kie.get("vkn") | |
| if vkn and f"<vkn>{vkn}</vkn>" in corrupted: | |
| bad_vkn = vkn.replace("0", "O", 1) if "0" in vkn else (vkn[:-1] + "O") | |
| corrupted = corrupted.replace(f"<vkn>{vkn}</vkn>", f"<vkn>{bad_vkn}</vkn>", 1) | |
| corrupted = ( | |
| corrupted.replace("BAŞKANLIĞI", "BASKANLIGI") | |
| .replace("MÜDÜRLÜĞÜ", "MUDURLUGU") | |
| .replace("İSTANBUL", "ISTANBUL") | |
| ) | |
| return corrupted | |
| def load_images_from_path(path: Path) -> List[Image.Image]: | |
| """Görüntü (.png/.jpg/.webp) veya PDF (.pdf) dosyasından PIL Image listesi yükler.""" | |
| if not path.exists(): | |
| raise FileNotFoundError(f"Girdi dosyası bulunamadı: {path}") | |
| if path.suffix.lower() == ".pdf": | |
| import fitz # PyMuPDF | |
| images: List[Image.Image] = [] | |
| with fitz.open(path) as doc: | |
| for page_index in range(len(doc)): | |
| page = doc.load_page(page_index) | |
| pix = page.get_pixmap(dpi=200) | |
| img = Image.open(io.BytesIO(pix.tobytes("png"))).convert("RGB") | |
| images.append(img) | |
| return images | |
| return [Image.open(path).convert("RGB")] | |
| def build_arg_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser( | |
| prog="turkceocr", | |
| description="TürkçeOCR (TürkçeOCR) — İki Katmanlı Türkçe OCR, Doküman VLM ve TurkceVerify CLI", | |
| ) | |
| input_group = parser.add_mutually_exclusive_group(required=False) | |
| input_group.add_argument( | |
| "--image", | |
| type=str, | |
| default=None, | |
| help="İşlenecek görüntü (.png, .jpg) veya PDF dosyasının yolu.", | |
| ) | |
| input_group.add_argument( | |
| "--pdf", | |
| type=str, | |
| default=None, | |
| help="İşlenecek PDF dosyasının yolu (PyMuPDF ile yüksek çözünürlüklü render).", | |
| ) | |
| input_group.add_argument( | |
| "--demo-doc", | |
| type=str, | |
| choices=list(DEMO_DOC_TYPES), | |
| default=None, | |
| help="Gerçekçi kaşeli/bozulmuş Türkçe belge sentezleyip uçtan uca TürkçeOCR çalıştırır.", | |
| ) | |
| input_group.add_argument( | |
| "--markdown-text", | |
| type=str, | |
| default=None, | |
| help="Doğrudan TurkceVerify ve Finansal Çözücü ile onarılacak ham Markdown metni.", | |
| ) | |
| input_group.add_argument( | |
| "--markdown-file", | |
| type=str, | |
| default=None, | |
| help="Doğrudan TurkceVerify ile onarılacak ham Markdown dosyası.", | |
| ) | |
| parser.add_argument( | |
| "--backbone", | |
| type=str, | |
| choices=list(SUPPORTED_VL_BACKBONES.keys()), | |
| default="lightonocr", | |
| help="Kullanılacak TürkçeOCR-VL-1B omurgası (lightonocr veya paddleocr_vl).", | |
| ) | |
| parser.add_argument( | |
| "--seed", | |
| type=int, | |
| default=2026, | |
| help="Sentetik demo belge üretimi için rastgelelik tohumu.", | |
| ) | |
| parser.add_argument( | |
| "--no-degradation", | |
| action="store_true", | |
| help="Demo belge üretiminde fiziksel bozulma ve kaşe bindirmesini devre dışı bırakır.", | |
| ) | |
| parser.add_argument( | |
| "--output-json", | |
| type=str, | |
| default=None, | |
| help="Yapılandırılmış TürkçeOCR sonucunun kaydedileceği JSON dosya yolu.", | |
| ) | |
| parser.add_argument( | |
| "--output-md", | |
| type=str, | |
| default=None, | |
| help="Onarılmış Markdown çıktısının kaydedileceği .md dosya yolu.", | |
| ) | |
| parser.add_argument( | |
| "--redact-pii", | |
| action="store_true", | |
| help="Birleşik Hukuk & KVKK PII motorunu çalıştırarak hassas verileri karartır.", | |
| ) | |
| parser.add_argument( | |
| "--redact-image", | |
| type=str, | |
| default=None, | |
| help="2B #000000 KVKK karartması uygulanmış görüntünün kaydedileceği dosya yolu.", | |
| ) | |
| parser.add_argument( | |
| "--quiet", | |
| action="store_true", | |
| help="Standart çıktıya JSON yazdırmayı bastırır.", | |
| ) | |
| return parser | |
| def execute_cli(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]: | |
| """ | |
| TürkçeOCR CLI argümanlarını çalıştırır ve yapılandırılmış sonuç sözlüğünü döndürür. | |
| """ | |
| parser = build_arg_parser() | |
| args = parser.parse_args(list(argv) if argv is not None else None) | |
| pipeline = TurkceOCRPipeline(vl_backbone=args.backbone) | |
| # Varsayılan olarak girdi verilmemişse --demo-doc invoice çalıştırılır | |
| mode = "demo_doc" | |
| image_obj: Optional[Image.Image] = None | |
| raw_markdown = "" | |
| line_predictions: List[Dict[str, Any]] = [] | |
| doc_type_label = "markdown_only" | |
| page_count = 1 | |
| if args.markdown_text is not None: | |
| mode = "markdown_text" | |
| raw_markdown = args.markdown_text | |
| elif args.markdown_file is not None: | |
| mode = "markdown_file" | |
| raw_markdown = Path(args.markdown_file).read_text(encoding="utf-8") | |
| elif args.image is not None or args.pdf is not None: | |
| mode = "pdf" if args.pdf is not None else "image" | |
| target_path = Path(args.pdf or args.image) | |
| pages = load_images_from_path(target_path) | |
| page_count = len(pages) | |
| image_obj = pages[0] | |
| doc_type_label = target_path.suffix.lstrip(".").lower() or "image" | |
| # Satır modeli ile hızlı tepe/orta/alt şerit okuması | |
| w, h = image_obj.size | |
| strip_coords = [ | |
| (0, int(h * 0.04), w, int(h * 0.14)), | |
| (0, int(h * 0.18), w, int(h * 0.28)), | |
| (0, int(h * 0.70), w, int(h * 0.82)), | |
| ] | |
| decoded_lines: List[str] = [] | |
| for idx, bbox in enumerate(strip_coords): | |
| crop = image_obj.crop(bbox) | |
| pred_text, conf = pipeline.recognize_line_image(crop) | |
| line_predictions.append( | |
| { | |
| "line_index": idx, | |
| "bbox": list(bbox), | |
| "predicted_text": pred_text, | |
| "confidence": round(float(conf), 4), | |
| } | |
| ) | |
| if pred_text.strip(): | |
| decoded_lines.append(pred_text.strip()) | |
| raw_markdown = "\n".join(decoded_lines) if decoded_lines else f"# TürkçeOCR Belge ({target_path.name})" | |
| else: | |
| demo_choice = args.demo_doc or "invoice" | |
| sample = synthesize_demo_document( | |
| doc_type=demo_choice, | |
| apply_degradation=not args.no_degradation, | |
| seed=args.seed, | |
| ) | |
| doc_type_label = sample.doc_type | |
| image_obj = sample.image | |
| raw_markdown = inject_realistic_ocr_corruptions(sample) | |
| for idx, (crop_img, gt_line) in enumerate(sample.line_crops[:4]): | |
| pred_text, conf = pipeline.recognize_line_image(crop_img) | |
| line_predictions.append( | |
| { | |
| "line_index": idx, | |
| "ground_truth_line": gt_line, | |
| "predicted_text": pred_text, | |
| "confidence": round(float(conf), 4), | |
| } | |
| ) | |
| page_out = pipeline.postprocess_document_markdown( | |
| raw_markdown=raw_markdown, | |
| image=image_obj, | |
| repair_financials=True, | |
| ) | |
| fin_dict: Optional[Dict[str, Any]] = None | |
| if page_out.financial_check is not None: | |
| fc = page_out.financial_check | |
| fin_dict = { | |
| "is_consistent": fc.is_consistent, | |
| "was_repaired": fc.was_repaired, | |
| "subtotal_matrah": fc.subtotal_matrah, | |
| "vat_kdv": fc.vat_kdv, | |
| "grand_total": fc.grand_total, | |
| "inferred_vat_rate": fc.inferred_vat_rate, | |
| "repair_note": fc.repair_note, | |
| } | |
| result: Dict[str, Any] = { | |
| "brand": "TürkçeOCR", | |
| "version": "0.2.0", | |
| "mode": mode, | |
| "doc_type": doc_type_label, | |
| "backbone": args.backbone, | |
| "resolved_vl_model_id": pipeline.vl_config.resolved_model_id(), | |
| "page_count": page_count, | |
| "tile_count": len(page_out.tile_bboxes), | |
| "tile_bboxes": [list(b) for b in page_out.tile_bboxes], | |
| "raw_markdown": page_out.raw_markdown, | |
| "verified_markdown": page_out.markdown, | |
| "valid_tckns": page_out.verification_report.valid_tckns, | |
| "valid_vkns": page_out.verification_report.valid_vkns, | |
| "valid_ibans": page_out.verification_report.valid_ibans, | |
| "repaired_fields": page_out.verification_report.repaired_fields, | |
| "financial_check": fin_dict, | |
| "line_recognitions": line_predictions, | |
| "special_tokens_supported": TURKCEOCR_SPECIAL_TOKENS, | |
| } | |
| if args.redact_pii or args.redact_image: | |
| entity_engine = UnifiedTurkishEntityEngine(use_ner=False) | |
| visual_redactor = VisualRedactor(entity_engine=entity_engine) | |
| target_img = image_obj if image_obj is not None else Image.new("RGB", (900, 600), color=(255, 255, 255)) | |
| redaction_res = visual_redactor.redact_document( | |
| image=target_img, | |
| raw_text=page_out.raw_markdown, | |
| normalized_text=page_out.markdown, | |
| ) | |
| result["redacted_markdown"] = redaction_res.redacted_text | |
| result["detected_entities"] = [e.to_dict() for e in redaction_res.entities] | |
| result["redaction_boxes"] = [b.to_dict() for b in redaction_res.redaction_boxes] | |
| result["redaction_coverage"] = round(float(redaction_res.redaction_coverage), 4) | |
| if args.redact_image: | |
| out_img_path = Path(args.redact_image) | |
| out_img_path.parent.mkdir(parents=True, exist_ok=True) | |
| redaction_res.redacted_image.save(out_img_path) | |
| result["redacted_image_path"] = str(out_img_path) | |
| if args.output_json: | |
| out_json_path = Path(args.output_json) | |
| out_json_path.parent.mkdir(parents=True, exist_ok=True) | |
| out_json_path.write_text( | |
| json.dumps(result, ensure_ascii=False, indent=2), | |
| encoding="utf-8", | |
| ) | |
| if args.output_md: | |
| out_md_path = Path(args.output_md) | |
| out_md_path.parent.mkdir(parents=True, exist_ok=True) | |
| out_md_path.write_text(page_out.markdown, encoding="utf-8") | |
| if not args.quiet: | |
| print(json.dumps(result, ensure_ascii=False, indent=2)) | |
| return result | |
| def main(argv: Optional[Sequence[str]] = None) -> int: | |
| """`turkceocr` ve `turkceocr` konsol giriş noktası.""" | |
| execute_cli(argv=argv) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |