garmentiq.landmark.detect

Detecting the predefined landmarks of a garment.

  1"""Detecting the predefined landmarks of a garment."""
  2import json
  3import os
  4from typing import Type, Union
  5import torch
  6import requests
  7import numpy as np
  8from garmentiq.utils import validate_garment_class_dict
  9from garmentiq.utils.device import resolve_device, empty_cache
 10from garmentiq.landmark.utils import (
 11    find_instruction_landmark_index,
 12    fill_instruction_landmark_coordinate,
 13)
 14from garmentiq.landmark.detection.utils import (
 15    input_image_transform,
 16    get_final_preds,
 17    transform_preds,
 18)
 19
 20
 21def detect(
 22    class_name: str,
 23    class_dict: dict,
 24    image_path: Union[str, np.ndarray],
 25    model: Type[torch.nn.Module],
 26    scale_std: float = 200.0,
 27    resize_dim: list[int, int] = [288, 384],
 28    normalize_mean: list[float, float, float] = [0.485, 0.456, 0.406],
 29    normalize_std: list[float, float, float] = [0.229, 0.224, 0.225],
 30    device: Union[str, torch.device] = "cpu",
 31):
 32    """
 33    Detects predefined landmarks on a garment image using a specified model and class instructions.
 34
 35    This function validates the input class dictionary and class name, loads the appropriate
 36    instruction schema (from local file or URL), preprocesses the image, runs it through
 37    the landmark detection model, and then transforms the detected heatmap predictions
 38    into image coordinates. The detected coordinates are then filled into the instruction data.
 39
 40    The model and the preprocessed input tensor are placed on `device`, so the same value should
 41    be passed here as was used when loading the model. Any accelerator memory cached during
 42    inference is released before returning.
 43
 44    Args:
 45        class_name (str): The name of the garment class (e.g., "vest dress", "trousers").
 46        class_dict (dict): A dictionary mapping class names to their properties, including
 47                           `num_predefined_points`, `index_range`, and `instruction` file path.
 48        image_path (Union[str, np.ndarray]): The path to the image file or a NumPy array of the image.
 49        model (Type[torch.nn.Module]): The loaded PyTorch landmark detection model.
 50        scale_std (float, optional): Standard scale for image transformation during preprocessing. Defaults to 200.0.
 51        resize_dim (list[int, int], optional): Target dimensions [width, height] for the transformed image.
 52                                               Defaults to [288, 384].
 53        normalize_mean (list[float, float, float], optional): Mean values for image normalization (RGB channels).
 54                                                              Defaults to [0.485, 0.456, 0.406].
 55        normalize_std (list[float, float, float], optional): Standard deviation values for image normalization (RGB channels).
 56                                                             Defaults to [0.229, 0.224, 0.225].
 57        device (Union[str, torch.device], optional): The device to run inference on, e.g. `"cpu"`,
 58                                                     `"cuda"`, `"cuda:0"`, or `"mps"`. Hardware
 59                                                     acceleration is opt-in; pass it explicitly to
 60                                                     use a GPU or Apple Silicon. Defaults to `"cpu"`.
 61
 62    Raises:
 63        ValueError: If `class_dict` is invalid, `class_name` is not found in `class_dict`, or the
 64                    requested `device` is invalid or unavailable on this machine.
 65        FileNotFoundError: If the instruction file is not found.
 66        ValueError: If loading instruction JSON from URL fails or `class_name` is not found in instruction file.
 67
 68    Returns:
 69        tuple:
 70            - preds_all (np.array): All predicted landmark coordinates (including non-predefined).
 71            - maxvals (np.array): Confidence scores for the predefined landmark predictions.
 72            - instruction_data (dict): The instruction dictionary updated with detected landmark coordinates and confidences.
 73    """
 74    device = resolve_device(device)
 75
 76    if not validate_garment_class_dict(class_dict):
 77        raise ValueError(
 78            "Provided class_dict is not in the expected garment_classes format."
 79        )
 80
 81    if class_name not in class_dict:
 82        raise ValueError(
 83            f"Invalid class '{class_name}'. Must be one of: {list(class_dict.keys())}"
 84        )
 85
 86    class_element = class_dict[class_name]
 87
 88    instruction_path = class_element["instruction"]
 89
 90    if instruction_path.startswith("http://") or instruction_path.startswith(
 91        "https://"
 92    ):
 93        try:
 94            response = requests.get(instruction_path)
 95            response.raise_for_status()
 96            instruction_data = response.json()
 97        except Exception as e:
 98            raise ValueError(
 99                f"Failed to load instruction JSON from URL: {instruction_path}\nError: {e}"
100            )
101    else:
102        if not os.path.exists(instruction_path):
103            raise FileNotFoundError(f"Instruction file not found: {instruction_path}")
104        with open(instruction_path, "r") as f:
105            instruction_data = json.load(f)
106
107    if class_name not in instruction_data:
108        raise ValueError(f"Class '{class_name}' not found in instruction file.")
109
110    (input_tensor, image_np, center, scale,) = input_image_transform(
111        image_path, scale_std, resize_dim, normalize_mean, normalize_std
112    )
113
114    model = model.to(device)
115    input_tensor = input_tensor.to(device)
116
117    with torch.no_grad():
118        np_output_heatmap = model(input_tensor).detach().cpu().numpy()
119
120    empty_cache(device)
121
122    preds_heatmap, maxvals = get_final_preds(
123        np_output_heatmap[
124            :, class_element["index_range"][0] : class_element["index_range"][1], :, :
125        ]
126    )
127
128    predefined_index = find_instruction_landmark_index(
129        instruction_data[class_name]["landmarks"], predefined=True
130    )
131    preds_all = np.stack([transform_preds(p, center, scale) for p in preds_heatmap])
132    preds = preds_all[:, predefined_index, :]
133
134    instruction_data[class_name]["landmarks"] = fill_instruction_landmark_coordinate(
135        instruction_landmarks=instruction_data[class_name]["landmarks"],
136        index=predefined_index,
137        fill_in_value=preds,
138    )
139
140    for idx in predefined_index:
141        instruction_data[class_name]["landmarks"][str(idx + 1)]["conf"] = float(
142            maxvals[0, idx, 0]
143        )
144
145    return preds_all, maxvals, instruction_data
def detect( class_name: str, class_dict: dict, image_path: Union[str, numpy.ndarray], model: Type[torch.nn.modules.module.Module], scale_std: float = 200.0, resize_dim: list[int, int] = [288, 384], normalize_mean: list[float, float, float] = [0.485, 0.456, 0.406], normalize_std: list[float, float, float] = [0.229, 0.224, 0.225], device: Union[str, torch.device] = 'cpu'):
 22def detect(
 23    class_name: str,
 24    class_dict: dict,
 25    image_path: Union[str, np.ndarray],
 26    model: Type[torch.nn.Module],
 27    scale_std: float = 200.0,
 28    resize_dim: list[int, int] = [288, 384],
 29    normalize_mean: list[float, float, float] = [0.485, 0.456, 0.406],
 30    normalize_std: list[float, float, float] = [0.229, 0.224, 0.225],
 31    device: Union[str, torch.device] = "cpu",
 32):
 33    """
 34    Detects predefined landmarks on a garment image using a specified model and class instructions.
 35
 36    This function validates the input class dictionary and class name, loads the appropriate
 37    instruction schema (from local file or URL), preprocesses the image, runs it through
 38    the landmark detection model, and then transforms the detected heatmap predictions
 39    into image coordinates. The detected coordinates are then filled into the instruction data.
 40
 41    The model and the preprocessed input tensor are placed on `device`, so the same value should
 42    be passed here as was used when loading the model. Any accelerator memory cached during
 43    inference is released before returning.
 44
 45    Args:
 46        class_name (str): The name of the garment class (e.g., "vest dress", "trousers").
 47        class_dict (dict): A dictionary mapping class names to their properties, including
 48                           `num_predefined_points`, `index_range`, and `instruction` file path.
 49        image_path (Union[str, np.ndarray]): The path to the image file or a NumPy array of the image.
 50        model (Type[torch.nn.Module]): The loaded PyTorch landmark detection model.
 51        scale_std (float, optional): Standard scale for image transformation during preprocessing. Defaults to 200.0.
 52        resize_dim (list[int, int], optional): Target dimensions [width, height] for the transformed image.
 53                                               Defaults to [288, 384].
 54        normalize_mean (list[float, float, float], optional): Mean values for image normalization (RGB channels).
 55                                                              Defaults to [0.485, 0.456, 0.406].
 56        normalize_std (list[float, float, float], optional): Standard deviation values for image normalization (RGB channels).
 57                                                             Defaults to [0.229, 0.224, 0.225].
 58        device (Union[str, torch.device], optional): The device to run inference on, e.g. `"cpu"`,
 59                                                     `"cuda"`, `"cuda:0"`, or `"mps"`. Hardware
 60                                                     acceleration is opt-in; pass it explicitly to
 61                                                     use a GPU or Apple Silicon. Defaults to `"cpu"`.
 62
 63    Raises:
 64        ValueError: If `class_dict` is invalid, `class_name` is not found in `class_dict`, or the
 65                    requested `device` is invalid or unavailable on this machine.
 66        FileNotFoundError: If the instruction file is not found.
 67        ValueError: If loading instruction JSON from URL fails or `class_name` is not found in instruction file.
 68
 69    Returns:
 70        tuple:
 71            - preds_all (np.array): All predicted landmark coordinates (including non-predefined).
 72            - maxvals (np.array): Confidence scores for the predefined landmark predictions.
 73            - instruction_data (dict): The instruction dictionary updated with detected landmark coordinates and confidences.
 74    """
 75    device = resolve_device(device)
 76
 77    if not validate_garment_class_dict(class_dict):
 78        raise ValueError(
 79            "Provided class_dict is not in the expected garment_classes format."
 80        )
 81
 82    if class_name not in class_dict:
 83        raise ValueError(
 84            f"Invalid class '{class_name}'. Must be one of: {list(class_dict.keys())}"
 85        )
 86
 87    class_element = class_dict[class_name]
 88
 89    instruction_path = class_element["instruction"]
 90
 91    if instruction_path.startswith("http://") or instruction_path.startswith(
 92        "https://"
 93    ):
 94        try:
 95            response = requests.get(instruction_path)
 96            response.raise_for_status()
 97            instruction_data = response.json()
 98        except Exception as e:
 99            raise ValueError(
100                f"Failed to load instruction JSON from URL: {instruction_path}\nError: {e}"
101            )
102    else:
103        if not os.path.exists(instruction_path):
104            raise FileNotFoundError(f"Instruction file not found: {instruction_path}")
105        with open(instruction_path, "r") as f:
106            instruction_data = json.load(f)
107
108    if class_name not in instruction_data:
109        raise ValueError(f"Class '{class_name}' not found in instruction file.")
110
111    (input_tensor, image_np, center, scale,) = input_image_transform(
112        image_path, scale_std, resize_dim, normalize_mean, normalize_std
113    )
114
115    model = model.to(device)
116    input_tensor = input_tensor.to(device)
117
118    with torch.no_grad():
119        np_output_heatmap = model(input_tensor).detach().cpu().numpy()
120
121    empty_cache(device)
122
123    preds_heatmap, maxvals = get_final_preds(
124        np_output_heatmap[
125            :, class_element["index_range"][0] : class_element["index_range"][1], :, :
126        ]
127    )
128
129    predefined_index = find_instruction_landmark_index(
130        instruction_data[class_name]["landmarks"], predefined=True
131    )
132    preds_all = np.stack([transform_preds(p, center, scale) for p in preds_heatmap])
133    preds = preds_all[:, predefined_index, :]
134
135    instruction_data[class_name]["landmarks"] = fill_instruction_landmark_coordinate(
136        instruction_landmarks=instruction_data[class_name]["landmarks"],
137        index=predefined_index,
138        fill_in_value=preds,
139    )
140
141    for idx in predefined_index:
142        instruction_data[class_name]["landmarks"][str(idx + 1)]["conf"] = float(
143            maxvals[0, idx, 0]
144        )
145
146    return preds_all, maxvals, instruction_data

