hitit-cuneiform-ocr / code /convert_to_coco.py
savastakan's picture
Initial upload: code + 5 record checkpoints + fuse
f211247 verified
Raw
History Blame Contribute Delete
18.2 kB
#!/usr/bin/env python3
"""
Hitit civi yazisi tablet anotasyonlarini (mark.txt) COCO formatina donusturur.
Kullanim:
python3 convert_to_coco.py --inspect # Sadece format kesfet, donusturme
python3 convert_to_coco.py --convert # COCO formatina donustur
python3 convert_to_coco.py --convert --merge-ebl # eBL verileriyle birlestir
Cikti:
output/coco_hitit_train.json
output/coco_hitit_val.json
output/images/ (symlink'ler)
"""
import json
import os
import sys
import glob
import argparse
import random
from collections import defaultdict
from pathlib import Path
BASE_DIR = Path(__file__).parent
TABLETS_DIR = BASE_DIR / "datasets" / "sources" / "hitit_local"
EBL_DIR = BASE_DIR / "datasets" / "sources" / "ebl_ocr" / "ready-for-training"
OUTPUT_DIR = BASE_DIR / "output"
# ============================================================
# ADIM 1: mark.txt formatini kesfet
# ============================================================
def inspect_mark_file(path):
"""Tek bir mark.txt dosyasinin yapisini analiz et."""
with open(path, "r", encoding="utf-8") as f:
raw = f.read()
# JSON parse
try:
data = json.loads(raw)
except json.JSONDecodeError:
# Belki UTF-16?
try:
with open(path, "r", encoding="utf-16") as f:
raw = f.read()
data = json.loads(raw)
except:
print(f" HATA: {path} JSON olarak parse edilemedi")
print(f" Ilk 500 karakter: {raw[:500]}")
return None
return data
def describe_structure(data, prefix="", depth=0, max_depth=4):
"""JSON yapisini ozetler."""
indent = " " * depth
if depth > max_depth:
print(f"{indent}...")
return
if isinstance(data, dict):
print(f"{indent}{prefix}dict (anahtarlar: {list(data.keys())})")
for key in list(data.keys())[:15]:
val = data[key]
if isinstance(val, (dict, list)):
describe_structure(val, prefix=f'"{key}": ', depth=depth + 1, max_depth=max_depth)
else:
valstr = str(val)
if len(valstr) > 100:
valstr = valstr[:100] + "..."
print(f"{indent} \"{key}\": {type(val).__name__} = {valstr}")
elif isinstance(data, list):
print(f"{indent}{prefix}list (uzunluk: {len(data)})")
if len(data) > 0:
# Ilk elemani goster
print(f"{indent} [0]:")
describe_structure(data[0], depth=depth + 1, max_depth=max_depth)
if len(data) > 1:
print(f"{indent} ... ({len(data) - 1} eleman daha)")
else:
valstr = str(data)
if len(valstr) > 100:
valstr = valstr[:100] + "..."
print(f"{indent}{prefix}{type(data).__name__} = {valstr}")
def inspect_all():
"""Tum mark.txt dosyalarini tara, format ozetini cikar."""
mark_files = sorted(glob.glob(str(TABLETS_DIR / "*" / "mark.txt")))
print(f"\n{'='*60}")
print(f"TOPLAM {len(mark_files)} mark.txt dosyasi bulundu")
print(f"{'='*60}\n")
if not mark_files:
print("HATA: Hicbir mark.txt dosyasi bulunamadi!")
return
# Ilk dosyayi detayli incele
first = mark_files[0]
print(f"--- {first} ---")
data = inspect_mark_file(first)
if data is None:
return
print(f"\nDosya boyutu: {os.path.getsize(first) / 1024:.1f} KB")
print(f"\nYAPI:")
describe_structure(data)
# Tum dosyalarin istatistiklerini topla
print(f"\n{'='*60}")
print("TUM DOSYALARIN OZETI")
print(f"{'='*60}")
total_annotations = 0
all_names = set()
all_types = set()
errors = []
for mf in mark_files:
try:
d = inspect_mark_file(mf)
if d is None:
errors.append(mf)
continue
annotations = extract_annotations(d)
total_annotations += len(annotations)
for ann in annotations:
if "name" in ann:
all_names.add(ann["name"])
if "text" in ann:
all_names.add(ann["text"])
if "type" in ann:
all_types.add(ann["type"])
except Exception as e:
errors.append(f"{mf}: {e}")
print(f"Toplam anotasyon sayisi: {total_annotations}")
print(f"Benzersiz sinif sayisi: {len(all_names)}")
print(f"Anotasyon tipleri: {all_types}")
print(f"Hatali dosyalar: {len(errors)}")
if errors:
for e in errors[:10]:
print(f" - {e}")
if all_names:
sorted_names = sorted(all_names)
print(f"\nSiniflar (ilk 50):")
for n in sorted_names[:50]:
print(f" - {n}")
if len(sorted_names) > 50:
print(f" ... ve {len(sorted_names) - 50} sinif daha")
# ============================================================
# ADIM 2: Anotasyonlari cikar (format-agnostik)
# ============================================================
def extract_annotations(data):
"""
JSON yapisindan anotasyonlari cikar.
Farkli olasi yapilari dener.
Her anotasyon: {x, y, width, height, name/text, ...}
"""
annotations = []
if isinstance(data, list):
for item in data:
ann = extract_single_annotation(item)
if ann:
annotations.append(ann)
elif isinstance(data, dict):
# Ust seviye dict olabilir, icerideki listelerden cikar
# Olasi anahtarlar: marks, annotations, objects, regions, shapes
for key in data:
val = data[key]
if isinstance(val, list) and len(val) > 0:
for item in val:
ann = extract_single_annotation(item)
if ann:
annotations.append(ann)
# Belki dict'in kendisi tek bir anotasyon?
if not annotations:
ann = extract_single_annotation(data)
if ann:
annotations.append(ann)
return annotations
def extract_single_annotation(item):
"""Tek bir anotasyon objesinden x, y, w, h ve sinif bilgisi cikar."""
if not isinstance(item, dict):
return None
ann = {}
# Bounding box bilgisi
# Durum 1: Dogrudan x, y, width, height
if "x" in item and "y" in item:
ann["x"] = float(item["x"])
ann["y"] = float(item["y"])
ann["width"] = float(item.get("width", 0))
ann["height"] = float(item.get("height", 0))
# Durum 2: "mark" icinde
elif "mark" in item and isinstance(item["mark"], dict):
mark = item["mark"]
if "x" in mark and "y" in mark:
ann["x"] = float(mark["x"])
ann["y"] = float(mark["y"])
ann["width"] = float(mark.get("width", 0))
ann["height"] = float(mark.get("height", 0))
# Durum 3: "rect" icinde
elif "rect" in item and isinstance(item["rect"], dict):
r = item["rect"]
if "x" in r and "y" in r:
ann["x"] = float(r["x"])
ann["y"] = float(r["y"])
ann["width"] = float(r.get("width", r.get("w", 0)))
ann["height"] = float(r.get("height", r.get("h", 0)))
if "x" not in ann:
return None
# Sinif/etiket bilgisi
for name_key in ["name", "text", "label", "class", "category", "comment"]:
if name_key in item and item[name_key]:
ann["name"] = str(item[name_key])
break
# "mark" icinde olabilir
if "mark" in item and isinstance(item["mark"], dict):
if name_key in item["mark"] and item["mark"][name_key]:
ann["name"] = str(item["mark"][name_key])
break
# ID
if "id" in item:
ann["id"] = item["id"]
# Type
if "type" in item:
ann["type"] = item["type"]
return ann
def extract_image_info(data):
"""JSON verisinden gorsel boyut bilgisini cikar."""
info = {}
if isinstance(data, dict):
for key in ("naturalWidth", "imageWidth", "width"):
if key in data:
info["width"] = int(data[key])
break
for key in ("naturalHeight", "imageHeight", "height"):
if key in data:
info["height"] = int(data[key])
break
# Ic icte olabilir
if "image" in data and isinstance(data["image"], dict):
img = data["image"]
for key in ("naturalWidth", "width"):
if key in img:
info["width"] = int(img[key])
break
for key in ("naturalHeight", "height"):
if key in img:
info["height"] = int(img[key])
break
return info
# ============================================================
# ADIM 3: COCO formatina donustur
# ============================================================
def convert_to_coco(val_ratio=0.2, seed=42):
"""Tum mark.txt dosyalarini COCO formatina donustur."""
mark_files = sorted(glob.glob(str(TABLETS_DIR / "*" / "mark.txt")))
print(f"\n{len(mark_files)} mark.txt dosyasi isleniyor...\n")
# Her tablet klasoru icin gorsel dosyasini bul
all_data = [] # [(tablet_id, image_path, annotations, image_info)]
category_names = set()
for mf in mark_files:
tablet_dir = Path(mf).parent
tablet_id = tablet_dir.name
# Gorseli bul (jpg)
images = list(tablet_dir.glob("*.jpg")) + list(tablet_dir.glob("*.png"))
if not images:
print(f" UYARI: {tablet_id} icin gorsel bulunamadi, atlaniyor")
continue
image_path = images[0]
# Anotasyonlari cikar
data = inspect_mark_file(mf)
if data is None:
continue
annotations = extract_annotations(data)
image_info = extract_image_info(data)
if not annotations:
print(f" UYARI: {tablet_id} icin anotasyon bulunamadi")
continue
for ann in annotations:
if "name" in ann:
category_names.add(ann["name"])
all_data.append((tablet_id, image_path, annotations, image_info))
if not all_data:
print("HATA: Hicbir veri islenmedi!")
return
# Kategori mapping olustur
sorted_categories = sorted(category_names)
cat_to_id = {name: i for i, name in enumerate(sorted_categories)}
print(f"\nToplam {len(all_data)} tablet islendi")
print(f"Toplam {sum(len(d[2]) for d in all_data)} anotasyon")
print(f"Toplam {len(sorted_categories)} sinif\n")
# Train/val bolustur
random.seed(seed)
indices = list(range(len(all_data)))
random.shuffle(indices)
val_count = max(1, int(len(indices) * val_ratio))
val_indices = set(indices[:val_count])
train_data = [all_data[i] for i in range(len(all_data)) if i not in val_indices]
val_data = [all_data[i] for i in range(len(all_data)) if i in val_indices]
print(f"Train: {len(train_data)} gorsel")
print(f"Val: {len(val_data)} gorsel\n")
# Output klasorlerini olustur
OUTPUT_DIR.mkdir(exist_ok=True)
img_dir = OUTPUT_DIR / "images"
img_dir.mkdir(exist_ok=True)
# COCO JSON olustur
for split_name, split_data in [("train", train_data), ("val", val_data)]:
coco = build_coco_json(split_data, cat_to_id, sorted_categories, img_dir)
out_path = OUTPUT_DIR / f"coco_hitit_{split_name}.json"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(coco, f, ensure_ascii=False, indent=2)
print(f"Kaydedildi: {out_path}")
# Kategori listesini ayri kaydet
cat_path = OUTPUT_DIR / "categories.json"
with open(cat_path, "w", encoding="utf-8") as f:
json.dump(
[{"id": cat_to_id[n], "name": n} for n in sorted_categories],
f, ensure_ascii=False, indent=2
)
print(f"Kaydedildi: {cat_path}")
def build_coco_json(split_data, cat_to_id, sorted_categories, img_dir):
"""COCO formatinda JSON olustur."""
images = []
annotations = []
ann_id = 0
for img_id, (tablet_id, image_path, anns, image_info) in enumerate(split_data):
# Gorsel bilgisi
w = image_info.get("width", 0)
h = image_info.get("height", 0)
# Boyut bilinmiyorsa dosyadan okumaya calis
if w == 0 or h == 0:
try:
from PIL import Image
with Image.open(image_path) as im:
w, h = im.size
except ImportError:
print(f" UYARI: PIL yuklu degil, {tablet_id} boyutu bilinmiyor")
except Exception as e:
print(f" UYARI: {tablet_id} gorsel okunamadi: {e}")
# Gorseli output/images altina sembolik link yap
ext = image_path.suffix
new_name = f"tablet_{tablet_id}{ext}"
link_path = img_dir / new_name
if not link_path.exists():
try:
os.symlink(image_path.resolve(), link_path)
except OSError:
# Symlink desteklenmiyorsa kopyala
import shutil
shutil.copy2(image_path, link_path)
images.append({
"id": img_id,
"file_name": new_name,
"height": h,
"width": w,
})
# Anotasyonlar
for ann in anns:
cat_name = ann.get("name", "unknown")
if cat_name not in cat_to_id:
continue
x, y = ann["x"], ann["y"]
bw, bh = ann["width"], ann["height"]
area = bw * bh
annotations.append({
"id": ann_id,
"image_id": img_id,
"category_id": cat_to_id[cat_name],
"bbox": [x, y, bw, bh],
"area": area,
"segmentation": [],
"iscrowd": 0,
})
ann_id += 1
coco = {
"images": images,
"annotations": annotations,
"categories": [{"id": cat_to_id[n], "name": n} for n in sorted_categories],
}
return coco
# ============================================================
# ADIM 4: eBL verileriyle birlestir
# ============================================================
def merge_with_ebl():
"""Hitit COCO verisini eBL COCO verisiyle birlestir."""
hitit_train = OUTPUT_DIR / "coco_hitit_train.json"
if not hitit_train.exists():
print("HATA: Once --convert ile Hitit verisini donusturun!")
return
# eBL COCO dosyasini bul
ebl_coco_paths = [
EBL_DIR / "coco-recognition" / "data" / "coco" / "annotations" / "instances_train2017.json",
EBL_DIR / "coco-two-stage" / "data" / "coco" / "annotations" / "instances_val2017.json",
]
for ebl_path in ebl_coco_paths:
if not ebl_path.exists():
print(f" eBL dosyasi bulunamadi: {ebl_path}")
continue
print(f"\n eBL dosyasi: {ebl_path}")
with open(ebl_path, "r") as f:
ebl_coco = json.load(f)
with open(hitit_train, "r") as f:
hitit_coco = json.load(f)
# Kategori birlesimi
hitit_cats = {c["name"]: c["id"] for c in hitit_coco["categories"]}
ebl_cats = {c["name"]: c["id"] for c in ebl_coco["categories"]}
all_cat_names = sorted(set(hitit_cats.keys()) | set(ebl_cats.keys()))
merged_cat_to_id = {name: i for i, name in enumerate(all_cat_names)}
# Hitit anotasyonlarini yeni ID'lerle guncelle
old_to_new_hitit = {hitit_cats[n]: merged_cat_to_id[n] for n in hitit_cats}
for ann in hitit_coco["annotations"]:
ann["category_id"] = old_to_new_hitit[ann["category_id"]]
# eBL anotasyonlarini yeni ID'lerle guncelle ve ID offset ekle
img_offset = max(im["id"] for im in hitit_coco["images"]) + 1
ann_offset = max(a["id"] for a in hitit_coco["annotations"]) + 1 if hitit_coco["annotations"] else 0
old_to_new_ebl = {ebl_cats[n]: merged_cat_to_id[n] for n in ebl_cats}
for ann in ebl_coco["annotations"]:
ann["id"] += ann_offset
ann["image_id"] += img_offset
ann["category_id"] = old_to_new_ebl[ann["category_id"]]
for im in ebl_coco["images"]:
im["id"] += img_offset
# Birlestir
merged = {
"images": hitit_coco["images"] + ebl_coco["images"],
"annotations": hitit_coco["annotations"] + ebl_coco["annotations"],
"categories": [{"id": merged_cat_to_id[n], "name": n} for n in all_cat_names],
}
out_name = f"coco_merged_{ebl_path.parent.parent.parent.name}.json"
out_path = OUTPUT_DIR / out_name
with open(out_path, "w", encoding="utf-8") as f:
json.dump(merged, f, ensure_ascii=False, indent=2)
print(f" Birlesik: {len(merged['images'])} gorsel, "
f"{len(merged['annotations'])} anotasyon, "
f"{len(merged['categories'])} sinif")
print(f" Kaydedildi: {out_path}")
# ============================================================
# MAIN
# ============================================================
def main():
parser = argparse.ArgumentParser(description="Hitit tablet anotasyonlarini COCO formatina donustur")
parser.add_argument("--inspect", action="store_true", help="Sadece mark.txt formatini kesfet")
parser.add_argument("--convert", action="store_true", help="COCO formatina donustur")
parser.add_argument("--merge-ebl", action="store_true", help="eBL verileriyle birlestir")
parser.add_argument("--val-ratio", type=float, default=0.2, help="Validasyon orani (varsayilan: 0.2)")
parser.add_argument("--seed", type=int, default=42, help="Random seed")
args = parser.parse_args()
if not any([args.inspect, args.convert, args.merge_ebl]):
# Hicbir flag verilmezse inspect yap
args.inspect = True
if args.inspect:
inspect_all()
if args.convert:
convert_to_coco(val_ratio=args.val_ratio, seed=args.seed)
if args.merge_ebl:
merge_with_ebl()
if __name__ == "__main__":
main()