π [Merge] branch 'DEPLOY' into SETUP
Browse files- README.md +1 -1
- examples/notebook_TensorRT.ipynb +130 -0
- yolo/config/model/v9-c.yaml +1 -1
- yolo/lazy.py +1 -1
- yolo/model/module.py +1 -1
- yolo/model/yolo.py +6 -4
- yolo/tools/drawer.py +2 -2
- yolo/utils/bounding_box_utils.py +5 -3
- yolo/utils/deploy_utils.py +3 -5
README.md
CHANGED
@@ -47,7 +47,7 @@ pip install -r requirements.txt
|
|
47 |
| ------------------ | :---------: | :-------: | :-------: |
|
48 |
| PyTorch | v1.12 | v2.3+ | v1.12 |
|
49 |
| ONNX | β
| β
| - |
|
50 |
-
| TensorRT |
|
51 |
| OpenVINO | - | π§ͺ | β |
|
52 |
|
53 |
</td></tr> </table>
|
|
|
47 |
| ------------------ | :---------: | :-------: | :-------: |
|
48 |
| PyTorch | v1.12 | v2.3+ | v1.12 |
|
49 |
| ONNX | β
| β
| - |
|
50 |
+
| TensorRT | β
| - | - |
|
51 |
| OpenVINO | - | π§ͺ | β |
|
52 |
|
53 |
</td></tr> </table>
|
examples/notebook_TensorRT.ipynb
ADDED
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": null,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [],
|
8 |
+
"source": [
|
9 |
+
"import os\n",
|
10 |
+
"import sys\n",
|
11 |
+
"from pathlib import Path\n",
|
12 |
+
"\n",
|
13 |
+
"import torch\n",
|
14 |
+
"from PIL import Image \n",
|
15 |
+
"from loguru import logger\n",
|
16 |
+
"from omegaconf import OmegaConf\n",
|
17 |
+
"\n",
|
18 |
+
"project_root = Path().resolve().parent\n",
|
19 |
+
"sys.path.append(str(project_root))\n",
|
20 |
+
"\n",
|
21 |
+
"from yolo import AugmentationComposer, bbox_nms, create_model, custom_logger, draw_bboxes, Vec2Box\n",
|
22 |
+
"from yolo.config.config import NMSConfig"
|
23 |
+
]
|
24 |
+
},
|
25 |
+
{
|
26 |
+
"cell_type": "code",
|
27 |
+
"execution_count": null,
|
28 |
+
"metadata": {},
|
29 |
+
"outputs": [],
|
30 |
+
"source": [
|
31 |
+
"MODEL = \"v9-c\"\n",
|
32 |
+
"DEVICE = \"cuda:0\"\n",
|
33 |
+
"\n",
|
34 |
+
"WEIGHT_PATH = f\"../weights/{MODEL}.pt\" \n",
|
35 |
+
"TRT_WEIGHT_PATH = f\"../weights/{MODEL}.trt\"\n",
|
36 |
+
"MODEL_CONFIG = f\"../yolo/config/model/{MODEL}.yaml\"\n",
|
37 |
+
"\n",
|
38 |
+
"IMAGE_PATH = \"../demo/images/inference/image.png\"\n",
|
39 |
+
"IMAGE_SIZE = (640, 640)\n",
|
40 |
+
"\n",
|
41 |
+
"custom_logger()\n",
|
42 |
+
"device = torch.device(DEVICE)\n",
|
43 |
+
"image = Image.open(IMAGE_PATH)"
|
44 |
+
]
|
45 |
+
},
|
46 |
+
{
|
47 |
+
"cell_type": "code",
|
48 |
+
"execution_count": null,
|
49 |
+
"metadata": {},
|
50 |
+
"outputs": [],
|
51 |
+
"source": [
|
52 |
+
"if os.path.exists(TRT_WEIGHT_PATH):\n",
|
53 |
+
" from torch2trt import TRTModule\n",
|
54 |
+
"\n",
|
55 |
+
" model_trt = TRTModule()\n",
|
56 |
+
" model_trt.load_state_dict(torch.load(TRT_WEIGHT_PATH))\n",
|
57 |
+
"else:\n",
|
58 |
+
" from torch2trt import torch2trt\n",
|
59 |
+
"\n",
|
60 |
+
" with open(MODEL_CONFIG) as stream:\n",
|
61 |
+
" cfg_model = OmegaConf.load(stream)\n",
|
62 |
+
"\n",
|
63 |
+
" model = create_model(cfg_model, weight_path=WEIGHT_PATH)\n",
|
64 |
+
" model = model.to(device).eval()\n",
|
65 |
+
"\n",
|
66 |
+
" dummy_input = torch.ones((1, 3, 640, 640)).to(device)\n",
|
67 |
+
" logger.info(f\"β»οΈ Creating TensorRT model\")\n",
|
68 |
+
" model_trt = torch2trt(model, [dummy_input])\n",
|
69 |
+
" torch.save(model_trt.state_dict(), TRT_WEIGHT_PATH)\n",
|
70 |
+
" logger.info(f\"π₯ TensorRT model saved to oonx.pt\")\n",
|
71 |
+
"\n",
|
72 |
+
"transform = AugmentationComposer([], IMAGE_SIZE)\n",
|
73 |
+
"vec2box = Vec2Box(model_trt, IMAGE_SIZE, device)\n"
|
74 |
+
]
|
75 |
+
},
|
76 |
+
{
|
77 |
+
"cell_type": "code",
|
78 |
+
"execution_count": null,
|
79 |
+
"metadata": {},
|
80 |
+
"outputs": [],
|
81 |
+
"source": [
|
82 |
+
"image, bbox = transform(image, torch.zeros(0, 5))\n",
|
83 |
+
"image = image.to(device)[None]"
|
84 |
+
]
|
85 |
+
},
|
86 |
+
{
|
87 |
+
"cell_type": "code",
|
88 |
+
"execution_count": null,
|
89 |
+
"metadata": {},
|
90 |
+
"outputs": [],
|
91 |
+
"source": [
|
92 |
+
"with torch.no_grad():\n",
|
93 |
+
" predict = model_trt(image)\n",
|
94 |
+
" predict = vec2box(predict[\"Main\"])\n",
|
95 |
+
"predict_box = bbox_nms(predict[0], predict[2], NMSConfig(0.5, 0.5))\n",
|
96 |
+
"draw_bboxes(image, predict_box)"
|
97 |
+
]
|
98 |
+
},
|
99 |
+
{
|
100 |
+
"cell_type": "markdown",
|
101 |
+
"metadata": {},
|
102 |
+
"source": [
|
103 |
+
"Sample Output:\n",
|
104 |
+
"\n",
|
105 |
+
""
|
106 |
+
]
|
107 |
+
}
|
108 |
+
],
|
109 |
+
"metadata": {
|
110 |
+
"kernelspec": {
|
111 |
+
"display_name": "yolomit",
|
112 |
+
"language": "python",
|
113 |
+
"name": "python3"
|
114 |
+
},
|
115 |
+
"language_info": {
|
116 |
+
"codemirror_mode": {
|
117 |
+
"name": "ipython",
|
118 |
+
"version": 3
|
119 |
+
},
|
120 |
+
"file_extension": ".py",
|
121 |
+
"mimetype": "text/x-python",
|
122 |
+
"name": "python",
|
123 |
+
"nbconvert_exporter": "python",
|
124 |
+
"pygments_lexer": "ipython3",
|
125 |
+
"version": "3.1.undefined"
|
126 |
+
}
|
127 |
+
},
|
128 |
+
"nbformat": 4,
|
129 |
+
"nbformat_minor": 2
|
130 |
+
}
|
yolo/config/model/v9-c.yaml
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
anchor:
|
2 |
reg_max: 16
|
3 |
-
|
4 |
|
5 |
model:
|
6 |
backbone:
|
|
|
1 |
anchor:
|
2 |
reg_max: 16
|
3 |
+
strides: [8, 16, 32]
|
4 |
|
5 |
model:
|
6 |
backbone:
|
yolo/lazy.py
CHANGED
@@ -25,7 +25,7 @@ def main(cfg: Config):
|
|
25 |
model = FastModelLoader(cfg).load_model()
|
26 |
else:
|
27 |
model = create_model(cfg.model, class_num=cfg.class_num, weight_path=cfg.weight)
|
28 |
-
|
29 |
|
30 |
vec2box = Vec2Box(model, cfg.image_size, device)
|
31 |
|
|
|
25 |
model = FastModelLoader(cfg).load_model()
|
26 |
else:
|
27 |
model = create_model(cfg.model, class_num=cfg.class_num, weight_path=cfg.weight)
|
28 |
+
model = model.to(device)
|
29 |
|
30 |
vec2box = Vec2Box(model, cfg.image_size, device)
|
31 |
|
yolo/model/module.py
CHANGED
@@ -105,7 +105,7 @@ class Anchor2Vec(nn.Module):
|
|
105 |
def forward(self, anchor_x: Tensor) -> Tensor:
|
106 |
anchor_x = rearrange(anchor_x, "B (P R) h w -> B R P h w", P=4)
|
107 |
vector_x = anchor_x.softmax(dim=1)
|
108 |
-
vector_x = self.anc2vec(vector_x)
|
109 |
return anchor_x, vector_x
|
110 |
|
111 |
|
|
|
105 |
def forward(self, anchor_x: Tensor) -> Tensor:
|
106 |
anchor_x = rearrange(anchor_x, "B (P R) h w -> B R P h w", P=4)
|
107 |
vector_x = anchor_x.softmax(dim=1)
|
108 |
+
vector_x = self.anc2vec(vector_x)[:, 0]
|
109 |
return anchor_x, vector_x
|
110 |
|
111 |
|
yolo/model/yolo.py
CHANGED
@@ -26,13 +26,15 @@ class YOLO(nn.Module):
|
|
26 |
self.layer_map = get_layer_map() # Get the map Dict[str: Module]
|
27 |
self.model: List[YOLOLayer] = nn.ModuleList()
|
28 |
self.build_model(model_cfg.model)
|
|
|
29 |
|
30 |
def build_model(self, model_arch: Dict[str, List[Dict[str, Dict[str, Dict]]]]):
|
31 |
self.layer_index = {}
|
32 |
output_dim, layer_idx = [3], 1
|
33 |
logger.info(f"π Building YOLO")
|
34 |
for arch_name in model_arch:
|
35 |
-
|
|
|
36 |
for layer_idx, layer_spec in enumerate(model_arch[arch_name], start=layer_idx):
|
37 |
layer_type, layer_info = next(iter(layer_spec.items()))
|
38 |
layer_args = layer_info.get("args", {})
|
@@ -45,7 +47,6 @@ class YOLO(nn.Module):
|
|
45 |
layer_args["in_channels"] = output_dim[source]
|
46 |
if "Detection" in layer_type:
|
47 |
layer_args["in_channels"] = [output_dim[idx] for idx in source]
|
48 |
-
if "Detection" in layer_type or "Anchor2Box" in layer_type:
|
49 |
layer_args["num_classes"] = self.num_classes
|
50 |
|
51 |
# create layers
|
@@ -135,6 +136,7 @@ def create_model(model_cfg: ModelConfig, weight_path: Optional[str], class_num:
|
|
135 |
if os.path.exists(weight_path):
|
136 |
# TODO: fix map_location
|
137 |
model.model.load_state_dict(torch.load(weight_path), strict=False)
|
138 |
-
logger.info("β
Success load model weight")
|
139 |
-
|
|
|
140 |
return model
|
|
|
26 |
self.layer_map = get_layer_map() # Get the map Dict[str: Module]
|
27 |
self.model: List[YOLOLayer] = nn.ModuleList()
|
28 |
self.build_model(model_cfg.model)
|
29 |
+
self.strides = getattr(model_cfg.anchor, "strides", None)
|
30 |
|
31 |
def build_model(self, model_arch: Dict[str, List[Dict[str, Dict[str, Dict]]]]):
|
32 |
self.layer_index = {}
|
33 |
output_dim, layer_idx = [3], 1
|
34 |
logger.info(f"π Building YOLO")
|
35 |
for arch_name in model_arch:
|
36 |
+
if model_arch[arch_name]:
|
37 |
+
logger.info(f" ποΈ Building {arch_name}")
|
38 |
for layer_idx, layer_spec in enumerate(model_arch[arch_name], start=layer_idx):
|
39 |
layer_type, layer_info = next(iter(layer_spec.items()))
|
40 |
layer_args = layer_info.get("args", {})
|
|
|
47 |
layer_args["in_channels"] = output_dim[source]
|
48 |
if "Detection" in layer_type:
|
49 |
layer_args["in_channels"] = [output_dim[idx] for idx in source]
|
|
|
50 |
layer_args["num_classes"] = self.num_classes
|
51 |
|
52 |
# create layers
|
|
|
136 |
if os.path.exists(weight_path):
|
137 |
# TODO: fix map_location
|
138 |
model.model.load_state_dict(torch.load(weight_path), strict=False)
|
139 |
+
logger.info("β
Success load model & weight")
|
140 |
+
else:
|
141 |
+
logger.info("β
Success load model")
|
142 |
return model
|
yolo/tools/drawer.py
CHANGED
@@ -13,7 +13,7 @@ def draw_bboxes(
|
|
13 |
img: Union[Image.Image, torch.Tensor],
|
14 |
bboxes: List[List[Union[int, float]]],
|
15 |
*,
|
16 |
-
idx2label: Optional[list],
|
17 |
):
|
18 |
"""
|
19 |
Draw bounding boxes on an image.
|
@@ -47,7 +47,7 @@ def draw_bboxes(
|
|
47 |
draw.rounded_rectangle(bbox, outline=(*color_map, 200), radius=5, width=2)
|
48 |
draw.rounded_rectangle(bbox, fill=(*color_map, 100), radius=5)
|
49 |
|
50 |
-
class_text = str(idx2label[int(class_id)] if idx2label else class_id)
|
51 |
label_text = f"{class_text}" + (f" {conf[0]: .0%}" if conf else "")
|
52 |
|
53 |
text_bbox = font.getbbox(label_text)
|
|
|
13 |
img: Union[Image.Image, torch.Tensor],
|
14 |
bboxes: List[List[Union[int, float]]],
|
15 |
*,
|
16 |
+
idx2label: Optional[list] = None,
|
17 |
):
|
18 |
"""
|
19 |
Draw bounding boxes on an image.
|
|
|
47 |
draw.rounded_rectangle(bbox, outline=(*color_map, 200), radius=5, width=2)
|
48 |
draw.rounded_rectangle(bbox, fill=(*color_map, 100), radius=5)
|
49 |
|
50 |
+
class_text = str(idx2label[int(class_id)] if idx2label else int(class_id))
|
51 |
label_text = f"{class_text}" + (f" {conf[0]: .0%}" if conf else "")
|
52 |
|
53 |
text_bbox = font.getbbox(label_text)
|
yolo/utils/bounding_box_utils.py
CHANGED
@@ -9,6 +9,7 @@ from torch import Tensor
|
|
9 |
from torchvision.ops import batched_nms
|
10 |
|
11 |
from yolo.config.config import MatcherConfig, ModelConfig, NMSConfig
|
|
|
12 |
|
13 |
|
14 |
def calculate_iou(bbox1, bbox2, metrics="iou") -> Tensor:
|
@@ -264,8 +265,8 @@ class BoxMatcher:
|
|
264 |
|
265 |
|
266 |
class Vec2Box:
|
267 |
-
def __init__(self, model, image_size, device
|
268 |
-
if
|
269 |
logger.info("π§Έ Found no anchor, Make a dummy test for auto-anchor size")
|
270 |
dummy_input = torch.zeros(1, 3, *image_size).to(device)
|
271 |
dummy_output = model(dummy_input)
|
@@ -274,7 +275,8 @@ class Vec2Box:
|
|
274 |
_, _, *anchor_num = predict_head[2].shape
|
275 |
anchors_num.append(anchor_num)
|
276 |
else:
|
277 |
-
|
|
|
278 |
anchor_grid, scaler = generate_anchors(image_size, anchors_num)
|
279 |
self.anchor_grid, self.scaler = anchor_grid.to(device), scaler.to(device)
|
280 |
self.anchor_norm = (anchor_grid / scaler[:, None])[None].to(device)
|
|
|
9 |
from torchvision.ops import batched_nms
|
10 |
|
11 |
from yolo.config.config import MatcherConfig, ModelConfig, NMSConfig
|
12 |
+
from yolo.model.yolo import YOLO
|
13 |
|
14 |
|
15 |
def calculate_iou(bbox1, bbox2, metrics="iou") -> Tensor:
|
|
|
265 |
|
266 |
|
267 |
class Vec2Box:
|
268 |
+
def __init__(self, model: YOLO, image_size, device):
|
269 |
+
if model.strides is None:
|
270 |
logger.info("π§Έ Found no anchor, Make a dummy test for auto-anchor size")
|
271 |
dummy_input = torch.zeros(1, 3, *image_size).to(device)
|
272 |
dummy_output = model(dummy_input)
|
|
|
275 |
_, _, *anchor_num = predict_head[2].shape
|
276 |
anchors_num.append(anchor_num)
|
277 |
else:
|
278 |
+
logger.info(f"πΆ Found anchor {model.strides}")
|
279 |
+
anchors_num = [[image_size[0] // stride, image_size[0] // stride] for stride in model.strides]
|
280 |
anchor_grid, scaler = generate_anchors(image_size, anchors_num)
|
281 |
self.anchor_grid, self.scaler = anchor_grid.to(device), scaler.to(device)
|
282 |
self.anchor_norm = (anchor_grid / scaler[:, None])[None].to(device)
|
yolo/utils/deploy_utils.py
CHANGED
@@ -30,9 +30,7 @@ class FastModelLoader:
|
|
30 |
return self._load_trt_model()
|
31 |
elif self.compiler == "deploy":
|
32 |
self.cfg.model.model.auxiliary = {}
|
33 |
-
return create_model(
|
34 |
-
self.cfg.model, class_num=self.cfg.class_num, weight_path=self.cfg.weight, device=self.device
|
35 |
-
)
|
36 |
|
37 |
def _load_onnx_model(self):
|
38 |
from onnxruntime import InferenceSession
|
@@ -91,9 +89,9 @@ class FastModelLoader:
|
|
91 |
from torch2trt import torch2trt
|
92 |
|
93 |
model = create_model(self.cfg.model, class_num=self.cfg.class_num, weight_path=self.cfg.weight).eval()
|
94 |
-
dummy_input = torch.ones((1, 3, *self.cfg.image_size))
|
95 |
logger.info(f"β»οΈ Creating TensorRT model")
|
96 |
-
model_trt = torch2trt(model, [dummy_input])
|
97 |
torch.save(model_trt.state_dict(), self.model_path)
|
98 |
logger.info(f"π₯ TensorRT model saved to {self.model_path}")
|
99 |
return model_trt
|
|
|
30 |
return self._load_trt_model()
|
31 |
elif self.compiler == "deploy":
|
32 |
self.cfg.model.model.auxiliary = {}
|
33 |
+
return create_model(self.cfg.model, class_num=self.cfg.class_num, weight_path=self.cfg.weight)
|
|
|
|
|
34 |
|
35 |
def _load_onnx_model(self):
|
36 |
from onnxruntime import InferenceSession
|
|
|
89 |
from torch2trt import torch2trt
|
90 |
|
91 |
model = create_model(self.cfg.model, class_num=self.cfg.class_num, weight_path=self.cfg.weight).eval()
|
92 |
+
dummy_input = torch.ones((1, 3, *self.cfg.image_size)).cuda()
|
93 |
logger.info(f"β»οΈ Creating TensorRT model")
|
94 |
+
model_trt = torch2trt(model.cuda(), [dummy_input])
|
95 |
torch.save(model_trt.state_dict(), self.model_path)
|
96 |
logger.info(f"π₯ TensorRT model saved to {self.model_path}")
|
97 |
return model_trt
|