|
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) |
|
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() |
|
scaled_flat_seg_data = [category_id] + list(chain(*scaled_seg_data)) |
|
seg_array_with_cat.append(scaled_flat_seg_data) |
|
|
|
return seg_array_with_cat |
|
|