انتقل إلى المحتوى

مرجع ل ultralytics/data/converter.py

ملاحظه

هذا الملف متاح في https://github.com/ultralytics/ultralytics/ نقطة / الرئيسية /ultralytics/ البيانات / المحول.py. إذا اكتشفت مشكلة ، فيرجى المساعدة في إصلاحها من خلال المساهمة في طلب 🛠️ سحب. شكرا لك 🙏!



ultralytics.data.converter.coco91_to_coco80_class()

يحول معرفات فئة COCO ذات 91 فهرسا إلى معرفات فئة COCO ذات 80 فهرسا.

ارجاع:

نوع وصف
list

قائمة ب 91 معرف فئة حيث يمثل الفهرس معرف فئة 80 فهرسا والقيمة هي معرف فئة الفهرس 91 المقابل.

شفرة المصدر في ultralytics/data/converter.py
14 15 16 17 18  19 20 21 22 23 24 25 26 27  28 29 30 31 32 33 34 35 36 37 38           39 40   41   42 43 44 45  46   47         48  49 50 51 52 53 54 55  56 57 58 59 60 61 62 63 64 65 66 67 68  69 70   71 72 73 74    75 76  77   78          79   80 81 82 83 84 85 86 87  88 89 90 91 92 93 94 95 96 97 98         99  100 101  102 103 104 105106  107 108109110 111 112 113 114
def coco91_to_coco80_class():
    """
    Converts 91-index COCO class IDs to 80-index COCO class IDs.

    Returns:
        (list): A list of 91 class IDs where the index represents the 80-index class ID and the value is the
            corresponding 91-index class ID.
    """
    return [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        None,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        None,
        24,
        25,
        None,
        None,
        26,
        27,
        28,
        29,
        30,
        31,
        32,
        33,
        34,
        35,
        36,
        37,
        38,
        39,
        None,
        40,
        41,
        42,
        43,
        44,
        45,
        46,
        47,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        57,
        58,
        59,
        None,
        60,
        None,
        None,
        61,
        None,
        62,
        63,
        64,
        65,
        66,
        67,
        68,
        69,
        70,
        71,
        72,
        None,
        73,
        74,
        75,
        76,
        77,
        78,
        79,
        None,
    ]



ultralytics.data.converter.coco80_to_coco91_class()

Converts 80-index (val2014) to 91-index (paper).
For details see https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/.

