garmentiq.grounding
Text-to-region grounding.
Grounding models turn a natural-language phrase into bounding boxes. This is what gives SAM 1 and SAM 2 text-prompted segmentation, since neither contains a text encoder. The boxes are model-agnostic, so this module is deliberately independent of the segmentation package.
The model classes are re-exported from transformers and documented upstream, so they
are not repeated here. GroundingDinoForObjectDetection, GroundingDinoConfig, and
GroundingDinoProcessor can all be imported from this module directly.
1# garmentiq/grounding/__init__.py 2"""Text-to-region grounding. 3 4Grounding models turn a natural-language phrase into bounding boxes. This is what 5gives SAM 1 and SAM 2 text-prompted segmentation, since neither contains a text 6encoder. The boxes are model-agnostic, so this module is deliberately independent 7of the segmentation package. 8 9The model classes are re-exported from `transformers` and documented upstream, so they 10are not repeated here. `GroundingDinoForObjectDetection`, `GroundingDinoConfig`, and 11`GroundingDinoProcessor` can all be imported from this module directly. 12""" 13from .grounding_dino import ( 14 load_grounding_config, 15 load_grounding_processor, 16 load_grounding_model, 17 ground_text_to_boxes, 18) 19 20_LAZY = { 21 "GroundingDinoForObjectDetection", 22 "GroundingDinoConfig", 23 "GroundingDinoProcessor", 24} 25 26 27def __getattr__(name): 28 """Defer transformers imports until a grounding class is actually requested.""" 29 if name in _LAZY: 30 from . import grounding_dino 31 32 return getattr(grounding_dino, name) 33 raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 34 35 36__all__ = [ 37 "load_grounding_config", 38 "load_grounding_processor", 39 "load_grounding_model", 40 "ground_text_to_boxes", 41] 42 43 44def __dir__(): 45 """List the lazily resolved model classes alongside the eager exports.""" 46 return sorted(set(__all__) | _LAZY)
57def load_grounding_config(model_dir: str): 58 """ 59 Reads and loads a local Grounding DINO configuration for offline use. 60 61 Grounding DINO turns a natural-language phrase into bounding boxes, which SAM 1 and 62 SAM 2 accept natively as prompts. Its configuration and tokenizer files are not 63 bundled with GarmentIQ because they belong to a separate upstream model, so download 64 the model once and point `model_dir` at the resulting directory. 65 66 Args: 67 model_dir (str): Path to a local directory containing the Grounding DINO 68 `config.json`, e.g. a download of `IDEA-Research/grounding-dino-tiny`. 69 70 Raises: 71 FileNotFoundError: If `model_dir` is not a directory or does not contain `config.json`. 72 ImportError: If the installed transformers release does not provide Grounding DINO. 73 74 Returns: 75 GroundingDinoConfig: The loaded configuration object, ready to be passed into 76 `GroundingDinoForObjectDetection`. 77 """ 78 if not os.path.isdir(model_dir): 79 raise FileNotFoundError( 80 f"Provided model_dir '{model_dir}' is not an existing directory." 81 ) 82 83 config_path = os.path.join(model_dir, "config.json") 84 if not os.path.exists(config_path): 85 raise FileNotFoundError(f"Offline config missing at {config_path}.") 86 87 with open(config_path, "r") as f: 88 config_dict = json.load(f) 89 90 return _resolve("GroundingDinoConfig").from_dict(config_dict)
Reads and loads a local Grounding DINO configuration for offline use.
Grounding DINO turns a natural-language phrase into bounding boxes, which SAM 1 and
SAM 2 accept natively as prompts. Its configuration and tokenizer files are not
bundled with GarmentIQ because they belong to a separate upstream model, so download
the model once and point model_dir at the resulting directory.
Arguments:
- model_dir (str): Path to a local directory containing the Grounding DINO
config.json, e.g. a download ofIDEA-Research/grounding-dino-tiny.
Raises:
- FileNotFoundError: If
model_diris not a directory or does not containconfig.json. - ImportError: If the installed transformers release does not provide Grounding DINO.
Returns:
GroundingDinoConfig: The loaded configuration object, ready to be passed into
GroundingDinoForObjectDetection.
93def load_grounding_processor(model_dir: str): 94 """ 95 Loads a local Grounding DINO processor for offline use. 96 97 The processor pairs an image processor with a text tokenizer, so the whole local 98 directory is required rather than a single JSON file. 99 100 Args: 101 model_dir (str): Path to a local directory containing the Grounding DINO processor 102 and tokenizer files. 103 104 Raises: 105 FileNotFoundError: If `model_dir` is not an existing directory. 106 ImportError: If the installed transformers release does not provide Grounding DINO. 107 108 Returns: 109 GroundingDinoProcessor: The instantiated processor ready for image and text inputs. 110 """ 111 if not os.path.isdir(model_dir): 112 raise FileNotFoundError( 113 f"Provided model_dir '{model_dir}' is not an existing directory." 114 ) 115 116 return _resolve("GroundingDinoProcessor").from_pretrained(model_dir)
Loads a local Grounding DINO processor for offline use.
The processor pairs an image processor with a text tokenizer, so the whole local directory is required rather than a single JSON file.
Arguments:
- model_dir (str): Path to a local directory containing the Grounding DINO processor and tokenizer files.
Raises:
- FileNotFoundError: If
model_diris not an existing directory. - ImportError: If the installed transformers release does not provide Grounding DINO.
Returns:
GroundingDinoProcessor: The instantiated processor ready for image and text inputs.
119def load_grounding_model( 120 model_dir: str, 121 device: Union[str, torch.device] = "cpu", 122): 123 """ 124 Loads a Grounding DINO model from a local directory, fully offline. 125 126 Grounding DINO ties several decoder heads to a shared set of weights, so its checkpoint 127 stores only one copy of them. Constructing the model from a config and calling 128 `load_state_dict` therefore leaves those tied tensors randomly initialised, which 129 silently degrades grounding quality. `from_pretrained` performs the tying correctly, 130 so it is used here rather than the generic loader used for other GarmentIQ models. 131 132 Args: 133 model_dir (str): Path to a local directory holding the Grounding DINO `config.json`, 134 weights, and tokenizer files, e.g. a download of 135 `IDEA-Research/grounding-dino-tiny`. 136 device (Union[str, torch.device], optional): The device to load the model onto, e.g. 137 `"cpu"`, `"cuda"`, or `"mps"`. 138 Default is `"cpu"`. 139 140 Raises: 141 FileNotFoundError: If `model_dir` is not an existing directory. 142 ValueError: If the requested `device` is invalid or unavailable on this machine. 143 ImportError: If the installed transformers release does not provide Grounding DINO. 144 145 Returns: 146 GroundingDinoForObjectDetection: The loaded model in evaluation mode, on `device`. 147 """ 148 if not os.path.isdir(model_dir): 149 raise FileNotFoundError( 150 f"Provided model_dir '{model_dir}' is not an existing directory." 151 ) 152 153 device = resolve_device(device) 154 model = _resolve("GroundingDinoForObjectDetection").from_pretrained(model_dir) 155 model = model.to(device) 156 model.eval() 157 return model
Loads a Grounding DINO model from a local directory, fully offline.
Grounding DINO ties several decoder heads to a shared set of weights, so its checkpoint
stores only one copy of them. Constructing the model from a config and calling
load_state_dict therefore leaves those tied tensors randomly initialised, which
silently degrades grounding quality. from_pretrained performs the tying correctly,
so it is used here rather than the generic loader used for other GarmentIQ models.
Arguments:
- model_dir (str): Path to a local directory holding the Grounding DINO
config.json, weights, and tokenizer files, e.g. a download ofIDEA-Research/grounding-dino-tiny. - device (Union[str, torch.device], optional): The device to load the model onto, e.g.
"cpu","cuda", or"mps". Default is"cpu".
Raises:
- FileNotFoundError: If
model_diris not an existing directory. - ValueError: If the requested
deviceis invalid or unavailable on this machine. - ImportError: If the installed transformers release does not provide Grounding DINO.
Returns:
GroundingDinoForObjectDetection: The loaded model in evaluation mode, on
device.
160def ground_text_to_boxes( 161 model, 162 processor, 163 image: Image.Image, 164 text: str, 165 box_threshold: float = 0.3, 166 text_threshold: float = 0.3, 167 max_boxes: Optional[int] = None, 168 device: Union[str, torch.device] = "cpu", 169): 170 """ 171 Converts a natural-language phrase into bounding boxes for an image. 172 173 This is the bridge that gives SAM 1 and SAM 2 text-prompted segmentation. Neither model 174 contains a text encoder, so the phrase is first grounded into boxes by Grounding DINO, 175 and those boxes are then used as ordinary geometric prompts for SAM. 176 177 Grounding DINO expects lowercase phrases terminated by a period; the text is normalised 178 to that convention automatically. 179 180 Args: 181 model (GroundingDinoForObjectDetection): The loaded Grounding DINO model. 182 processor (GroundingDinoProcessor): The matching processor with its tokenizer. 183 image (PIL.Image.Image): The image to ground the phrase against. 184 text (str): The natural-language prompt, e.g. `"a shirt"` or `"trousers"`. 185 box_threshold (float, optional): Minimum box confidence to keep a detection. 186 Default is 0.3. 187 text_threshold (float, optional): Minimum text-matching score to keep a detection. 188 Default is 0.3. 189 max_boxes (int, optional): If given, keep at most this many highest-scoring boxes. 190 Default is None, meaning keep all boxes above threshold. 191 device (Union[str, torch.device], optional): The device to run grounding on, e.g. 192 `"cpu"`, `"cuda"`, or `"mps"`. 193 Default is `"cpu"`. 194 195 Raises: 196 ValueError: If `text` is empty, if the requested `device` is unavailable, or if no 197 region of the image matches the phrase at the given thresholds. 198 199 Returns: 200 list[list[float]]: Bounding boxes in `[x_min, y_min, x_max, y_max]` pixel coordinates, 201 ordered by descending confidence. 202 """ 203 if not text or not text.strip(): 204 raise ValueError("A non-empty text prompt is required for grounding.") 205 206 device = resolve_device(device) 207 model = model.to(device) 208 209 # Grounding DINO is trained on lowercase phrases ending with a period. 210 caption = text.strip().lower() 211 if not caption.endswith("."): 212 caption = caption + "." 213 214 inputs = inputs_to_device( 215 processor(images=image, text=caption, return_tensors="pt"), device 216 ) 217 218 with torch.no_grad(): 219 outputs = model(**inputs) 220 221 results = processor.post_process_grounded_object_detection( 222 outputs, 223 inputs["input_ids"], 224 threshold=box_threshold, 225 text_threshold=text_threshold, 226 target_sizes=[(image.height, image.width)], 227 )[0] 228 229 boxes = results["boxes"].detach().cpu() 230 scores = results["scores"].detach().cpu() 231 232 if boxes.numel() == 0: 233 raise ValueError( 234 f"Grounding model found no region matching text prompt {text!r} at " 235 f"box_threshold={box_threshold} and text_threshold={text_threshold}. " 236 f"Try lowering the thresholds or rephrasing the prompt." 237 ) 238 239 order = torch.argsort(scores, descending=True) 240 boxes = boxes[order] 241 if max_boxes is not None: 242 boxes = boxes[:max_boxes] 243 244 return [[float(v) for v in box] for box in boxes]
Converts a natural-language phrase into bounding boxes for an image.
This is the bridge that gives SAM 1 and SAM 2 text-prompted segmentation. Neither model contains a text encoder, so the phrase is first grounded into boxes by Grounding DINO, and those boxes are then used as ordinary geometric prompts for SAM.
Grounding DINO expects lowercase phrases terminated by a period; the text is normalised to that convention automatically.
Arguments:
- model (GroundingDinoForObjectDetection): The loaded Grounding DINO model.
- processor (GroundingDinoProcessor): The matching processor with its tokenizer.
- image (PIL.Image.Image): The image to ground the phrase against.
- text (str): The natural-language prompt, e.g.
"a shirt"or"trousers". - box_threshold (float, optional): Minimum box confidence to keep a detection. Default is 0.3.
- text_threshold (float, optional): Minimum text-matching score to keep a detection. Default is 0.3.
- max_boxes (int, optional): If given, keep at most this many highest-scoring boxes. Default is None, meaning keep all boxes above threshold.
- device (Union[str, torch.device], optional): The device to run grounding on, e.g.
"cpu","cuda", or"mps". Default is"cpu".
Raises:
- ValueError: If
textis empty, if the requesteddeviceis unavailable, or if no region of the image matches the phrase at the given thresholds.
Returns:
list[list[float]]: Bounding boxes in
[x_min, y_min, x_max, y_max]pixel coordinates, ordered by descending confidence.