Register
Login
Resources
Docs Blog Datasets Glossary Case Studies Tutorials & Webinars
Product
Data Engine LLMs Platform Enterprise
Pricing Explore
Connect to our Discord channel

#21256 Add `DATASET_DIR` relative path compatibility for grounding datasets

Merged
Ghost merged 1 commits into Ultralytics:main from ultralytics:yoloe-datadir
@@ -56,11 +56,11 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
     from ultralytics.utils import WEIGHTS_DIR
     from ultralytics.utils import WEIGHTS_DIR
 
 
     # Remove files
     # Remove files
-    models = [path for x in ["*.onnx", "*.torchscript"] for path in WEIGHTS_DIR.rglob(x)]
+    models = [path for x in {"*.onnx", "*.torchscript"} for path in WEIGHTS_DIR.rglob(x)]
     for file in ["decelera_portrait_min.mov", "bus.jpg", "yolo11n.onnx", "yolo11n.torchscript"] + models:
     for file in ["decelera_portrait_min.mov", "bus.jpg", "yolo11n.onnx", "yolo11n.torchscript"] + models:
         Path(file).unlink(missing_ok=True)
         Path(file).unlink(missing_ok=True)
 
 
     # Remove directories
     # Remove directories
-    models = [path for x in ["*.mlpackage", "*_openvino_model"] for path in WEIGHTS_DIR.rglob(x)]
+    models = [path for x in {"*.mlpackage", "*_openvino_model"} for path in WEIGHTS_DIR.rglob(x)]
     for directory in [WEIGHTS_DIR / "path with spaces", TMP.parents[1] / ".pytest_cache", TMP] + models:
     for directory in [WEIGHTS_DIR / "path with spaces", TMP.parents[1] / ".pytest_cache", TMP] + models:
         shutil.rmtree(directory, ignore_errors=True)
         shutil.rmtree(directory, ignore_errors=True)
Discard
@@ -204,7 +204,7 @@ def test_track_stream(model):
 @pytest.mark.parametrize("task,model,data", TASK_MODEL_DATA)
 @pytest.mark.parametrize("task,model,data", TASK_MODEL_DATA)
 def test_val(task: str, model: str, data: str) -> None:
 def test_val(task: str, model: str, data: str) -> None:
     """Test the validation mode of the YOLO model."""
     """Test the validation mode of the YOLO model."""
-    for plots in [True, False]:  # Test both cases i.e. plots=True and plots=False
+    for plots in {True, False}:  # Test both cases i.e. plots=True and plots=False
         metrics = YOLO(model).val(data=data, imgsz=32, plots=plots)
         metrics = YOLO(model).val(data=data, imgsz=32, plots=plots)
         metrics.to_df()
         metrics.to_df()
         metrics.to_csv()
         metrics.to_csv()