Detects predefined landmarks on a garment image using a specified model and class instructions.

This function validates the input class dictionary and class name, loads the appropriate instruction schema (from local file or URL), preprocesses the image, runs it through the landmark detection model, and then transforms the detected heatmap predictions into image coordinates. The detected coordinates are then filled into the instruction data.

The model and the preprocessed input tensor are placed on device, so the same value should be passed here as was used when loading the model. Any accelerator memory cached during inference is released before returning.

Arguments:
  • class_name (str): The name of the garment class (e.g., "vest dress", "trousers").
  • class_dict (dict): A dictionary mapping class names to their properties, including num_predefined_points, index_range, and instruction file path.
  • image_path (Union[str, np.ndarray]): The path to the image file or a NumPy array of the image.
  • model (Type[torch.nn.Module]): The loaded PyTorch landmark detection model.
  • scale_std (float, optional): Standard scale for image transformation during preprocessing. Defaults to 200.0.
  • resize_dim (list[int, int], optional): Target dimensions [width, height] for the transformed image. Defaults to [288, 384].
  • normalize_mean (list[float, float, float], optional): Mean values for image normalization (RGB channels). Defaults to [0.485, 0.456, 0.406].
  • normalize_std (list[float, float, float], optional): Standard deviation values for image normalization (RGB channels). Defaults to [0.229, 0.224, 0.225].
  • device (Union[str, torch.device], optional): The device to run inference on, e.g. "cpu", "cuda", "cuda:0", or "mps". Hardware acceleration is opt-in; pass it explicitly to use a GPU or Apple Silicon. Defaults to "cpu".
Raises:
  • ValueError: If class_dict is invalid, class_name is not found in class_dict, or the requested device is invalid or unavailable on this machine.
  • FileNotFoundError: If the instruction file is not found.
  • ValueError: If loading instruction JSON from URL fails or class_name is not found in instruction file.
Returns:

tuple: - preds_all (np.array): All predicted landmark coordinates (including non-predefined). - maxvals (np.array): Confidence scores for the predefined landmark predictions. - instruction_data (dict): The instruction dictionary updated with detected landmark coordinates and confidences.