Fangming Guo commited on
Upload 43 files
Browse files- .gitattributes +2 -0
- AX615/yolov8m_640x640_npu1.axmodel +3 -0
- AX615/yolov8m_640x640_npu2.axmodel +3 -0
- AX615/yolov8n_640x640_npu1.axmodel +3 -0
- AX615/yolov8n_640x640_npu2.axmodel +3 -0
- AX615/yolov8s_640x640_npu1.axmodel +3 -0
- AX615/yolov8s_640x640_npu2.axmodel +3 -0
- AX620E/yolov8l_640x640_npu1.axmodel +3 -0
- AX620E/yolov8l_640x640_npu2.axmodel +3 -0
- AX620E/yolov8m_640x640_npu1.axmodel +3 -0
- AX620E/yolov8m_640x640_npu2.axmodel +3 -0
- AX620E/yolov8n_640x640_npu1.axmodel +3 -0
- AX620E/yolov8n_640x640_npu2.axmodel +3 -0
- AX620E/yolov8s_640x640_npu1.axmodel +3 -0
- AX620E/yolov8s_640x640_npu2.axmodel +3 -0
- AX620E/yolov8x_640x640_npu1.axmodel +3 -0
- AX620E/yolov8x_640x640_npu2.axmodel +3 -0
- AX637/yolov8l_640x640_npu1.axmodel +3 -0
- AX637/yolov8m_640x640_npu1.axmodel +3 -0
- AX637/yolov8n_640x640_npu1.axmodel +3 -0
- AX637/yolov8s_640x640_npu1.axmodel +3 -0
- AX637/yolov8x_640x640_npu1.axmodel +3 -0
- AX650/yolov8l_640x640_npu1.axmodel +3 -0
- AX650/yolov8l_640x640_npu3.axmodel +3 -0
- AX650/yolov8m_640x640_npu1.axmodel +3 -0
- AX650/yolov8m_640x640_npu3.axmodel +3 -0
- AX650/yolov8n_640x640_npu1.axmodel +3 -0
- AX650/yolov8n_640x640_npu3.axmodel +3 -0
- AX650/yolov8s_640x640_npu1.axmodel +3 -0
- AX650/yolov8s_640x640_npu3.axmodel +3 -0
- AX650/yolov8x_640x640_npu1.axmodel +3 -0
- AX650/yolov8x_640x640_npu3.axmodel +3 -0
- ax_infer.py +230 -0
- bus.jpg +3 -0
- coco_1000.tar +3 -0
- config.json +32 -0
- export_onnx.py +91 -0
- onnx_infer.py +269 -0
- result_yolov8_det.jpg +3 -0
- yolov8l_640x640.onnx +3 -0
- yolov8m_640x640.onnx +3 -0
- yolov8n_640x640.onnx +3 -0
- yolov8s_640x640.onnx +3 -0
- yolov8x_640x640.onnx +3 -0
.gitattributes
CHANGED
|
@@ -44,3 +44,5 @@ axcl_aarch64/axcl_yolov8 filter=lfs diff=lfs merge=lfs -text
|
|
| 44 |
axcl_x86_64/axcl_yolov8 filter=lfs diff=lfs merge=lfs -text
|
| 45 |
yolov8_axcl_out.jpg filter=lfs diff=lfs merge=lfs -text
|
| 46 |
*.axmodel filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 44 |
axcl_x86_64/axcl_yolov8 filter=lfs diff=lfs merge=lfs -text
|
| 45 |
yolov8_axcl_out.jpg filter=lfs diff=lfs merge=lfs -text
|
| 46 |
*.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
bus.jpg filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
result_yolov8_det.jpg filter=lfs diff=lfs merge=lfs -text
|
AX615/yolov8m_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:318f485b377273c2d9107ff5869f58b0c01fe6343afee56afd9ae792d856c335
|
| 3 |
+
size 28682377
|
AX615/yolov8m_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7b70f750bd5d9dcda95c7ad28f5176d8f7752ce2384c1c7d0b0ba0200b02f2eb
|
| 3 |
+
size 26630729
|
AX615/yolov8n_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:34f90d2d742214630ee1034a487cd4d4fdd61be31cda3fbddf818b86d4113be7
|
| 3 |
+
size 3670985
|
AX615/yolov8n_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d71eae7f16a7358c6b2f182f0d1c695c5a2cf1f48eae9b1a8dee3c322621299d
|
| 3 |
+
size 3397645
|
AX615/yolov8s_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f8158f466b2cb8e8aebdc35cf27ceb55792b2d32c4d2f25a72568c7805a760ab
|
| 3 |
+
size 12468873
|
AX615/yolov8s_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:794c436981a263431461f5b4c969ed613fea06c584f2977c7f5d4d984abd3640
|
| 3 |
+
size 11503501
|
AX620E/yolov8l_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31a202c34e0f9feb6e9c5395d10115271a59944507465d42a46fdd4abbb30b59
|
| 3 |
+
size 50315789
|
AX620E/yolov8l_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:985cd993878a0786b9b9625972f109273e0557bbd4d02e14eac4903d54c13678
|
| 3 |
+
size 44955325
|
AX620E/yolov8m_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:87ef023288128267d9de1ab0f685d475a54e801e213ef2400d2b3f315f7f3120
|
| 3 |
+
size 29841021
|
AX620E/yolov8m_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d55dcf21bdcfbfc5571afb10894484cfbd2b1142fe97432b1ea23d9966468a89
|
| 3 |
+
size 26738901
|
AX620E/yolov8n_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:87d1d0029203056c9372bca4da68a396e2bf71914263ae8a3712668e1ef03d60
|
| 3 |
+
size 3810681
|
AX620E/yolov8n_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fb40333377b7f3444ce53fd9382d7d1d3a3e36bd87536614378bd2101a5218c5
|
| 3 |
+
size 3424381
|
AX620E/yolov8s_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cf2c4a73878be48aafb01316029b32043844b71408afc36329cf6d36e67c63c6
|
| 3 |
+
size 12639153
|
AX620E/yolov8s_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:56ff92fb5c14d73c58e9d2f0573c04bf1c5e47cfe5b11ac316c3412aae19dfe8
|
| 3 |
+
size 11529925
|
AX620E/yolov8x_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:664668e09033507a78d4185ef7a1de670959bca9e17dbfd6aa21aa730d33ffb4
|
| 3 |
+
size 80537453
|
AX620E/yolov8x_640x640_npu2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c7a9ef5096de6fa6a8acc3fcb7fe10c7872ae58aadcafb73da7f9c2f03f2a45e
|
| 3 |
+
size 70628301
|
AX637/yolov8l_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c1bdff37d0c432d3b73215a7da9777742bfb25e24eeb7cffc0ea1a6eeb7b8883
|
| 3 |
+
size 44417625
|
AX637/yolov8m_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cf9e5ce79d61fbf4586f77794132be4dff296d2410348f337b0569ba2dd9fa9d
|
| 3 |
+
size 26311565
|
AX637/yolov8n_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3829468bd86c4f5bcf1595df7e1ebdf827ff5a824962745dccbd64a41237329e
|
| 3 |
+
size 3629097
|
AX637/yolov8s_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c818a96de1f8f921fe98388949dd4b5cdac559fd0e588aa43c87fa960bd78ed0
|
| 3 |
+
size 11394521
|
AX637/yolov8x_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:120d6e1ba59ab9b7eb835ada8e2f6c658cd3b133a591098f1201d653d8da0dff
|
| 3 |
+
size 69938665
|
AX650/yolov8l_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:077fd62f40e710c680e5dfaf3865ee32196818695a5b1d39d764b2f79b2df366
|
| 3 |
+
size 45101257
|
AX650/yolov8l_640x640_npu3.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fd19e855fffc6237d1e99e06cabd98b64448131ae0b6fa502ffee9f424d31e2c
|
| 3 |
+
size 45526493
|
AX650/yolov8m_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:23583617c0c630a248feb7f27fd0b219c41ae373e2bb2d3b5ba06b022492c128
|
| 3 |
+
size 26735549
|
AX650/yolov8m_640x640_npu3.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:da0ba156916b0ac626ab588f5de845f7c13692cbd5c6f5df8bf2d45bdfac2101
|
| 3 |
+
size 27064817
|
AX650/yolov8n_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5d1be0d0b0fcdbf6b0c6b097b124b51653c2c8347129beab519c59586cf42a87
|
| 3 |
+
size 3443549
|
AX650/yolov8n_640x640_npu3.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e5563cc868a98e9ee051cb9941844be934c55cd5a37264d10bff534dd613e86b
|
| 3 |
+
size 3700265
|
AX650/yolov8s_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aab82f706f0dec0a6e4244813ab1883d2b1291e348800437a488f1de23c2a905
|
| 3 |
+
size 11616925
|
AX650/yolov8s_640x640_npu3.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f112959508faa56c2d7b9a17e7ba48b9a0967bc95fb499cfc47993ce099fb0ce
|
| 3 |
+
size 11860681
|
AX650/yolov8x_640x640_npu1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c13a043ea5ab6cd939841ce4aba4c071120df0b703ab5b185da34cfc398a7beb
|
| 3 |
+
size 70652329
|
AX650/yolov8x_640x640_npu3.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5c951cd2f7406c8f175010f64dbecdfafe3cc07d97065d026414622458a9fe7c
|
| 3 |
+
size 71141277
|
ax_infer.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# Usage: python ax_infer.py --model-path yolov8n_640x640.axmodel --test-img bus.jpg --img-save-path result_yolov8_det.jpg --score-thres 0.25 --nms-thres 0.7 --providers AxEngineExecutionProvider
|
| 3 |
+
import os
|
| 4 |
+
import cv2
|
| 5 |
+
import numpy as np
|
| 6 |
+
from time import time
|
| 7 |
+
import argparse
|
| 8 |
+
import logging
|
| 9 |
+
import axengine as ort
|
| 10 |
+
|
| 11 |
+
logging.basicConfig(
|
| 12 |
+
level=logging.DEBUG,
|
| 13 |
+
format='[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s',
|
| 14 |
+
datefmt='%H:%M:%S'
|
| 15 |
+
)
|
| 16 |
+
logger = logging.getLogger("YOLOv8-Det")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def infer_hw_layout(shape):
|
| 20 |
+
"""Infer input height, width and layout from model input shape."""
|
| 21 |
+
shape = list(shape)
|
| 22 |
+
if len(shape) == 4 and shape[-1] == 3:
|
| 23 |
+
h = int(shape[1] or 640)
|
| 24 |
+
w = int(shape[2] or 640)
|
| 25 |
+
return h, w, "NHWC"
|
| 26 |
+
if len(shape) == 4 and shape[1] == 3:
|
| 27 |
+
h = int(shape[2] or 640)
|
| 28 |
+
w = int(shape[3] or 640)
|
| 29 |
+
return h, w, "NCHW"
|
| 30 |
+
return 640, 640, "NCHW"
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def softmax(x, axis=-1):
|
| 34 |
+
"""Compute softmax along axis."""
|
| 35 |
+
e_x = np.exp(x - np.max(x, axis=axis, keepdims=True))
|
| 36 |
+
return e_x / np.sum(e_x, axis=axis, keepdims=True)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def dfl_decode(box_pred, reg_max=16):
|
| 40 |
+
"""Decode DFL (Distribution Focal Loss) box predictions to ltrb distances."""
|
| 41 |
+
N = box_pred.shape[0]
|
| 42 |
+
box_pred = box_pred.reshape(N, 4, reg_max)
|
| 43 |
+
box_pred = softmax(box_pred, axis=-1)
|
| 44 |
+
proj = np.arange(reg_max, dtype=np.float32)
|
| 45 |
+
return np.sum(box_pred * proj, axis=-1) # (N, 4)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def main():
|
| 49 |
+
ap = argparse.ArgumentParser(description='YOLOv8-Det Inference (AXERARuntime)')
|
| 50 |
+
ap.add_argument('--model-path', type=str, default='yolov8n_640x640.axmodel')
|
| 51 |
+
ap.add_argument('--test-img', type=str, default='bus.jpg')
|
| 52 |
+
ap.add_argument('--img-save-path', type=str, default='result_yolov8_det.jpg')
|
| 53 |
+
ap.add_argument('--score-thres', type=float, default=0.25)
|
| 54 |
+
ap.add_argument('--nms-thres', type=float, default=0.7)
|
| 55 |
+
ap.add_argument('--providers', type=str, default='AxEngineExecutionProvider')
|
| 56 |
+
opt = ap.parse_args()
|
| 57 |
+
|
| 58 |
+
if not os.path.exists(opt.model_path):
|
| 59 |
+
logger.error(f"Model not found: {opt.model_path}")
|
| 60 |
+
return
|
| 61 |
+
|
| 62 |
+
t0 = time()
|
| 63 |
+
providers = [p.strip() for p in opt.providers.split(",") if p.strip()] or None
|
| 64 |
+
sess = ort.InferenceSession(opt.model_path, providers=providers)
|
| 65 |
+
logger.debug(f"\033[1;31mLoad model time = {(time() - t0) * 1000:.2f} ms\033[0m")
|
| 66 |
+
|
| 67 |
+
inp = sess.get_inputs()[0]
|
| 68 |
+
input_name = inp.name
|
| 69 |
+
m_h, m_w, layout = infer_hw_layout(inp.shape)
|
| 70 |
+
|
| 71 |
+
img = cv2.imread(opt.test_img)
|
| 72 |
+
if img is None:
|
| 73 |
+
logger.error(f"Image not found or unreadable: {opt.test_img}")
|
| 74 |
+
return
|
| 75 |
+
|
| 76 |
+
# Preprocess
|
| 77 |
+
t0 = time()
|
| 78 |
+
orig_h, orig_w = img.shape[:2]
|
| 79 |
+
scale = min(m_h / orig_h, m_w / orig_w)
|
| 80 |
+
new_w, new_h = int(orig_w * scale), int(orig_h * scale)
|
| 81 |
+
|
| 82 |
+
resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
|
| 83 |
+
padded = cv2.copyMakeBorder(
|
| 84 |
+
resized, 0, m_h - new_h, 0, m_w - new_w,
|
| 85 |
+
cv2.BORDER_CONSTANT, value=(127, 127, 127)
|
| 86 |
+
)
|
| 87 |
+
rgb = cv2.cvtColor(padded, cv2.COLOR_BGR2RGB)
|
| 88 |
+
input_tensor = rgb[None, ...].astype(np.uint8) if layout == "NHWC" else np.transpose(rgb, (2, 0, 1))[None, ...].astype(np.uint8)
|
| 89 |
+
logger.debug(f"\033[1;31mPre-process time = {(time() - t0) * 1000:.2f} ms\033[0m")
|
| 90 |
+
|
| 91 |
+
# Inference
|
| 92 |
+
t0 = time()
|
| 93 |
+
ort_outputs = sess.run(None, {input_name: input_tensor})
|
| 94 |
+
out_metas = sess.get_outputs()
|
| 95 |
+
logger.debug(f"\033[1;31mForward time = {(time() - t0) * 1000:.2f} ms\033[0m")
|
| 96 |
+
|
| 97 |
+
# Post-process
|
| 98 |
+
t0 = time()
|
| 99 |
+
strides = (8, 16, 32)
|
| 100 |
+
conf_raw = -np.log(1 / opt.score_thres - 1)
|
| 101 |
+
detections = []
|
| 102 |
+
|
| 103 |
+
output_items = []
|
| 104 |
+
for meta, data in zip(out_metas, ort_outputs):
|
| 105 |
+
shape = list(meta.shape)
|
| 106 |
+
if any(s is None or isinstance(s, str) for s in shape):
|
| 107 |
+
shape = list(data.shape)
|
| 108 |
+
output_items.append((data, shape))
|
| 109 |
+
|
| 110 |
+
# Process each scale (6 outputs: 2 outputs per scale [box, cls])
|
| 111 |
+
for scale_idx, stride in enumerate(strides):
|
| 112 |
+
box_idx = scale_idx * 2
|
| 113 |
+
cls_idx = scale_idx * 2 + 1
|
| 114 |
+
|
| 115 |
+
if box_idx >= len(output_items) or cls_idx >= len(output_items):
|
| 116 |
+
continue
|
| 117 |
+
|
| 118 |
+
box_data, box_shape = output_items[box_idx]
|
| 119 |
+
cls_data, cls_shape = output_items[cls_idx]
|
| 120 |
+
|
| 121 |
+
H, W = box_shape[1], box_shape[2]
|
| 122 |
+
box_channels = box_shape[-1]
|
| 123 |
+
|
| 124 |
+
# Determine if DFL is used (YOLOv8: 4*reg_max = 64)
|
| 125 |
+
reg_max = None
|
| 126 |
+
if box_channels > 4 and box_channels % 4 == 0:
|
| 127 |
+
reg_max = box_channels // 4
|
| 128 |
+
|
| 129 |
+
box_data = box_data[0].reshape(-1, box_channels)
|
| 130 |
+
cls_data = cls_data[0].reshape(-1, cls_shape[-1])
|
| 131 |
+
|
| 132 |
+
# Get max class scores
|
| 133 |
+
cls_scores = np.max(cls_data, axis=1)
|
| 134 |
+
cls_ids = np.argmax(cls_data, axis=1)
|
| 135 |
+
|
| 136 |
+
valid_mask = cls_scores >= conf_raw
|
| 137 |
+
if not np.any(valid_mask):
|
| 138 |
+
continue
|
| 139 |
+
|
| 140 |
+
v_box = box_data[valid_mask]
|
| 141 |
+
v_score = 1 / (1 + np.exp(-cls_scores[valid_mask]))
|
| 142 |
+
v_id = cls_ids[valid_mask]
|
| 143 |
+
|
| 144 |
+
gy, gx = np.indices((H, W))
|
| 145 |
+
anchors = np.stack((gx.ravel(), gy.ravel()), axis=-1).astype(np.float32) + 0.5
|
| 146 |
+
anchors = anchors[valid_mask]
|
| 147 |
+
|
| 148 |
+
# Decode boxes (DFL -> ltrb -> xyxy)
|
| 149 |
+
if reg_max is not None:
|
| 150 |
+
v_box = dfl_decode(v_box, reg_max)
|
| 151 |
+
lt = v_box[:, :2]
|
| 152 |
+
rb = v_box[:, 2:]
|
| 153 |
+
x1y1 = anchors - lt
|
| 154 |
+
x2y2 = anchors + rb
|
| 155 |
+
boxes = np.hstack([x1y1, x2y2]) * stride
|
| 156 |
+
|
| 157 |
+
for i in range(len(boxes)):
|
| 158 |
+
detections.append([*boxes[i], v_score[i], v_id[i]])
|
| 159 |
+
|
| 160 |
+
logger.debug(f"\033[1;31mPost-process time = {(time() - t0) * 1000:.2f} ms\033[0m")
|
| 161 |
+
|
| 162 |
+
if len(detections) == 0:
|
| 163 |
+
logger.info("No detections found.")
|
| 164 |
+
cv2.imwrite(opt.img_save_path, img)
|
| 165 |
+
return
|
| 166 |
+
|
| 167 |
+
detections = np.array(detections)
|
| 168 |
+
|
| 169 |
+
# NMS
|
| 170 |
+
xywh = detections[:, :4].copy()
|
| 171 |
+
xywh[:, 2] = xywh[:, 2] - xywh[:, 0] # w
|
| 172 |
+
xywh[:, 3] = xywh[:, 3] - xywh[:, 1] # h
|
| 173 |
+
|
| 174 |
+
indices = cv2.dnn.NMSBoxes(xywh.tolist(), detections[:, 4].tolist(), opt.score_thres, opt.nms_thres)
|
| 175 |
+
|
| 176 |
+
if len(indices) == 0:
|
| 177 |
+
logger.info("No detections after NMS.")
|
| 178 |
+
cv2.imwrite(opt.img_save_path, img)
|
| 179 |
+
return
|
| 180 |
+
|
| 181 |
+
indices = indices.flatten()
|
| 182 |
+
final_dets = detections[indices]
|
| 183 |
+
|
| 184 |
+
# Scale to original image coordinates
|
| 185 |
+
final_dets[:, :4] = final_dets[:, :4] / scale
|
| 186 |
+
final_dets[:, [0, 2]] = np.clip(final_dets[:, [0, 2]], 0, orig_w)
|
| 187 |
+
final_dets[:, [1, 3]] = np.clip(final_dets[:, [1, 3]], 0, orig_h)
|
| 188 |
+
|
| 189 |
+
# COCO class names
|
| 190 |
+
coco_names = [
|
| 191 |
+
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
|
| 192 |
+
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
|
| 193 |
+
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
|
| 194 |
+
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
|
| 195 |
+
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
|
| 196 |
+
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
|
| 197 |
+
"potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard",
|
| 198 |
+
"cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
|
| 199 |
+
"scissors", "teddy bear", "hair drier", "toothbrush"
|
| 200 |
+
]
|
| 201 |
+
|
| 202 |
+
base_colors = [
|
| 203 |
+
(255, 42, 4), (235, 219, 11), (243, 243, 243), (183, 223, 0), (104, 31, 17),
|
| 204 |
+
(221, 111, 255), (79, 68, 255), (0, 237, 204), (68, 243, 0), (255, 0, 189),
|
| 205 |
+
(255, 180, 0), (186, 0, 221), (255, 255, 0), (0, 192, 38), (179, 255, 1),
|
| 206 |
+
(255, 36, 125), (104, 0, 123), (108, 27, 255), (47, 109, 252), (11, 255, 162),
|
| 207 |
+
]
|
| 208 |
+
|
| 209 |
+
logger.info(f"\033[1;32mDraw Results ({len(final_dets)} objects): \033[0m")
|
| 210 |
+
|
| 211 |
+
for det in final_dets:
|
| 212 |
+
box = det[:4].astype(int)
|
| 213 |
+
conf = det[4]
|
| 214 |
+
cls_id = int(det[5])
|
| 215 |
+
color = [int(c) for c in base_colors[cls_id % len(base_colors)]]
|
| 216 |
+
cls_name = coco_names[cls_id] if cls_id < len(coco_names) else str(cls_id)
|
| 217 |
+
|
| 218 |
+
logger.info(f"({box[0]}, {box[1]}, {box[2]}, {box[3]}) -> {cls_name}: {conf:.2f}")
|
| 219 |
+
|
| 220 |
+
cv2.rectangle(img, (box[0], box[1]), (box[2], box[3]), color, 2)
|
| 221 |
+
label = f"{cls_name} {conf:.2f}"
|
| 222 |
+
cv2.putText(img, label, (box[0], box[1] - 10),
|
| 223 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
|
| 224 |
+
|
| 225 |
+
cv2.imwrite(opt.img_save_path, img)
|
| 226 |
+
logger.info(f"Saved to {opt.img_save_path}")
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
if __name__ == "__main__":
|
| 230 |
+
main()
|
bus.jpg
ADDED
|
Git LFS Details
|
coco_1000.tar
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f3e18a198658270e19ced079de7a404e3478e69c2ef94fb47c87ddf056e6a541
|
| 3 |
+
size 163112960
|
config.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "ONNX",
|
| 3 |
+
"npu_mode": "NPU3",
|
| 4 |
+
"quant": {
|
| 5 |
+
"input_configs": [
|
| 6 |
+
{
|
| 7 |
+
"tensor_name": "images",
|
| 8 |
+
"calibration_dataset": "./coco_1000.tar",
|
| 9 |
+
"calibration_size": 32,
|
| 10 |
+
"calibration_mean": [0, 0, 0],
|
| 11 |
+
"calibration_std": [255.0, 255.0, 255.0]
|
| 12 |
+
}
|
| 13 |
+
],
|
| 14 |
+
"calibration_method": "MinMax",
|
| 15 |
+
"precision_analysis": true,
|
| 16 |
+
"precision_analysis_method":"EndToEnd"
|
| 17 |
+
},
|
| 18 |
+
"input_processors": [
|
| 19 |
+
{
|
| 20 |
+
"tensor_name": "images",
|
| 21 |
+
"tensor_format": "BGR",
|
| 22 |
+
"src_format": "BGR",
|
| 23 |
+
"src_dtype": "U8",
|
| 24 |
+
"src_layout": "NHWC"
|
| 25 |
+
}
|
| 26 |
+
],
|
| 27 |
+
"output_processors": [
|
| 28 |
+
],
|
| 29 |
+
"compiler": {
|
| 30 |
+
"check": 0
|
| 31 |
+
}
|
| 32 |
+
}
|
export_onnx.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import torch
|
| 3 |
+
from ultralytics import YOLO
|
| 4 |
+
from ultralytics.nn.modules.head import Detect
|
| 5 |
+
import os
|
| 6 |
+
import shutil
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def npu_detect_forward(self, x):
|
| 10 |
+
"""
|
| 11 |
+
YOLOv8 Detect Head Modified for NPU.
|
| 12 |
+
|
| 13 |
+
YOLOv8 Detect structure (NOT end2end):
|
| 14 |
+
- cv2: box regression layers (DFL, out channels = 4 * reg_max = 64)
|
| 15 |
+
- cv3: classification layers (out channels = nc, nc=80 for COCO)
|
| 16 |
+
|
| 17 |
+
Output:
|
| 18 |
+
List of Tensors (6 items for 3 scales), Layout: NHWC.
|
| 19 |
+
For each scale:
|
| 20 |
+
- Box_Raw (B, H, W, 4*reg_max), <-- raw DFL box logits (decode in post-process)
|
| 21 |
+
- Cls_Raw (B, H, W, nc), <-- class scores (raw logits), nc=80
|
| 22 |
+
"""
|
| 23 |
+
if not isinstance(x, (list, tuple)):
|
| 24 |
+
x = [x]
|
| 25 |
+
|
| 26 |
+
res = []
|
| 27 |
+
|
| 28 |
+
box_layers = self.cv2
|
| 29 |
+
cls_layers = self.cv3
|
| 30 |
+
|
| 31 |
+
for i in range(self.nl):
|
| 32 |
+
# 1. Box branch (raw DFL logits) - NHWC
|
| 33 |
+
bboxes = box_layers[i](x[i]).permute(0, 2, 3, 1)
|
| 34 |
+
|
| 35 |
+
# 2. Cls branch (raw logits) - NHWC
|
| 36 |
+
scores = cls_layers[i](x[i]).permute(0, 2, 3, 1)
|
| 37 |
+
|
| 38 |
+
res.append(bboxes)
|
| 39 |
+
res.append(scores)
|
| 40 |
+
|
| 41 |
+
return res
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def batch_export_yolov8_det():
|
| 45 |
+
variants = ['n', 's', 'm', 'l', 'x']
|
| 46 |
+
imgsz = 640
|
| 47 |
+
|
| 48 |
+
# Execute Monkey Patch
|
| 49 |
+
Detect.forward = npu_detect_forward
|
| 50 |
+
print("Monkey patch applied for Detect: Output Layout forced to NHWC (Box, Cls for each scale).")
|
| 51 |
+
|
| 52 |
+
for v in variants:
|
| 53 |
+
model_name = f"yolov8{v}"
|
| 54 |
+
pt_path = f"{model_name}.pt"
|
| 55 |
+
onnx_final_name = f"{model_name}_640x640.onnx"
|
| 56 |
+
print(f"\n--- Processing {model_name} ---")
|
| 57 |
+
try:
|
| 58 |
+
# Load model
|
| 59 |
+
model = YOLO(pt_path)
|
| 60 |
+
|
| 61 |
+
# Reapply monkey patch
|
| 62 |
+
Detect.forward = npu_detect_forward
|
| 63 |
+
|
| 64 |
+
# Ensure the model's head also uses the new forward
|
| 65 |
+
if hasattr(model.model, 'model') and len(model.model.model) > 0:
|
| 66 |
+
head = model.model.model[-1]
|
| 67 |
+
if isinstance(head, Detect):
|
| 68 |
+
head.forward = lambda x: npu_detect_forward(head, x)
|
| 69 |
+
|
| 70 |
+
# Execute export
|
| 71 |
+
exported_path = model.export(
|
| 72 |
+
format="onnx",
|
| 73 |
+
imgsz=imgsz,
|
| 74 |
+
dynamic=False,
|
| 75 |
+
opset=11,
|
| 76 |
+
simplify=True,
|
| 77 |
+
nms=False
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
# Move and rename
|
| 81 |
+
if exported_path:
|
| 82 |
+
shutil.move(exported_path, onnx_final_name)
|
| 83 |
+
print(f"Success: {onnx_final_name}")
|
| 84 |
+
except Exception as e:
|
| 85 |
+
print(f"Failed to export {model_name}: {e}")
|
| 86 |
+
import traceback
|
| 87 |
+
traceback.print_exc()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
if __name__ == "__main__":
|
| 91 |
+
batch_export_yolov8_det()
|
onnx_infer.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import onnxruntime as ort
|
| 3 |
+
import cv2
|
| 4 |
+
import numpy as np
|
| 5 |
+
import argparse
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def preprocess_image(image, input_size=(640, 640)):
|
| 10 |
+
"""
|
| 11 |
+
Preprocess image with left-top aligned letterbox (same as official YOLO).
|
| 12 |
+
|
| 13 |
+
Args:
|
| 14 |
+
image: BGR image (H, W, C)
|
| 15 |
+
input_size: (height, width) target size
|
| 16 |
+
|
| 17 |
+
Returns:
|
| 18 |
+
input_tensor: (1, 3, H, W) float32 tensor normalized to [0, 1]
|
| 19 |
+
scale: scale ratio used for resizing
|
| 20 |
+
original_shape: (orig_h, orig_w) original image shape
|
| 21 |
+
"""
|
| 22 |
+
orig_h, orig_w = image.shape[:2]
|
| 23 |
+
m_h, m_w = input_size
|
| 24 |
+
|
| 25 |
+
# Calculate scale (keep aspect ratio)
|
| 26 |
+
scale = min(m_h / orig_h, m_w / orig_w)
|
| 27 |
+
|
| 28 |
+
# Resize
|
| 29 |
+
new_w, new_h = int(orig_w * scale), int(orig_h * scale)
|
| 30 |
+
img_resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
|
| 31 |
+
|
| 32 |
+
# Pad to target size (left-top aligned)
|
| 33 |
+
input_bgr = cv2.copyMakeBorder(
|
| 34 |
+
img_resized, 0, m_h - new_h, 0, m_w - new_w,
|
| 35 |
+
cv2.BORDER_CONSTANT, value=(114, 114, 114)
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
# BGR -> RGB, normalize to [0, 1]
|
| 39 |
+
input_rgb = cv2.cvtColor(input_bgr, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
|
| 40 |
+
|
| 41 |
+
# HWC -> CHW, add batch dimension
|
| 42 |
+
input_tensor = np.transpose(input_rgb, (2, 0, 1))[None, ...]
|
| 43 |
+
|
| 44 |
+
return input_tensor, scale, (orig_h, orig_w)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def softmax(x, axis=-1):
|
| 48 |
+
"""Compute softmax along axis."""
|
| 49 |
+
e_x = np.exp(x - np.max(x, axis=axis, keepdims=True))
|
| 50 |
+
return e_x / np.sum(e_x, axis=axis, keepdims=True)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def dfl_decode(box_pred, reg_max=16):
|
| 54 |
+
"""
|
| 55 |
+
Decode DFL (Distribution Focal Loss) box predictions.
|
| 56 |
+
|
| 57 |
+
Args:
|
| 58 |
+
box_pred: (N, 4 * reg_max) raw box predictions
|
| 59 |
+
reg_max: number of DFL bins
|
| 60 |
+
|
| 61 |
+
Returns:
|
| 62 |
+
(N, 4) decoded ltrb distances
|
| 63 |
+
"""
|
| 64 |
+
N = box_pred.shape[0]
|
| 65 |
+
box_pred = box_pred.reshape(N, 4, reg_max)
|
| 66 |
+
box_pred = softmax(box_pred, axis=-1)
|
| 67 |
+
proj = np.arange(reg_max, dtype=np.float32)
|
| 68 |
+
return np.sum(box_pred * proj, axis=-1) # (N, 4)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def decode_bboxes(bbox_preds, anchors, stride, reg_max=None):
|
| 72 |
+
"""
|
| 73 |
+
Decode bounding boxes from predictions.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
bbox_preds: (N, 4) or (N, 4*reg_max) - distance predictions in ltrb format
|
| 77 |
+
anchors: (N, 2) - anchor points (x, y) with offset 0.5
|
| 78 |
+
stride: scalar - stride value
|
| 79 |
+
reg_max: if not None, apply DFL decoding first
|
| 80 |
+
|
| 81 |
+
Returns:
|
| 82 |
+
boxes: (N, 4) in xyxy format
|
| 83 |
+
"""
|
| 84 |
+
if reg_max is not None and bbox_preds.shape[-1] == 4 * reg_max:
|
| 85 |
+
bbox_preds = dfl_decode(bbox_preds, reg_max)
|
| 86 |
+
|
| 87 |
+
# dist2bbox: ltrb to xyxy
|
| 88 |
+
lt = bbox_preds[:, :2] # left, top
|
| 89 |
+
rb = bbox_preds[:, 2:] # right, bottom
|
| 90 |
+
|
| 91 |
+
x1y1 = anchors - lt
|
| 92 |
+
x2y2 = anchors + rb
|
| 93 |
+
|
| 94 |
+
boxes = np.hstack([x1y1, x2y2]) * stride
|
| 95 |
+
return boxes
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def scale_boxes_lefttop(boxes, scale, orig_shape):
|
| 99 |
+
"""Scale boxes from model output to original image coordinates."""
|
| 100 |
+
boxes = boxes.copy()
|
| 101 |
+
boxes[..., :4] /= scale
|
| 102 |
+
boxes[..., [0, 2]] = np.clip(boxes[..., [0, 2]], 0, orig_shape[1])
|
| 103 |
+
boxes[..., [1, 3]] = np.clip(boxes[..., [1, 3]], 0, orig_shape[0])
|
| 104 |
+
return boxes
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def main():
|
| 108 |
+
parser = argparse.ArgumentParser(description='YOLOv8-Det ONNX Inference')
|
| 109 |
+
parser.add_argument('-m', '--model', type=str, default='yolov8n_640x640.onnx',
|
| 110 |
+
dest='model_path', help='Path to YOLOv8 Detection *.onnx Model.')
|
| 111 |
+
parser.add_argument('-i', '--img', type=str, default='bus.jpg',
|
| 112 |
+
dest='test_img', help='Path to Test Image.')
|
| 113 |
+
parser.add_argument('-o', '--output', type=str, default='result_yolov8_det.jpg',
|
| 114 |
+
dest='img_save_path', help='Path to Save Result Image.')
|
| 115 |
+
parser.add_argument('--score-thres', type=float, default=0.25,
|
| 116 |
+
help='Confidence threshold.')
|
| 117 |
+
parser.add_argument('--nms-thres', type=float, default=0.7,
|
| 118 |
+
help='IoU threshold for NMS.')
|
| 119 |
+
opt = parser.parse_args()
|
| 120 |
+
|
| 121 |
+
if not os.path.exists(opt.model_path):
|
| 122 |
+
print(f"Error: Model not found: {opt.model_path}")
|
| 123 |
+
return
|
| 124 |
+
|
| 125 |
+
if not os.path.exists(opt.test_img):
|
| 126 |
+
print(f"Error: Image not found: {opt.test_img}")
|
| 127 |
+
return
|
| 128 |
+
|
| 129 |
+
# Load ONNX model
|
| 130 |
+
providers = ['CUDAExecutionProvider', 'CPUExecutionProvider']
|
| 131 |
+
try:
|
| 132 |
+
session = ort.InferenceSession(opt.model_path, providers=providers)
|
| 133 |
+
except:
|
| 134 |
+
session = ort.InferenceSession(opt.model_path, providers=['CPUExecutionProvider'])
|
| 135 |
+
|
| 136 |
+
input_name = session.get_inputs()[0].name
|
| 137 |
+
output_names = [o.name for o in session.get_outputs()]
|
| 138 |
+
input_shape = session.get_inputs()[0].shape
|
| 139 |
+
imgsz = (input_shape[2], input_shape[3])
|
| 140 |
+
|
| 141 |
+
# Load image
|
| 142 |
+
img0 = cv2.imread(opt.test_img)
|
| 143 |
+
if img0 is None:
|
| 144 |
+
print(f"Error: Cannot read image: {opt.test_img}")
|
| 145 |
+
return
|
| 146 |
+
|
| 147 |
+
# Preprocess
|
| 148 |
+
img, scale, orig_shape = preprocess_image(img0.copy(), imgsz)
|
| 149 |
+
|
| 150 |
+
# Inference
|
| 151 |
+
outputs = session.run(output_names, {input_name: img})
|
| 152 |
+
|
| 153 |
+
# Post-process
|
| 154 |
+
strides = [8, 16, 32]
|
| 155 |
+
conf_raw = -np.log(1 / opt.score_thres - 1)
|
| 156 |
+
detections = []
|
| 157 |
+
|
| 158 |
+
# Process each scale (6 outputs: 2 outputs per scale [box, cls])
|
| 159 |
+
for scale_idx, stride in enumerate(strides):
|
| 160 |
+
box_idx = scale_idx * 2
|
| 161 |
+
cls_idx = scale_idx * 2 + 1
|
| 162 |
+
|
| 163 |
+
if box_idx >= len(outputs) or cls_idx >= len(outputs):
|
| 164 |
+
continue
|
| 165 |
+
|
| 166 |
+
box_data = outputs[box_idx] # (1, H, W, C) where C = 4 or 4*reg_max
|
| 167 |
+
cls_data = outputs[cls_idx] # (1, H, W, nc)
|
| 168 |
+
|
| 169 |
+
H, W = box_data.shape[1:3]
|
| 170 |
+
box_channels = box_data.shape[-1]
|
| 171 |
+
|
| 172 |
+
# Determine if DFL is used (YOLOv8: 4*reg_max = 64)
|
| 173 |
+
reg_max = None
|
| 174 |
+
if box_channels > 4 and box_channels % 4 == 0:
|
| 175 |
+
reg_max = box_channels // 4
|
| 176 |
+
|
| 177 |
+
# Reshape to (H*W, ...)
|
| 178 |
+
box_data = box_data[0].reshape(-1, box_channels)
|
| 179 |
+
cls_data = cls_data[0].reshape(-1, cls_data.shape[-1])
|
| 180 |
+
|
| 181 |
+
# Get max class scores
|
| 182 |
+
cls_scores = np.max(cls_data, axis=1)
|
| 183 |
+
cls_ids = np.argmax(cls_data, axis=1)
|
| 184 |
+
|
| 185 |
+
# Filter by confidence (on raw logits)
|
| 186 |
+
valid_mask = cls_scores >= conf_raw
|
| 187 |
+
if not np.any(valid_mask):
|
| 188 |
+
continue
|
| 189 |
+
|
| 190 |
+
v_box = box_data[valid_mask]
|
| 191 |
+
v_score = 1 / (1 + np.exp(-cls_scores[valid_mask]))
|
| 192 |
+
v_id = cls_ids[valid_mask]
|
| 193 |
+
|
| 194 |
+
# Generate anchors for this scale (grid indices + 0.5)
|
| 195 |
+
gy, gx = np.indices((H, W))
|
| 196 |
+
anchors = np.stack((gx.ravel(), gy.ravel()), axis=-1).astype(np.float32) + 0.5
|
| 197 |
+
anchors = anchors[valid_mask]
|
| 198 |
+
|
| 199 |
+
# Decode boxes
|
| 200 |
+
boxes = decode_bboxes(v_box, anchors, stride, reg_max)
|
| 201 |
+
|
| 202 |
+
# Store detections
|
| 203 |
+
for i in range(len(boxes)):
|
| 204 |
+
detections.append([*boxes[i], v_score[i], v_id[i]])
|
| 205 |
+
|
| 206 |
+
if len(detections) == 0:
|
| 207 |
+
print("No detections found.")
|
| 208 |
+
cv2.imwrite(opt.img_save_path, img0)
|
| 209 |
+
return
|
| 210 |
+
|
| 211 |
+
detections = np.array(detections)
|
| 212 |
+
|
| 213 |
+
# NMS
|
| 214 |
+
xywh = detections[:, :4].copy()
|
| 215 |
+
xywh[:, 2] = xywh[:, 2] - xywh[:, 0] # w
|
| 216 |
+
xywh[:, 3] = xywh[:, 3] - xywh[:, 1] # h
|
| 217 |
+
|
| 218 |
+
indices = cv2.dnn.NMSBoxes(xywh.tolist(), detections[:, 4].tolist(), opt.score_thres, opt.nms_thres)
|
| 219 |
+
|
| 220 |
+
if len(indices) == 0:
|
| 221 |
+
print("No detections after NMS.")
|
| 222 |
+
cv2.imwrite(opt.img_save_path, img0)
|
| 223 |
+
return
|
| 224 |
+
|
| 225 |
+
indices = indices.flatten()
|
| 226 |
+
detections = detections[indices]
|
| 227 |
+
|
| 228 |
+
# Scale to original image
|
| 229 |
+
detections[:, :4] = scale_boxes_lefttop(detections[:, :4], scale, orig_shape)
|
| 230 |
+
|
| 231 |
+
# COCO class names
|
| 232 |
+
coco_names = [
|
| 233 |
+
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
|
| 234 |
+
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
|
| 235 |
+
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
|
| 236 |
+
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
|
| 237 |
+
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
|
| 238 |
+
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
|
| 239 |
+
"potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard",
|
| 240 |
+
"cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
|
| 241 |
+
"scissors", "teddy bear", "hair drier", "toothbrush"
|
| 242 |
+
]
|
| 243 |
+
|
| 244 |
+
# Official Ultralytics colors (BGR format)
|
| 245 |
+
base_colors = [
|
| 246 |
+
(255, 42, 4), (235, 219, 11), (243, 243, 243), (183, 223, 0), (104, 31, 17),
|
| 247 |
+
(221, 111, 255), (79, 68, 255), (0, 237, 204), (68, 243, 0), (255, 0, 189),
|
| 248 |
+
(255, 180, 0), (186, 0, 221), (255, 255, 0), (0, 192, 38), (179, 255, 1),
|
| 249 |
+
(255, 36, 125), (104, 0, 123), (108, 27, 255), (47, 109, 252), (11, 255, 162),
|
| 250 |
+
]
|
| 251 |
+
|
| 252 |
+
for det in detections:
|
| 253 |
+
box = det[:4].astype(int)
|
| 254 |
+
conf = det[4]
|
| 255 |
+
cls_id = int(det[5])
|
| 256 |
+
color = [int(c) for c in base_colors[cls_id % len(base_colors)]]
|
| 257 |
+
cls_name = coco_names[cls_id] if cls_id < len(coco_names) else str(cls_id)
|
| 258 |
+
|
| 259 |
+
cv2.rectangle(img0, (box[0], box[1]), (box[2], box[3]), color, 2)
|
| 260 |
+
label = f"{cls_name} {conf:.2f}"
|
| 261 |
+
cv2.putText(img0, label, (box[0], box[1] - 10),
|
| 262 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
|
| 263 |
+
|
| 264 |
+
cv2.imwrite(opt.img_save_path, img0)
|
| 265 |
+
print(f"Done! Found {len(detections)} objects. Result saved to {opt.img_save_path}")
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
if __name__ == "__main__":
|
| 269 |
+
main()
|
result_yolov8_det.jpg
ADDED
|
Git LFS Details
|
yolov8l_640x640.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:848ed042b6aae973feef19605808fb964a60ef13e032e1010a54a772c4364332
|
| 3 |
+
size 174782655
|
yolov8m_640x640.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:61fae246d49e5a395d8ddd694a7212cdbcf2be76a2f97d1d80fc011a333e6207
|
| 3 |
+
size 103631997
|
yolov8n_640x640.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3e006d4f9b2b77b0b1846cf8104f9de620419dfa3ab63d651426ab25e50444aa
|
| 3 |
+
size 12673498
|
yolov8s_640x640.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7dcda830f3390e95453076524f56b074cd201ec0044e797a84ed2304a4fe3ec7
|
| 3 |
+
size 44692292
|
yolov8x_640x640.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1a5ef92546eabe7794d0d9f10f6cdea7f84e27a993f667b1d8e47f19810650bd
|
| 3 |
+
size 272911938
|