Example:
    ```python
    import numpy as np

    a = np.loadtxt('data/coco.names', dtype='str', delimiter='

') b = np.loadtxt ('data / coco_paper.names' ، dtype = 'str' ، المحدد =' ') x1 = [قائمة (أ [i] == ب) .index (صحيح) + 1 ل i في النطاق (80)] # darknet إلى coco x2 = [list(b[i] == a).index(True) إن وجد(b[i] == a) آخر لا شيء ل i في النطاق(91)] # كوكو إلى الشبكة المظلمة '''

شفرة المصدر في ultralytics/data/converter.py
117 118 119 120 121 122 123 124 125 126 127 128129 130 131 132 133 134 135 136 137 138139 140141142 143 144 145 146 147 148149 150 151 152 153 154 155 156 157 158159 160 161 162 163 164 165 166 167168169 170 171 172 173 174 175 176 177178 179 180 181 182 183 184 185 186 187 188189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205206207208 209 210 211 212213
def coco80_to_coco91_class():
    """
    Converts 80-index (val2014) to 91-index (paper).
    For details see https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/.

    Example:
        ```python
        import numpy as np

        a = np.loadtxt('data/coco.names', dtype='str', delimiter='\n')
        b = np.loadtxt('data/coco_paper.names', dtype='str', delimiter='\n')
        x1 = [list(a[i] == b).index(True) + 1 for i in range(80)]  # darknet to coco
        x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)]  # coco to darknet
        ```
    """
    return [
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        24,
        25,
        27,
        28,
        31,
        32,
        33,
        34,
        35,
        36,
        37,
        38,
        39,
        40,
        41,
        42,
        43,
        44,
        46,
        47,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        57,
        58,
        59,
        60,
        61,
        62,
        63,
        64,
        65,
        67,
        70,
        72,
        73,
        74,
        75,
        76,
        77,
        78,
        79,
        80,
        81,
        82,
        84,
        85,
        86,
        87,
        88,
        89,
        90,
    ]



ultralytics.data.converter.convert_coco(labels_dir='../coco/annotations/', save_dir='coco_converted/', use_segments=False, use_keypoints=False, cls91to80=True)

تحويل التعليقات التوضيحية لمجموعة بيانات COCO إلى ملف YOLO تنسيق التعليق التوضيحي المناسب للتدريب YOLO نماذج.

البارامترات:

اسم نوع وصف افتراضي
labels_dir str

المسار إلى الدليل الذي يحتوي على ملفات التعليقات التوضيحية لمجموعة بيانات COCO.

'../coco/annotations/'
save_dir str

المسار إلى الدليل لحفظ النتائج فيه.

'coco_converted/'
use_segments bool

ما إذا كان سيتم تضمين أقنعة التجزئة في الإخراج.

False
use_keypoints bool

ما إذا كنت تريد تضمين التعليقات التوضيحية للنقاط الرئيسية في الإخراج.

False
cls91to80 bool

ما إذا كنت تريد تعيين 91 معرفا لفئة COCO إلى 80 معرفا لفئة COCO المقابلة.

True
مثل
from ultralytics.data.converter import convert_coco

convert_coco('../datasets/coco/annotations/', use_segments=True, use_keypoints=False, cls91to80=True)
الناتج

يولد ملفات الإخراج في دليل الإخراج المحدد.

شفرة المصدر في ultralytics/data/converter.py
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238239 240241242 243244 245 246 247 248249 250 251 252 253 254 255 256 257 258259 260 261 262 263 264 265 266 267268269 270271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304305306 307 308 309 310 311 312 313 314 315316317
def convert_coco(
    labels_dir="../coco/annotations/",
    save_dir="coco_converted/",
    use_segments=False,
    use_keypoints=False,
    cls91to80=True,
):
    """
    Converts COCO dataset annotations to a YOLO annotation format  suitable for training YOLO models.

    Args:
        labels_dir (str, optional): Path to directory containing COCO dataset annotation files.
        save_dir (str, optional): Path to directory to save results to.
        use_segments (bool, optional): Whether to include segmentation masks in the output.
        use_keypoints (bool, optional): Whether to include keypoint annotations in the output.
        cls91to80 (bool, optional): Whether to map 91 COCO class IDs to the corresponding 80 COCO class IDs.

    Example:
        ```python
        from ultralytics.data.converter import convert_coco

        convert_coco('../datasets/coco/annotations/', use_segments=True, use_keypoints=False, cls91to80=True)
        ```

    Output:
        Generates output files in the specified output directory.
    """

    # Create dataset directory
    save_dir = increment_path(save_dir)  # increment if save directory already exists
    for p in save_dir / "labels", save_dir / "images":
        p.mkdir(parents=True, exist_ok=True)  # make dir

    # Convert classes
    coco80 = coco91_to_coco80_class()

    # Import json
    for json_file in sorted(Path(labels_dir).resolve().glob("*.json")):
        fn = Path(save_dir) / "labels" / json_file.stem.replace("instances_", "")  # folder name
        fn.mkdir(parents=True, exist_ok=True)
        with open(json_file) as f:
            data = json.load(f)

        # Create image dict
        images = {f'{x["id"]:d}': x for x in data["images"]}
        # Create image-annotations dict
        imgToAnns = defaultdict(list)
        for ann in data["annotations"]:
            imgToAnns[ann["image_id"]].append(ann)

        # Write labels file
        for img_id, anns in TQDM(imgToAnns.items(), desc=f"Annotations {json_file}"):
            img = images[f"{img_id:d}"]
            h, w, f = img["height"], img["width"], img["file_name"]

            bboxes = []
            segments = []
            keypoints = []
            for ann in anns:
                if ann["iscrowd"]:
                    continue
                # The COCO box format is [top left x, top left y, width, height]
                box = np.array(ann["bbox"], dtype=np.float64)
                box[:2] += box[2:] / 2  # xy top-left corner to center
                box[[0, 2]] /= w  # normalize x
                box[[1, 3]] /= h  # normalize y
                if box[2] <= 0 or box[3] <= 0:  # if w <= 0 and h <= 0
                    continue

                cls = coco80[ann["category_id"] - 1] if cls91to80 else ann["category_id"] - 1  # class
                box = [cls] + box.tolist()
                if box not in bboxes:
                    bboxes.append(box)
                    if use_segments and ann.get("segmentation") is not None:
                        if len(ann["segmentation"]) == 0:
                            segments.append([])
                            continue
                        elif len(ann["segmentation"]) > 1:
                            s = merge_multi_segment(ann["segmentation"])
                            s = (np.concatenate(s, axis=0) / np.array([w, h])).reshape(-1).tolist()
                        else:
                            s = [j for i in ann["segmentation"] for j in i]  # all segments concatenated
                            s = (np.array(s).reshape(-1, 2) / np.array([w, h])).reshape(-1).tolist()
                        s = [cls] + s
                        segments.append(s)
                    if use_keypoints and ann.get("keypoints") is not None:
                        keypoints.append(
                            box + (np.array(ann["keypoints"]).reshape(-1, 3) / np.array([w, h, 1])).reshape(-1).tolist()
                        )

            # Write
            with open((fn / f).with_suffix(".txt"), "a") as file:
                for i in range(len(bboxes)):
                    if use_keypoints:
                        line = (*(keypoints[i]),)  # cls, box, keypoints
                    else:
                        line = (
                            *(segments[i] if use_segments and len(segments[i]) > 0 else bboxes[i]),
                        )  # cls, box or segments
                    file.write(("%g " * len(line)).rstrip() % line + "\n")

    LOGGER.info(f"COCO data converted successfully.\nResults saved to {save_dir.resolve()}")



ultralytics.data.converter.convert_dota_to_yolo_obb(dota_root_path)

تحويل التعليقات التوضيحية لمجموعة بيانات DOTA إلى YOLO تنسيق OBB (المربع المحيط الموجه).

تعالج الوظيفة الصور في مجلدات "القطار" و "val" لمجموعة بيانات DOTA. لكل صورة ، يقرأ التسمية المقترنة من دليل التسميات الأصلية وتكتب تسميات جديدة في YOLO تنسيق OBB إلى دليل جديد.

البارامترات:

اسم نوع وصف افتراضي
dota_root_path str

مسار الدليل الجذر لمجموعة بيانات DOTA.

مطلوب
مثل
from ultralytics.data.converter import convert_dota_to_yolo_obb

convert_dota_to_yolo_obb('path/to/DOTA')
تلاحظ

بنية الدليل المفترضة لمجموعة بيانات DOTA:

- DOTA
    ├─ images
    │   ├─ train
    │   └─ val
    └─ labels
        ├─ train_original
        └─ val_original

بعد التنفيذ ، ستقوم الوظيفة بتنظيم الملصقات إلى:

- DOTA
    └─ labels
        ├─ train
        └─ val
شفرة المصدر في ultralytics/data/converter.py
320 321 322 323 324 325 326 327 328329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347348349 350351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378379 380381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398399 400 401 402403 404 405 406 407408409410411412 413
def convert_dota_to_yolo_obb(dota_root_path: str):
    """
    Converts DOTA dataset annotations to YOLO OBB (Oriented Bounding Box) format.

    The function processes images in the 'train' and 'val' folders of the DOTA dataset. For each image, it reads the
    associated label from the original labels directory and writes new labels in YOLO OBB format to a new directory.

    Args:
        dota_root_path (str): The root directory path of the DOTA dataset.

    Example:
        ```python
        from ultralytics.data.converter import convert_dota_to_yolo_obb

        convert_dota_to_yolo_obb('path/to/DOTA')
        ```

    Notes:
        The directory structure assumed for the DOTA dataset:

            - DOTA
                ├─ images
                │   ├─ train
                │   └─ val
                └─ labels
                    ├─ train_original
                    └─ val_original

        After execution, the function will organize the labels into:

            - DOTA
                └─ labels
                    ├─ train
                    └─ val
    """
    dota_root_path = Path(dota_root_path)

    # Class names to indices mapping
    class_mapping = {
        "plane": 0,
        "ship": 1,
        "storage-tank": 2,
        "baseball-diamond": 3,
        "tennis-court": 4,
        "basketball-court": 5,
        "ground-track-field": 6,
        "harbor": 7,
        "bridge": 8,
        "large-vehicle": 9,
        "small-vehicle": 10,
        "helicopter": 11,
        "roundabout": 12,
        "soccer-ball-field": 13,
        "swimming-pool": 14,
        "container-crane": 15,
        "airport": 16,
        "helipad": 17,
    }

    def convert_label(image_name, image_width, image_height, orig_label_dir, save_dir):
        """Converts a single image's DOTA annotation to YOLO OBB format and saves it to a specified directory."""
        orig_label_path = orig_label_dir / f"{image_name}.txt"
        save_path = save_dir / f"{image_name}.txt"

        with orig_label_path.open("r") as f, save_path.open("w") as g:
            lines = f.readlines()
            for line in lines:
                parts = line.strip().split()
                if len(parts) < 9:
                    continue
                class_name = parts[8]
                class_idx = class_mapping[class_name]
                coords = [float(p) for p in parts[:8]]
                normalized_coords = [
                    coords[i] / image_width if i % 2 == 0 else coords[i] / image_height for i in range(8)
                ]
                formatted_coords = ["{:.6g}".format(coord) for coord in normalized_coords]
                g.write(f"{class_idx} {' '.join(formatted_coords)}\n")

    for phase in ["train", "val"]:
        image_dir = dota_root_path / "images" / phase
        orig_label_dir = dota_root_path / "labels" / f"{phase}_original"
        save_dir = dota_root_path / "labels" / phase

        save_dir.mkdir(parents=True, exist_ok=True)

        image_paths = list(image_dir.iterdir())
        for image_path in TQDM(image_paths, desc=f"Processing {phase} images"):
            if image_path.suffix != ".png":
                continue
            image_name_without_ext = image_path.stem
            img = cv2.imread(str(image_path))
            h, w = img.shape[:2]
            convert_label(image_name_without_ext, w, h, orig_label_dir, save_dir)



ultralytics.data.converter.min_index(arr1, arr2)

ابحث عن زوج من الفهارس بأقصر مسافة بين صفيفين من نقاط 2D.

البارامترات:

اسم نوع وصف افتراضي
arr1 ndarray

مصفوفة NumPy من الشكل (N ، 2) تمثل نقاط N 2D.

مطلوب
arr2 ndarray

مجموعة NumPy من الشكل (M ، 2) تمثل نقاط M 2D.

مطلوب

ارجاع:

نوع وصف
tuple

مجموعة تحتوي على فهارس النقاط ذات أقصر مسافة في arr1 و arr2 على التوالي.

شفرة المصدر في ultralytics/data/converter.py
416 417 418 419 420 421 422 423 424 425 426 427 428
def min_index(arr1, arr2):
    """
    Find a pair of indexes with the shortest distance between two arrays of 2D points.

    Args:
        arr1 (np.ndarray): A NumPy array of shape (N, 2) representing N 2D points.
        arr2 (np.ndarray): A NumPy array of shape (M, 2) representing M 2D points.

    Returns:
        (tuple): A tuple containing the indexes of the points with the shortest distance in arr1 and arr2 respectively.
    """
    dis = ((arr1[:, None, :] - arr2[None, :, :]) ** 2).sum(-1)
    return np.unravel_index(np.argmin(dis, axis=None), dis.shape)



ultralytics.data.converter.merge_multi_segment(segments)

دمج مقاطع متعددة في قائمة واحدة عن طريق ربط الإحداثيات بالحد الأدنى للمسافة بين كل مقطع. تربط هذه الوظيفة هذه الإحداثيات بخط رفيع لدمج جميع الأجزاء في مقطع واحد.

البارامترات:

اسم نوع وصف افتراضي
segments List[List]

التقسيمات الأصلية في ملف JSON الخاص ب COCO. كل عنصر عبارة عن قائمة من الإحداثيات ، مثل [تجزئة 1 ، تجزئة 2,...].

مطلوب

ارجاع:

اسم نوع وصف
s List[ndarray]

قائمة بالمقاطع المتصلة الممثلة كمصفوفات NumPy.

شفرة المصدر في ultralytics/data/converter.py
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448449 450 451 452 453 454455 456457458 459460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475476 477 478
def merge_multi_segment(segments):
    """
    Merge multiple segments into one list by connecting the coordinates with the minimum distance between each segment.
    This function connects these coordinates with a thin line to merge all segments into one.

    Args:
        segments (List[List]): Original segmentations in COCO's JSON file.
                               Each element is a list of coordinates, like [segmentation1, segmentation2,...].

    Returns:
        s (List[np.ndarray]): A list of connected segments represented as NumPy arrays.
    """
    s = []
    segments = [np.array(i).reshape(-1, 2) for i in segments]
    idx_list = [[] for _ in range(len(segments))]

    # Record the indexes with min distance between each segment
    for i in range(1, len(segments)):
        idx1, idx2 = min_index(segments[i - 1], segments[i])
        idx_list[i - 1].append(idx1)
        idx_list[i].append(idx2)

    # Use two round to connect all the segments
    for k in range(2):
        # Forward connection
        if k == 0:
            for i, idx in enumerate(idx_list):
                # Middle segments have two indexes, reverse the index of middle segments
                if len(idx) == 2 and idx[0] > idx[1]:
                    idx = idx[::-1]
                    segments[i] = segments[i][::-1, :]

                segments[i] = np.roll(segments[i], -idx[0], axis=0)
                segments[i] = np.concatenate([segments[i], segments[i][:1]])
                # Deal with the first segment and the last one
                if i in [0, len(idx_list) - 1]:
                    s.append(segments[i])
                else:
                    idx = [0, idx[1] - idx[0]]
                    s.append(segments[i][idx[0] : idx[1] + 1])

        else:
            for i in range(len(idx_list) - 1, -1, -1):
                if i not in [0, len(idx_list) - 1]:
                    idx = idx_list[i]
                    nidx = abs(idx[1] - idx[0])
                    s.append(segments[i][nidx:])
    return s



ultralytics.data.converter.yolo_bbox2segment(im_dir, save_dir=None, sam_model='sam_b.pt')

تحويل مجموعة بيانات الكشف عن الكائنات الموجودة (المربعات المحيطة) إلى مجموعة بيانات تجزئة أو مربع محيط موجه (OBB) في YOLO تنسيق. يولد بيانات التجزئة باستخدام SAM التعليق التلقائي حسب الحاجة.

البارامترات:

اسم نوع وصف افتراضي
im_dir str | Path

المسار إلى دليل الصور للتحويل.

مطلوب
save_dir str | Path

مسار لحفظ التسميات التي تم إنشاؤها ، سيتم حفظ التسميات إلى labels-segment في نفس مستوى الدليل من im_dir إذا كان save_dir لا شيء. الافتراضي: لا يوجد.

None
sam_model str

نموذج تجزئة لاستخدامه لبيانات التجزئة الوسيطة ؛ اختياري.

'sam_b.pt'
تلاحظ

بنية دليل الإدخال المفترضة لمجموعة البيانات:

- im_dir
    ├─ 001.jpg
    ├─ ..
    └─ NNN.jpg
- labels
    ├─ 001.txt
    ├─ ..
    └─ NNN.txt
شفرة المصدر في ultralytics/data/converter.py
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497498 499 500 501 502 503  504 505 506 507 508509 510 511512513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537538539 540541 542
def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt"):
    """
    Converts existing object detection dataset (bounding boxes) to segmentation dataset or oriented bounding box (OBB)
    in YOLO format. Generates segmentation data using SAM auto-annotator as needed.

    Args:
        im_dir (str | Path): Path to image directory to convert.
        save_dir (str | Path): Path to save the generated labels, labels will be saved
            into `labels-segment` in the same directory level of `im_dir` if save_dir is None. Default: None.
        sam_model (str): Segmentation model to use for intermediate segmentation data; optional.

    Notes:
        The input directory structure assumed for dataset:

            - im_dir
                ├─ 001.jpg
                ├─ ..
                └─ NNN.jpg
            - labels
                ├─ 001.txt
                ├─ ..
                └─ NNN.txt
    """
    from ultralytics.data import YOLODataset
    from ultralytics.utils.ops import xywh2xyxy
    from ultralytics.utils import LOGGER
    from ultralytics import SAM
    from tqdm import tqdm

    # NOTE: add placeholder to pass class index check
    dataset = YOLODataset(im_dir, data=dict(names=list(range(1000))))
    if len(dataset.labels[0]["segments"]) > 0:  # if it's segment data
        LOGGER.info("Segmentation labels detected, no need to generate new ones!")
        return

    LOGGER.info("Detection labels detected, generating segment labels by SAM model!")
    sam_model = SAM(sam_model)
    for l in tqdm(dataset.labels, total=len(dataset.labels), desc="Generating segment labels"):
        h, w = l["shape"]
        boxes = l["bboxes"]
        if len(boxes) == 0:  # skip empty labels
            continue
        boxes[:, [0, 2]] *= w
        boxes[:, [1, 3]] *= h
        im = cv2.imread(l["im_file"])
        sam_results = sam_model(im, bboxes=xywh2xyxy(boxes), verbose=False, save=False)
        l["segments"] = sam_results[0].masks.xyn

    save_dir = Path(save_dir) if save_dir else Path(im_dir).parent / "labels-segment"
    save_dir.mkdir(parents=True, exist_ok=True)
    for l in dataset.labels:
        texts = []
        lb_name = Path(l["im_file"]).with_suffix(".txt").name
        txt_file = save_dir / lb_name
        cls = l["cls"]
        for i, s in enumerate(l["segments"]):
            line = (int(cls[i]), *s.reshape(-1))
            texts.append(("%g " * len(line)).rstrip() % line)
        if texts:
            with open(txt_file, "a") as f:
                f.writelines(text + "\n" for text in texts)
    LOGGER.info(f"Generated segment labels saved in {save_dir}")





تم إنشاؤه في 2023-11-12, اخر تحديث 2024-01-23
المؤلفون: جلين جوشر (4) ، يضحك س (1)