Download code/src/preprocessing/view_canonicalization.py from savastakan/hitit-cuneiform-ocr: direct link, hf CLI and curl.
- Browser
- Download file 3.69 kB
-
https://huggingface.co/savastakan/hitit-cuneiform-ocr/resolve/main/code/src/preprocessing/view_canonicalization.py
- Command line
-
hf download hf://savastakan/hitit-cuneiform-ocr/code/src/preprocessing/view_canonicalization.py
-
curl -L -o view_canonicalization.py https://huggingface.co/savastakan/hitit-cuneiform-ocr/resolve/main/code/src/preprocessing/view_canonicalization.py
3.69 kB
| #!/usr/bin/env python3 | |
| """View-aware rotation canonicalization (heuristic). | |
| view_ile_hint tablet_view alanı zaten var. Şimdi: aspect ratio + view hint ile | |
| doğru orientation'a rotate et (offline pass). | |
| """ | |
| import json, os | |
| from pathlib import Path | |
| from concurrent.futures import ProcessPoolExecutor | |
| import numpy as np | |
| from PIL import Image | |
| ROOT = Path("/arf/scratch/stakan/hitit-proje") | |
| SOURCES = ROOT / "datasets" / "sources" | |
| def infer_canonical_rotation(img_path, view_hint): | |
| """ | |
| View hint + aspect ratio ile kanonik rotation tespit et. | |
| Return: rotation_degrees (0, 90, 180, 270) ve landscape_score | |
| """ | |
| try: | |
| with Image.open(img_path) as img: | |
| w, h = img.size | |
| aspect = w / max(h, 1) | |
| # Tablet'ler genelde landscape veya kare. Çok aspect lehinde olan bir view'a göre: | |
| # - obverse/reverse: landscape (aspect>1) tipik | |
| # - edges: portrait (aspect<1) veya ince | |
| # Bu basit heuristic; asıl model-based classifier ayrı. | |
| if view_hint in ('obverse', 'reverse'): | |
| return 0 if aspect >= 0.7 else 90 # portrait'i landscape'e çevir | |
| elif view_hint in ('left_edge', 'right_edge'): | |
| return 90 if aspect > 1 else 0 | |
| return 0 | |
| except Exception: | |
| return 0 | |
| def worker(item): | |
| rid, path, view = item | |
| return (rid, infer_canonical_rotation(path, view)) | |
| def main(): | |
| items = [] | |
| for d in sorted(SOURCES.iterdir()): | |
| if not d.is_dir(): continue | |
| mp = d / "manifest.jsonl" | |
| if not mp.exists(): continue | |
| with open(mp) as f: | |
| for line in f: | |
| r = json.loads(line) | |
| p = r.get('path') | |
| if p and r.get('storage') == 'fs' and r.get('integrity_ok') is True: | |
| items.append((r['id'], p, r.get('view', 'unknown'))) | |
| # Unique by path (daha hızlı) | |
| seen_paths = set() | |
| uniq = [] | |
| for rid, path, view in items: | |
| if path in seen_paths: continue | |
| seen_paths.add(path) | |
| uniq.append((rid, path, view)) | |
| print(f"Tarama: {len(uniq):,} unique image") | |
| results = {} | |
| with ProcessPoolExecutor(max_workers=200) as ex: | |
| for rid, rot in ex.map(worker, uniq, chunksize=500): | |
| results[rid] = rot | |
| # Manifest güncelle | |
| from collections import Counter | |
| rot_counts = Counter() | |
| for d in sorted(SOURCES.iterdir()): | |
| if not d.is_dir(): continue | |
| for mf in ['manifest.jsonl', 'manifest_classification.jsonl', 'manifest_detection.jsonl']: | |
| mp = d / mf | |
| if not mp.exists(): continue | |
| records = [] | |
| with open(mp) as f: | |
| for line in f: | |
| r = json.loads(line) | |
| rot = results.get(r.get('id')) | |
| if rot is not None: | |
| r['canonical_rotation_deg'] = int(rot) | |
| if mf == 'manifest.jsonl': | |
| rot_counts[rot] += 1 | |
| records.append(r) | |
| with open(mp, 'w') as f: | |
| for r in records: | |
| f.write(json.dumps(r, ensure_ascii=False) + '\n') | |
| print(f"Rotation counts: {dict(rot_counts)}") | |
| with open(ROOT / "datasets" / "processed" / "view_canonicalization_report.json", 'w') as f: | |
| json.dump({ | |
| "method": "heuristic (aspect + view_hint)", | |
| "n_images_processed": len(results), | |
| "rotation_distribution": dict(rot_counts), | |
| "note": "Heuristic v1. Model-based classifier (MobileNetV3) için ayrı training gerekir." | |
| }, f, indent=2, ensure_ascii=False) | |
| if __name__ == '__main__': | |
| main() | |