Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions config_infer_primary_yoloWorld.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[property]
gpu-id=0
net-scale-factor=0.0039215697906911373
model-color-format=0
onnx-file=yolov8l-worldv2.onnx
model-engine-file=model_b1_gpu0_fp32.engine
#int8-calib-file=calib.table
labelfile-path=labels.txt
batch-size=1
network-mode=0
num-detected-classes=80
interval=0
gie-unique-id=1
process-mode=1
network-type=0
cluster-mode=2
maintain-aspect-ratio=1
symmetric-padding=1
#workspace-size=2000
parse-bbox-func-name=NvDsInferParseYolo
#parse-bbox-func-name=NvDsInferParseYoloCuda
custom-lib-path=nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so
engine-create-func-name=NvDsInferYoloCudaEngineGet

[class-attrs-all]
nms-iou-threshold=0.45
pre-cluster-threshold=0.25
topk=300
205 changes: 205 additions & 0 deletions docs/YOLO_World.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
# YOLO World usage

**NOTE**: The yaml file is not required.

* [Convert model](#convert-model)
* [Compile the lib](#compile-the-lib)
* [Edit the config_infer_primary_yoloWorld file](#edit-the-config_infer_primary_yoloWorld-file)
* [Edit the deepstream_app_config file](#edit-the-deepstream_app_config-file)
* [Testing the model](#testing-the-model)

##

### Convert model

#### 1. Download the YOLO World repo and install the requirements

```
git clone https://github.com/ultralytics/ultralytics.git
cd ultralytics
pip3 install -r requirements.txt
python3 setup.py install
pip3 install onnx onnxsim onnxruntime
```

**NOTE**: It is recommended to use Python virtualenv.

#### 2. Copy conversor

Copy the `export_yoloWorld.py` file from `DeepStream-Yolo/utils` directory to the `ultralytics` folder.

#### 3. Download the model

Download the `pt` file from [YOLO-World](https://docs.ultralytics.com/models/yolo-world/#key-features) releases (example for yolov8l-worldv2.pt)

```
wget https://github.com/ultralytics/assets/releases/download/v8.1.0/yolov8l-worldv2.pt
```

**NOTE**: You can use your custom model.

#### 4. Convert model

Generate the ONNX model file (example for yolov8l-worldv2.pt)

```
python3 export_yoloWorld.py -w yolov8l-worldv2.pt --dynamic --simplify
```

**NOTE**: To set custom classes
```
python3 export_yoloWorld.py -w yolov8l-worldv2.pt --dynamic --simplify --custom-classes "person, dachshund, navy tie"
```

**NOTE**: To change the inference size (defaut: 640)

```
-s SIZE
--size SIZE
-s HEIGHT WIDTH
--size HEIGHT WIDTH
```

Example for 1280

```
-s 1280
```

or

```
-s 1280 1280
```

**NOTE**: To simplify the ONNX model (DeepStream >= 6.0)

```
--simplify
```

**NOTE**: To use dynamic batch-size (DeepStream >= 6.1)

```
--dynamic
```

**NOTE**: To use static batch-size (example for batch-size = 4)

```
--batch 4
```

**NOTE**: If you are using the DeepStream 5.1, remove the `--dynamic` arg and use opset 12 or lower. The default opset is 16.

```
--opset 12
```

#### 5. Copy generated files

Copy the generated ONNX model file and labels.txt file (if generated) to the `DeepStream-Yolo` folder.

##

### Compile the lib

Open the `DeepStream-Yolo` folder and compile the lib

* DeepStream 6.3 on x86 platform

```
CUDA_VER=12.1 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 6.2 on x86 platform

```
CUDA_VER=11.8 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 6.1.1 on x86 platform

```
CUDA_VER=11.7 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 6.1 on x86 platform

```
CUDA_VER=11.6 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 6.0.1 / 6.0 on x86 platform

```
CUDA_VER=11.4 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 5.1 on x86 platform

```
CUDA_VER=11.1 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 6.3 / 6.2 / 6.1.1 / 6.1 on Jetson platform

```
CUDA_VER=11.4 make -C nvdsinfer_custom_impl_Yolo
```

* DeepStream 6.0.1 / 6.0 / 5.1 on Jetson platform

```
CUDA_VER=10.2 make -C nvdsinfer_custom_impl_Yolo
```

##

### Edit the config_infer_primary_yoloWorld file

Edit the `config_infer_primary_yoloWorld.txt` file according to your model (example for yolov8l-worldv2.pt with 80 classes)

```
[property]
...
onnx-file=yolov8l-worldv2.onnx
...
num-detected-classes=80
...
parse-bbox-func-name=NvDsInferParseYolo
...
```

**NOTE**: The **YOLOv8** resizes the input with center padding. To get better accuracy, use

```
[property]
...
maintain-aspect-ratio=1
symmetric-padding=1
...
```

##

### Edit the deepstream_app_config file

```
...
[primary-gie]
...
config-file=config_infer_primary_yoloWorld.txt
```

##

### Testing the model

```
deepstream-app -c deepstream_app_config.txt
```

**NOTE**: The TensorRT engine file may take a very long time to generate (sometimes more than 10 minutes).

**NOTE**: For more information about custom models configuration (`batch-size`, `network-mode`, etc), please check the [`docs/customModels.md`](customModels.md) file.
138 changes: 138 additions & 0 deletions utils/export_yoloWorld.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import os
import sys
import argparse
import warnings
import onnx
import torch
import torch.nn as nn
from copy import deepcopy
from ultralytics import YOLOWorld
from ultralytics.utils.torch_utils import select_device
from ultralytics.nn.modules import C2f, WorldDetect


class DeepStreamOutput(nn.Module):
def __init__(self):
super().__init__()

def forward(self, x):
x = x.transpose(1, 2)
boxes = x[:, :, :4]
scores, classes = torch.max(x[:, :, 4:], 2, keepdim=True)
classes = classes.float()
return boxes, scores, classes


def suppress_warnings():
warnings.filterwarnings('ignore', category=torch.jit.TracerWarning)
warnings.filterwarnings('ignore', category=UserWarning)
warnings.filterwarnings('ignore', category=DeprecationWarning)


def yolow_export(model, device):
for p in model.parameters():
p.requires_grad = False
model.eval()
model.float()
model = model.fuse()
for k, m in model.named_modules():
if isinstance(m, WorldDetect):
m.dynamic = False
m.export = True
m.format = 'onnx'
elif isinstance(m, C2f):
m.forward = m.forward_split
return model


def main(args):
suppress_warnings()

print('\nStarting: %s' % args.weights)

print('Opening YOLO World model\n')

model = YOLOWorld(args.weights)
print(f"{'#'*10} Original Model Names {'#'*10}\n{model.names}\n")


# set custom classes
if args.custom_classes is not None:
custom_class_list = [item.strip() for item in args.custom_classes.split(',')]

model.set_classes(custom_class_list)
model.model.txt_feats = model.model.txt_feats.detach()
print(f"{'#'*10} Custom Model Names {'#'*10}\n{model.names}\n")


# create labels.txt
if len(model.names.keys()) > 0:
print('\nCreating labels.txt file')
f = open('labels.txt', 'w')
for name in model.names.values():
f.write(name + '\n')
f.close()


device = select_device('cpu')
model = deepcopy(model.model).to(device)
model = yolow_export(model, device)

model = nn.Sequential(model, DeepStreamOutput())

img_size = args.size * 2 if len(args.size) == 1 else args.size

onnx_input_im = torch.zeros(args.batch, 3, *img_size).to(device)
onnx_output_file = os.path.basename(args.weights).split('.pt')[0] + '.onnx'

dynamic_axes = {
'input': {
0: 'batch'
},
'boxes': {
0: 'batch'
},
'scores': {
0: 'batch'
},
'classes': {
0: 'batch'
}
}

print('\nExporting the model to ONNX')
torch.onnx.export(model, onnx_input_im, onnx_output_file, verbose=False, opset_version=args.opset,
do_constant_folding=True, input_names=['input'], output_names=['boxes', 'scores', 'classes'],
dynamic_axes=dynamic_axes if args.dynamic else None)

if args.simplify:
print('Simplifying the ONNX model')
import onnxsim
model_onnx = onnx.load(onnx_output_file)
model_onnx, _ = onnxsim.simplify(model_onnx)
onnx.save(model_onnx, onnx_output_file)

print('Done: %s\n' % onnx_output_file)


def parse_args():
parser = argparse.ArgumentParser(description='DeepStream YOLO World conversion')
parser.add_argument('-w', '--weights', required=True, help='Input weights (.pt) file path (required)')
parser.add_argument('-s', '--size', nargs='+', type=int, default=[640], help='Inference size [H,W] (default [640])')
parser.add_argument('--opset', type=int, default=16, help='ONNX opset version')
parser.add_argument('--simplify', action='store_true', help='ONNX simplify model')
parser.add_argument('--dynamic', action='store_true', help='Dynamic batch-size')
parser.add_argument('--batch', type=int, default=1, help='Static batch-size')
parser.add_argument('--custom-classes', type=str, help='Define custom classes, separated by comma')

args = parser.parse_args()
if not os.path.isfile(args.weights):
raise SystemExit('Invalid weights file')
if args.dynamic and args.batch > 1:
raise SystemExit('Cannot set dynamic batch-size and static batch-size at same time')
return args


if __name__ == '__main__':

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args = parse_args()
sys.exit(main(args))