wzf19947 commited on
Commit
45b2cae
·
1 Parent(s): 0feb214

增加nanodet、更新各平台模型

Browse files
Files changed (40) hide show
  1. model/AX620E/yolov8n_npu1.axmodel → CPP/ax_nanodetplus_qrcode_batch +2 -2
  2. README.md +80 -52
  3. images/qrcode_25.jpg +0 -0
  4. images/qrcode_30.jpg +0 -0
  5. model/AX620E/{yolov8n_npu2.axmodel → nanodet-plus-m_630_npu1.axmodel} +2 -2
  6. model/AX620E/{yolov5n_npu1.axmodel → yolo11n_630_npu1.axmodel} +2 -2
  7. model/AX620E/{yolov5n_npu2.axmodel → yolo12n_630_npu1.axmodel} +2 -2
  8. model/AX620E/yolov10n_630_npu1.axmodel +3 -0
  9. model/AX620E/yolov5n_630_npu1.axmodel +3 -0
  10. model/AX620E/yolov8n_630_npu1.axmodel +3 -0
  11. model/AX620E/yolov9t_630_npu1.axmodel +3 -0
  12. model/AX637/deimv2_hgnetv2_femto_coco_npu1.axmodel +0 -3
  13. model/AX637/nanodet-plus-m_637_npu1.axmodel +3 -0
  14. model/AX637/yolo11n_637_npu1.axmodel +3 -0
  15. model/AX637/yolo12n_637_npu1.axmodel +3 -0
  16. model/AX637/yolov10n_637_npu1.axmodel +3 -0
  17. model/AX637/yolov5n_637_npu1.axmodel +3 -0
  18. model/AX637/yolov5n_npu1.axmodel +0 -3
  19. model/AX637/yolov8n_637_npu1.axmodel +3 -0
  20. model/AX637/yolov8n_npu1.axmodel +0 -3
  21. model/AX637/yolov9t_637_npu1.axmodel +3 -0
  22. model/AX650/deimv2_femto_650_npu1_u16.axmodel +3 -0
  23. model/AX650/deimv2_hgnetv2_femto_coco_npu3.axmodel +0 -3
  24. model/AX650/nanodet-plus-m_650_npu1.axmodel +3 -0
  25. model/AX650/yolo11n_650_npu1.axmodel +3 -0
  26. model/AX650/yolo12n_650_npu1.axmodel +3 -0
  27. model/AX650/yolov10n_650_npu1.axmodel +3 -0
  28. model/AX650/yolov5n_650_npu1.axmodel +3 -0
  29. model/AX650/yolov5n_npu3.axmodel +0 -3
  30. model/AX650/yolov8n_650_npu1.axmodel +3 -0
  31. model/AX650/yolov8n_npu3.axmodel +0 -3
  32. model/AX650/yolov9t_650_npu1.axmodel +3 -0
  33. model/CPP/deimv2_hgnetv2_femto_coco_cpp_npu3.axmodel +0 -3
  34. model/CPP/yolov5n_cpp_npu3.axmodel +0 -3
  35. model/CPP/yolov8n_cpp_npu3.axmodel +0 -3
  36. python/QRCode_axmodel_infer_DEIMv2.py +17 -16
  37. python/QRCode_axmodel_infer_Nanodet.py +715 -0
  38. python/QRCode_axmodel_infer_v5.py +8 -5
  39. python/QRCode_axmodel_infer_v8.py +4 -3
  40. python/QRCode_onnx_infer_Nanodet.py +718 -0
model/AX620E/yolov8n_npu1.axmodel → CPP/ax_nanodetplus_qrcode_batch RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:f0d52b2b3692be7e00e9ce710ed3155e47499a0f580749f66ffe9cf32230bfb7
3
- size 3548343
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b736080ed5b459822c615e815c7f5914a1b21ea1d20644780ffcf5d74ac82813
3
+ size 6424712
README.md CHANGED
@@ -9,7 +9,7 @@ This version of QRCode detetion model has been converted to run on the Axera NPU
9
 
10
  This model has been optimized with the following LoRA:
11
 
12
- Compatible with Pulsar2 version: 4.2
13
 
14
  ## Convert tools links:
15
 
@@ -19,7 +19,7 @@ For those who are interested in model conversion, you can try to export axmodel
19
 
