From f2ddc963342e944214dc9d459a1e1dc4bf599aa1 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Mon, 8 Apr 2024 12:25:02 +0200 Subject: [PATCH 1/6] Add `IS_JETSON` constant (#9852) Co-authored-by: UltralyticsAssistant --- ultralytics/utils/__init__.py | 36 +++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/ultralytics/utils/__init__.py b/ultralytics/utils/__init__.py index cda2aaae60..3a27062457 100644 --- a/ultralytics/utils/__init__.py +++ b/ultralytics/utils/__init__.py @@ -406,6 +406,20 @@ DEFAULT_CFG_KEYS = DEFAULT_CFG_DICT.keys() DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT) +def read_device_model() -> str: + """ + Reads the device model information from the system and caches it for quick access. Used by is_jetson() and + is_raspberrypi(). + + Returns: + (str): Model file contents if read successfully or empty string otherwise. + """ + with contextlib.suppress(Exception): + with open("/proc/device-tree/model") as f: + return f.read() + return "" + + def is_ubuntu() -> bool: """ Check if the OS is Ubuntu. @@ -473,10 +487,18 @@ def is_raspberrypi() -> bool: Returns: (bool): True if running on a Raspberry Pi, False otherwise. """ - with contextlib.suppress(Exception): - with open("/proc/device-tree/model") as f: - return "Raspberry Pi" in f.read() - return False + return "Raspberry Pi" in PROC_DEVICE_MODEL + + +def is_jetson() -> bool: + """ + Determines if the Python environment is running on a Jetson Nano or Jetson Orin device by checking the device model + information. + + Returns: + (bool): True if running on a Jetson Nano or Jetson Orin, False otherwise. + """ + return "Jetson" in PROC_DEVICE_MODEL # i.e. "Jetson Nano" or "Jetson Orin" def is_online() -> bool: @@ -658,9 +680,11 @@ def get_user_config_dir(sub_dir="Ultralytics"): # Define constants (required below) +PROC_DEVICE_MODEL = read_device_model() # is_jetson() and is_raspberrypi() depend on this constant ONLINE = is_online() IS_COLAB = is_colab() IS_DOCKER = is_docker() +IS_JETSON = is_jetson() IS_JUPYTER = is_jupyter() IS_KAGGLE = is_kaggle() IS_PIP_PACKAGE = is_pip_package() @@ -696,8 +720,8 @@ def colorstr(*input): (str): The input string wrapped with ANSI escape codes for the specified color and style. Examples: - >>> colorstr('blue', 'bold', 'hello world') - >>> '\033[34m\033[1mhello world\033[0m' + >>> colorstr("blue", "bold", "hello world") + >>> "\033[34m\033[1mhello world\033[0m" """ *args, string = input if len(input) > 1 else ("blue", "bold", input[0]) # color arguments, string colors = { From c8388d952f5559cf0ca217fc47836b2a9ad88414 Mon Sep 17 00:00:00 2001 From: Lakshantha Dissanayake Date: Mon, 8 Apr 2024 12:52:19 -0700 Subject: [PATCH 2/6] Fix proc_device_model name for NVIDIA Jetson (#9876) Co-authored-by: Glenn Jocher --- ultralytics/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultralytics/utils/__init__.py b/ultralytics/utils/__init__.py index 3a27062457..77745014c9 100644 --- a/ultralytics/utils/__init__.py +++ b/ultralytics/utils/__init__.py @@ -498,7 +498,7 @@ def is_jetson() -> bool: Returns: (bool): True if running on a Jetson Nano or Jetson Orin, False otherwise. """ - return "Jetson" in PROC_DEVICE_MODEL # i.e. "Jetson Nano" or "Jetson Orin" + return "NVIDIA" in PROC_DEVICE_MODEL # i.e. "NVIDIA Jetson Nano" or "NVIDIA Orin NX" def is_online() -> bool: From bc636d08b798412c7eecda974f0019b90566dc57 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Mon, 8 Apr 2024 22:55:18 +0200 Subject: [PATCH 3/6] Improve PNNX download robustness (#9884) --- ultralytics/engine/exporter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ultralytics/engine/exporter.py b/ultralytics/engine/exporter.py index 1b95d76a72..be750a3ada 100644 --- a/ultralytics/engine/exporter.py +++ b/ultralytics/engine/exporter.py @@ -529,12 +529,12 @@ class Exporter: f"or in {ROOT}. See PNNX repo for full installation instructions." ) system = "macos" if MACOS else "windows" if WINDOWS else "linux-aarch64" if ARM64 else "linux" - _, assets = get_github_assets(repo="pnnx/pnnx", retry=True) - if assets: + try: + _, assets = get_github_assets(repo="pnnx/pnnx", retry=True) url = [x for x in assets if f"{system}.zip" in x][0] - else: + except Exception as e: url = f"https://github.com/pnnx/pnnx/releases/download/20240226/pnnx-20240226-{system}.zip" - LOGGER.warning(f"{prefix} WARNING ⚠️ PNNX GitHub assets not found, using default {url}") + LOGGER.warning(f"{prefix} WARNING ⚠️ PNNX GitHub assets not found: {e}, using default {url}") asset = attempt_download_asset(url, repo="pnnx/pnnx", release="latest") if check_is_path_safe(Path.cwd(), asset): # avoid path traversal security vulnerability unzip_dir = Path(asset).with_suffix("") From e040ce06180ccf005407425844a1bda17f121357 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Tue, 9 Apr 2024 13:25:54 +0200 Subject: [PATCH 4/6] Update cla.yml (#9905) --- .github/workflows/cla.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 376974e7d1..be9c2e446b 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -1,4 +1,6 @@ # Ultralytics YOLO 🚀, AGPL-3.0 license +# Ultralytics Contributor License Agreement (CLA) action https://docs.ultralytics.com/help/CLA +# This workflow automatically requests Pull Requests (PR) authors to sign the Ultralytics CLA before PRs can be merged name: CLA Assistant on: @@ -21,7 +23,7 @@ jobs: uses: contributor-assistant/github-action@v2.3.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # must be repository secret token + # must be repository secret PAT PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} with: path-to-signatures: "signatures/version1/cla.json" From 417c429ec4862a09b32cf5a62c389f574d3cf88e Mon Sep 17 00:00:00 2001 From: Laughing <61612323+Laughing-q@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:21:52 +0800 Subject: [PATCH 5/6] OBB: Fix distorted plotting (#9899) --- ultralytics/data/augment.py | 9 +++++++-- ultralytics/utils/plotting.py | 12 ++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ultralytics/data/augment.py b/ultralytics/data/augment.py index bb6590e58c..9cf1e12731 100644 --- a/ultralytics/data/augment.py +++ b/ultralytics/data/augment.py @@ -975,17 +975,22 @@ class Format: 1 if self.mask_overlap else nl, img.shape[0] // self.mask_ratio, img.shape[1] // self.mask_ratio ) labels["masks"] = masks - if self.normalize: - instances.normalize(w, h) labels["img"] = self._format_img(img) labels["cls"] = torch.from_numpy(cls) if nl else torch.zeros(nl) labels["bboxes"] = torch.from_numpy(instances.bboxes) if nl else torch.zeros((nl, 4)) if self.return_keypoint: labels["keypoints"] = torch.from_numpy(instances.keypoints) + if self.normalize: + labels["keypoints"][..., 0] /= w + labels["keypoints"][..., 1] /= h if self.return_obb: labels["bboxes"] = ( xyxyxyxy2xywhr(torch.from_numpy(instances.segments)) if len(instances.segments) else torch.zeros((0, 5)) ) + # NOTE: need to normalize obb in xywhr format for width-height consistency + if self.normalize: + labels["bboxes"][:, [0, 2]] /= w + labels["bboxes"][:, [1, 3]] /= h # Then we can use collate_fn if self.batch_idx: labels["batch_idx"] = torch.zeros(nl) diff --git a/ultralytics/utils/plotting.py b/ultralytics/utils/plotting.py index 303228ffc5..946425b348 100644 --- a/ultralytics/utils/plotting.py +++ b/ultralytics/utils/plotting.py @@ -838,16 +838,16 @@ def plot_images( if len(bboxes): boxes = bboxes[idx] conf = confs[idx] if confs is not None else None # check for confidence presence (label vs pred) - is_obb = boxes.shape[-1] == 5 # xywhr - boxes = ops.xywhr2xyxyxyxy(boxes) if is_obb else ops.xywh2xyxy(boxes) if len(boxes): if boxes[:, :4].max() <= 1.1: # if normalized with tolerance 0.1 - boxes[..., 0::2] *= w # scale to pixels - boxes[..., 1::2] *= h + boxes[..., [0, 2]] *= w # scale to pixels + boxes[..., [1, 3]] *= h elif scale < 1: # absolute coords need scale if image scales boxes[..., :4] *= scale - boxes[..., 0::2] += x - boxes[..., 1::2] += y + boxes[..., 0] += x + boxes[..., 1] += y + is_obb = boxes.shape[-1] == 5 # xywhr + boxes = ops.xywhr2xyxyxyxy(boxes) if is_obb else ops.xywh2xyxy(boxes) for j, box in enumerate(boxes.astype(np.int64).tolist()): c = classes[j] color = colors(c) From ea03db998474f835a5ffe5b04b55c993a93e3b3b Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Wed, 10 Apr 2024 17:46:41 +0200 Subject: [PATCH 6/6] Pin PNNX to avoid PNNX==20240410 bug (#9953) Co-authored-by: UltralyticsAssistant --- .github/workflows/ci.yaml | 2 +- ultralytics/engine/exporter.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9154d13481..ae13398501 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -123,7 +123,7 @@ jobs: run: coverage run -a --source=ultralytics -m ultralytics.cfg.__init__ benchmark model='path with spaces/yolov8s-worldv2.pt' imgsz=160 verbose=0.318 - name: Benchmark SegmentationModel shell: bash - run: coverage run -a --source=ultralytics -m ultralytics.cfg.__init__ benchmark model='path with spaces/${{ matrix.model }}-seg.pt' imgsz=160 verbose=0.281 + run: coverage run -a --source=ultralytics -m ultralytics.cfg.__init__ benchmark model='path with spaces/${{ matrix.model }}-seg.pt' imgsz=160 verbose=0.280 - name: Benchmark ClassificationModel shell: bash run: coverage run -a --source=ultralytics -m ultralytics.cfg.__init__ benchmark model='path with spaces/${{ matrix.model }}-cls.pt' imgsz=160 verbose=0.166 diff --git a/ultralytics/engine/exporter.py b/ultralytics/engine/exporter.py index be750a3ada..c5385e803b 100644 --- a/ultralytics/engine/exporter.py +++ b/ultralytics/engine/exporter.py @@ -529,12 +529,16 @@ class Exporter: f"or in {ROOT}. See PNNX repo for full installation instructions." ) system = "macos" if MACOS else "windows" if WINDOWS else "linux-aarch64" if ARM64 else "linux" - try: - _, assets = get_github_assets(repo="pnnx/pnnx", retry=True) - url = [x for x in assets if f"{system}.zip" in x][0] - except Exception as e: - url = f"https://github.com/pnnx/pnnx/releases/download/20240226/pnnx-20240226-{system}.zip" - LOGGER.warning(f"{prefix} WARNING ⚠️ PNNX GitHub assets not found: {e}, using default {url}") + + # PNNX link fixed at 20240226 due to bug in 20240410 + # try: + # _, assets = get_github_assets(repo="pnnx/pnnx", retry=True) + # url = [x for x in assets if f"{system}.zip" in x][0] + # except Exception as e: + # url = f"https://github.com/pnnx/pnnx/releases/download/20240226/pnnx-20240226-{system}.zip" + # LOGGER.warning(f"{prefix} WARNING ⚠️ PNNX GitHub assets not found: {e}, using default {url}") + + url = f"https://github.com/pnnx/pnnx/releases/download/20240226/pnnx-20240226-{system}.zip" asset = attempt_download_asset(url, repo="pnnx/pnnx", release="latest") if check_is_path_safe(Path.cwd(), asset): # avoid path traversal security vulnerability unzip_dir = Path(asset).with_suffix("")