@@ -390,7 +390,7 @@ def test_cfg_init():
         check_dict_alignment({"a": 1}, {"b": 2})
         check_dict_alignment({"a": 1}, {"b": 2})
     copy_default_cfg()
     copy_default_cfg()
     (Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml")).unlink(missing_ok=False)
     (Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml")).unlink(missing_ok=False)
-    [smart_value(x) for x in ["none", "true", "false"]]
+    [smart_value(x) for x in {"none", "true", "false"}]
 
 
 
 
 def test_utils_init():
 def test_utils_init():
Discard
@@ -496,7 +496,7 @@ def convert_dota_to_yolo_obb(dota_root_path: str):
                 formatted_coords = [f"{coord:.6g}" for coord in normalized_coords]
                 formatted_coords = [f"{coord:.6g}" for coord in normalized_coords]
                 g.write(f"{class_idx} {' '.join(formatted_coords)}\n")
                 g.write(f"{class_idx} {' '.join(formatted_coords)}\n")
 
 
-    for phase in ["train", "val"]:
+    for phase in {"train", "val"}:
         image_dir = dota_root_path / "images" / phase
         image_dir = dota_root_path / "images" / phase
         orig_label_dir = dota_root_path / "labels" / f"{phase}_original"
         orig_label_dir = dota_root_path / "labels" / f"{phase}_original"
         save_dir = dota_root_path / "labels" / phase
         save_dir = dota_root_path / "labels" / phase
@@ -684,7 +684,7 @@ def create_synthetic_coco_dataset():
     # Create synthetic images
     # Create synthetic images
     shutil.rmtree(dir / "labels" / "test2017", ignore_errors=True)  # Remove test2017 directory as not needed
     shutil.rmtree(dir / "labels" / "test2017", ignore_errors=True)  # Remove test2017 directory as not needed
     with ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
     with ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
-        for subset in ["train2017", "val2017"]:
+        for subset in {"train2017", "val2017"}:
             subset_dir = dir / "images" / subset
             subset_dir = dir / "images" / subset
             subset_dir.mkdir(parents=True, exist_ok=True)
             subset_dir.mkdir(parents=True, exist_ok=True)
 
 
Discard
@@ -295,7 +295,7 @@ def split_trainval(
     for r in rates:
     for r in rates:
         crop_sizes.append(int(crop_size / r))
         crop_sizes.append(int(crop_size / r))
         gaps.append(int(gap / r))
         gaps.append(int(gap / r))
-    for split in ["train", "val"]:
+    for split in {"train", "val"}:
         split_images_and_labels(data_root, save_dir, split, crop_sizes, gaps)
         split_images_and_labels(data_root, save_dir, split, crop_sizes, gaps)
 
 
 
 
Discard
@@ -931,7 +931,7 @@ class TinyViT(nn.Module):
             if layer.downsample is not None:
             if layer.downsample is not None:
                 layer.downsample.apply(lambda x: _set_lr_scale(x, lr_scales[i - 1]))
                 layer.downsample.apply(lambda x: _set_lr_scale(x, lr_scales[i - 1]))
         assert i == depth
         assert i == depth
-        for m in [self.norm_head, self.head]:
+        for m in {self.norm_head, self.head}:
             m.apply(lambda x: _set_lr_scale(x, lr_scales[-1]))
             m.apply(lambda x: _set_lr_scale(x, lr_scales[-1]))
 
 
         for k, p in self.named_parameters():
         for k, p in self.named_parameters():
Discard
@@ -71,7 +71,7 @@ class DetectionValidator(BaseValidator):
         """
         """
         batch["img"] = batch["img"].to(self.device, non_blocking=True)
         batch["img"] = batch["img"].to(self.device, non_blocking=True)
         batch["img"] = (batch["img"].half() if self.args.half else batch["img"].float()) / 255
         batch["img"] = (batch["img"].half() if self.args.half else batch["img"].float()) / 255
-        for k in ["batch_idx", "cls", "bboxes"]:
+        for k in {"batch_idx", "cls", "bboxes"}:
             batch[k] = batch[k].to(self.device)
             batch[k] = batch[k].to(self.device)
 
 
         return batch
         return batch
Discard
@@ -1,9 +1,11 @@
 # Ultralytics ๐Ÿš€ AGPL-3.0 License - https://ultralytics.com/license
 # Ultralytics ๐Ÿš€ AGPL-3.0 License - https://ultralytics.com/license
 
 
+from pathlib import Path
+
 from ultralytics.data import YOLOConcatDataset, build_grounding, build_yolo_dataset
 from ultralytics.data import YOLOConcatDataset, build_grounding, build_yolo_dataset
 from ultralytics.data.utils import check_det_dataset
 from ultralytics.data.utils import check_det_dataset
 from ultralytics.models.yolo.world import WorldTrainer
 from ultralytics.models.yolo.world import WorldTrainer
-from ultralytics.utils import DEFAULT_CFG, LOGGER
+from ultralytics.utils import DATASETS_DIR, DEFAULT_CFG, LOGGER
 from ultralytics.utils.torch_utils import de_parallel
 from ultralytics.utils.torch_utils import de_parallel
 
 
 
 
@@ -136,7 +138,7 @@ class WorldTrainerFromScratch(WorldTrainer):
             if d.get("minival") is None:  # for lvis dataset
             if d.get("minival") is None:  # for lvis dataset
                 continue
                 continue
             d["minival"] = str(d["path"] / d["minival"])
             d["minival"] = str(d["path"] / d["minival"])
-        for s in ["train", "val"]:
+        for s in {"train", "val"}:
             final_data[s] = [d["train" if s == "train" else val_split] for d in data[s]]
             final_data[s] = [d["train" if s == "train" else val_split] for d in data[s]]
             # save grounding data if there's one
             # save grounding data if there's one
             grounding_data = data_yaml[s].get("grounding_data")
             grounding_data = data_yaml[s].get("grounding_data")
@@ -145,6 +147,10 @@ class WorldTrainerFromScratch(WorldTrainer):
             grounding_data = grounding_data if isinstance(grounding_data, list) else [grounding_data]
             grounding_data = grounding_data if isinstance(grounding_data, list) else [grounding_data]
             for g in grounding_data:
             for g in grounding_data:
                 assert isinstance(g, dict), f"Grounding data should be provided in dict format, but got {type(g)}"
                 assert isinstance(g, dict), f"Grounding data should be provided in dict format, but got {type(g)}"
+                for k in {"img_path", "json_file"}:
+                    path = Path(g[k])
+                    if not path.exists() and not path.is_absolute():
+                        g[k] = str((DATASETS_DIR / g[k]).resolve())  # path relative to DATASETS_DIR
             final_data[s] += grounding_data
             final_data[s] += grounding_data
         # assign the first val dataset as currently only one validation set is supported
         # assign the first val dataset as currently only one validation set is supported
         data["val"] = data["val"][0]
         data["val"] = data["val"][0]
Discard
@@ -115,7 +115,7 @@ class BaseSolution:
         self.device = self.CFG["device"]
         self.device = self.CFG["device"]
 
 
         self.track_add_args = {  # Tracker additional arguments for advance configuration
         self.track_add_args = {  # Tracker additional arguments for advance configuration
-            k: self.CFG[k] for k in ["iou", "conf", "device", "max_det", "half", "tracker"]
+            k: self.CFG[k] for k in {"iou", "conf", "device", "max_det", "half", "tracker"}
         }  # verbose must be passed to track method; setting it False in YOLO still logs the track information.
         }  # verbose must be passed to track method; setting it False in YOLO still logs the track information.
 
 
         if is_cli and self.CFG["source"] is None:
         if is_cli and self.CFG["source"] is None:
Discard
@@ -488,7 +488,7 @@ class ConfusionMatrix(DataExportMixin):
         if ticklabels != "auto":
         if ticklabels != "auto":
             ax.set_xticklabels(ticklabels, fontsize=tick_fontsize, rotation=90, ha="center")
             ax.set_xticklabels(ticklabels, fontsize=tick_fontsize, rotation=90, ha="center")
             ax.set_yticklabels(ticklabels, fontsize=tick_fontsize)
             ax.set_yticklabels(ticklabels, fontsize=tick_fontsize)
-        for s in ["left", "right", "bottom", "top", "outline"]:
+        for s in {"left", "right", "bottom", "top", "outline"}:
             if s != "outline":
             if s != "outline":
                 ax.spines[s].set_visible(False)  # Confusion matrix plot don't have outline
                 ax.spines[s].set_visible(False)  # Confusion matrix plot don't have outline
             cbar.ax.spines[s].set_visible(False)
             cbar.ax.spines[s].set_visible(False)
Discard
@@ -610,8 +610,8 @@ def plot_labels(boxes, cls, names=(), save_dir=Path(""), on_plot=None):
     ax[3].hist2d(x["width"], x["height"], bins=50, cmap=subplot_3_4_color)
     ax[3].hist2d(x["width"], x["height"], bins=50, cmap=subplot_3_4_color)
     ax[3].set_xlabel("width")
     ax[3].set_xlabel("width")
     ax[3].set_ylabel("height")
     ax[3].set_ylabel("height")
-    for a in [0, 1, 2, 3]:
-        for s in ["top", "right", "left", "bottom"]:
+    for a in {0, 1, 2, 3}:
+        for s in {"top", "right", "left", "bottom"}:
             ax[a].spines[s].set_visible(False)
             ax[a].spines[s].set_visible(False)
 
 
     fname = save_dir / "labels.jpg"
     fname = save_dir / "labels.jpg"
Discard