20
  - [Pulsar2 Link, How to Convert ONNX to axmodel](https://pulsar2-docs.readthedocs.io/en/latest/pulsar2/introduction.html)
21
 
22
- - [The repo of AXera Platform](https://github.com/AXERA-TECH/ax-samples),which you can compile the c++ demo
23
 
24
  ## Support Platform
25
 
@@ -30,19 +30,34 @@ For those who are interested in model conversion, you can try to export axmodel
30
  - [爱芯派2](https://axera-pi-2-docs-cn.readthedocs.io/zh-cn/latest/index.html)
31
  - [Module-LLM](https://docs.m5stack.com/zh_CN/module/Module-LLM)
32
  - [LLM630 Compute Kit](https://docs.m5stack.com/zh_CN/core/LLM630%20Compute%20Kit)
 
33
 
34
  |Chips|model|cost|
35
  |--|--|--|
36
- ||yolov5n|0.73 ms|
37
- ||yolov8n|1.31 ms|
38
- ||yolov9t|1.89 ms|
39
- |AX650|yolov10n|1.44 ms|
40
- ||yolo11n|1.39 ms|
41
- ||yolo12n|2.49 ms|
42
- ||DEIMv2_femto(u16)|1.79 ms|
43
-
44
- |AX630C|yolov5n|2.57 ms|
45
- |AX630C|yolov8n|5.89 ms|
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  ## How to use
48
 
@@ -50,46 +65,59 @@ Download all files from this repository to the device
50
 
51
  ```
52
 
53
- root@ax650:~/QRCode_det# tree
54
  .
55
- |-- CPP
56
- | |-- ax_deimv2_qrcode_batch
57
- | |-- ax_yolov5_qrcode_batch
58
- | `-- ax_yolov8_qrcode_batch
59
- |-- README.md
60
- |-- images
61
- | |-- qrcode_01.jpg
62
- | |-- qrcode_02.jpg
63
- | |-- qrcode_03.jpg
64
- | ....
65
- | `-- qrcode_55.jpg
66
- |-- model
67
- | |-- AX620E
68
- | | |-- yolov5n_npu1.axmodel
69
- | | |-- yolov5n_npu2.axmodel
70
- | | |-- yolov8n_npu1.axmodel
71
- | | `-- yolov8n_npu2.axmodel
72
- | |-- AX637
73
- | | |-- deimv2_hgnetv2_femto_coco_npu1.axmodel
74
- | | |-- yolov5n_npu1.axmodel
75
- | | `-- yolov8n_npu1.axmodel
76
- | |-- AX650
77
- | | |-- deimv2_hgnetv2_femto_coco_npu3.axmodel
78
- | | |-- yolov5n_npu3.axmodel
79
- | | `-- yolov8n_npu3.axmodel
80
- | `-- CPP
81
- | |-- deimv2_hgnetv2_femto_coco_cpp_npu3.axmodel
82
- | |-- yolov5n_cpp_npu3.axmodel
83
- | `-- yolov8n_cpp_npu3.axmodel
84
- |-- python
85
- | |-- QRCode_axmodel_infer_DEIMv2.py
86
- | |-- QRCode_axmodel_infer_v5.py
87
- | |-- QRCode_axmodel_infer_v8.py
88
- | |-- QRCode_onnx_infer_DEIMv2.py
89
- | |-- QRCode_onnx_infer_v5.py
90
- | |-- QRCode_onnx_infer_v8.py
91
- | `-- requirements.txt
92
- `-- result.png
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  ```
95
 
@@ -135,7 +163,7 @@ Output:
135
 
136
  ##### C++
137
  ```
138
- ./ax_xxx_qrcode_batch -m xxx_cpp_npu3.axmodel -i images/ -o res/
139
  ```
140
 
141
  Output:
 
9
 
10
  This model has been optimized with the following LoRA:
11
 
12
+ Compatible with Pulsar2 version: 5.1
13
 
14
  ## Convert tools links:
15
 
 
19
 
20
  - [Pulsar2 Link, How to Convert ONNX to axmodel](https://pulsar2-docs.readthedocs.io/en/latest/pulsar2/introduction.html)
21
 
22
+ - [The repo of AXera Platform](https://github.com/AXERA-TECH/ax-samples),which you can learn how to compile the C++ demo
23
 
24
  ## Support Platform
25
 
 
30
  - [爱芯派2](https://axera-pi-2-docs-cn.readthedocs.io/zh-cn/latest/index.html)
31
  - [Module-LLM](https://docs.m5stack.com/zh_CN/module/Module-LLM)
32
  - [LLM630 Compute Kit](https://docs.m5stack.com/zh_CN/core/LLM630%20Compute%20Kit)
33
+ - AX637
34
 
35
  |Chips|model|cost|
36
  |--|--|--|
37
+ ||yolov5n|1.73 ms|
38
+ ||yolov8n|3.64 ms|
39
+ ||yolov9t|4.75 ms|
40
+ |AX650|yolov10n|3.67 ms|
41
+ ||yolo11n|3.42 ms|
42
+ ||yolo12n|6.87 ms|
43
+ ||NanodetPlus|2.16 ms|
44
+ ||DEIMv2_femto(u16)|3.76 ms|
45
+ |||
46
+ ||yolov5n|5.79 ms|
47
+ ||yolov8n|9.26 ms|
48
+ ||yolov9t|11.6 ms|
49
+ |AX630C|yolov10n|9.71 ms|
50
+ ||yolo11n|9.65 ms|
51
+ ||yolo12n|20.24 ms|
52
+ ||NanodetPlus|5.93 ms|
53
+ |||
54
+ ||yolov5n|2.11 ms|
55
+ ||yolov8n|4.04 ms|
56
+ ||yolov9t|4.91 ms|
57
+ |AX637|yolov10n|4.05 ms|
58
+ ||yolo11n|3.84 ms|
59
+ ||yolo12n|6.40 ms|
60
+ ||NanodetPlus|2.38 ms|
61
 
62
  ## How to use
63
 
 
65
 
66
  ```
67
 
 
68
  .
69
+ ├── config.json
70
+ ├── CPP
71
+ │   ├── ax_deimv2_qrcode_batch
72
+ │   ├── ax_nanodetplus_qrcode_batch
73
+ │   ├── ax_yolov5_qrcode_batch
74
+ │   └── ax_yolov8_qrcode_batch
75
+ ├── cpp_result.png
76
+ ├── images
77
+ │   ├── qrcode_01.jpg
78
+ │   ├── qrcode_02.jpg
79
+ │   ├── qrcode_03.jpg
80
+ | ├── ...
81
+ │   └── qrcode_55.jpg
82
+ ├── model
83
+ │   ├── AX620E
84
+ │   │   ├── nanodet-plus-m_630_npu1.axmodel
85
+ │   │   ├── yolo11n_630_npu1.axmodel
86
+ │   │   ├── yolo12n_630_npu1.axmodel
87
+ │   │   ├── yolov10n_630_npu1.axmodel
88
+ │   │   ├── yolov5n_630_npu1.axmodel
89
+ │   │   ├── yolov8n_630_npu1.axmodel
90
+ │   │   └── yolov9t_630_npu1.axmodel
91
+ │   ├── AX637
92
+ │   │   ├── nanodet-plus-m_637_npu1.axmodel
93
+ │   │   ├── yolo11n_637_npu1.axmodel
94
+ │   │   ├── yolo12n_637_npu1.axmodel
95
+ │   │   ├── yolov10n_637_npu1.axmodel
96
+ │   │   ├── yolov5n_637_npu1.axmodel
97
+ │   │   ├── yolov8n_637_npu1.axmodel
98
+ │   │   └── yolov9t_637_npu1.axmodel
99
+ │   └── AX650
100
+ │   ├── deimv2_femto_650_npu1_u16.axmodel
101
+ │   ├── nanodet-plus-m_650_npu1.axmodel
102
+ │   ├── yolo11n_650_npu1.axmodel
103
+ │   ├── yolo12n_650_npu1.axmodel
104
+ │   ├── yolov10n_650_npu1.axmodel
105
+ │   ├── yolov5n_650_npu1.axmodel
106
+ │   ├── yolov8n_650_npu1.axmodel
107
+ │   └── yolov9t_650_npu1.axmodel
108
+ ├── py_result.png
109
+ ├── python
110
+ │   ├── QRCode_axmodel_infer_DEIMv2.py
111
+ │   ├── QRCode_axmodel_infer_Nanodet.py
112
+ │   ├── QRCode_axmodel_infer_v5.py
113
+ │   ├── QRCode_axmodel_infer_v8.py
114
+ │   ├── QRCode_onnx_infer_DEIMv2.py
115
+ │   ├── QRCode_onnx_infer_Nanodet.py
116
+ │   ├── QRCode_onnx_infer_v5.py
117
+ │   ├── QRCode_onnx_infer_v8.py
118
+ │   └���─ requirements.txt
119
+ └── README.md
120
+
121
 
122
  ```
123
 
 
163
 
164
  ##### C++
165
  ```
166
+ ./ax_xxx_qrcode_batch -m xxx_npu1.axmodel -i images/
167
  ```
168
 
169
  Output:
images/qrcode_25.jpg DELETED
Binary file (80.1 kB)
 
images/qrcode_30.jpg DELETED
Binary file (78.9 kB)
 
model/AX620E/{yolov8n_npu2.axmodel → nanodet-plus-m_630_npu1.axmodel} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:e18042a394d440cc6fc73a7b35871a189081961f3a5cb72301e76edc9799d3a3
3
- size 3208843
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5480ca6e968f13fc3146cea39d3d5043e251fa9d7c4971a3ec813daefbd67a5b
3
+ size 1899764
model/AX620E/{yolov5n_npu1.axmodel → yolo11n_630_npu1.axmodel} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:cb93d21015ca639308bdd42cc3275c5776cfdd5d67c822b08d1d10843db767a9
3
- size 2089800
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0af92b5e73b0c419421c58be8c942b48e199358ec021714e11830b764a5d8d43
3
+ size 3290722
model/AX620E/{yolov5n_npu2.axmodel → yolo12n_630_npu1.axmodel} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:a73396b9bc57a097554cf5d9c54f745b2ea4c0f24a3644f0317d13cd415bb4e2
3
- size 1854620
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f57663e403e10ec989405d8e9cd93bea32c38ce1e67e6fcc9f1bcfd486bc9782
3
+ size 4468598
model/AX620E/yolov10n_630_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5749f1768cf1f3f01f345975453ac924d81b8005b558ff0cd0fe5dd4b6f96bd8
3
+ size 2952122
model/AX620E/yolov5n_630_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:178447ef748f9e89668094b588b42d64e28a3dccee50247548cbaceac2edf253
3
+ size 2070769
model/AX620E/yolov8n_630_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6449c6f85e949e2621906807b3a36e7c02aa1cb03748def2d813dbdff285ead7
3
+ size 3548856
model/AX620E/yolov9t_630_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d861ef093708e85e2b3c8bcf7963343c3583d33fba47cc3fece00fd8e6a09571
3
+ size 2979512
model/AX637/deimv2_hgnetv2_femto_coco_npu1.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:1a15e46fcca5f762a7076aefc8eab68bb9db0d072dd42270b215968633d9a009
3
- size 1568889
 
 
 
 
model/AX637/nanodet-plus-m_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8e1cafbbae5cc1ea1491170518dcb7daffd9ff04b3f95514f0a1fd1f56f78351
3
+ size 3584897
model/AX637/yolo11n_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2c020f4d48a8196292422336c47ba125bf1752ca645b42052e480c4cd65a73fb
3
+ size 2874030
model/AX637/yolo12n_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8c62fde3b1ff6d2c01a4580a89b797e19be52ebceb9d5053f3e45ffcd6191d36
3
+ size 4164182
model/AX637/yolov10n_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4a2bda66e4b6b69bdc8ed88b55d0f5e41faca9c88dfa51a055c1cefa234cc5f4
3
+ size 3005816
model/AX637/yolov5n_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e8ba900b4a1a6e37f3ef9890305a6d35d01d06b7a8c3822ea872291ebfd5fa2b
3
+ size 1865855
model/AX637/yolov5n_npu1.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:13f44b48e782769a57aba01e07d6724f60a507dbdb0ecf35e44f3838ac58798e
3
- size 1865062
 
 
 
 
model/AX637/yolov8n_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4c9dd054118ca8b52041accd0c5a5147e6344f91a68b26c35e3f98d9460db558
3
+ size 3178420
model/AX637/yolov8n_npu1.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:80a03632e1c762094f7bbc6115674270b5c23db8fcc8dcdaf65443988b6e0292
3
- size 3178331
 
 
 
 
model/AX637/yolov9t_637_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f7bbdcce782f6f56fc6a0dd3aa9206a1fdfbc5cafc10c6a180f48a9aea878b5f
3
+ size 2771528
model/AX650/deimv2_femto_650_npu1_u16.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dd252aa527a23eb1794d11af264d4650dfe9248eac84f87a5bfb555a3f23a7aa
3
+ size 1753703
model/AX650/deimv2_hgnetv2_femto_coco_npu3.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:1bd80d8338290a4e88c26bf994604559c094838227286382959966c497db738e
3
- size 2204278
 
 
 
 
model/AX650/nanodet-plus-m_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:765665a81790917431660f6c5c5a3f57ea6e4360951c863c8fac5e5c7e6f655d
3
+ size 2120369
model/AX650/yolo11n_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fb71cb0f04a65c489425d1b6c83a28a7510cde99062d81147106e5b798805647
3
+ size 2995284
model/AX650/yolo12n_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6a743c1f7d9476cf284b4cb4f7c0731fd366a81d8e4d34fc9a334cf4a157603
3
+ size 3392100
model/AX650/yolov10n_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4c0a84073c9488b0943f8e2c0c479583e1c89bd8589e31646767418e788a5533
3
+ size 2735472
model/AX650/yolov5n_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:358edd177ed91eda32047cee72283d11201e4d1996a44e5631008abb224e015e
3
+ size 1925943
model/AX650/yolov5n_npu3.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:34ea4a3e74bd9dde388086cb60cd51f9d53c7734bf52f2e21de9a6d71de0b194
3
- size 2003219
 
 
 
 
model/AX650/yolov8n_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c10cde32e29ecc33395e2cfeab4645c83c9a45afcb0e946f02993a30bcf63a0
3
+ size 3247992
model/AX650/yolov8n_npu3.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:d05be5bad116511e552849e16793b8b3ea19238a8fa1dc00935a18fb58207e81
3
- size 3488708
 
 
 
 
model/AX650/yolov9t_650_npu1.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3fcf218afc4ae555daa137570e3ff16088ffd9b3de5fc5537690ba93091ca5b8
3
+ size 2706764
model/CPP/deimv2_hgnetv2_femto_coco_cpp_npu3.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a7559ef2f367d70e54b46cdbdf2fa39e139387064799cc73f00b613a30b3133d
3
- size 2197074
 
 
 
 
model/CPP/yolov5n_cpp_npu3.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:2a59402253a3e4f14c6ff93d939f6bdb889b86858dcd729c6f2d82efe96f6da1
3
- size 2001946
 
 
 
 
model/CPP/yolov8n_cpp_npu3.axmodel DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:44eef3ec1e8d5d7cb8925dcfe4b25dd1826445baa18b0cf4db4deb5e87f7412e
3
- size 3487467
 
 
 
 
python/QRCode_axmodel_infer_DEIMv2.py CHANGED
@@ -227,18 +227,19 @@ def process_image(sess, im_pil, post_processor, size=640, model_size='s'):
227
  resized_im_pil, ratio, pad_w, pad_h = resize_with_aspect_ratio(im_pil, size)
228
  orig_size = torch.tensor([[resized_im_pil.size[1], resized_im_pil.size[0]]])
229
 
230
- transforms = T.Compose([
231
- T.ToTensor(),
232
- T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
233
- if model_size not in ['atto', 'femto', 'pico', 'n']
234
- else T.Lambda(lambda x: x)
235
- ])
236
-
237
- im_data = transforms(resized_im_pil).unsqueeze(0)
238
-
 
239
  output = sess.run(
240
  output_names=None,
241
- input_feed={'images': im_data.numpy()}
242
  )
243
 
244
  output = {"pred_logits": torch.from_numpy(output[0]), "pred_boxes": torch.from_numpy(output[1])}
@@ -262,10 +263,10 @@ class QRCodeDecoder:
262
  for idx, region in enumerate(regions):
263
  x1, y1, x2, y2 = region
264
  # 外扩缓解检测截断,视检测情况而定
265
- # x1-=15
266
- # y1-=15
267
- # x2+=15
268
- # y2+=15
269
  # 裁剪图像
270
  cropped = image[y1:y2, x1:x2]
271
  if cropped.size > 0:
@@ -309,12 +310,12 @@ class QRCodeDecoder:
309
  if __name__ == '__main__':
310
 
311
  #load the ONNX model
312
- sess = axe.InferenceSession('deimv2_hgnetv2_femto_coco_npu3.axmodel')
313
  size = sess.get_inputs()[0].shape[2]
314
 
315
  #QRCode decoder
316
  decoder = QRCodeDecoder()
317
- img_path = './images'
318
  det_path='./DEIMv2_det_res'
319
  crop_path='./DEIMv2_crop_res'
320
 
 
227
  resized_im_pil, ratio, pad_w, pad_h = resize_with_aspect_ratio(im_pil, size)
228
  orig_size = torch.tensor([[resized_im_pil.size[1], resized_im_pil.size[0]]])
229
 
230
+ # transforms = T.Compose([
231
+ # T.ToTensor(),
232
+ # T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
233
+ # if model_size not in ['atto', 'femto', 'pico', 'n']
234
+ # else T.Lambda(lambda x: x)
235
+ # ])
236
+
237
+ # im_data = transforms(resized_im_pil).unsqueeze(0)
238
+ im_data = np.array(resized_im_pil)
239
+ im_data = np.expand_dims(im_data, axis=0).astype(np.uint8)
240
  output = sess.run(
241
  output_names=None,
242
+ input_feed={'images': im_data}
243
  )
244
 
245
  output = {"pred_logits": torch.from_numpy(output[0]), "pred_boxes": torch.from_numpy(output[1])}
 
263
  for idx, region in enumerate(regions):
264
  x1, y1, x2, y2 = region
265
  # 外扩缓解检测截断,视检测情况而定
266
+ x1-=15
267
+ y1-=15
268
+ x2+=15
269
+ y2+=15
270
  # 裁剪图像
271
  cropped = image[y1:y2, x1:x2]
272
  if cropped.size > 0:
 
310
  if __name__ == '__main__':
311
 
312
  #load the ONNX model
313
+ sess = axe.InferenceSession('deimv2_femto_650_npu1_u16.axmodel')
314
  size = sess.get_inputs()[0].shape[2]
315
 
316
  #QRCode decoder
317
  decoder = QRCodeDecoder()
318
+ img_path = './qrcode_test'
319
  det_path='./DEIMv2_det_res'
320
  crop_path='./DEIMv2_crop_res'
321
 
python/QRCode_axmodel_infer_Nanodet.py ADDED
@@ -0,0 +1,715 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import glob
3
+ import time
4
+ import cv2
5
+ import numpy as np
6
+ import pyzbar.pyzbar as pyzbar
7
+ import axengine as axe
8
+ import math
9
+
10
+ names = ["QRCode"]
11
+
12
+ def sigmoid(x):
13
+ return 1 / (1 + np.exp(-x))
14
+ def model_load(model):
15
+ session = axe.InferenceSession(model)
16
+ input_name = session.get_inputs()[0].name
17
+ output_names = [ x.name for x in session.get_outputs()]
18
+ return session, output_names
19
+
20
+ def data_process_cv2(frame, input_shape):
21
+ im0 = cv2.imread(frame)
22
+ img = cv2.resize(im0, input_shape, interpolation=cv2.INTER_AREA)
23
+ org_data = img.copy()
24
+ img = np.ascontiguousarray(img)
25
+ img = np.expand_dims(img, 0)
26
+ return img, im0, org_data
27
+
28
+
29
+ def multiclass_nms(
30
+ multi_bboxes, multi_scores, score_thr, nms_cfg, max_num=-1, score_factors=None
31
+ ):
32
+ num_classes = multi_scores.shape[1] - 1 # exclude background
33
+
34
+ # Reshape bboxes
35
+ if multi_bboxes.shape[1] > 4:
36
+ # (N, 4*C) -> (N, C, 4)
37
+ bboxes = multi_bboxes.reshape(multi_scores.shape[0], -1, 4)
38
+ else:
39
+ # (N, 4) -> (N, 1, 4) -> (N, C, 4) via repeat
40
+ bboxes = np.tile(multi_bboxes[:, None, :], (1, num_classes, 1))
41
+
42
+ scores = multi_scores[:, :-1].copy() # (N, C)
43
+
44
+ # Apply score factors if provided
45
+ if score_factors is not None:
46
+ scores = scores * score_factors[:, None]
47
+
48
+ # Filter by score threshold
49
+ valid_mask = scores > score_thr # (N, C)
50
+
51
+ # Get indices where valid
52
+ valid_indices = np.where(valid_mask)
53
+ if len(valid_indices[0]) == 0:
54
+ # No valid boxes
55
+ return np.zeros((0, 5), dtype=np.float32), np.zeros((0,), dtype=np.int64)
56
+
57
+ # Extract valid bboxes, scores, labels
58
+ bbox_indices, class_indices = valid_indices
59
+ bboxes_valid = bboxes[bbox_indices, class_indices] # (K, 4)
60
+ scores_valid = scores[valid_indices] # (K,)
61
+ labels_valid = class_indices.astype(np.int64) # (K,)
62
+
63
+ # Concatenate bboxes and scores for NMS input: (K, 5)
64
+ dets_input = np.concatenate([bboxes_valid, scores_valid[:, None]], axis=1) # (K, 5)
65
+
66
+ # Perform NMS (you need a NumPy NMS implementation)
67
+ keep = nms_numpy(dets_input, iou_threshold=nms_cfg.get('iou_threshold', 0.5))
68
+
69
+ dets = dets_input[keep]
70
+ labels = labels_valid[keep]
71
+
72
+ if max_num > 0 and len(keep) > max_num:
73
+ dets = dets[:max_num]
74
+ labels = labels[:max_num]
75
+
76
+ return dets, labels
77
+ def nms_numpy(dets, iou_threshold=0.5):
78
+ if dets.size == 0:
79
+ return []
80
+
81
+ x1 = dets[:, 0]
82
+ y1 = dets[:, 1]
83
+ x2 = dets[:, 2]
84
+ y2 = dets[:, 3]
85
+ scores = dets[:, 4]
86
+
87
+ areas = (x2 - x1 + 1) * (y2 - y1 + 1)
88
+ order = scores.argsort()[::-1] # descending order
89
+
90
+ keep = []
91
+ while order.size > 0:
92
+ i = order[0]
93
+ keep.append(i)
94
+
95
+ xx1 = np.maximum(x1[i], x1[order[1:]])
96
+ yy1 = np.maximum(y1[i], y1[order[1:]])
97
+ xx2 = np.minimum(x2[i], x2[order[1:]])
98
+ yy2 = np.minimum(y2[i], y2[order[1:]])
99
+
100
+ w = np.maximum(0.0, xx2 - xx1 + 1)
101
+ h = np.maximum(0.0, yy2 - yy1 + 1)
102
+ inter = w * h
103
+
104
+ iou = inter / (areas[i] + areas[order[1:]] - inter)
105
+ inds = np.where(iou <= iou_threshold)[0]
106
+ order = order[inds + 1]
107
+
108
+ return keep
109
+ def batched_nms(boxes, scores, idxs, nms_cfg, class_agnostic=False):
110
+ nms_cfg_ = nms_cfg.copy()
111
+ class_agnostic = nms_cfg_.pop("class_agnostic", class_agnostic)
112
+
113
+ if class_agnostic:
114
+ boxes_for_nms = boxes
115
+ else:
116
+ max_coordinate = boxes.max()
117
+ # offsets = idxs * (max_coordinate + 1)
118
+ offsets = idxs.astype(boxes.dtype) * (max_coordinate + 1)
119
+ boxes_for_nms = boxes + offsets[:, None]
120
+
121
+ nms_type = nms_cfg_.pop("type", "nms") # unused in numpy version
122
+ split_thr = nms_cfg_.pop("split_thr", 10000)
123
+
124
+ if len(boxes_for_nms) < split_thr:
125
+ # Call your NumPy NMS function (e.g., nms_numpy)
126
+ keep = nms_numpy(boxes_for_nms, scores, **nms_cfg_)
127
+ keep = np.array(keep, dtype=np.int64)
128
+ boxes = boxes[keep]
129
+ scores = scores[keep]
130
+ else:
131
+ # Large case: process per class/group
132
+ total_mask = np.zeros(scores.shape, dtype=bool)
133
+ unique_ids = np.unique(idxs)
134
+
135
+ for id_val in unique_ids:
136
+ mask = (idxs == id_val)
137
+ mask_indices = np.where(mask)[0] # indices where condition is True
138
+
139
+ if len(mask_indices) == 0:
140
+ continue
141
+
142
+ keep_in_group = nms_numpy(
143
+ boxes_for_nms[mask_indices],
144
+ scores[mask_indices],
145
+ **nms_cfg_
146
+ )
147
+ keep_in_group = np.array(keep_in_group, dtype=np.int64)
148
+ selected_global_indices = mask_indices[keep_in_group]
149
+ total_mask[selected_global_indices] = True
150
+
151
+ keep = np.where(total_mask)[0]
152
+ # Sort by scores descending
153
+ sorted_indices = np.argsort(-scores[keep]) # negative for descending
154
+ keep = keep[sorted_indices]
155
+ boxes = boxes[keep]
156
+ scores = scores[keep]
157
+
158
+ # Concatenate boxes and scores -> (K, 5)
159
+ dets = np.concatenate([boxes, scores[:, None]], axis=-1)
160
+ return dets, keep
161
+
162
+ def scale_boxes_no_letter(img1_shape, boxes, img0_shape):
163
+ gain = (img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])
164
+
165
+ boxes[..., [0, 2]] /= gain[1]
166
+ boxes[..., [1, 3]] /= gain[0]
167
+ clip_boxes(boxes, img0_shape)
168
+ return boxes
169
+
170
+ def clip_boxes(boxes, shape):
171
+ boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1])
172
+ boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0])
173
+
174
+ _COLORS = (
175
+ np.array(
176
+ [
177
+ 0.000,
178
+ 0.447,
179
+ 0.741,
180
+ 0.850,
181
+ 0.325,
182
+ 0.098,
183
+ 0.929,
184
+ 0.694,
185
+ 0.125,
186
+ 0.494,
187
+ 0.184,
188
+ 0.556,
189
+ 0.466,
190
+ 0.674,
191
+ 0.188,
192
+ 0.301,
193
+ 0.745,
194
+ 0.933,
195
+ 0.635,
196
+ 0.078,
197
+ 0.184,
198
+ 0.300,
199
+ 0.300,
200
+ 0.300,
201
+ 0.600,
202
+ 0.600,
203
+ 0.600,
204
+ 1.000,
205
+ 0.000,
206
+ 0.000,
207
+ 1.000,
208
+ 0.500,
209
+ 0.000,
210
+ 0.749,
211
+ 0.749,
212
+ 0.000,
213
+ 0.000,
214
+ 1.000,
215
+ 0.000,
216
+ 0.000,
217
+ 0.000,
218
+ 1.000,
219
+ 0.667,
220
+ 0.000,
221
+ 1.000,
222
+ 0.333,
223
+ 0.333,
224
+ 0.000,
225
+ 0.333,
226
+ 0.667,
227
+ 0.000,
228
+ 0.333,
229
+ 1.000,
230
+ 0.000,
231
+ 0.667,
232
+ 0.333,
233
+ 0.000,
234
+ 0.667,
235
+ 0.667,
236
+ 0.000,
237
+ 0.667,
238
+ 1.000,
239
+ 0.000,
240
+ 1.000,
241
+ 0.333,
242
+ 0.000,
243
+ 1.000,
244
+ 0.667,
245
+ 0.000,
246
+ 1.000,
247
+ 1.000,
248
+ 0.000,
249
+ 0.000,
250
+ 0.333,
251
+ 0.500,
252
+ 0.000,
253
+ 0.667,
254
+ 0.500,
255
+ 0.000,
256
+ 1.000,
257
+ 0.500,
258
+ 0.333,
259
+ 0.000,
260
+ 0.500,
261
+ 0.333,
262
+ 0.333,
263
+ 0.500,
264
+ 0.333,
265
+ 0.667,
266
+ 0.500,
267
+ 0.333,
268
+ 1.000,
269
+ 0.500,
270
+ 0.667,
271
+ 0.000,
272
+ 0.500,
273
+ 0.667,
274
+ 0.333,
275
+ 0.500,
276
+ 0.667,
277
+ 0.667,
278
+ 0.500,
279
+ 0.667,
280
+ 1.000,
281
+ 0.500,
282
+ 1.000,
283
+ 0.000,
284
+ 0.500,
285
+ 1.000,
286
+ 0.333,
287
+ 0.500,
288
+ 1.000,
289
+ 0.667,
290
+ 0.500,
291
+ 1.000,
292
+ 1.000,
293
+ 0.500,
294
+ 0.000,
295
+ 0.333,
296
+ 1.000,
297
+ 0.000,
298
+ 0.667,
299
+ 1.000,
300
+ 0.000,
301
+ 1.000,
302
+ 1.000,
303
+ 0.333,
304
+ 0.000,
305
+ 1.000,
306
+ 0.333,
307
+ 0.333,
308
+ 1.000,
309
+ 0.333,
310
+ 0.667,
311
+ 1.000,
312
+ 0.333,
313
+ 1.000,
314
+ 1.000,
315
+ 0.667,
316
+ 0.000,
317
+ 1.000,
318
+ 0.667,
319
+ 0.333,
320
+ 1.000,
321
+ 0.667,
322
+ 0.667,
323
+ 1.000,
324
+ 0.667,
325
+ 1.000,
326
+ 1.000,
327
+ 1.000,
328
+ 0.000,
329
+ 1.000,
330
+ 1.000,
331
+ 0.333,
332
+ 1.000,
333
+ 1.000,
334
+ 0.667,
335
+ 1.000,
336
+ 0.333,
337
+ 0.000,
338
+ 0.000,
339
+ 0.500,
340
+ 0.000,
341
+ 0.000,
342
+ 0.667,
343
+ 0.000,
344
+ 0.000,
345
+ 0.833,
346
+ 0.000,
347
+ 0.000,
348
+ 1.000,
349
+ 0.000,
350
+ 0.000,
351
+ 0.000,
352
+ 0.167,
353
+ 0.000,
354
+ 0.000,
355
+ 0.333,
356
+ 0.000,
357
+ 0.000,
358
+ 0.500,
359
+ 0.000,
360
+ 0.000,
361
+ 0.667,
362
+ 0.000,
363
+ 0.000,
364
+ 0.833,
365
+ 0.000,
366
+ 0.000,
367
+ 1.000,
368
+ 0.000,
369
+ 0.000,
370
+ 0.000,
371
+ 0.167,
372
+ 0.000,
373
+ 0.000,
374
+ 0.333,
375
+ 0.000,
376
+ 0.000,
377
+ 0.500,
378
+ 0.000,
379
+ 0.000,
380
+ 0.667,
381
+ 0.000,
382
+ 0.000,
383
+ 0.833,
384
+ 0.000,
385
+ 0.000,
386
+ 1.000,
387
+ 0.000,
388
+ 0.000,
389
+ 0.000,
390
+ 0.143,
391
+ 0.143,
392
+ 0.143,
393
+ 0.286,
394
+ 0.286,
395
+ 0.286,
396
+ 0.429,
397
+ 0.429,
398
+ 0.429,
399
+ 0.571,
400
+ 0.571,
401
+ 0.571,
402
+ 0.714,
403
+ 0.714,
404
+ 0.714,
405
+ 0.857,
406
+ 0.857,
407
+ 0.857,
408
+ 0.000,
409
+ 0.447,
410
+ 0.741,
411
+ 0.314,
412
+ 0.717,
413
+ 0.741,
414
+ 0.50,
415
+ 0.5,
416
+ 0,
417
+ ]
418
+ )
419
+ .astype(np.float32)
420
+ .reshape(-1, 3)
421
+ )
422
+
423
+ def distance2bbox(points, distance, max_shape=None):
424
+ x1 = points[..., 0] - distance[..., 0]
425
+ y1 = points[..., 1] - distance[..., 1]
426
+ x2 = points[..., 0] + distance[..., 2]
427
+ y2 = points[..., 1] + distance[..., 3]
428
+ if max_shape is not None:
429
+ x1 = np.clip(x1, a_min=0, a_max=max_shape[1])
430
+ y1 = np.clip(y1, a_min=0, a_max=max_shape[0])
431
+ x2 = np.clip(x2, a_min=0, a_max=max_shape[1])
432
+ y2 = np.clip(y2, a_min=0, a_max=max_shape[0])
433
+ return np.stack([x1, y1, x2, y2], axis=-1)
434
+
435
+ def integral_numpy(x, reg_max=16):
436
+ """
437
+ NumPy equivalent of the Integral layer in NanoDet.
438
+
439
+ Computes: sum(softmax(logits) * [0, 1, ..., reg_max]) for each of the 4 directions.
440
+
441
+ Args:
442
+ x (np.ndarray): Input array of shape (..., 4 * (reg_max + 1))
443
+ reg_max (int): Maximum value of discrete set. Default: 16.
444
+
445
+ Returns:
446
+ np.ndarray: Integral result of shape (..., 4)
447
+ """
448
+ # Save original leading shape (e.g., (N,) or (N, H, W))
449
+ leading_shape = x.shape[:-1] # everything except last dim
450
+ total_channels = x.shape[-1]
451
+
452
+ assert total_channels == 4 * (reg_max + 1), \
453
+ f"Last dimension must be 4*(reg_max+1)={4*(reg_max+1)}, but got {total_channels}"
454
+
455
+ # Reshape to (..., 4, reg_max + 1)
456
+ x = x.reshape(*leading_shape, 4, reg_max + 1)
457
+
458
+ # Apply softmax along the last axis (dim=-1)
459
+ # For numerical stability: subtract max
460
+ x_max = np.max(x, axis=-1, keepdims=True)
461
+ exp_x = np.exp(x - x_max)
462
+ softmax_x = exp_x / np.sum(exp_x, axis=-1, keepdims=True) # (..., 4, reg_max+1)
463
+
464
+ # Project vector: [0, 1, 2, ..., reg_max]
465
+ project = np.arange(reg_max + 1, dtype=x.dtype) # shape (reg_max+1,)
466
+
467
+ # Compute weighted sum: sum(softmax_x * project) over last dimension
468
+ # Broadcasting: (..., 4, reg_max+1) * (reg_max+1,) -> (..., 4, reg_max+1)
469
+ integral_result = np.sum(softmax_x * project, axis=-1) # (..., 4)
470
+
471
+ return integral_result
472
+
473
+ def overlay_bbox_cv(img, dets, class_names, score_thresh):
474
+ all_box = []
475
+ for label in dets:
476
+ for bbox in dets[label]:
477
+ score = bbox[-1]
478
+ if score > score_thresh:
479
+ x0, y0, x1, y1 = [int(i) for i in bbox[:4]]
480
+ all_box.append([label, x0, y0, x1, y1, score])
481
+ all_box.sort(key=lambda v: v[5])
482
+ # for box in all_box:
483
+ # label, x0, y0, x1, y1, score = box
484
+ # # color = self.cmap(i)[:3]
485
+ # color = (_COLORS[label] * 255).astype(np.uint8).tolist()
486
+ # text = "{}:{:.1f}%".format(class_names[label], score * 100)
487
+ # txt_color = (0, 0, 0) if np.mean(_COLORS[label]) > 0.5 else (255, 255, 255)
488
+ # font = cv2.FONT_HERSHEY_SIMPLEX
489
+ # txt_size = cv2.getTextSize(text, font, 0.5, 2)[0]
490
+ # cv2.rectangle(img, (x0, y0), (x1, y1), color, 2)
491
+
492
+ # cv2.rectangle(
493
+ # img,
494
+ # (x0, y0 - txt_size[1] - 1),
495
+ # (x0 + txt_size[0] + txt_size[1], y0 - 1),
496
+ # color,
497
+ # -1,
498
+ # )
499
+ # cv2.putText(img, text, (x0, y0 - 1), font, 0.5, txt_color, thickness=1)
500
+ return img, all_box
501
+
502
+ class NanoDetONNXInfer:
503
+ def __init__(self, model_path, imgsz=[416, 416]):
504
+ self.model_path = model_path
505
+ self.session, self.output_names = model_load(self.model_path)
506
+ self.imgsz = imgsz
507
+ self.reg_max = 7
508
+ self.reg_max1= self.reg_max + 1
509
+ self.distribution_project = np.arange(self.reg_max + 1)
510
+ self.nc = len(names)
511
+ self.no = self.nc + self.reg_max1 * 4
512
+ self.stride = [8, 16, 32, 64]
513
+
514
+ def get_bboxes(self, cls_preds, reg_preds):
515
+ """Decode the outputs to bboxes.
516
+ Args:
517
+ cls_preds (Tensor): Shape (num_imgs, num_points, num_classes).
518
+ reg_preds (Tensor): Shape (num_imgs, num_points, 4 * (regmax + 1)).
519
+ img_metas (dict): Dict of image info.
520
+
521
+ Returns:
522
+ results_list (list[tuple]): List of detection bboxes and labels.
523
+ """
524
+ b = cls_preds.shape[0]
525
+
526
+ featmap_sizes = [
527
+ (math.ceil(self.imgsz[0] / stride), math.ceil(self.imgsz[1]) / stride)
528
+ for stride in self.stride
529
+ ]
530
+
531
+ # get grid cells of one image
532
+ mlvl_center_priors = [
533
+ self.get_single_level_center_priors(
534
+ b,
535
+ featmap_sizes[i],
536
+ stride,
537
+ dtype=np.float32,
538
+ )
539
+ for i, stride in enumerate(self.stride)
540
+ ]
541
+
542
+ center_priors = np.concatenate(mlvl_center_priors, axis=1)
543
+ integral_result = integral_numpy(reg_preds, reg_max=self.reg_max) # (N, 4)
544
+ scale = center_priors[..., 2][..., None] # shape (N, 1) or (N, H, W, 1)
545
+ dis_preds = integral_result * scale
546
+ bboxes = distance2bbox(center_priors[..., :2], dis_preds, max_shape=self.imgsz)
547
+ scores = 1.0 / (1.0 + np.exp(-cls_preds)) # sigmoid
548
+ result_list = []
549
+ for i in range(b):
550
+ # add a dummy background class at the end of all labels
551
+ # same with mmdetection2.0
552
+ score, bbox = scores[i], bboxes[i]
553
+ padding = np.zeros((score.shape[0], 1), dtype=score.dtype)
554
+ score = np.concatenate([score, padding], axis=1)
555
+ results = multiclass_nms(
556
+ bbox,
557
+ score,
558
+ score_thr=0.05,
559
+ nms_cfg=dict(type="nms", iou_threshold=0.6),
560
+ max_num=100,
561
+ )
562
+ result_list.append(results)
563
+ return result_list
564
+ def get_single_level_center_priors(self,batch_size, featmap_size, stride, dtype):
565
+ h, w = featmap_size
566
+ x_range = (np.arange(w, dtype=dtype)) * stride
567
+ y_range = (np.arange(h, dtype=dtype)) * stride
568
+ y, x = np.meshgrid(y_range, x_range, indexing='ij')
569
+ y = y.flatten()
570
+ x = x.flatten()
571
+ strides = np.full((x.shape[0],), stride, dtype=dtype)
572
+ priors = np.stack([x, y, strides, strides], axis=-1)
573
+ return np.tile(priors[None, :, :], (batch_size, 1, 1))
574
+
575
+ def detect_objects(self, image, save_path):
576
+ outputs=[]
577
+ im, im0, org_data = data_process_cv2(image, self.imgsz)
578
+ img_name = os.path.basename(image).split('.')[0]
579
+ infer_start_time = time.time()
580
+ x = self.session.run(None, {self.session.get_inputs()[0].name: im})
581
+ infer_end_time = time.time()
582
+ print(f"infer time: {infer_end_time - infer_start_time:.4f}s")
583
+ x = [np.transpose(x[i],(0,3,1,2)) for i in range(4)] #to nchw
584
+ for i in range(len(x)):
585
+ reg_pred = x[i][:, :self.reg_max1 * 4,:,:]
586
+ cls_pred = x[i][:, self.reg_max1 * 4:,:,:]
587
+ out = np.concatenate([cls_pred, reg_pred], axis=1)
588
+ outputs.append(out.reshape(out.shape[0], out.shape[1], -1))
589
+ preds = np.concatenate(outputs, axis=2).transpose(0, 2, 1)
590
+
591
+ cls_scores = preds[:, :, :self.nc]
592
+ bbox_preds = preds[:, :, self.nc:]
593
+ pred = self.get_bboxes(cls_scores, bbox_preds)[0]
594
+ res = self.post_process(pred, org_data, im0, save_path, img_name)
595
+ result_img, bbox_res = overlay_bbox_cv(im0, res, names, score_thresh=0.35)
596
+ return bbox_res, result_img
597
+ def post_process(self, result, im, im0, save_path, img_name):
598
+ det_result = {}
599
+ det_bboxes, det_labels = result
600
+ det_bboxes[:, :4] = scale_boxes_no_letter(im.shape[:2], det_bboxes[:, :4], im0.shape).round()
601
+ classes = det_labels
602
+ for i in range(self.nc):
603
+ inds = classes == i
604
+ det_result[i] = np.concatenate(
605
+ [
606
+ det_bboxes[inds, :4].astype(np.float32),
607
+ det_bboxes[inds, 4:5].astype(np.float32),
608
+ ],
609
+ axis=1,
610
+ ).tolist()
611
+
612
+ return det_result
613
+
614
+ class QRCodeDecoder:
615
+ def crop_qr_regions(self, image, regions):
616
+ """
617
+ 根据检测到的边界框裁剪二维码区域
618
+ """
619
+ cropped_images = []
620
+ for idx, region in enumerate(regions):
621
+ label, x1, y1, x2, y2, score = region
622
+ # 外扩15个像素缓解因检测截断造成无法识别的情况,视检测情况而定
623
+ x1-=15
624
+ y1-=15
625
+ x2+=15
626
+ y2+=15
627
+ # 裁剪图像
628
+ cropped = image[y1:y2, x1:x2]
629
+ if cropped.size > 0:
630
+ cropped_images.append({
631
+ 'image': cropped,
632
+ 'bbox': region,
633
+ })
634
+ return cropped_images
635
+
636
+ def decode_qrcode_pyzbar(self, cropped_image):
637
+ """
638
+ 使用pyzbar解码二维码
639
+ """
640
+ try:
641
+ # 转换为灰度图像
642
+ if len(cropped_image.shape) == 3:
643
+ gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
644
+ else:
645
+ gray = cropped_image
646
+ # 使用pyzbar解码
647
+ decoded_objects = pyzbar.decode(gray)
648
+ results = []
649
+ for obj in decoded_objects:
650
+ try:
651
+ data = obj.data.decode('utf-8')
652
+ results.append({
653
+ 'data': data,
654
+ 'type': obj.type,
655
+ 'points': obj.polygon
656
+ })
657
+ except:
658
+ continue
659
+
660
+ return results
661
+ except Exception as e:
662
+ print(f"decode error: {e}")
663
+ return []
664
+
665
+ if __name__ == '__main__':
666
+ import time
667
+
668
+ detector = NanoDetONNXInfer(model_path='./nanodet-plus-m_416_QR.axmodel',imgsz=[416,416])
669
+ decoder = QRCodeDecoder()
670
+ img_path = './qrcode_test'
671
+ det_path='./det_res'
672
+ crop_path='./crop_res'
673
+ os.makedirs(det_path, exist_ok=True)
674
+ os.makedirs(crop_path, exist_ok=True)
675
+ imgs = glob.glob(f"{img_path}/*.jpg")
676
+ totoal = len(imgs)
677
+ success = 0
678
+ fail = 0
679
+ start_time = time.time()
680
+ for idx,img in enumerate(imgs):
681
+ pic_name=os.path.basename(img).split('.')[0]
682
+ loop_start_time = time.time()
683
+ det_result, res_img = detector.detect_objects(img,det_path)
684
+ # cv2.imwrite(os.path.join(det_path, pic_name+'.jpg'), res_img)
685
+ # print('det_result:',det_result)
686
+ # Crop deteted QRCode & decode QRCode by pyzbar
687
+ cropped_images = decoder.crop_qr_regions(res_img, det_result)
688
+ # for i,cropped in enumerate(cropped_images):
689
+ # cv2.imwrite(os.path.join(crop_path, f'{pic_name}_crop_{i}.jpg'), cropped['image'])
690
+
691
+ all_decoded_results = []
692
+ for i, cropped_data in enumerate(cropped_images):
693
+ decoded_results = decoder.decode_qrcode_pyzbar(cropped_data['image'])
694
+ all_decoded_results.extend(decoded_results)
695
+
696
+ # for result in decoded_results:
697
+ # print(f"decode result: {result['data']} (type: {result['type']})")
698
+ if all_decoded_results:
699
+ success += 1
700
+ print(f"{pic_name} 识别成功!")
701
+ else:
702
+ fail += 1
703
+ print(f"{pic_name} 识别失败!")
704
+ loop_end_time = time.time()
705
+ print(f"图片 {img} 处理耗时: {loop_end_time - loop_start_time:.4f} 秒")
706
+
707
+ end_time = time.time() # 记录总结束时间
708
+ total_time = end_time - start_time # 记录总耗时
709
+
710
+ print(f"总共测试图片数量: {totoal}")
711
+ print(f"识别成功数量: {success}")
712
+ print(f"识别失败数量: {fail}")
713
+ print(f"识别成功率: {success/totoal*100:.2f}%")
714
+ print(f"整体处理耗时: {total_time:.4f} 秒")
715
+ print(f"平均每张图片处理耗时: {total_time/totoal:.4f} 秒")
python/QRCode_axmodel_infer_v5.py CHANGED
@@ -280,9 +280,10 @@ class Yolov5QRcodeDetector:
280
 
281
  def preprocess_image(self, img, img_size=(640, 640)):
282
  img, _, _ = letterbox(img, img_size, auto=False, stride=32)
283
- img = np.ascontiguousarray(img[:, :, ::-1].transpose(2, 0, 1))
 
284
  # img = np.asarray(img, dtype=np.float32)
285
- img = np.asarray(img, dtype=np.uint8)
286
  img = np.expand_dims(img, 0)
287
  # img /= 255.0
288
  return img
@@ -300,6 +301,7 @@ class Yolov5QRcodeDetector:
300
  grid = torch.stack((xv, yv), 2).expand(shape) - 0.5 # add grid offset, i.e. y = 2.0 * x - 0.5
301
  anchor_grid = (self.anchors[i] * self.stride[i]).view((1, na, 1, 1, 2)).expand(shape)
302
  return grid, anchor_grid
 
303
  def postprocess(self, preds, img_shape, im0):
304
  z = [] # inference output
305
  for i,pred in enumerate(preds):
@@ -322,7 +324,8 @@ class Yolov5QRcodeDetector:
322
 
323
  if len(det):
324
  # Rescale boxes from img_size to im0 size
325
- scale_coords(img_shape[2:], det[:, :4], im0.shape, kpt_label=False)
 
326
 
327
  # Print results
328
  for c in det[:, 5].unique():
@@ -397,12 +400,12 @@ class QRCodeDecoder:
397
  if __name__ == '__main__':
398
  import time
399
 
400
- model = './yolov5n_npu3.axmodel'
401
  input_size = [640,640]
402
  detector = Yolov5QRcodeDetector(model)
403
  # Crop deteted QRCode & decode QRCode by pyzbar
404
  decoder = QRCodeDecoder()
405
- pic_path = './images/'
406
  det_path='./v5_det_res'
407
  crop_path='./v5_crop_res'
408
  os.makedirs(det_path, exist_ok=True)
 
280
 
281
  def preprocess_image(self, img, img_size=(640, 640)):
282
  img, _, _ = letterbox(img, img_size, auto=False, stride=32)
283
+ # img = np.ascontiguousarray(img[:, :, ::-1].transpose(2, 0, 1))
284
+ img = np.ascontiguousarray(img).astype(np.uint8)
285
  # img = np.asarray(img, dtype=np.float32)
286
+ # img = np.asarray(img, dtype=np.uint8)
287
  img = np.expand_dims(img, 0)
288
  # img /= 255.0
289
  return img
 
301
  grid = torch.stack((xv, yv), 2).expand(shape) - 0.5 # add grid offset, i.e. y = 2.0 * x - 0.5
302
  anchor_grid = (self.anchors[i] * self.stride[i]).view((1, na, 1, 1, 2)).expand(shape)
303
  return grid, anchor_grid
304
+
305
  def postprocess(self, preds, img_shape, im0):
306
  z = [] # inference output
307
  for i,pred in enumerate(preds):
 
324
 
325
  if len(det):
326
  # Rescale boxes from img_size to im0 size
327
+ # scale_coords(img_shape[2:], det[:, :4], im0.shape, kpt_label=False)
328
+ scale_coords(img_shape[1:3], det[:, :4], im0.shape, kpt_label=False)
329
 
330
  # Print results
331
  for c in det[:, 5].unique():
 
400
  if __name__ == '__main__':
401
  import time
402
 
403
+ model = './yolov5n_650_npu1.axmodel'
404
  input_size = [640,640]
405
  detector = Yolov5QRcodeDetector(model)
406
  # Crop deteted QRCode & decode QRCode by pyzbar
407
  decoder = QRCodeDecoder()
408
+ pic_path = './qrcode_test/'
409
  det_path='./v5_det_res'
410
  crop_path='./v5_crop_res'
411
  os.makedirs(det_path, exist_ok=True)
python/QRCode_axmodel_infer_v8.py CHANGED
@@ -49,7 +49,8 @@ def data_process_cv2(frame, input_shape):
49
  im0 = cv2.imread(frame)
50
  img = letterbox(im0, input_shape, auto=False, stride=32)[0]
51
  org_data = img.copy()
52
- img = np.ascontiguousarray(img[:, :, ::-1].transpose(2, 0, 1))
 
53
  img = np.asarray(img, dtype=np.uint8)
54
  img = np.expand_dims(img, 0)
55
  # img /= 255.0
@@ -506,9 +507,9 @@ class QRCodeDecoder:
506
  if __name__ == '__main__':
507
  import time
508
 
509
- detector = YOLOV8Detector(model_path='./yolov8n_npu3.axmodel',imgsz=[640,640])
510
  decoder = QRCodeDecoder()
511
- img_path = './images'
512
  det_path='./v8_det_res'
513
  crop_path='./v8_crop_res'
514
  os.makedirs(det_path, exist_ok=True)
 
49
  im0 = cv2.imread(frame)
50
  img = letterbox(im0, input_shape, auto=False, stride=32)[0]
51
  org_data = img.copy()
52
+ # img = np.ascontiguousarray(img[:, :, ::-1].transpose(2, 0, 1))
53
+ img = np.ascontiguousarray(img[:, :, ::-1])
54
  img = np.asarray(img, dtype=np.uint8)
55
  img = np.expand_dims(img, 0)
56
  # img /= 255.0
 
507
  if __name__ == '__main__':
508
  import time
509
 
510
+ detector = YOLOV8Detector(model_path='./yolov8n_650_npu1.axmodel',imgsz=[640,640])
511
  decoder = QRCodeDecoder()
512
+ img_path = './qrcode_test'
513
  det_path='./v8_det_res'
514
  crop_path='./v8_crop_res'
515
  os.makedirs(det_path, exist_ok=True)
python/QRCode_onnx_infer_Nanodet.py ADDED
@@ -0,0 +1,718 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import glob
3
+ import time
4
+ import cv2
5
+ import numpy as np
6
+ import pyzbar.pyzbar as pyzbar
7
+ import onnxruntime as ort
8
+ import math
9
+
10
+ names = ["QRCode"]
11
+
12
+ def sigmoid(x):
13
+ return 1 / (1 + np.exp(-x))
14
+ def model_load(model):
15
+ providers = ['CPUExecutionProvider']
16
+ session = ort.InferenceSession(model, providers=providers)
17
+ input_name = session.get_inputs()[0].name
18
+ output_names = [ x.name for x in session.get_outputs()]
19
+ return session, output_names
20
+
21
+ def data_process_cv2(frame, input_shape):
22
+ mean = np.array([103.53, 116.28, 123.675], dtype=np.float32).reshape(1, 1, 3)
23
+ std = np.array([57.375, 57.12, 58.395], dtype=np.float32).reshape(1, 1, 3)
24
+ im0 = cv2.imread(frame)
25
+ img = cv2.resize(im0, input_shape, interpolation=cv2.INTER_AREA).astype(np.float32)
26
+ org_data = img.copy()
27
+ img = (img - mean) / std
28
+ img = np.ascontiguousarray(img.transpose(2, 0, 1))
29
+ img = np.expand_dims(img, 0)
30
+ return img, im0, org_data
31
+
32
+ def multiclass_nms(
33
+ multi_bboxes, multi_scores, score_thr, nms_cfg, max_num=-1, score_factors=None
34
+ ):
35
+ num_classes = multi_scores.shape[1] - 1 # exclude background
36
+
37
+ # Reshape bboxes
38
+ if multi_bboxes.shape[1] > 4:
39
+ # (N, 4*C) -> (N, C, 4)
40
+ bboxes = multi_bboxes.reshape(multi_scores.shape[0], -1, 4)
41
+ else:
42
+ # (N, 4) -> (N, 1, 4) -> (N, C, 4) via repeat
43
+ bboxes = np.tile(multi_bboxes[:, None, :], (1, num_classes, 1))
44
+
45
+ scores = multi_scores[:, :-1].copy() # (N, C)
46
+
47
+ # Apply score factors if provided
48
+ if score_factors is not None:
49
+ scores = scores * score_factors[:, None]
50
+
51
+ # Filter by score threshold
52
+ valid_mask = scores > score_thr # (N, C)
53
+
54
+ # Get indices where valid
55
+ valid_indices = np.where(valid_mask)
56
+ if len(valid_indices[0]) == 0:
57
+ # No valid boxes
58
+ return np.zeros((0, 5), dtype=np.float32), np.zeros((0,), dtype=np.int64)
59
+
60
+ # Extract valid bboxes, scores, labels
61
+ bbox_indices, class_indices = valid_indices
62
+ bboxes_valid = bboxes[bbox_indices, class_indices] # (K, 4)
63
+ scores_valid = scores[valid_indices] # (K,)
64
+ labels_valid = class_indices.astype(np.int64) # (K,)
65
+
66
+ # Concatenate bboxes and scores for NMS input: (K, 5)
67
+ dets_input = np.concatenate([bboxes_valid, scores_valid[:, None]], axis=1) # (K, 5)
68
+
69
+ # Perform NMS (you need a NumPy NMS implementation)
70
+ keep = nms_numpy(dets_input, iou_threshold=nms_cfg.get('iou_threshold', 0.5))
71
+
72
+ dets = dets_input[keep]
73
+ labels = labels_valid[keep]
74
+
75
+ if max_num > 0 and len(keep) > max_num:
76
+ dets = dets[:max_num]
77
+ labels = labels[:max_num]
78
+
79
+ return dets, labels
80
+ def nms_numpy(dets, iou_threshold=0.5):
81
+ if dets.size == 0:
82
+ return []
83
+
84
+ x1 = dets[:, 0]
85
+ y1 = dets[:, 1]
86
+ x2 = dets[:, 2]
87
+ y2 = dets[:, 3]
88
+ scores = dets[:, 4]
89
+
90
+ areas = (x2 - x1 + 1) * (y2 - y1 + 1)
91
+ order = scores.argsort()[::-1] # descending order
92
+
93
+ keep = []
94
+ while order.size > 0:
95
+ i = order[0]
96
+ keep.append(i)
97
+
98
+ xx1 = np.maximum(x1[i], x1[order[1:]])
99
+ yy1 = np.maximum(y1[i], y1[order[1:]])
100
+ xx2 = np.minimum(x2[i], x2[order[1:]])
101
+ yy2 = np.minimum(y2[i], y2[order[1:]])
102
+
103
+ w = np.maximum(0.0, xx2 - xx1 + 1)
104
+ h = np.maximum(0.0, yy2 - yy1 + 1)
105
+ inter = w * h
106
+
107
+ iou = inter / (areas[i] + areas[order[1:]] - inter)
108
+ inds = np.where(iou <= iou_threshold)[0]
109
+ order = order[inds + 1]
110
+
111
+ return keep
112
+ def batched_nms(boxes, scores, idxs, nms_cfg, class_agnostic=False):
113
+ nms_cfg_ = nms_cfg.copy()
114
+ class_agnostic = nms_cfg_.pop("class_agnostic", class_agnostic)
115
+
116
+ if class_agnostic:
117
+ boxes_for_nms = boxes
118
+ else:
119
+ max_coordinate = boxes.max()
120
+ # offsets = idxs * (max_coordinate + 1)
121
+ offsets = idxs.astype(boxes.dtype) * (max_coordinate + 1)
122
+ boxes_for_nms = boxes + offsets[:, None]
123
+
124
+ nms_type = nms_cfg_.pop("type", "nms") # unused in numpy version
125
+ split_thr = nms_cfg_.pop("split_thr", 10000)
126
+
127
+ if len(boxes_for_nms) < split_thr:
128
+ # Call your NumPy NMS function (e.g., nms_numpy)
129
+ keep = nms_numpy(boxes_for_nms, scores, **nms_cfg_)
130
+ keep = np.array(keep, dtype=np.int64)
131
+ boxes = boxes[keep]
132
+ scores = scores[keep]
133
+ else:
134
+ # Large case: process per class/group
135
+ total_mask = np.zeros(scores.shape, dtype=bool)
136
+ unique_ids = np.unique(idxs)
137
+
138
+ for id_val in unique_ids:
139
+ mask = (idxs == id_val)
140
+ mask_indices = np.where(mask)[0] # indices where condition is True
141
+
142
+ if len(mask_indices) == 0:
143
+ continue
144
+
145
+ keep_in_group = nms_numpy(
146
+ boxes_for_nms[mask_indices],
147
+ scores[mask_indices],
148
+ **nms_cfg_
149
+ )
150
+ keep_in_group = np.array(keep_in_group, dtype=np.int64)
151
+ selected_global_indices = mask_indices[keep_in_group]
152
+ total_mask[selected_global_indices] = True
153
+
154
+ keep = np.where(total_mask)[0]
155
+ # Sort by scores descending
156
+ sorted_indices = np.argsort(-scores[keep]) # negative for descending
157
+ keep = keep[sorted_indices]
158
+ boxes = boxes[keep]
159
+ scores = scores[keep]
160
+
161
+ # Concatenate boxes and scores -> (K, 5)
162
+ dets = np.concatenate([boxes, scores[:, None]], axis=-1)
163
+ return dets, keep
164
+
165
+ def scale_boxes_no_letter(img1_shape, boxes, img0_shape):
166
+ gain = (img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])
167
+
168
+ boxes[..., [0, 2]] /= gain[1]
169
+ boxes[..., [1, 3]] /= gain[0]
170
+ clip_boxes(boxes, img0_shape)
171
+ return boxes
172
+
173
+ def clip_boxes(boxes, shape):
174
+ boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1])
175
+ boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0])
176
+
177
+ _COLORS = (
178
+ np.array(
179
+ [
180
+ 0.000,
181
+ 0.447,
182
+ 0.741,
183
+ 0.850,
184
+ 0.325,
185
+ 0.098,
186
+ 0.929,
187
+ 0.694,
188
+ 0.125,
189
+ 0.494,
190
+ 0.184,
191
+ 0.556,
192
+ 0.466,
193
+ 0.674,
194
+ 0.188,
195
+ 0.301,
196
+ 0.745,
197
+ 0.933,
198
+ 0.635,
199
+ 0.078,
200
+ 0.184,
201
+ 0.300,
202
+ 0.300,
203
+ 0.300,
204
+ 0.600,
205
+ 0.600,
206
+ 0.600,
207
+ 1.000,
208
+ 0.000,
209
+ 0.000,
210
+ 1.000,
211
+ 0.500,
212
+ 0.000,
213
+ 0.749,
214
+ 0.749,
215
+ 0.000,
216
+ 0.000,
217
+ 1.000,
218
+ 0.000,
219
+ 0.000,
220
+ 0.000,
221
+ 1.000,
222
+ 0.667,
223
+ 0.000,
224
+ 1.000,
225
+ 0.333,
226
+ 0.333,
227
+ 0.000,
228
+ 0.333,
229
+ 0.667,
230
+ 0.000,
231
+ 0.333,
232
+ 1.000,
233
+ 0.000,
234
+ 0.667,
235
+ 0.333,
236
+ 0.000,
237
+ 0.667,
238
+ 0.667,
239
+ 0.000,
240
+ 0.667,
241
+ 1.000,
242
+ 0.000,
243
+ 1.000,
244
+ 0.333,
245
+ 0.000,
246
+ 1.000,
247
+ 0.667,
248
+ 0.000,
249
+ 1.000,
250
+ 1.000,
251
+ 0.000,
252
+ 0.000,
253
+ 0.333,
254
+ 0.500,
255
+ 0.000,
256
+ 0.667,
257
+ 0.500,
258
+ 0.000,
259
+ 1.000,
260
+ 0.500,
261
+ 0.333,
262
+ 0.000,
263
+ 0.500,
264
+ 0.333,
265
+ 0.333,
266
+ 0.500,
267
+ 0.333,
268
+ 0.667,
269
+ 0.500,
270
+ 0.333,
271
+ 1.000,
272
+ 0.500,
273
+ 0.667,
274
+ 0.000,
275
+ 0.500,
276
+ 0.667,
277
+ 0.333,
278
+ 0.500,
279
+ 0.667,
280
+ 0.667,
281
+ 0.500,
282
+ 0.667,
283
+ 1.000,
284
+ 0.500,
285
+ 1.000,
286
+ 0.000,
287
+ 0.500,
288
+ 1.000,
289
+ 0.333,
290
+ 0.500,
291
+ 1.000,
292
+ 0.667,
293
+ 0.500,
294
+ 1.000,
295
+ 1.000,
296
+ 0.500,
297
+ 0.000,
298
+ 0.333,
299
+ 1.000,
300
+ 0.000,
301
+ 0.667,
302
+ 1.000,
303
+ 0.000,
304
+ 1.000,
305
+ 1.000,
306
+ 0.333,
307
+ 0.000,
308
+ 1.000,
309
+ 0.333,
310
+ 0.333,
311
+ 1.000,
312
+ 0.333,
313
+ 0.667,
314
+ 1.000,
315
+ 0.333,
316
+ 1.000,
317
+ 1.000,
318
+ 0.667,
319
+ 0.000,
320
+ 1.000,
321
+ 0.667,
322
+ 0.333,
323
+ 1.000,
324
+ 0.667,
325
+ 0.667,
326
+ 1.000,
327
+ 0.667,
328
+ 1.000,
329
+ 1.000,
330
+ 1.000,
331
+ 0.000,
332
+ 1.000,
333
+ 1.000,
334
+ 0.333,
335
+ 1.000,
336
+ 1.000,
337
+ 0.667,
338
+ 1.000,
339
+ 0.333,
340
+ 0.000,
341
+ 0.000,
342
+ 0.500,
343
+ 0.000,
344
+ 0.000,
345
+ 0.667,
346
+ 0.000,
347
+ 0.000,
348
+ 0.833,
349
+ 0.000,
350
+ 0.000,
351
+ 1.000,
352
+ 0.000,
353
+ 0.000,
354
+ 0.000,
355
+ 0.167,
356
+ 0.000,
357
+ 0.000,
358
+ 0.333,
359
+ 0.000,
360
+ 0.000,
361
+ 0.500,
362
+ 0.000,
363
+ 0.000,
364
+ 0.667,
365
+ 0.000,
366
+ 0.000,
367
+ 0.833,
368
+ 0.000,
369
+ 0.000,
370
+ 1.000,
371
+ 0.000,
372
+ 0.000,
373
+ 0.000,
374
+ 0.167,
375
+ 0.000,
376
+ 0.000,
377
+ 0.333,
378
+ 0.000,
379
+ 0.000,
380
+ 0.500,
381
+ 0.000,
382
+ 0.000,
383
+ 0.667,
384
+ 0.000,
385
+ 0.000,
386
+ 0.833,
387
+ 0.000,
388
+ 0.000,
389
+ 1.000,
390
+ 0.000,
391
+ 0.000,
392
+ 0.000,
393
+ 0.143,
394
+ 0.143,
395
+ 0.143,
396
+ 0.286,
397
+ 0.286,
398
+ 0.286,
399
+ 0.429,
400
+ 0.429,
401
+ 0.429,
402
+ 0.571,
403
+ 0.571,
404
+ 0.571,
405
+ 0.714,
406
+ 0.714,
407
+ 0.714,
408
+ 0.857,
409
+ 0.857,
410
+ 0.857,
411
+ 0.000,
412
+ 0.447,
413
+ 0.741,
414
+ 0.314,
415
+ 0.717,
416
+ 0.741,
417
+ 0.50,
418
+ 0.5,
419
+ 0,
420
+ ]
421
+ )
422
+ .astype(np.float32)
423
+ .reshape(-1, 3)
424
+ )
425
+
426
+ def distance2bbox(points, distance, max_shape=None):
427
+ x1 = points[..., 0] - distance[..., 0]
428
+ y1 = points[..., 1] - distance[..., 1]
429
+ x2 = points[..., 0] + distance[..., 2]
430
+ y2 = points[..., 1] + distance[..., 3]
431
+ if max_shape is not None:
432
+ x1 = np.clip(x1, a_min=0, a_max=max_shape[1])
433
+ y1 = np.clip(y1, a_min=0, a_max=max_shape[0])
434
+ x2 = np.clip(x2, a_min=0, a_max=max_shape[1])
435
+ y2 = np.clip(y2, a_min=0, a_max=max_shape[0])
436
+ return np.stack([x1, y1, x2, y2], axis=-1)
437
+
438
+ def integral_numpy(x, reg_max=16):
439
+ """
440
+ NumPy equivalent of the Integral layer in NanoDet.
441
+
442
+ Computes: sum(softmax(logits) * [0, 1, ..., reg_max]) for each of the 4 directions.
443
+
444
+ Args:
445
+ x (np.ndarray): Input array of shape (..., 4 * (reg_max + 1))
446
+ reg_max (int): Maximum value of discrete set. Default: 16.
447
+
448
+ Returns:
449
+ np.ndarray: Integral result of shape (..., 4)
450
+ """
451
+ # Save original leading shape (e.g., (N,) or (N, H, W))
452
+ leading_shape = x.shape[:-1] # everything except last dim
453
+ total_channels = x.shape[-1]
454
+
455
+ assert total_channels == 4 * (reg_max + 1), \
456
+ f"Last dimension must be 4*(reg_max+1)={4*(reg_max+1)}, but got {total_channels}"
457
+
458
+ # Reshape to (..., 4, reg_max + 1)
459
+ x = x.reshape(*leading_shape, 4, reg_max + 1)
460
+
461
+ # Apply softmax along the last axis (dim=-1)
462
+ # For numerical stability: subtract max
463
+ x_max = np.max(x, axis=-1, keepdims=True)
464
+ exp_x = np.exp(x - x_max)
465
+ softmax_x = exp_x / np.sum(exp_x, axis=-1, keepdims=True) # (..., 4, reg_max+1)
466
+
467
+ # Project vector: [0, 1, 2, ..., reg_max]
468
+ project = np.arange(reg_max + 1, dtype=x.dtype) # shape (reg_max+1,)
469
+
470
+ # Compute weighted sum: sum(softmax_x * project) over last dimension
471
+ # Broadcasting: (..., 4, reg_max+1) * (reg_max+1,) -> (..., 4, reg_max+1)
472
+ integral_result = np.sum(softmax_x * project, axis=-1) # (..., 4)
473
+
474
+ return integral_result
475
+
476
+ def overlay_bbox_cv(img, dets, class_names, score_thresh):
477
+ all_box = []
478
+ for label in dets:
479
+ for bbox in dets[label]:
480
+ score = bbox[-1]
481
+ if score > score_thresh:
482
+ x0, y0, x1, y1 = [int(i) for i in bbox[:4]]
483
+ all_box.append([label, x0, y0, x1, y1, score])
484
+ all_box.sort(key=lambda v: v[5])
485
+ # for box in all_box:
486
+ # label, x0, y0, x1, y1, score = box
487
+ # # color = self.cmap(i)[:3]
488
+ # color = (_COLORS[label] * 255).astype(np.uint8).tolist()
489
+ # text = "{}:{:.1f}%".format(class_names[label], score * 100)
490
+ # txt_color = (0, 0, 0) if np.mean(_COLORS[label]) > 0.5 else (255, 255, 255)
491
+ # font = cv2.FONT_HERSHEY_SIMPLEX
492
+ # txt_size = cv2.getTextSize(text, font, 0.5, 2)[0]
493
+ # cv2.rectangle(img, (x0, y0), (x1, y1), color, 2)
494
+
495
+ # cv2.rectangle(
496
+ # img,
497
+ # (x0, y0 - txt_size[1] - 1),
498
+ # (x0 + txt_size[0] + txt_size[1], y0 - 1),
499
+ # color,
500
+ # -1,
501
+ # )
502
+ # cv2.putText(img, text, (x0, y0 - 1), font, 0.5, txt_color, thickness=1)
503
+ return img, all_box
504
+
505
+ class NanoDetONNXInfer:
506
+ def __init__(self, model_path, imgsz=[416, 416]):
507
+ self.model_path = model_path
508
+ self.session, self.output_names = model_load(self.model_path)
509
+ self.imgsz = imgsz
510
+ self.reg_max = 7
511
+ self.reg_max1= self.reg_max + 1
512
+ self.distribution_project = np.arange(self.reg_max + 1)
513
+ self.nc = len(names)
514
+ self.no = self.nc + self.reg_max1 * 4
515
+ self.stride = [8, 16, 32, 64]
516
+
517
+ def get_bboxes(self, cls_preds, reg_preds):
518
+ """Decode the outputs to bboxes.
519
+ Args:
520
+ cls_preds (Tensor): Shape (num_imgs, num_points, num_classes).
521
+ reg_preds (Tensor): Shape (num_imgs, num_points, 4 * (regmax + 1)).
522
+ img_metas (dict): Dict of image info.
523
+
524
+ Returns:
525
+ results_list (list[tuple]): List of detection bboxes and labels.
526
+ """
527
+ b = cls_preds.shape[0]
528
+
529
+ featmap_sizes = [
530
+ (math.ceil(self.imgsz[0] / stride), math.ceil(self.imgsz[1]) / stride)
531
+ for stride in self.stride
532
+ ]
533
+
534
+ # get grid cells of one image
535
+ mlvl_center_priors = [
536
+ self.get_single_level_center_priors(
537
+ b,
538
+ featmap_sizes[i],
539
+ stride,
540
+ dtype=np.float32,
541
+ )
542
+ for i, stride in enumerate(self.stride)
543
+ ]
544
+
545
+ center_priors = np.concatenate(mlvl_center_priors, axis=1)
546
+ integral_result = integral_numpy(reg_preds, reg_max=self.reg_max) # (N, 4)
547
+ scale = center_priors[..., 2][..., None] # shape (N, 1) or (N, H, W, 1)
548
+ dis_preds = integral_result * scale
549
+ bboxes = distance2bbox(center_priors[..., :2], dis_preds, max_shape=self.imgsz)
550
+ scores = 1.0 / (1.0 + np.exp(-cls_preds)) # sigmoid
551
+ result_list = []
552
+ for i in range(b):
553
+ # add a dummy background class at the end of all labels
554
+ # same with mmdetection2.0
555
+ score, bbox = scores[i], bboxes[i]
556
+ padding = np.zeros((score.shape[0], 1), dtype=score.dtype)
557
+ score = np.concatenate([score, padding], axis=1)
558
+ results = multiclass_nms(
559
+ bbox,
560
+ score,
561
+ score_thr=0.05,
562
+ nms_cfg=dict(type="nms", iou_threshold=0.6),
563
+ max_num=100,
564
+ )
565
+ result_list.append(results)
566
+ return result_list
567
+ def get_single_level_center_priors(self,batch_size, featmap_size, stride, dtype):
568
+ h, w = featmap_size
569
+ x_range = (np.arange(w, dtype=dtype)) * stride
570
+ y_range = (np.arange(h, dtype=dtype)) * stride
571
+ y, x = np.meshgrid(y_range, x_range, indexing='ij')
572
+ y = y.flatten()
573
+ x = x.flatten()
574
+ strides = np.full((x.shape[0],), stride, dtype=dtype)
575
+ priors = np.stack([x, y, strides, strides], axis=-1)
576
+ return np.tile(priors[None, :, :], (batch_size, 1, 1))
577
+
578
+ def detect_objects(self, image, save_path):
579
+ outputs=[]
580
+ im, im0, org_data = data_process_cv2(image, self.imgsz)
581
+ img_name = os.path.basename(image).split('.')[0]
582
+ infer_start_time = time.time()
583
+ x = self.session.run(None, {self.session.get_inputs()[0].name: im})
584
+ infer_end_time = time.time()
585
+ print(f"infer time: {infer_end_time - infer_start_time:.4f}s")
586
+ x = [np.transpose(x[i],(0,3,1,2)) for i in range(4)] #to nchw
587
+ for i in range(len(x)):
588
+ reg_pred = x[i][:, :self.reg_max1 * 4,:,:]
589
+ cls_pred = x[i][:, self.reg_max1 * 4:,:,:]
590
+ out = np.concatenate([cls_pred, reg_pred], axis=1)
591
+ outputs.append(out.reshape(out.shape[0], out.shape[1], -1))
592
+ preds = np.concatenate(outputs, axis=2).transpose(0, 2, 1)
593
+
594
+ cls_scores = preds[:, :, :self.nc]
595
+ bbox_preds = preds[:, :, self.nc:]
596
+ pred = self.get_bboxes(cls_scores, bbox_preds)[0]
597
+ res = self.post_process(pred, org_data, im0, save_path, img_name)
598
+ result_img, bbox_res = overlay_bbox_cv(im0, res, names, score_thresh=0.35)
599
+ return bbox_res, result_img
600
+ def post_process(self, result, im, im0, save_path, img_name):
601
+ det_result = {}
602
+ det_bboxes, det_labels = result
603
+ det_bboxes[:, :4] = scale_boxes_no_letter(im.shape[:2], det_bboxes[:, :4], im0.shape).round()
604
+ classes = det_labels
605
+ for i in range(self.nc):
606
+ inds = classes == i
607
+ det_result[i] = np.concatenate(
608
+ [
609
+ det_bboxes[inds, :4].astype(np.float32),
610
+ det_bboxes[inds, 4:5].astype(np.float32),
611
+ ],
612
+ axis=1,
613
+ ).tolist()
614
+
615
+ return det_result
616
+
617
+ class QRCodeDecoder:
618
+ def crop_qr_regions(self, image, regions):
619
+ """
620
+ 根据检测到的边界框裁剪二维码区域
621
+ """
622
+ cropped_images = []
623
+ for idx, region in enumerate(regions):
624
+ label, x1, y1, x2, y2, score = region
625
+ # 外扩15个像素缓解因检测截断造成无法识别的情况,视检测情况而定
626
+ x1-=15
627
+ y1-=15
628
+ x2+=15
629
+ y2+=15
630
+ # 裁剪图像
631
+ cropped = image[y1:y2, x1:x2]
632
+ if cropped.size > 0:
633
+ cropped_images.append({
634
+ 'image': cropped,
635
+ 'bbox': region,
636
+ })
637
+ return cropped_images
638
+
639
+ def decode_qrcode_pyzbar(self, cropped_image):
640
+ """
641
+ 使用pyzbar解码二维码
642
+ """
643
+ try:
644
+ # 转换为灰度图像
645
+ if len(cropped_image.shape) == 3:
646
+ gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
647
+ else:
648
+ gray = cropped_image
649
+ # 使用pyzbar解码
650
+ decoded_objects = pyzbar.decode(gray)
651
+ results = []
652
+ for obj in decoded_objects:
653
+ try:
654
+ data = obj.data.decode('utf-8')
655
+ results.append({
656
+ 'data': data,
657
+ 'type': obj.type,
658
+ 'points': obj.polygon
659
+ })
660
+ except:
661
+ continue
662
+
663
+ return results
664
+ except Exception as e:
665
+ print(f"decode error: {e}")
666
+ return []
667
+
668
+ if __name__ == '__main__':
669
+ import time
670
+
671
+ detector = NanoDetONNXInfer(model_path='./nanodet-plus-m_416_QR.onnx',imgsz=[416,416])
672
+ decoder = QRCodeDecoder()
673
+ img_path = './qrcode_test'
674
+ det_path='./det_res'
675
+ crop_path='./crop_res'
676
+ os.makedirs(det_path, exist_ok=True)
677
+ os.makedirs(crop_path, exist_ok=True)
678
+ imgs = glob.glob(f"{img_path}/*.jpg")
679
+ totoal = len(imgs)
680
+ success = 0
681
+ fail = 0
682
+ start_time = time.time()
683
+ for idx,img in enumerate(imgs):
684
+ pic_name=os.path.basename(img).split('.')[0]
685
+ loop_start_time = time.time()
686
+ det_result, res_img = detector.detect_objects(img,det_path)
687
+ # cv2.imwrite(os.path.join(det_path, pic_name+'.jpg'), res_img)
688
+ # print('det_result:',det_result)
689
+ # Crop deteted QRCode & decode QRCode by pyzbar
690
+ cropped_images = decoder.crop_qr_regions(res_img, det_result)
691
+ # for i,cropped in enumerate(cropped_images):
692
+ # cv2.imwrite(os.path.join(crop_path, f'{pic_name}_crop_{i}.jpg'), cropped['image'])
693
+
694
+ all_decoded_results = []
695
+ for i, cropped_data in enumerate(cropped_images):
696
+ decoded_results = decoder.decode_qrcode_pyzbar(cropped_data['image'])
697
+ all_decoded_results.extend(decoded_results)
698
+
699
+ # for result in decoded_results:
700
+ # print(f"decode result: {result['data']} (type: {result['type']})")
701
+ if all_decoded_results:
702
+ success += 1
703
+ print(f"{pic_name} 识别成功!")
704
+ else:
705
+ fail += 1
706
+ print(f"{pic_name} 识别失败!")
707
+ loop_end_time = time.time()
708
+ print(f"图片 {img} 处理耗时: {loop_end_time - loop_start_time:.4f} 秒")
709
+
710
+ end_time = time.time() # 记录总结束时间
711
+ total_time = end_time - start_time # 记录总耗时
712
+
713
+ print(f"总共测试图片数量: {totoal}")
714
+ print(f"识别成功数量: {success}")
715
+ print(f"识别失败数量: {fail}")
716
+ print(f"识别成功率: {success/totoal*100:.2f}%")
717
+ print(f"整体处理耗时: {total_time:.4f} 秒")
718
+ print(f"平均每张图片处理耗时: {total_time/totoal:.4f} 秒")