COCO JSON Formatında Dönüştürme Yapmadan YOLO Eğitimi Nasıl Yapılır#
COCO JSON formatındaki Annotations, önce .txt dosyalarına dönüştürmeye gerek kalmadan doğrudan Ultralytics YOLO eğitimi için kullanılabilir. Bu, COCO JSON'ı anında ayrıştırmak için YOLODataset alt sınıfı oluşturarak ve bunu özel bir eğitici aracılığıyla eğitim boru hattına bağlayarak çalışır.
Neden Doğrudan COCO JSON Üzerinden Eğitim Yapılmalı#
Bu yaklaşım, COCO JSON'ı tek gerçek doğruluk kaynağı (single source of truth) olarak tutar; convert_coco() çağrısı, dizin yeniden düzenlemesi veya ara etiket dosyası gerekmez. YOLO26 ve diğer tüm Ultralytics YOLO nesne algılama modelleri desteklenir. Bölütleme ve poz modelleri ek etiket alanları gerektirir (bkz. SSS).
Standart convert_coco() iş akışı için COCO to YOLO Conversion guide kılavuzuna bakın.
Mimariye Genel Bakış#
İki sınıf gereklidir:
COCODataset— COCO JSON'ı okur ve eğitim sırasında bounding boxes öğelerini bellekte YOLO formatına dönüştürürCOCOTrainer— varsayılanYOLODatasetyerineCOCODatasetkullanmak içinbuild_dataset()öğesini geçersiz kılar
Bu uygulama, JSON ek açıklamalarını da doğrudan okuyan yerleşik GroundingDataset ile aynı deseni izler. Üç yöntem geçersiz kılınmıştır: get_img_files(), cache_labels() ve get_labels().
COCO JSON Veri Kümesi Sınıfını Oluşturma#
COCODataset sınıfı, YOLODataset sınıfından türetilir ve etiket yükleme mantığını geçersiz kılar. Bir etiket dizininden .txt dosyalarını okumak yerine, COCO JSON dosyasını açar, görüntüye göre gruplandırılmış ek açıklamalar üzerinde yineler ve her bir sınırlayıcı kutuyu COCO piksel formatı [x_min, y_min, width, height] değerinden YOLO normalleştirilmiş merkez formatı [x_center, y_center, width, height] değerine dönüştürür. Kalabalık ek açıklamaları (iscrowd: 1) ve sıfır alanlı kutular otomatik olarak atlanır.
get_img_files() yöntemi boş bir liste döndürür çünkü görüntü yolları, cache_labels() içindeki JSON file_name alanından çözümlenir. Kategori kimlikleri sıralanır ve sıfır tabanlı sınıf indekslerine yeniden eşlenir; böylece hem 1 tabanlı (standart COCO) hem de bitişik olmayan kimlik şemaları doğru şekilde çalışır.
import json
from collections import defaultdict
from pathlib import Path
import numpy as np
from ultralytics.data.dataset import DATASET_CACHE_VERSION, YOLODataset
from ultralytics.data.utils import get_hash, load_dataset_cache_file, save_dataset_cache_file
from ultralytics.utils import TQDM
class COCODataset(YOLODataset):
"""Dataset that reads COCO JSON annotations directly without conversion to .txt files."""
def __init__(self, *args, json_file="", **kwargs):
"""Initialize the dataset with a COCO JSON annotation file."""
self.json_file = json_file
super().__init__(*args, data={"channels": 3}, **kwargs)
def get_img_files(self, img_path):
"""Image paths are resolved from the JSON file, not from scanning a directory."""
return []
def cache_labels(self, path=Path("./labels.cache")):
"""Parse COCO JSON and convert annotations to YOLO format. Results are saved to a .cache file."""
x = {"labels": []}
with open(self.json_file) as f:
coco = json.load(f)
# Sort categories by ID and map to 0-indexed classes
categories = {cat["id"]: i for i, cat in enumerate(sorted(coco["categories"], key=lambda c: c["id"]))}
img_to_anns = defaultdict(list)
for ann in coco["annotations"]:
img_to_anns[ann["image_id"]].append(ann)
for img_info in TQDM(coco["images"], desc="reading annotations"):
h, w = img_info["height"], img_info["width"]
im_file = Path(self.img_path) / img_info["file_name"]
if not im_file.exists():
continue
self.im_files.append(str(im_file))
bboxes = []
for ann in img_to_anns.get(img_info["id"], []):
if ann.get("iscrowd", False):
continue
# COCO: [x, y, w, h] top-left in pixels -> YOLO: [cx, cy, w, h] center normalized
box = np.array(ann["bbox"], dtype=np.float32)
box[:2] += box[2:] / 2 # top-left to center
box[[0, 2]] /= w # normalize x
box[[1, 3]] /= h # normalize y
if box[2] <= 0 or box[3] <= 0:
continue
cls = categories[ann["category_id"]]
bboxes.append([cls, *box.tolist()])
lb = np.array(bboxes, dtype=np.float32) if bboxes else np.zeros((0, 5), dtype=np.float32)
x["labels"].append(
{
"im_file": str(im_file),
"shape": (h, w),
"cls": lb[:, 0:1],
"bboxes": lb[:, 1:],
"segments": [],
"normalized": True,
"bbox_format": "xywh",
}
)
x["hash"] = get_hash([self.json_file, str(self.img_path)])
save_dataset_cache_file(self.prefix, path, x, DATASET_CACHE_VERSION)
return x
def get_labels(self):
"""Load labels from .cache file if available, otherwise parse JSON and create the cache."""
cache_path = Path(self.json_file).with_suffix(".cache")
try:
cache = load_dataset_cache_file(cache_path)
assert cache["version"] == DATASET_CACHE_VERSION
assert cache["hash"] == get_hash([self.json_file, str(self.img_path)])
self.im_files = [lb["im_file"] for lb in cache["labels"]]
except (FileNotFoundError, AssertionError, AttributeError, KeyError, ModuleNotFoundError):
cache = self.cache_labels(cache_path)
cache.pop("hash", None)
cache.pop("version", None)
return cache["labels"]Ayrıştırılmış etiketler, JSON'ın yanında bir .cache dosyasına kaydedilir (örn. instances_train.cache). Sonraki eğitim çalıştırmalarında önbellek doğrudan yüklenir ve JSON ayrıştırma adımı atlanır. JSON dosyası değişirse, hash kontrolü başarısız olur ve önbellek otomatik olarak yeniden oluşturulur.
Veri Kümesini Eğitim Hattına Bağlama#
Eğiticide gereken tek değişiklik build_dataset() öğesini geçersiz kılmaktır. Varsayılan DetectionTrainer, .txt etiket dosyalarını tarayan bir YOLODataset oluşturur. Bunu COCODataset ile değiştirerek eğitici, bunun yerine doğrudan COCO JSON'dan okur.
JSON dosya yolu, veri yapılandırmasındaki özel bir train_json / val_json alanından alınır (bkz. dataset.yaml Yapılandırması). Eğitim sırasında mode="train", train_json değerine çözümlenir; doğrulama sırasında ise mode="val", val_json değerine çözümlenir. Eğer val_json ayarlanmamışsa, varsayılan olarak train_json kullanılır.
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import colorstr
class COCOTrainer(DetectionTrainer):
"""Trainer that uses COCODataset for direct COCO JSON training."""
def build_dataset(self, img_path, mode="train", batch=None):
"""Build a COCODataset for the given split using the JSON file from the data config."""
json_file = self.data["train_json"] if mode == "train" else self.data.get("val_json", self.data["train_json"])
return COCODataset(
img_path=img_path,
json_file=json_file,
imgsz=self.args.imgsz,
batch_size=batch,
augment=mode == "train",
hyp=self.args,
rect=self.args.rect or mode == "val",
cache=self.args.cache or None,
single_cls=self.args.single_cls or False,
stride=int(self.model.stride.max()) if hasattr(self, "model") and self.model else 32,
pad=0.0 if mode == "train" else 0.5,
prefix=colorstr(f"{mode}: "),
task=self.args.task,
classes=self.args.classes,
fraction=self.args.fraction if mode == "train" else 1.0,
)COCO JSON için dataset.yaml yapılandırma#
dataset.yaml, görüntü dizinlerini bulmak için standart path, train ve val alanlarını kullanır. İki ek alan (train_json ve val_json), COCOTrainer tarafından okunan COCO ek açıklama dosyalarını belirtir. nc ve names alanları, JSON içindeki categories sıralı düzeniyle eşleşecek şekilde sınıf sayısını ve adlarını tanımlar.
path: /path/to/my_dataset/images # root with train/ and val/ image subfolders
train: train
val: val
# COCO JSON annotation files (use absolute paths; these custom keys are not resolved against `path`)
train_json: /path/to/my_dataset/annotations/instances_train.json
val_json: /path/to/my_dataset/annotations/instances_val.json
nc: 80
names:
0: person
1: bicycle
# ... remaining class namesBeklenen dizin yapısı:
my_dataset/
images/
train/
img_001.jpg
...
val/
img_100.jpg
...
annotations/
instances_train.json
instances_val.json
dataset.yamlCOCO JSON Üzerinde Eğitim Çalıştırma#
Veri kümesi sınıfı, eğitici sınıfı ve YAML yapılandırması hazır olduğunda, eğitim standart model.train() çağrısıyla çalışır. Normal bir eğitim çalıştırmasından tek fark, Ultralytics'e varsayılan veri yükleyici yerine özel veri yükleyiciyi kullanmasını söyleyen trainer=COCOTrainer argümanıdır.
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.train(data="dataset.yaml", epochs=100, imgsz=640, trainer=COCOTrainer)Tam eğitim boru hattı; doğrulama, kontrol noktası kaydetme ve metrik kaydı dahil olmak üzere beklendiği gibi çalışır.
Tam Uygulama#
Kolaylık olması açısından tam uygulama, tek bir kopyala-yapıştır betiği olarak aşağıda verilmiştir. Özel veri kümesini, özel eğiticiyi ve eğitim çağrısını içerir. Bunu dataset.yaml dosyanızın yanına kaydedin ve doğrudan çalıştırın.
import json
from collections import defaultdict
from pathlib import Path
import numpy as np
from ultralytics import YOLO
from ultralytics.data.dataset import DATASET_CACHE_VERSION, YOLODataset
from ultralytics.data.utils import get_hash, load_dataset_cache_file, save_dataset_cache_file
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import TQDM, colorstr
class COCODataset(YOLODataset):
"""Dataset that reads COCO JSON annotations directly without conversion to .txt files."""
def __init__(self, *args, json_file="", **kwargs):
"""Initialize the dataset with a COCO JSON annotation file."""
self.json_file = json_file
super().__init__(*args, data={"channels": 3}, **kwargs)
def get_img_files(self, img_path):
"""Image paths are resolved from the JSON file, not from scanning a directory."""
return []
def cache_labels(self, path=Path("./labels.cache")):
"""Parse COCO JSON and convert annotations to YOLO format. Results are saved to a .cache file."""
x = {"labels": []}
with open(self.json_file) as f:
coco = json.load(f)
categories = {cat["id"]: i for i, cat in enumerate(sorted(coco["categories"], key=lambda c: c["id"]))}
img_to_anns = defaultdict(list)
for ann in coco["annotations"]:
img_to_anns[ann["image_id"]].append(ann)
for img_info in TQDM(coco["images"], desc="reading annotations"):
h, w = img_info["height"], img_info["width"]
im_file = Path(self.img_path) / img_info["file_name"]
if not im_file.exists():
continue
self.im_files.append(str(im_file))
bboxes = []
for ann in img_to_anns.get(img_info["id"], []):
if ann.get("iscrowd", False):
continue
box = np.array(ann["bbox"], dtype=np.float32)
box[:2] += box[2:] / 2
box[[0, 2]] /= w
box[[1, 3]] /= h
if box[2] <= 0 or box[3] <= 0:
continue
cls = categories[ann["category_id"]]
bboxes.append([cls, *box.tolist()])
lb = np.array(bboxes, dtype=np.float32) if bboxes else np.zeros((0, 5), dtype=np.float32)
x["labels"].append(
{
"im_file": str(im_file),
"shape": (h, w),
"cls": lb[:, 0:1],
"bboxes": lb[:, 1:],
"segments": [],
"normalized": True,
"bbox_format": "xywh",
}
)
x["hash"] = get_hash([self.json_file, str(self.img_path)])
save_dataset_cache_file(self.prefix, path, x, DATASET_CACHE_VERSION)
return x
def get_labels(self):
"""Load labels from .cache file if available, otherwise parse JSON and create the cache."""
cache_path = Path(self.json_file).with_suffix(".cache")
try:
cache = load_dataset_cache_file(cache_path)
assert cache["version"] == DATASET_CACHE_VERSION
assert cache["hash"] == get_hash([self.json_file, str(self.img_path)])
self.im_files = [lb["im_file"] for lb in cache["labels"]]
except (FileNotFoundError, AssertionError, AttributeError, KeyError, ModuleNotFoundError):
cache = self.cache_labels(cache_path)
cache.pop("hash", None)
cache.pop("version", None)
return cache["labels"]
class COCOTrainer(DetectionTrainer):
"""Trainer that uses COCODataset for direct COCO JSON training."""
def build_dataset(self, img_path, mode="train", batch=None):
"""Build a COCODataset for the given split using the JSON file from the data config."""
json_file = self.data["train_json"] if mode == "train" else self.data.get("val_json", self.data["train_json"])
return COCODataset(
img_path=img_path,
json_file=json_file,
imgsz=self.args.imgsz,
batch_size=batch,
augment=mode == "train",
hyp=self.args,
rect=self.args.rect or mode == "val",
cache=self.args.cache or None,
single_cls=self.args.single_cls or False,
stride=int(self.model.stride.max()) if hasattr(self, "model") and self.model else 32,
pad=0.0 if mode == "train" else 0.5,
prefix=colorstr(f"{mode}: "),
task=self.args.task,
classes=self.args.classes,
fraction=self.args.fraction if mode == "train" else 1.0,
)
model = YOLO("yolo26n.pt")
model.train(data="dataset.yaml", epochs=100, imgsz=640, trainer=COCOTrainer)Artık açıklamaların tek doğruluk kaynağı olarak kaldığı ve ara .txt dosyalarının bulunmadığı, Ultralytics YOLO'yu doğrudan COCO JSON üzerinde eğiten minimum düzeyde bir veri setine ve eğitiye sahipsin. Segmentasyon ve poz tahmini konularını kapsayacak şekilde cache_labels() yöntemini segments veya keypoints ile genişlet ve hyperparameter ayarlama önerileri için Model Training Tips kılavuzuna göz at.
SSS#
convert_coco(), bir defaya mahsus dönüştürme işlemi olarak diske.txtetiket dosyaları yazar. Bu yaklaşım ise her eğitim çalıştırmasının başında JSON'ı ayrıştırır ve ek açıklamaları bellekte dönüştürür. Kalıcı YOLO formatında etiketler tercih edildiğindeconvert_coco()kullan; ek dosyalar oluşturmadan COCO JSON'ı tek gerçek doğruluk kaynağı olarak tutmak istediğinde bu yaklaşımı kullan.Varsayılan olarak YOLO
.txtetiketlerini bekleyen mevcut Ultralytics boru hattıyla doğrudan yapılamaz. Bu kılavuz, gereken minimum özel kodu (bir veri kümesi sınıfı ve bir eğitici sınıfı) sağlar. Tanımlandıktan sonra eğitim yalnızca standart birmodel.train()çağrısı gerektirir.Bu kılavuz object detection konusunu kapsar. instance segmentation desteği eklemek için her etiket sözlüğünün
segmentsalanına COCO açıklamalarındakisegmentationçokgen verilerini dahil et. pose estimation içinkeypointsekle.GroundingDatasetsource code, segmentleri işlemek için referans bir uygulama sağlar.Evet.
COCODataset,YOLODatasetöğesini genişletir; bu nedenle mosaic, mixup, copy-paste ve diğerleri dahil tüm yerleşik data augmentations herhangi bir değişiklik yapmadan çalışır.Kategoriler
iddeğerine göre sıralanır ve 0'dan başlayan ardışık indekslere eşlenir. Bu, 1 tabanlı kimlikleri (standart COCO), 0 tabanlı kimlikleri ve bitişik olmayan kimlikleri işler.dataset.yamliçindekinamessözlüğü, COCOcategoriesdizisiyle aynı sıralı düzeni izlemelidir.COCO JSON, ilk eğitim çalıştırmasında bir kez ayrıştırılır. Ayrıştırılmış etiketler bir
.cachedosyasına kaydedilir, böylece sonraki çalıştırmalar yeniden ayrıştırma yapmadan anında yüklenir. Ek açıklamalar bellekte tutulduğu için eğitim hızı standart YOLO eğitimiyle aynıdır. JSON dosyası değişirse önbellek otomatik olarak yeniden oluşturulur.