كيفية تحويل تعليقات COCO إلى تنسيق YOLO#
يتطلب تدريب نماذج Ultralytics YOLO تعليقات بتنسيق YOLO، لكن العديد من أدوات التعليق الشائعة تصدّرها بتنسيق COCO JSON بدلًا من ذلك. يوضح هذا الدليل كيفية تحويل تعليقات COCO إلى تنسيق YOLO وبدء تدريب نماذج كشف الأجسام وتقسيم المثيلات وتقدير الوضعية.
للتدريب مباشرةً باستخدام COCO JSON دون إنشاء ملفات .txt، راجع تدريب YOLO على COCO JSON دون تحويل.
لماذا التحويل من COCO إلى YOLO؟#
يخزّن تنسيق COCO JSON جميع التعليقات في ملف واحد، بينما يستخدم YOLO ملفًا نصيًا واحدًا لكل صورة بإحداثيات مطبّعة. ويُعد التحويل ضروريًا للأسباب التالية:
- تتطلب نماذج YOLO ملفات تسميات
.txt، بحيث يكون لكل صورة ملف واحد يتضمنclass x_center y_center width heightبإحداثيات مطبّعة. - يستخدم COCO JSON إحداثيات البكسل بتنسيق
[x_min, y_min, width, height]، مع ملف JSON واحد لجميع الصور. - تختلف معرّفات الفئات — يستخدم COCO قيم
category_idاعتباطية، بينما يتطلب YOLO معرّفات فئات تبدأ من الصفر.
| الميزة | COCO JSON | YOLO TXT |
|---|---|---|
| البنية | ملف JSON واحد لجميع الصور | ملف .txt واحد لكل صورة |
| تنسيق BBox | [x_min, y_min, width, height] بالبكسل | تطبيع class x_center y_center width height (0-1) |
| معرّفات الفئات | category_id (يمكن أن يبدأ من أي رقم) | يبدأ الفهرس من الصفر (يبدأ من 0) |
| التجزئة | مصفوفات المضلعات في الحقل segmentation | إحداثيات المضلعات بعد معرّف الفئة |
| النقاط المفتاحية | [x, y, visibility, ...] بالبكسل | تطبيع [x, y, visibility, ...] |
البدء السريع#
أسرع طريقة لتحويل تعليقات COCO التوضيحية وبدء التدريب:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/", # الدليل الذي يحتوي على ملفات JSON الخاصة بك
save_dir="my_dataset/converted/", # مكان حفظ التسميات المحوّلة
cls91to80=False, # اضبطها على False لمجموعات البيانات المخصصة (راجع التحذير أدناه)
)بعد التحويل، نظّم بنية الدليل، وأنشئ ملف dataset.yaml، ثم ابدأ التدريب. راجع الدليل التفصيلي خطوة بخطوة أدناه.
الإعداد الافتراضي cls91to80=True مصمم فقط لمجموعة بيانات COCO القياسية ذات 80 فئة للكائنات، إذ يعيّن 91 معرّف فئة غير متتابع إلى 80 معرّف فئة متتابعًا. لأي مجموعة بيانات مخصصة، يجب ضبط cls91to80=False؛ وإلا فستُعيّن معرّفات الفئات لديك على نحو خاطئ دون تنبيه، وسيتعلم نموذجك فئات غير صحيحة.
دليل التحويل خطوة بخطوة#
1. جهّز مجموعة بيانات COCO#
تتخذ مجموعة البيانات النموذجية بتنسيق COCO، والمصدّرة من أدوات التعليقات التوضيحية، البنية التالية:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ ├── img_002.jpg
│ │ └── ...
│ └── val/
│ ├── img_100.jpg
│ └── ...
└── annotations/
├── instances_train.json
└── instances_val.jsonيتبع كل ملف JSON مواصفات تنسيق بيانات COCO ويتضمن ثلاثة حقول مطلوبة — images وannotations وcategories:
{
"images": [{ "id": 1, "file_name": "img_001.jpg", "width": 640, "height": 480 }],
"annotations": [
{
"id": 1,
"image_id": 1,
"category_id": 1,
"bbox": [100, 50, 200, 150],
"area": 30000,
"iscrowd": 0
}
],
"categories": [
{ "id": 1, "name": "helmet" },
{ "id": 2, "name": "vest" }
]
}2. حوّل التعليقات التوضيحية#
استخدم الدالة convert_coco() لتحويل تعليقات COCO التوضيحية بصيغة JSON إلى تنسيق YOLO .txt:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/",
save_dir="my_dataset/converted/",
cls91to80=False,
)ينشئ convert_coco() ملف .txt لكل صورة تتضمن تعليقات توضيحية، داخل دليل فرعي labels/ يحمل اسم كل ملف JSON بعد حذف البادئة instances_ (لذا ينتج عن instances_train.json الملف labels/train/). تُتخطى الصور التي لا تحتوي على تعليقات توضيحية ولا يُنشأ لها ملف تسمية، ولذلك قد لا تعكس شجرة labels/ جميع الصور:
my_dataset/converted/
├── images/ # created but left empty
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...لا يستبدل convert_coco() مطلقًا ملف save_dir موجودًا: إذا كان my_dataset/converted/ موجودًا بالفعل، فستكتب إعادة التشغيل إلى my_dataset/converted-2/ بدلًا منه. احذف الإخراج السابق (أو غيّر save_dir) قبل إعادة التشغيل، وإلا فستقرأ الخطوات التالية تسميات قديمة.
3. نظّم بنية الدليل#
بعد التحويل، يجب وضع ملفات التسميات بجوار الصور. يتوقع YOLO دليل labels/ يعكس بنية دليل images/:
import shutil
from pathlib import Path
converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")
# تسمّي convert_coco كل دليل فرعي باسم ملف JSON الخاص به (بعد حذف البادئة "instances_"),
# لذا كرّر على الأدلة الفرعية الفعلية بدلًا من افتراض وجود "train"/"val".
for src in converted_dir.iterdir():
if not src.is_dir():
continue
dst = dataset_dir / "labels" / src.name
dst.mkdir(parents=True, exist_ok=True)
for f in src.glob("*.txt"):
shutil.move(str(f), str(dst / f.name))ينبغي أن تبدو بنية مجموعة البيانات النهائية كما يلي:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. أنشئ dataset.yaml#
أنشئ ملف إعدادات dataset.yaml يعيّن فئات COCO إلى أسماء فئات YOLO. يحدد هذا الملف لـ YOLO موقع بياناتك والفئات المطلوب اكتشافها:
import json
from pathlib import Path
import yaml
# اقرأ الفئات من ملف JSON الخاص بـ COCO
with open("my_dataset/annotations/instances_train.json") as f:
coco = json.load(f)
# أنشئ أسماء فئات تطابق مخرجات convert_coco (category_id - 1)
categories = sorted(coco["categories"], key=lambda x: x["id"])
names = {cat["id"] - 1: cat["name"] for cat in categories}
# ملاحظة: يعيّن convert_coco معرّفات الفئات كالتالي: category_id - 1، لذا يجب أن تكون قيمة category_id
# بدءًا من 1. إذا كانت فئاتك تبدأ من 0، فأضف 1 إلى كل معرّف أولًا.
# أنشئ dataset.yaml
dataset = {
"path": str(Path("my_dataset").resolve()),
"train": "images/train",
"val": "images/val",
"names": names,
}
with open("my_dataset/dataset.yaml", "w") as f:
yaml.dump(dataset, f, default_flow_style=False)ملف YAML الناتج:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestلمزيد من التفاصيل حول تنسيق YAML لمجموعة البيانات، راجع دليل إعداد مجموعة البيانات.
5. درّب نموذج YOLO#
بعد تجهيز مجموعة البيانات المحوّلة، درّب نموذج YOLO:
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # حمّل نموذجًا مدرّبًا مسبقًا
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)للحصول على نصائح التدريب وأفضل الممارسات، راجع دليل تدريب النماذج.
6. تحقّق من التحويل#
قبل التدريب، افحص عيّنة من ملفات التسميات للتأكد من صحة معرّفات الفئات والإحداثيات:
from pathlib import Path
label_file = Path("my_dataset/labels/train/img_001.txt")
for line in label_file.read_text().strip().splitlines():
parts = line.split()
cls_id = int(parts[0])
coords = [float(v) for v in parts[1:5]]
assert cls_id >= 0, f"Negative class ID {cls_id} — category_id in your JSON may start from 0"
assert all(0 <= v <= 1 for v in coords), f"Coordinates out of [0, 1] range: {coords}"إذا ظهرت معرّفات فئات سالبة، فمن المرجح أن ملف COCO JSON يستخدم category_id الذي يبدأ من 0. أضف 1 إلى جميع قيم category_id في ملف JSON قبل تشغيل convert_coco()، لأنه يعيّن معرّفات الفئات وفقًا لـ category_id - 1.
استكشاف المشكلات الشائعة وإصلاحها#
معرّفات فئات خاطئة بعد التحويل#
إذا درّب نموذجك لكنه يكتشف فئات كائنات خاطئة، فمن المرجح أنك تستخدم cls91to80=True (الإعداد الافتراضي) مع مجموعة بيانات مخصصة. يعيّن هذا قيم category_id عبر جدول البحث COCO من 91 إلى 80، وهو صحيح فقط لمجموعة بيانات COCO القياسية. أما category_id الذي لا يقابله شيء ضمن COCO-80، فلا يُعيّن إلى أي شيء، ويؤدي إلى ظهور TypeError: must be real number, not NoneType أثناء التحويل بدلًا من إنشاء تسميات خاطئة.
الحل: استخدم دائمًا cls91to80=False لمجموعات البيانات المخصصة.
لم يُعثر على تسميات أثناء التدريب#
إذا أفاد فحص التسميات بوجود 0 images, N backgrounds ثم توقف التدريب مع ValueError: train: No labels found in .../labels/train.cache، فهذا يعني أن ملفات التسميات ليست في الدليل المتوقع. يحفظ convert_coco() التسميات في دليل إخراج منفصل (مثل save_dir/labels/train/)، لكن YOLO يتوقع وجود labels/ موازيًا لـ images/ داخل دليل مجموعة البيانات.
الحل: انقل ملفات التسميات لتتوافق مع بنية الدليل المتوقعة. تأكد من أن labels/train/ يقع بجوار images/train/.
خطأ KeyError أثناء التحويل#
إذا ظهر لك KeyError: 'bbox' أو أخطاء مشابهة عند تشغيل convert_coco()، فمن المرجح أن labels_dir يحتوي على ملفات JSON ليست خاصة بالنسخ (مثل captions_train2017.json) ولها بنية تعليقات توضيحية مختلفة.
الحل: لا تضع في labels_dir إلا ملفات JSON الخاصة بتعليقات النسخ التوضيحية (مثل instances_train2017.json).
ملفات تسميات فارغة بعد التحويل#
إذا اكتمل التحويل لكن ملفات .txt فارغة أو مفقودة، فقد تحتوي جميع التعليقات التوضيحية على iscrowd: 1 (وهو أمر شائع مع الأقنعة التي ينشئها SAM)، أو قد يكون عرض مربعات الإحاطة أو ارتفاعها صفرًا. يؤدي التشغيل باستخدام use_keypoints=True على تصدير مخصص للاكتشاف فقط إلى النتيجة نفسها، لأن التعليقات التوضيحية التي لا تحتوي على الحقل keypoints تُتخطى بالكامل.
الحل: افحص قيم iscrowd في التعليقات التوضيحية بصيغة JSON. إذا كنت تستخدم أقنعة SAM، فعالج ملف JSON مسبقًا لتعيين iscrowd: 0. إذا مرّرت use_keypoints=True، فتأكد من أن تعليقاتك التوضيحية تتضمن فعلًا keypoints.
مضلعات على شكل مربعات ناتجة عن تعليقات الأقنعة التوضيحية#
إذا سجّل use_segments=True الرسالة annotations without a usable polygon، فهذا يعني أن بعض التعليقات التوضيحية لا تحتوي على قيمة segmentation، أو أن قيمتها ليست قائمة تضم ثلاثة أزواج إحداثيات على الأقل. تشمل الأسباب الشائعة عمليات التصدير المخصصة للاكتشاف فقط، التي تترك الحقل مفقودًا أو فارغًا، وترميز الأطوال المتتالية (RLE) في COCO ({"counts": ..., "size": ...})، الذي تكتبه أدوات تصدير الأقنعة الثنائية مثل SAM؛ وتُعامل بالطريقة نفسها قائمة الإحداثيات المسطحة التي لا تحتوي على قائمة مضلعات، والحدود المؤلفة من نقطة أو نقطتين، والقيم الأخرى ذات التنسيق غير الصحيح. يحتفظ التعليق التوضيحي بالمضلعات الصالحة المتبقية، ويعود إلى صف مقطع على هيئة مربع الإحاطة عند عدم وجود أي مضلع صالح، لذا تظل التسميات صالحة، لكن تلك الصفوف لا تتضمن تفاصيل القناع.
الحل: أعد تصدير التعليقات التوضيحية مع مقاطع مضلعية، أو فك ترميز أقنعة RLE إلى مضلعات قبل تشغيل convert_coco()، أو صحّح أي قيم segmentation ذات تنسيق غير صحيح.
فجوات في معرّفات الفئات ضمن التسميات المحوّلة#
إذا كانت معرّفات الفئات في ملفات التسميات غير متتابعة (مثلًا: 0 و4 و9 بدلًا من 0 و1 و2)، فإن أداة التعليقات التوضيحية تستخدم قيم category_id غير المتتابعة.
الحل: تحقّق من أن معرّفات الفئات في ملفات .txt تطابق القاموس names في dataset.yaml. أعد تعيين المعرّفات إلى قيم متتابعة عند الحاجة.
للاطلاع على تفاصيل API الكاملة وأوصاف المعلمات، راجع مرجع API الخاص بـ convert_coco.
الأسئلة الشائعة#
استخدم الدالة
convert_coco()من Ultralytics لتحويل تعليقات COCO التوضيحية بصيغة JSON إلى تنسيق YOLO.txt. اضبطcls91to80=Falseلمجموعات البيانات المخصصة:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)بعد التحويل، أعد تنظيم ملفات التسميات بحيث يعكس
labels/دليلimages/، ثم أنشئ ملفdataset.yaml. راجع الدليل التفصيلي خطوة بخطوة للاطلاع على سير العمل الكامل.يحدث ذلك لأن
convert_coco()يحفظ التسميات في دليل فرعي داخلsave_dir/labels/(مثلsave_dir/labels/train/) بدلًا من حفظها مباشرةً فيlabels/train/ضمن مجموعة البيانات، بجوارimages/train/. يتوقع YOLO وجود التسميات بمحاذاة الصور — فعلى سبيل المثال، يحتاجimages/train/img.jpgإلىlabels/train/img.txt. انقل التسميات المحوّلة لتتوافق مع هذه البنية. راجع إصلاح بنية الدليل.تتحكم المعلمة
cls91to80في كيفية تعيين قيم COCOcategory_idإلى معرّفات فئات YOLO. عند ضبطTrue(الإعداد الافتراضي)، يُطبّق جدول البحثcoco91_to_coco80_class()المصمم لمجموعة بيانات COCO القياسية، التي تضم 80 فئة ذات معرّفات غير متتابعة (1-90). بالنسبة إلى مجموعات البيانات المخصصة، اضبط دائمًاcls91to80=False— إذ يطرح ذلك 1 ببساطة من كل قيمةcategory_idلإنشاء معرّفات فئات تبدأ من الصفر.ليس من دون كتابة تعليمات برمجية مخصصة. يتوقع مسار التدريب الافتراضي تسميات YOLO
.txt، بملف واحد لكل صورة؛ لذا شغّلconvert_coco()واتبع هذا الدليل التفصيلي خطوة بخطوة، أو أنشئ فئة فرعية من مجموعة البيانات لتحليل COCO JSON أثناء التشغيل — راجع تدريب YOLO على COCO JSON دون تحويل. لمزيد من المعلومات حول التنسيقات المدعومة، راجع تنسيقات مجموعات البيانات.نعم، استخدم
use_segments=Trueعند استدعاءconvert_coco()لتضمين أقنعة التجزئة المضلعّية في تسميات YOLO المحوّلة. ينتج عن ذلك ملفات تسميات متوافقة مع نماذج تجزئة YOLO:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)استخدم
use_keypoints=Trueلتحويل تعليقات النقاط المفتاحية التوضيحية في COCO بغرض التدريب على تقدير الوضعية:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)لاحظ أنه إذا ضُبط كل من
use_segmentsوuse_keypointsعلىTrue، فلن تُكتب في ملفات التسميات إلا النقاط المفتاحية — ويُتجاهل المقاطع بصمت.