hitit-cuneiform-ocr / code /src /preprocessing /sam_polygon_run.py
savastakan's picture
Initial upload: code + 5 record checkpoints + fuse
f211247 verified
Raw
History Blame Contribute Delete
7.26 kB
#!/usr/bin/env python3
"""SAM 2.1 polygon annotation — GPU run.
Detection bbox'larını prompt olarak kullanıp RLE mask üretir.
Output: her kaynağın detection.coco.json dosyasına 'segmentation' alanı ekler.
"""
import json, os, argparse, time
from pathlib import Path
ROOT = Path("/arf/scratch/stakan/hitit-proje")
SOURCES = ROOT / "datasets" / "sources"
def main():
import torch
from PIL import Image
import numpy as np
ap = argparse.ArgumentParser()
ap.add_argument('--source', default='all', help='all|hitit_local|compvis|yeni_veri|deepscribe')
ap.add_argument('--limit', type=int, default=0)
args = ap.parse_args()
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Device: {device}", flush=True)
# SAM 2.1 import (ultralytics YOLO'dan sam kullanabiliriz; veya segment_anything_2 paketi)
try:
from ultralytics import SAM
sam = SAM("sam2.1_l.pt") # auto-download
model_name = "sam2.1_l (ultralytics)"
except Exception as e:
print(f"Ultralytics SAM yüklenmedi: {e}; alternatif deneniyor", flush=True)
try:
from segment_anything import SamPredictor, sam_model_registry
print("segment_anything varsa — manual checkpoint path gerekir")
return
except ImportError:
print("SAM kurulu değil. Install: pip install ultralytics", flush=True)
return
source_dirs = ['hitit_local', 'compvis', 'yeni_veri', 'deepscribe'] if args.source == 'all' else [args.source]
try:
from pycocotools import mask as mask_utils
except ImportError:
os.system("pip install --user --quiet pycocotools")
from pycocotools import mask as mask_utils
t0 = time.time()
total_done = 0
for src_name in source_dirs:
src = SOURCES / src_name
mp = src / "manifest_detection.jsonl"
if not mp.exists(): continue
records = []
with open(mp) as f:
for line in f:
records.append(json.loads(line))
if args.limit:
records = records[:args.limit]
print(f"\n{src_name}: {len(records)} record", flush=True)
out_json = src / "detection.coco.json"
coco = json.load(open(out_json)) if out_json.exists() else {
"info": {"description": f"{src_name} detection with SAM 2.1 masks"},
"images": [], "annotations": [], "categories": []
}
ann_id = 1 + (max([a['id'] for a in coco.get('annotations', [])], default=0))
cat_map = {c['name']: c['id'] for c in coco.get('categories', [])}
img_id = 1 + (max([i['id'] for i in coco.get('images', [])], default=0))
existing_img_paths = {i.get('file_name') for i in coco.get('images', [])}
for ri, r in enumerate(records):
p = r.get('path')
if not p or not os.path.exists(p): continue
if p in existing_img_paths: continue
# Bbox topla (YOLO label_path veya extra.bboxes)
bboxes_xyxy = []
labels = []
w = r.get('width') or 0
h = r.get('height') or 0
if not (w and h):
try:
with Image.open(p) as img:
w, h = img.size
except: continue
lp = r.get('label_path')
if lp and os.path.exists(lp):
try:
with open(lp) as f:
for ln in f:
parts = ln.strip().split()
if len(parts) == 5:
cls, cx, cy, bw, bh = parts
cx, cy, bw, bh = float(cx)*w, float(cy)*h, float(bw)*w, float(bh)*h
x1, y1, x2, y2 = cx-bw/2, cy-bh/2, cx+bw/2, cy+bh/2
bboxes_xyxy.append([x1, y1, x2, y2])
labels.append(f"class_{cls}")
except: pass
extra = r.get('extra') or {}
if isinstance(extra, dict):
for b in extra.get('bboxes', []):
if not isinstance(b, dict): continue
lbl = b.get('class_name') or b.get('mzl_label') or b.get('train_label') or 'unknown'
yolo = b.get('yolo_bbox')
xyxy = b.get('bbox')
if yolo and len(yolo) == 4:
cx, cy, bw, bh = yolo
cx, cy, bw, bh = cx*w, cy*h, bw*w, bh*h
bboxes_xyxy.append([cx-bw/2, cy-bh/2, cx+bw/2, cy+bh/2])
labels.append(str(lbl))
elif xyxy and len(xyxy) == 4:
bboxes_xyxy.append([float(x) for x in xyxy])
labels.append(str(lbl))
if not bboxes_xyxy: continue
# SAM predict
try:
results = sam(p, bboxes=bboxes_xyxy, verbose=False)
except Exception as e:
print(f" skip {p}: {e}", flush=True)
continue
# Image entry
coco['images'].append({
"id": img_id,
"file_name": p,
"width": w, "height": h,
"fold": r.get('fold'),
"tablet_id": r.get('tablet_id'),
})
# Annotations
if results and hasattr(results[0], 'masks') and results[0].masks is not None:
masks = results[0].masks.data.cpu().numpy() # (N, H, W) bool
for mi, (mask, lbl, bb) in enumerate(zip(masks, labels, bboxes_xyxy)):
if lbl not in cat_map:
cat_map[lbl] = len(cat_map) + 1
coco['categories'].append({"id": cat_map[lbl], "name": lbl, "supercategory": "sign"})
# RLE encode
mask_u8 = (mask > 0).astype(np.uint8)
rle = mask_utils.encode(np.asfortranarray(mask_u8))
rle['counts'] = rle['counts'].decode('utf-8')
x1, y1, x2, y2 = bb
coco['annotations'].append({
"id": ann_id,
"image_id": img_id,
"category_id": cat_map[lbl],
"bbox": [x1, y1, x2-x1, y2-y1],
"area": float(mask_u8.sum()),
"iscrowd": 0,
"segmentation": rle,
})
ann_id += 1
img_id += 1
total_done += 1
if (ri+1) % 100 == 0:
elapsed = time.time() - t0
rate = total_done / max(elapsed, 1)
print(f" [{src_name}] {ri+1}/{len(records)} ({rate:.1f} img/s)", flush=True)
with open(out_json, 'w') as f:
json.dump(coco, f, ensure_ascii=False)
print(f" {src_name}: yazıldı {out_json}{len(coco['images'])} img, {len(coco['annotations'])} ann", flush=True)
if __name__ == '__main__':
main()