import json import os from itertools import chain from os import path from typing import Any, Dict, List, Optional, Tuple import numpy as np from loguru import logger from yolo.tools.data_conversion import discretize_categories def locate_label_paths(dataset_path: str, phase_name: str): """ Find the path to label files for a specified dataset and phase(e.g. training). Args: dataset_path (str): The path to the root directory of the dataset. phase_name (str): The name of the phase for which labels are being searched (e.g., "train", "val", "test"). Returns: Tuple[str, str]: A tuple containing the path to the labels file and the file format ("json" or "txt"). """ json_labels_path = path.join(dataset_path, "annotations", f"instances_{phase_name}.json") txt_labels_path = path.join(dataset_path, "labels", phase_name) if path.isfile(json_labels_path): return json_labels_path, "json" elif path.isdir(txt_labels_path): txt_files = [f for f in os.listdir(txt_labels_path) if f.endswith(".txt")] if txt_files: return txt_labels_path, "txt" logger.warning("No labels found in the specified dataset path and phase name.") return [], None def create_image_metadata(labels_path: str) -> Tuple[Dict[str, List], Dict[str, Dict]]: """ Create a dictionary containing image information and annotations indexed by image ID. Args: labels_path (str): The path to the annotation json file. Returns: - annotations_index: A dictionary where keys are image IDs and values are lists of annotations. - image_info_dict: A dictionary where keys are image file names without extension and values are image information dictionaries. """ with open(labels_path, "r") as file: labels_data = json.load(file) id_to_idx = discretize_categories(labels_data.get("categories", [])) if "categories" in labels_data else None annotations_index = organize_annotations_by_image(labels_data, id_to_idx) # check lookup is a good name? image_info_dict = {path.splitext(img["file_name"])[0]: img for img in labels_data["images"]} return annotations_index, image_info_dict def organize_annotations_by_image(data: Dict[str, Any], id_to_idx: Optional[Dict[int, int]]): """ Use image index to lookup every annotations Args: data (Dict[str, Any]): A dictionary containing annotation data. Returns: Dict[int, List[Dict[str, Any]]]: A dictionary where keys are image IDs and values are lists of annotations. Annotations with "iscrowd" set to True are excluded from the index. """ annotation_lookup = {} for anno in data["annotations"]: if anno["iscrowd"]: continue image_id = anno["image_id"] if id_to_idx: anno["category_id"] = id_to_idx[anno["category_id"]] if image_id not in annotation_lookup: annotation_lookup[image_id] = [] annotation_lookup[image_id].append(anno) return annotation_lookup def scale_segmentation( annotations: List[Dict[str, Any]], image_dimensions: Dict[str, int] ) -> Optional[List[List[float]]]: """ Scale the segmentation data based on image dimensions and return a list of scaled segmentation data. Args: annotations (List[Dict[str, Any]]): A list of annotation dictionaries. image_dimensions (Dict[str, int]): A dictionary containing image dimensions (height and width). Returns: Optional[List[List[float]]]: A list of scaled segmentation data, where each sublist contains category_id followed by scaled (x, y) coordinates. """ if annotations is None: return None seg_array_with_cat = [] h, w = image_dimensions["height"], image_dimensions["width"] for anno in annotations: category_id = anno["category_id"] seg_list = [item for sublist in anno["segmentation"] for item in sublist] scaled_seg_data = ( np.array(seg_list).reshape(-1, 2) / [w, h] ).tolist() # make the list group in x, y pairs and scaled with image width, height scaled_flat_seg_data = [category_id] + list(chain(*scaled_seg_data)) # flatten the scaled_seg_data list seg_array_with_cat.append(scaled_flat_seg_data) return seg_array_with_cat