Upload 32 files
Browse files- .gitattributes +12 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/README.md +58 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/CMakeLists.txt +30 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/main.cpp +370 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/test.png +3 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem +3 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/run_test.py +147 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/test.png +3 -0
- model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/utils.py +160 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/README.md +58 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/CMakeLists.txt +30 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/main.cpp +370 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/test.png +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/models/cutoff_yolov6m_fp16.qnn223.ctx.bin.aidem +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/run_test.py +147 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/test.png +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/utils.py +160 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/README.md +58 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/CMakeLists.txt +30 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/main.cpp +370 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/test.png +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/run_test.py +147 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/test.png +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/utils.py +160 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/README.md +58 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/CMakeLists.txt +30 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/main.cpp +370 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/test.png +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/run_test.py +147 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/test.png +3 -0
- model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/utils.py +160 -0
.gitattributes
CHANGED
@@ -33,3 +33,15 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/test.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem filter=lfs diff=lfs merge=lfs -text
|
38 |
+
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/test.png filter=lfs diff=lfs merge=lfs -text
|
39 |
+
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/test.png filter=lfs diff=lfs merge=lfs -text
|
40 |
+
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/models/cutoff_yolov6m_fp16.qnn223.ctx.bin.aidem filter=lfs diff=lfs merge=lfs -text
|
41 |
+
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/test.png filter=lfs diff=lfs merge=lfs -text
|
42 |
+
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/test.png filter=lfs diff=lfs merge=lfs -text
|
43 |
+
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem filter=lfs diff=lfs merge=lfs -text
|
44 |
+
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/test.png filter=lfs diff=lfs merge=lfs -text
|
45 |
+
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/test.png filter=lfs diff=lfs merge=lfs -text
|
46 |
+
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem filter=lfs diff=lfs merge=lfs -text
|
47 |
+
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/test.png filter=lfs diff=lfs merge=lfs -text
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Model Information
|
2 |
+
### Source model
|
3 |
+
|
4 |
+
- Input shape: 1x3x640x640
|
5 |
+
- Number of parameters: 33.24M
|
6 |
+
- Model size: 133.20MB
|
7 |
+
- Output shape: 1x8400x85
|
8 |
+
|
9 |
+
Source model repository: [yolov6](https://github.com/meituan/YOLOv6/tree/main)
|
10 |
+
|
11 |
+
### Converted model
|
12 |
+
|
13 |
+
- Precision: INT8
|
14 |
+
- Backend: QNN2.23
|
15 |
+
- Target Device: FV01 QCS6490
|
16 |
+
|
17 |
+
## Inference with AidLite SDK
|
18 |
+
|
19 |
+
### SDK installation
|
20 |
+
Model Farm uses AidLite SDK as the model inference SDK. For details, please refer to the [AidLite Developer Documentation](https://v2.docs.aidlux.com/en/sdk-api/aidlite-sdk/)
|
21 |
+
|
22 |
+
- install AidLite SDK
|
23 |
+
|
24 |
+
```bash
|
25 |
+
# Install the appropriate version of the aidlite sdk
|
26 |
+
sudo aid-pkg update
|
27 |
+
sudo aid-pkg install aidlite-sdk
|
28 |
+
# Download the qnn version that matches the above backend. Eg Install QNN2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
29 |
+
sudo aid-pkg install aidlite-{QNN VERSION}
|
30 |
+
# eg: Install QNN 2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
31 |
+
```
|
32 |
+
|
33 |
+
- Verify AidLite SDK
|
34 |
+
|
35 |
+
```bash
|
36 |
+
# aidlite sdk c++ check
|
37 |
+
python3 -c "import aidlite; print(aidlite.get_library_version())"
|
38 |
+
|
39 |
+
# aidlite sdk python check
|
40 |
+
python3 -c "import aidlite; print(aidlite.get_py_library_version())"
|
41 |
+
```
|
42 |
+
|
43 |
+
### Run demo
|
44 |
+
|
45 |
+
#### python
|
46 |
+
```bash
|
47 |
+
cd yolov6m/model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite
|
48 |
+
python3 python/run_test.py --target_model ./models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem --imgs ./python/test.png --invoke_nums 10
|
49 |
+
|
50 |
+
```
|
51 |
+
|
52 |
+
#### cpp
|
53 |
+
```bash
|
54 |
+
cd yolov6m/model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp
|
55 |
+
mkdir build && cd build
|
56 |
+
cmake .. && make
|
57 |
+
./run_test --target_model ../../models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem --imgs ../test.png --invoke_nums 10
|
58 |
+
```
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/CMakeLists.txt
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
cmake_minimum_required (VERSION 3.5)
|
2 |
+
project("run_test")
|
3 |
+
|
4 |
+
find_package(OpenCV REQUIRED)
|
5 |
+
|
6 |
+
message(STATUS "oPENCV Library status:")
|
7 |
+
message(STATUS ">version:${OpenCV_VERSION}")
|
8 |
+
message(STATUS "Include:${OpenCV_INCLUDE_DIRS}")
|
9 |
+
|
10 |
+
set(CMAKE_CXX_FLAGS "-Wno-error=deprecated-declarations -Wno-deprecated-declarations")
|
11 |
+
|
12 |
+
include_directories(
|
13 |
+
/usr/local/include
|
14 |
+
/usr/include/opencv4
|
15 |
+
)
|
16 |
+
|
17 |
+
link_directories(
|
18 |
+
/usr/local/lib/
|
19 |
+
)
|
20 |
+
|
21 |
+
file(GLOB SRC_LISTS
|
22 |
+
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
|
23 |
+
)
|
24 |
+
|
25 |
+
add_executable(run_test ${SRC_LISTS})
|
26 |
+
|
27 |
+
target_link_libraries(run_test
|
28 |
+
aidlite
|
29 |
+
${OpenCV_LIBS}
|
30 |
+
)
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/main.cpp
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#include <iostream>
|
2 |
+
#include <string>
|
3 |
+
#include <algorithm>
|
4 |
+
#include <cctype>
|
5 |
+
#include <opencv2/opencv.hpp>
|
6 |
+
#include <aidlux/aidlite/aidlite.hpp>
|
7 |
+
#include <vector>
|
8 |
+
#include <numeric>
|
9 |
+
|
10 |
+
const float INPUT_WIDTH = 640.0;
|
11 |
+
const float INPUT_HEIGHT = 640.0;
|
12 |
+
const float SCORE_THRESHOLD = 0.25;
|
13 |
+
const float NMS_THRESHOLD = 0.45;
|
14 |
+
const float CONFIDENCE_THRESHOLD = 0.25;
|
15 |
+
const uint32_t size = 640;
|
16 |
+
const uint32_t out_size = 8400;
|
17 |
+
|
18 |
+
const int FONT_FACE = cv::FONT_HERSHEY_SIMPLEX;
|
19 |
+
cv::Scalar WHITE = cv::Scalar(255,255,255);
|
20 |
+
|
21 |
+
const float FONT_SCALE = 1;
|
22 |
+
const int THICKNESS = 2;
|
23 |
+
using namespace Aidlux::Aidlite;
|
24 |
+
|
25 |
+
struct Args {
|
26 |
+
std::string target_model = "../../models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem";
|
27 |
+
std::string imgs = "../test.png";
|
28 |
+
int invoke_nums = 10;
|
29 |
+
std::string model_type = "QNN";
|
30 |
+
};
|
31 |
+
|
32 |
+
Args parse_args(int argc, char* argv[]) {
|
33 |
+
Args args;
|
34 |
+
for (int i = 1; i < argc; ++i) {
|
35 |
+
std::string arg = argv[i];
|
36 |
+
if (arg == "--target_model" && i + 1 < argc) {
|
37 |
+
args.target_model = argv[++i];
|
38 |
+
} else if (arg == "--imgs" && i + 1 < argc) {
|
39 |
+
args.imgs = argv[++i];
|
40 |
+
} else if (arg == "--invoke_nums" && i + 1 < argc) {
|
41 |
+
args.invoke_nums = std::stoi(argv[++i]);
|
42 |
+
} else if (arg == "--model_type" && i + 1 < argc) {
|
43 |
+
args.model_type = argv[++i];
|
44 |
+
}
|
45 |
+
}
|
46 |
+
return args;
|
47 |
+
}
|
48 |
+
|
49 |
+
std::string to_lower(const std::string& str) {
|
50 |
+
std::string lower_str = str;
|
51 |
+
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), [](unsigned char c) {
|
52 |
+
return std::tolower(c);
|
53 |
+
});
|
54 |
+
return lower_str;
|
55 |
+
}
|
56 |
+
|
57 |
+
|
58 |
+
void concatenate(float* qnn_trans_data, float* qnn_mul_data, int batch, int num_elements, int trans_dim, int mul_dim, std::vector<float>& output) {
|
59 |
+
int out_dim = trans_dim + mul_dim + 1;
|
60 |
+
output.resize(batch * num_elements * out_dim);
|
61 |
+
for (int i = 0; i < batch * num_elements; ++i) {
|
62 |
+
std::memcpy(&output[i * out_dim], &qnn_mul_data[i * mul_dim], mul_dim * sizeof(float));
|
63 |
+
float max_val = *std::max_element(&qnn_trans_data[i * trans_dim], &qnn_trans_data[i * trans_dim + trans_dim]);
|
64 |
+
output[i * out_dim + 4] = max_val;
|
65 |
+
std::memcpy(&output[i * out_dim + 5], &qnn_trans_data[i * trans_dim], trans_dim * sizeof(float));
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
cv::Mat letterbox(cv::Mat im, cv::Size new_shape = cv::Size(640, 640),
|
70 |
+
cv::Scalar color = cv::Scalar(114, 114, 114),
|
71 |
+
bool auto_pad = true, bool scaleup = true, int stride = 32) {
|
72 |
+
// current shape [height, width]
|
73 |
+
cv::Size shape = im.size();
|
74 |
+
int height = shape.height;
|
75 |
+
int width = shape.width;
|
76 |
+
|
77 |
+
if (new_shape.width == 0) {
|
78 |
+
new_shape = cv::Size(new_shape.height, new_shape.height);
|
79 |
+
}
|
80 |
+
|
81 |
+
// Scale ratio (new / old)
|
82 |
+
float r = std::min((float)new_shape.height / height, (float)new_shape.width / width);
|
83 |
+
if (!scaleup) {
|
84 |
+
// only scale down, do not scale up (for better val mAP)
|
85 |
+
r = std::min(r, 1.0f);
|
86 |
+
}
|
87 |
+
|
88 |
+
// Compute padding
|
89 |
+
cv::Size new_unpad(round(width * r), round(height * r));
|
90 |
+
int dw = new_shape.width - new_unpad.width;
|
91 |
+
int dh = new_shape.height - new_unpad.height;
|
92 |
+
|
93 |
+
// minimum rectangle
|
94 |
+
if (auto_pad) {
|
95 |
+
dw = dw % stride;
|
96 |
+
dh = dh % stride;
|
97 |
+
}
|
98 |
+
|
99 |
+
dw /= 2; // divide padding into 2 sides
|
100 |
+
dh /= 2;
|
101 |
+
|
102 |
+
// resize
|
103 |
+
if (cv::Size(width, height) != new_unpad) {
|
104 |
+
cv::resize(im, im, new_unpad, 0, 0, cv::INTER_LINEAR);
|
105 |
+
}
|
106 |
+
|
107 |
+
int top = round(dh - 0.1);
|
108 |
+
int bottom = round(dh + 0.1);
|
109 |
+
int left = round(dw - 0.1);
|
110 |
+
int right = round(dw + 0.1);
|
111 |
+
|
112 |
+
cv::copyMakeBorder(im, im, top, bottom, left, right, cv::BORDER_CONSTANT, color);
|
113 |
+
return im;
|
114 |
+
}
|
115 |
+
cv::Scalar generate_colors(int i, bool bgr = false) {
|
116 |
+
static const std::vector<std::string> hex_colors = {
|
117 |
+
"FF3838", "FF9D97", "FF701F", "FFB21D", "CFD231", "48F90A",
|
118 |
+
"92CC17", "3DDB86", "1A9334", "00D4BB", "2C99A8", "00C2FF",
|
119 |
+
"344593", "6473FF", "0018EC", "8438FF", "520085", "CB38FF",
|
120 |
+
"FF95C8", "FF37C7"
|
121 |
+
};
|
122 |
+
|
123 |
+
int num = hex_colors.size();
|
124 |
+
std::string hex = hex_colors[i % num];
|
125 |
+
|
126 |
+
int r = std::stoi(hex.substr(0, 2), nullptr, 16);
|
127 |
+
int g = std::stoi(hex.substr(2, 2), nullptr, 16);
|
128 |
+
int b = std::stoi(hex.substr(4, 2), nullptr, 16);
|
129 |
+
|
130 |
+
if (bgr)
|
131 |
+
return cv::Scalar(b, g, r);
|
132 |
+
else
|
133 |
+
return cv::Scalar(r, g, b);
|
134 |
+
}
|
135 |
+
|
136 |
+
void draw_label(cv::Mat& input_image, std::string label, int left, int top, cv::Scalar color)
|
137 |
+
{
|
138 |
+
int baseLine;
|
139 |
+
cv::Size label_size = cv::getTextSize(label, FONT_FACE, FONT_SCALE, THICKNESS, &baseLine);
|
140 |
+
int y = top - label_size.height - baseLine;
|
141 |
+
if (y < 0) {
|
142 |
+
y = top + label_size.height + baseLine;
|
143 |
+
}
|
144 |
+
cv::Point tlc(left, y);
|
145 |
+
cv::Point brc(left + label_size.width, y + label_size.height + baseLine);
|
146 |
+
rectangle(input_image, tlc, brc, color, cv::FILLED);
|
147 |
+
putText(input_image, label, cv::Point(left, y + label_size.height), FONT_FACE, FONT_SCALE, WHITE, THICKNESS);
|
148 |
+
}
|
149 |
+
|
150 |
+
|
151 |
+
cv::Mat post_process(cv::Mat &input_image, std::vector<float> &outputs, const std::vector<std::string> &class_name)
|
152 |
+
{
|
153 |
+
// Initialize vectors to hold respective outputs while unwrapping detections.
|
154 |
+
std::vector<int> class_ids;
|
155 |
+
std::vector<float> confidences;
|
156 |
+
std::vector<cv::Rect> boxes;
|
157 |
+
|
158 |
+
// Resizing factor.
|
159 |
+
float r = std::min(INPUT_WIDTH / (float)input_image.cols, INPUT_HEIGHT / (float)input_image.rows);
|
160 |
+
int new_unpad_w = round(input_image.cols * r);
|
161 |
+
int new_unpad_h = round(input_image.rows * r);
|
162 |
+
int dw = (int)INPUT_WIDTH - new_unpad_w;
|
163 |
+
int dh = (int)INPUT_HEIGHT - new_unpad_h;
|
164 |
+
dw /= 24;
|
165 |
+
dh /= 24;
|
166 |
+
|
167 |
+
// Iterate through outputs for each box prediction
|
168 |
+
for (int i = 0; i < outputs.size(); i+=85)
|
169 |
+
{
|
170 |
+
float confidence = outputs[i+4];
|
171 |
+
if (confidence >= CONFIDENCE_THRESHOLD)
|
172 |
+
{
|
173 |
+
// Create a 1x80 Mat and store class scores of 80 classes.
|
174 |
+
cv::Mat scores(1, class_name.size(), CV_32FC1, outputs.data() + i + 5);
|
175 |
+
cv::Point class_id;
|
176 |
+
double max_class_score;
|
177 |
+
|
178 |
+
// For multi-label, check each class score
|
179 |
+
for (int c = 0; c < class_name.size(); c++) {
|
180 |
+
float class_score = scores.at<float>(0, c);
|
181 |
+
|
182 |
+
// If class score is above threshold, consider this class for the box
|
183 |
+
if (class_score > SCORE_THRESHOLD) {
|
184 |
+
// Store class ID and confidence in the pre-defined respective vectors.
|
185 |
+
confidences.push_back(confidence * class_score); // Multiply with confidence
|
186 |
+
class_ids.push_back(c); // class index
|
187 |
+
// Center and box dimension.
|
188 |
+
float cx = outputs[i];
|
189 |
+
float cy = outputs[i+1];
|
190 |
+
float w = outputs[i+2];
|
191 |
+
float h = outputs[i+3];
|
192 |
+
|
193 |
+
float x0 = (cx - 0.5f * w - dw) / r;
|
194 |
+
float y0 = (cy - 0.5f * h - dh) / r;
|
195 |
+
float x1 = (cx + 0.5f * w - dw) / r;
|
196 |
+
float y1 = (cy + 0.5f * h - dh) / r;
|
197 |
+
|
198 |
+
int left = int(x0);
|
199 |
+
int top = int(y0);
|
200 |
+
int width = int(x1 - x0);
|
201 |
+
int height = int(y1 - y0);
|
202 |
+
|
203 |
+
// Store good detections in the boxes vector.
|
204 |
+
boxes.push_back(cv::Rect(left, top, width, height));
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
// Perform Non Maximum Suppression and draw predictions.
|
211 |
+
std::vector<int> indices;
|
212 |
+
cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices);
|
213 |
+
printf("Detected {%ld} targets.\n", indices.size());
|
214 |
+
|
215 |
+
// Loop over NMS results and draw bounding boxes
|
216 |
+
for (int i = 0; i < indices.size(); i++)
|
217 |
+
{
|
218 |
+
int idx = indices[i];
|
219 |
+
cv::Rect box = boxes[idx];
|
220 |
+
|
221 |
+
int left = box.x;
|
222 |
+
int top = box.y;
|
223 |
+
int width = box.width;
|
224 |
+
int height = box.height;
|
225 |
+
cv::Scalar color = generate_colors(class_ids[idx]);
|
226 |
+
// Draw bounding box.
|
227 |
+
rectangle(input_image, cv::Point(left, top), cv::Point(left + width, top + height), color, 3*THICKNESS);
|
228 |
+
|
229 |
+
// Get the label for the class name and its confidence.
|
230 |
+
std::string label = cv::format("%.2f", confidences[idx]);
|
231 |
+
label = class_name[class_ids[idx]] + ":" + label;
|
232 |
+
// Draw class labels.
|
233 |
+
draw_label(input_image, label, left, top, color);
|
234 |
+
}
|
235 |
+
printf("Processing finished.\n");
|
236 |
+
return input_image;
|
237 |
+
}
|
238 |
+
|
239 |
+
|
240 |
+
int invoke(const Args& args) {
|
241 |
+
std::cout << "Start main ... ... Model Path: " << args.target_model << "\n"
|
242 |
+
<< "Image Path: " << args.imgs << "\n"
|
243 |
+
<< "Inference Nums: " << args.invoke_nums << "\n"
|
244 |
+
<< "Model Type: " << args.model_type << "\n";
|
245 |
+
Model* model = Model::create_instance(args.target_model);
|
246 |
+
if(model == nullptr){
|
247 |
+
printf("Create model failed !\n");
|
248 |
+
return EXIT_FAILURE;
|
249 |
+
}
|
250 |
+
Config* config = Config::create_instance();
|
251 |
+
if(config == nullptr){
|
252 |
+
printf("Create config failed !\n");
|
253 |
+
return EXIT_FAILURE;
|
254 |
+
}
|
255 |
+
config->implement_type = ImplementType::TYPE_LOCAL;
|
256 |
+
std::string model_type_lower = to_lower(args.model_type);
|
257 |
+
if (model_type_lower == "qnn"){
|
258 |
+
config->framework_type = FrameworkType::TYPE_QNN223;
|
259 |
+
} else if (model_type_lower == "snpe2" || model_type_lower == "snpe") {
|
260 |
+
config->framework_type = FrameworkType::TYPE_SNPE2;
|
261 |
+
}
|
262 |
+
config->accelerate_type = AccelerateType::TYPE_DSP;
|
263 |
+
config->is_quantify_model = 1;
|
264 |
+
|
265 |
+
std::vector<std::vector<uint32_t>> input_shapes = {{1, size, size, 3}};
|
266 |
+
std::vector<std::vector<uint32_t>> output_shapes = {{1, out_size, 80}, {1, out_size, 4}};
|
267 |
+
model->set_model_properties(input_shapes, DataType::TYPE_FLOAT32, output_shapes, DataType::TYPE_FLOAT32);
|
268 |
+
std::unique_ptr<Interpreter> fast_interpreter = InterpreterBuilder::build_interpretper_from_model_and_config(model, config);
|
269 |
+
if(fast_interpreter == nullptr){
|
270 |
+
printf("build_interpretper_from_model_and_config failed !\n");
|
271 |
+
return EXIT_FAILURE;
|
272 |
+
}
|
273 |
+
int result = fast_interpreter->init();
|
274 |
+
if(result != EXIT_SUCCESS){
|
275 |
+
printf("interpreter->init() failed !\n");
|
276 |
+
return EXIT_FAILURE;
|
277 |
+
}
|
278 |
+
// load model
|
279 |
+
fast_interpreter->load_model();
|
280 |
+
if(result != EXIT_SUCCESS){
|
281 |
+
printf("interpreter->load_model() failed !\n");
|
282 |
+
return EXIT_FAILURE;
|
283 |
+
}
|
284 |
+
printf("detect model load success!\n");
|
285 |
+
cv::Size img_size(size, size);
|
286 |
+
|
287 |
+
cv::Mat img_src = cv::imread(args.imgs);
|
288 |
+
printf("img_src cols: %d, img_src rows: %d\n", img_src.cols, img_src.rows);
|
289 |
+
cv::Mat img_ori = img_src.clone();
|
290 |
+
cv::cvtColor(img_ori, img_ori, cv::COLOR_BGR2RGB);
|
291 |
+
cv::Mat resized_img = letterbox(img_ori, img_size);
|
292 |
+
cv::Mat input_img = cv::Mat::zeros(img_size, CV_32FC3);
|
293 |
+
resized_img.convertTo(resized_img, CV_32FC3, 1.0 / 255.0);
|
294 |
+
resized_img.copyTo(input_img(cv::Rect(0, 0, resized_img.cols, resized_img.rows)));
|
295 |
+
float *qnn_trans_data = nullptr;
|
296 |
+
float *qnn_mul_data = nullptr;
|
297 |
+
|
298 |
+
std::vector<float> invoke_time;
|
299 |
+
for (int i = 0; i < args.invoke_nums; ++i) {
|
300 |
+
result = fast_interpreter->set_input_tensor(0, input_img.data);
|
301 |
+
if(result != EXIT_SUCCESS){
|
302 |
+
printf("interpreter->set_input_tensor() failed !\n");
|
303 |
+
return EXIT_FAILURE;
|
304 |
+
}
|
305 |
+
// 开始计时
|
306 |
+
auto t1 = std::chrono::high_resolution_clock::now();
|
307 |
+
result = fast_interpreter->invoke();
|
308 |
+
auto t2 = std::chrono::high_resolution_clock::now();
|
309 |
+
std::chrono::duration<double> cost_time = t2 - t1;
|
310 |
+
invoke_time.push_back(cost_time.count() * 1000);
|
311 |
+
if(result != EXIT_SUCCESS){
|
312 |
+
printf("interpreter->invoke() failed !\n");
|
313 |
+
return EXIT_FAILURE;
|
314 |
+
}
|
315 |
+
uint32_t out_data_1 = 0;
|
316 |
+
result = fast_interpreter->get_output_tensor(0, (void**)&qnn_trans_data, &out_data_1);
|
317 |
+
if(result != EXIT_SUCCESS){
|
318 |
+
printf("interpreter->get_output_tensor() 1 failed !\n");
|
319 |
+
return EXIT_FAILURE;
|
320 |
+
}
|
321 |
+
uint32_t out_data_2 = 0;
|
322 |
+
result = fast_interpreter->get_output_tensor(1, (void**)&qnn_mul_data, &out_data_2);
|
323 |
+
if(result != EXIT_SUCCESS){
|
324 |
+
printf("interpreter->get_output_tensor() 2 failed !\n");
|
325 |
+
return EXIT_FAILURE;
|
326 |
+
}
|
327 |
+
}
|
328 |
+
|
329 |
+
float max_invoke_time = *std::max_element(invoke_time.begin(), invoke_time.end());
|
330 |
+
float min_invoke_time = *std::min_element(invoke_time.begin(), invoke_time.end());
|
331 |
+
float mean_invoke_time = std::accumulate(invoke_time.begin(), invoke_time.end(), 0.0f) / args.invoke_nums;
|
332 |
+
float var_invoketime = 0.0f;
|
333 |
+
for (auto time : invoke_time) {
|
334 |
+
var_invoketime += (time - mean_invoke_time) * (time - mean_invoke_time);
|
335 |
+
}
|
336 |
+
var_invoketime /= args.invoke_nums;
|
337 |
+
printf("=======================================\n");
|
338 |
+
printf("QNN inference %d times :\n --mean_invoke_time is %f \n --max_invoke_time is %f \n --min_invoke_time is %f \n --var_invoketime is %f\n",
|
339 |
+
args.invoke_nums, mean_invoke_time, max_invoke_time, min_invoke_time, var_invoketime);
|
340 |
+
printf("=======================================\n");
|
341 |
+
|
342 |
+
std::vector<std::string> class_list = {
|
343 |
+
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
|
344 |
+
"truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter",
|
345 |
+
"bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear",
|
346 |
+
"zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
|
347 |
+
"frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
|
348 |
+
"baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
|
349 |
+
"wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
|
350 |
+
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",
|
351 |
+
"cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet",
|
352 |
+
"TV", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
|
353 |
+
"oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
|
354 |
+
"scissors", "teddy bear", "hair drier", "toothbrush"
|
355 |
+
};
|
356 |
+
|
357 |
+
// post process
|
358 |
+
std::vector<float> qnn_concat;
|
359 |
+
concatenate(qnn_trans_data, qnn_mul_data, 1, out_size, 80, 4, qnn_concat);
|
360 |
+
cv::Mat img = post_process(img_src, qnn_concat, class_list);
|
361 |
+
cv::imwrite("./results.png", img);
|
362 |
+
fast_interpreter->destory();
|
363 |
+
return 0;
|
364 |
+
}
|
365 |
+
|
366 |
+
|
367 |
+
int main(int argc, char* argv[]) {
|
368 |
+
Args args = parse_args(argc, argv);
|
369 |
+
return invoke(args);
|
370 |
+
}
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/cpp/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2b5f70de77d86203135bf45778bb7448565566682bf659a418155d6148b19a13
|
3 |
+
size 38488656
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/run_test.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, torch, cv2
|
2 |
+
import numpy as np
|
3 |
+
import time
|
4 |
+
import aidlite
|
5 |
+
import argparse
|
6 |
+
from utils import letterbox,plot_box_and_label,rescale,generate_colors,non_max_suppression
|
7 |
+
import torch
|
8 |
+
|
9 |
+
|
10 |
+
def process_image(path, img_size):
|
11 |
+
img_src = cv2.imread(path)
|
12 |
+
img_src = cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB)
|
13 |
+
image = letterbox(img_src, img_size)[0]
|
14 |
+
new_h,new_w,_=image.shape
|
15 |
+
input_img = np.zeros((img_size[0], img_size[1], 3), np.uint8)
|
16 |
+
input_img[0:new_h, 0:new_w] = image
|
17 |
+
input_img = input_img.astype(np.float32)
|
18 |
+
input_img /= 255 # 0 - 255 to 0.0 - 1.0
|
19 |
+
input_img = np.expand_dims(input_img,0)
|
20 |
+
return image,input_img, img_src
|
21 |
+
|
22 |
+
def main(args):
|
23 |
+
print("Start main ... ...")
|
24 |
+
# aidlite.set_log_level(aidlite.LogLevel.INFO)
|
25 |
+
# aidlite.log_to_stderr()
|
26 |
+
# print(f"Aidlite library version : {aidlite.get_library_version()}")
|
27 |
+
# print(f"Aidlite python library version : {aidlite.get_py_library_version()}")
|
28 |
+
|
29 |
+
size=640
|
30 |
+
out_size=8400
|
31 |
+
config = aidlite.Config.create_instance()
|
32 |
+
if config is None:
|
33 |
+
print("Create config failed !")
|
34 |
+
return False
|
35 |
+
config.implement_type = aidlite.ImplementType.TYPE_LOCAL
|
36 |
+
if args.model_type.lower()=="qnn":
|
37 |
+
config.framework_type = aidlite.FrameworkType.TYPE_QNN223
|
38 |
+
elif args.model_type.lower()=="snpe2" or args.model_type.lower()=="snpe":
|
39 |
+
config.framework_type = aidlite.FrameworkType.TYPE_SNPE2
|
40 |
+
|
41 |
+
config.accelerate_type = aidlite.AccelerateType.TYPE_DSP
|
42 |
+
config.is_quantify_model = 1
|
43 |
+
|
44 |
+
|
45 |
+
model = aidlite.Model.create_instance(args.target_model)
|
46 |
+
if model is None:
|
47 |
+
print("Create model failed !")
|
48 |
+
return False
|
49 |
+
input_shapes = [[1, size, size, 3]]
|
50 |
+
output_shapes = [[1, out_size,80],[1, out_size,4]]
|
51 |
+
model.set_model_properties(input_shapes, aidlite.DataType.TYPE_FLOAT32,
|
52 |
+
output_shapes, aidlite.DataType.TYPE_FLOAT32)
|
53 |
+
|
54 |
+
interpreter = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model, config)
|
55 |
+
if interpreter is None:
|
56 |
+
print("build_interpretper_from_model_and_config failed !")
|
57 |
+
return None
|
58 |
+
result = interpreter.init()
|
59 |
+
if result != 0:
|
60 |
+
print(f"interpreter init failed !")
|
61 |
+
return False
|
62 |
+
result = interpreter.load_model()
|
63 |
+
if result != 0:
|
64 |
+
print("interpreter load model failed !")
|
65 |
+
return False
|
66 |
+
print("detect model load success!")
|
67 |
+
|
68 |
+
# image process
|
69 |
+
img_size=[size,size]
|
70 |
+
resize_img,input_img, img_src = process_image(args.imgs, img_size)
|
71 |
+
|
72 |
+
# qnn run
|
73 |
+
invoke_time=[]
|
74 |
+
for i in range(args.invoke_nums):
|
75 |
+
result = interpreter.set_input_tensor(0, input_img.data)
|
76 |
+
if result != 0:
|
77 |
+
print("interpreter set_input_tensor() failed")
|
78 |
+
|
79 |
+
t1=time.time()
|
80 |
+
result = interpreter.invoke()
|
81 |
+
cost_time = (time.time()-t1)*1000
|
82 |
+
invoke_time.append(cost_time)
|
83 |
+
|
84 |
+
if result != 0:
|
85 |
+
print("interpreter set_input_tensor() failed")
|
86 |
+
|
87 |
+
qnn_trans = interpreter.get_output_tensor(0).reshape(1,out_size,80)
|
88 |
+
qnn_mul = interpreter.get_output_tensor(1).reshape(1,out_size,4)
|
89 |
+
|
90 |
+
result = interpreter.destory()
|
91 |
+
|
92 |
+
## time 统计
|
93 |
+
max_invoke_time = max(invoke_time)
|
94 |
+
min_invoke_time = min(invoke_time)
|
95 |
+
mean_invoke_time = sum(invoke_time)/args.invoke_nums
|
96 |
+
var_invoketime=np.var(invoke_time)
|
97 |
+
print("=======================================")
|
98 |
+
print(f"QNN inference {args.invoke_nums} times :\n --mean_invoke_time is {mean_invoke_time} \n --max_invoke_time is {max_invoke_time} \n --min_invoke_time is {min_invoke_time} \n --var_invoketime is {var_invoketime}")
|
99 |
+
print("=======================================")
|
100 |
+
|
101 |
+
# 后处理
|
102 |
+
conf_thres =0.25 #@param {type:"number"}
|
103 |
+
iou_thres =0.45 #@param {type:"number"}
|
104 |
+
max_det= 1000#@param {type:"integer"}
|
105 |
+
agnostic_nms= False #@param {type:"boolean"}
|
106 |
+
classes =None
|
107 |
+
hide_labels = False #@param {type:"boolean"}
|
108 |
+
hide_conf= False #@param {type:"boolean"}
|
109 |
+
|
110 |
+
qnn_conf = np.ones((1,out_size,1))
|
111 |
+
qnn_predict=np.concatenate((qnn_mul,qnn_conf,qnn_trans), axis=2)
|
112 |
+
pred_results =torch.from_numpy(qnn_predict.copy())
|
113 |
+
det = non_max_suppression(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)[0]
|
114 |
+
|
115 |
+
class_names=[ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
|
116 |
+
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
|
117 |
+
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
|
118 |
+
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
|
119 |
+
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
120 |
+
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
|
121 |
+
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
|
122 |
+
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
|
123 |
+
'hair drier', 'toothbrush' ]
|
124 |
+
|
125 |
+
img_ori = img_src.copy()
|
126 |
+
print(f"Detected {len(det)} targets.")
|
127 |
+
if len(det):
|
128 |
+
det[:, :4] = rescale(resize_img.shape[:2], det[:, :4], img_src.shape).round()
|
129 |
+
for *xyxy, conf, cls in reversed(det):
|
130 |
+
class_num = int(cls)
|
131 |
+
label = None if hide_labels else (class_names[class_num] if hide_conf else f'{class_names[class_num]} {conf:.2f}')
|
132 |
+
plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=generate_colors(class_num, True))
|
133 |
+
|
134 |
+
cv2.imwrite("./python/results.png",cv2.cvtColor(img_ori,cv2.COLOR_RGB2BGR))
|
135 |
+
|
136 |
+
def parser_args():
|
137 |
+
parser = argparse.ArgumentParser(description="Run model benchmarks")
|
138 |
+
parser.add_argument('--target_model',type=str,default='./models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem',help="inference model path")
|
139 |
+
parser.add_argument('--imgs',type=str,default='./python/test.png',help="Predict images path")
|
140 |
+
parser.add_argument('--invoke_nums',type=int,default=10,help="Inference nums")
|
141 |
+
parser.add_argument('--model_type',type=str,default='QNN',help="run backend")
|
142 |
+
args = parser.parse_args()
|
143 |
+
return args
|
144 |
+
|
145 |
+
if __name__ == "__main__":
|
146 |
+
args = parser_args()
|
147 |
+
main(args)
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs6490_qnn2.23_int8_aidlite/python/utils.py
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
import torch
|
4 |
+
import torchvision
|
5 |
+
|
6 |
+
def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
|
7 |
+
'''Resize and pad image while meeting stride-multiple constraints.'''
|
8 |
+
shape = im.shape[:2] # current shape [height, width]
|
9 |
+
if isinstance(new_shape, int):
|
10 |
+
new_shape = (new_shape, new_shape)
|
11 |
+
elif isinstance(new_shape, list) and len(new_shape) == 1:
|
12 |
+
new_shape = (new_shape[0], new_shape[0])
|
13 |
+
|
14 |
+
# Scale ratio (new / old)
|
15 |
+
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
|
16 |
+
if not scaleup: # only scale down, do not scale up (for better val mAP)
|
17 |
+
r = min(r, 1.0)
|
18 |
+
|
19 |
+
# Compute padding
|
20 |
+
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
|
21 |
+
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
|
22 |
+
|
23 |
+
if auto: # minimum rectangle
|
24 |
+
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
|
25 |
+
|
26 |
+
dw /= 2 # divide padding into 2 sides
|
27 |
+
dh /= 2
|
28 |
+
|
29 |
+
if shape[::-1] != new_unpad: # resize
|
30 |
+
im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
|
31 |
+
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
|
32 |
+
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
|
33 |
+
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
|
34 |
+
|
35 |
+
return im, r, (left, top)
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
|
40 |
+
def xywh2xyxy(x):
|
41 |
+
'''Convert boxes with shape [n, 4] from [x, y, w, h] to [x1, y1, x2, y2] where x1y1 is top-left, x2y2=bottom-right.'''
|
42 |
+
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
43 |
+
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
|
44 |
+
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
|
45 |
+
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
|
46 |
+
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
|
47 |
+
return y
|
48 |
+
|
49 |
+
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, max_det=300):
|
50 |
+
"""Runs Non-Maximum Suppression (NMS) on inference results.
|
51 |
+
This code is borrowed from: https://github.com/ultralytics/yolov5/blob/47233e1698b89fc437a4fb9463c815e9171be955/utils/general.py#L775
|
52 |
+
Args:
|
53 |
+
prediction: (tensor), with shape [N, 5 + num_classes], N is the number of bboxes.
|
54 |
+
conf_thres: (float) confidence threshold.
|
55 |
+
iou_thres: (float) iou threshold.
|
56 |
+
classes: (None or list[int]), if a list is provided, nms only keep the classes you provide.
|
57 |
+
agnostic: (bool), when it is set to True, we do class-independent nms, otherwise, different class would do nms respectively.
|
58 |
+
multi_label: (bool), when it is set to True, one box can have multi labels, otherwise, one box only huave one label.
|
59 |
+
max_det:(int), max number of output bboxes.
|
60 |
+
|
61 |
+
Returns:
|
62 |
+
list of detections, echo item is one tensor with shape (num_boxes, 6), 6 is for [xyxy, conf, cls].
|
63 |
+
"""
|
64 |
+
|
65 |
+
num_classes = prediction.shape[2] - 5 # number of classes
|
66 |
+
pred_candidates = torch.logical_and(prediction[..., 4] > conf_thres, torch.max(prediction[..., 5:], axis=-1)[0] > conf_thres) # candidates
|
67 |
+
# Check the parameters.
|
68 |
+
assert 0 <= conf_thres <= 1, f'conf_thresh must be in 0.0 to 1.0, however {conf_thres} is provided.'
|
69 |
+
assert 0 <= iou_thres <= 1, f'iou_thres must be in 0.0 to 1.0, however {iou_thres} is provided.'
|
70 |
+
|
71 |
+
# Function settings.
|
72 |
+
max_wh = 4096 # maximum box width and height
|
73 |
+
max_nms = 30000 # maximum number of boxes put into torchvision.ops.nms()
|
74 |
+
time_limit = 10.0 # quit the function when nms cost time exceed the limit time.
|
75 |
+
multi_label &= num_classes > 1 # multiple labels per box
|
76 |
+
|
77 |
+
output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0]
|
78 |
+
for img_idx, x in enumerate(prediction): # image index, image inference
|
79 |
+
x = x[pred_candidates[img_idx]] # confidence
|
80 |
+
|
81 |
+
# If no box remains, skip the next process.
|
82 |
+
if not x.shape[0]:
|
83 |
+
continue
|
84 |
+
|
85 |
+
# confidence multiply the objectness
|
86 |
+
x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf
|
87 |
+
|
88 |
+
# (center x, center y, width, height) to (x1, y1, x2, y2)
|
89 |
+
box = xywh2xyxy(x[:, :4])
|
90 |
+
|
91 |
+
# Detections matrix's shape is (n,6), each row represents (xyxy, conf, cls)
|
92 |
+
if multi_label:
|
93 |
+
box_idx, class_idx = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T
|
94 |
+
x = torch.cat((box[box_idx], x[box_idx, class_idx + 5, None], class_idx[:, None].float()), 1)
|
95 |
+
else: # Only keep the class with highest scores.
|
96 |
+
conf, class_idx = x[:, 5:].max(1, keepdim=True)
|
97 |
+
x = torch.cat((box, conf, class_idx.float()), 1)[conf.view(-1) > conf_thres]
|
98 |
+
|
99 |
+
# Filter by class, only keep boxes whose category is in classes.
|
100 |
+
if classes is not None:
|
101 |
+
x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]
|
102 |
+
|
103 |
+
# Check shape
|
104 |
+
num_box = x.shape[0] # number of boxes
|
105 |
+
if not num_box: # no boxes kept.
|
106 |
+
continue
|
107 |
+
elif num_box > max_nms: # excess max boxes' number.
|
108 |
+
x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence
|
109 |
+
|
110 |
+
# Batched NMS
|
111 |
+
class_offset = x[:, 5:6] * (0 if agnostic else max_wh) # classes
|
112 |
+
boxes, scores = x[:, :4] + class_offset, x[:, 4] # boxes (offset by class), scores
|
113 |
+
keep_box_idx = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
|
114 |
+
if keep_box_idx.shape[0] > max_det: # limit detections
|
115 |
+
keep_box_idx = keep_box_idx[:max_det]
|
116 |
+
|
117 |
+
output[img_idx] = x[keep_box_idx]
|
118 |
+
|
119 |
+
return output
|
120 |
+
|
121 |
+
def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX):
|
122 |
+
# Add one xyxy box to image with label
|
123 |
+
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
|
124 |
+
cv2.rectangle(image, p1, p2, color, thickness=lw, lineType=cv2.LINE_AA)
|
125 |
+
if label:
|
126 |
+
tf = max(lw - 1, 1) # font thickness
|
127 |
+
w, h = cv2.getTextSize(label, 0, fontScale=lw / 3, thickness=tf)[0] # text width, height
|
128 |
+
outside = p1[1] - h - 3 >= 0 # label fits outside box
|
129 |
+
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
|
130 |
+
cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA) # filled
|
131 |
+
cv2.putText(image, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), font, lw / 3, txt_color,
|
132 |
+
thickness=tf, lineType=cv2.LINE_AA)
|
133 |
+
|
134 |
+
def generate_colors(i, bgr=False):
|
135 |
+
hex = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
|
136 |
+
'2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
|
137 |
+
palette = []
|
138 |
+
for iter in hex:
|
139 |
+
h = '#' + iter
|
140 |
+
palette.append(tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)))
|
141 |
+
num = len(palette)
|
142 |
+
color = palette[int(i) % num]
|
143 |
+
return (color[2], color[1], color[0]) if bgr else color
|
144 |
+
|
145 |
+
def rescale(ori_shape, boxes, target_shape):
|
146 |
+
'''Rescale the output to the original image shape'''
|
147 |
+
ratio = min(ori_shape[0] / target_shape[0], ori_shape[1] / target_shape[1])
|
148 |
+
padding = (ori_shape[1] - target_shape[1] * ratio) / 2, (ori_shape[0] - target_shape[0] * ratio) / 2
|
149 |
+
|
150 |
+
boxes[:, [0, 2]] -= padding[0]
|
151 |
+
boxes[:, [1, 3]] -= padding[1]
|
152 |
+
boxes[:, :4] /= ratio
|
153 |
+
|
154 |
+
boxes[:, 0].clamp_(0, target_shape[1]) # x1
|
155 |
+
boxes[:, 1].clamp_(0, target_shape[0]) # y1
|
156 |
+
boxes[:, 2].clamp_(0, target_shape[1]) # x2
|
157 |
+
boxes[:, 3].clamp_(0, target_shape[0]) # y2
|
158 |
+
|
159 |
+
return boxes
|
160 |
+
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Model Information
|
2 |
+
### Source model
|
3 |
+
|
4 |
+
- Input shape: 1x3x640x640
|
5 |
+
- Number of parameters: 33.24M
|
6 |
+
- Model size: 133.20MB
|
7 |
+
- Output shape: 1x8400x85
|
8 |
+
|
9 |
+
Source model repository: [yolov6](https://github.com/meituan/YOLOv6/tree/main)
|
10 |
+
|
11 |
+
### Converted model
|
12 |
+
|
13 |
+
- Precision: FP16
|
14 |
+
- Backend: QNN2.23
|
15 |
+
- Target Device: SNM972 QCS8550
|
16 |
+
|
17 |
+
## Inference with AidLite SDK
|
18 |
+
|
19 |
+
### SDK installation
|
20 |
+
Model Farm uses AidLite SDK as the model inference SDK. For details, please refer to the [AidLite Developer Documentation](https://v2.docs.aidlux.com/en/sdk-api/aidlite-sdk/)
|
21 |
+
|
22 |
+
- install AidLite SDK
|
23 |
+
|
24 |
+
```bash
|
25 |
+
# Install the appropriate version of the aidlite sdk
|
26 |
+
sudo aid-pkg update
|
27 |
+
sudo aid-pkg install aidlite-sdk
|
28 |
+
# Download the qnn version that matches the above backend. Eg Install QNN2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
29 |
+
sudo aid-pkg install aidlite-{QNN VERSION}
|
30 |
+
# eg: Install QNN 2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
31 |
+
```
|
32 |
+
|
33 |
+
- Verify AidLite SDK
|
34 |
+
|
35 |
+
```bash
|
36 |
+
# aidlite sdk c++ check
|
37 |
+
python3 -c "import aidlite; print(aidlite.get_library_version())"
|
38 |
+
|
39 |
+
# aidlite sdk python check
|
40 |
+
python3 -c "import aidlite; print(aidlite.get_py_library_version())"
|
41 |
+
```
|
42 |
+
|
43 |
+
### Run demo
|
44 |
+
|
45 |
+
#### python
|
46 |
+
```bash
|
47 |
+
cd yolov6m/model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite
|
48 |
+
python3 python/run_test.py --target_model ./models/cutoff_yolov6m_fp16.qnn223.ctx.bin.aidem --imgs ./python/test.png --invoke_nums 10
|
49 |
+
|
50 |
+
```
|
51 |
+
|
52 |
+
#### cpp
|
53 |
+
```bash
|
54 |
+
cd yolov6m/model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp
|
55 |
+
mkdir build && cd build
|
56 |
+
cmake .. && make
|
57 |
+
./run_test --target_model ../../models/cutoff_yolov6m_fp16.qnn223.ctx.bin.aidem --imgs ../test.png --invoke_nums 10
|
58 |
+
```
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/CMakeLists.txt
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
cmake_minimum_required (VERSION 3.5)
|
2 |
+
project("run_test")
|
3 |
+
|
4 |
+
find_package(OpenCV REQUIRED)
|
5 |
+
|
6 |
+
message(STATUS "oPENCV Library status:")
|
7 |
+
message(STATUS ">version:${OpenCV_VERSION}")
|
8 |
+
message(STATUS "Include:${OpenCV_INCLUDE_DIRS}")
|
9 |
+
|
10 |
+
set(CMAKE_CXX_FLAGS "-Wno-error=deprecated-declarations -Wno-deprecated-declarations")
|
11 |
+
|
12 |
+
include_directories(
|
13 |
+
/usr/local/include
|
14 |
+
/usr/include/opencv4
|
15 |
+
)
|
16 |
+
|
17 |
+
link_directories(
|
18 |
+
/usr/local/lib/
|
19 |
+
)
|
20 |
+
|
21 |
+
file(GLOB SRC_LISTS
|
22 |
+
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
|
23 |
+
)
|
24 |
+
|
25 |
+
add_executable(run_test ${SRC_LISTS})
|
26 |
+
|
27 |
+
target_link_libraries(run_test
|
28 |
+
aidlite
|
29 |
+
${OpenCV_LIBS}
|
30 |
+
)
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/main.cpp
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#include <iostream>
|
2 |
+
#include <string>
|
3 |
+
#include <algorithm>
|
4 |
+
#include <cctype>
|
5 |
+
#include <opencv2/opencv.hpp>
|
6 |
+
#include <aidlux/aidlite/aidlite.hpp>
|
7 |
+
#include <vector>
|
8 |
+
#include <numeric>
|
9 |
+
|
10 |
+
const float INPUT_WIDTH = 640.0;
|
11 |
+
const float INPUT_HEIGHT = 640.0;
|
12 |
+
const float SCORE_THRESHOLD = 0.25;
|
13 |
+
const float NMS_THRESHOLD = 0.45;
|
14 |
+
const float CONFIDENCE_THRESHOLD = 0.25;
|
15 |
+
const uint32_t size = 640;
|
16 |
+
const uint32_t out_size = 8400;
|
17 |
+
|
18 |
+
const int FONT_FACE = cv::FONT_HERSHEY_SIMPLEX;
|
19 |
+
cv::Scalar WHITE = cv::Scalar(255,255,255);
|
20 |
+
|
21 |
+
const float FONT_SCALE = 1;
|
22 |
+
const int THICKNESS = 2;
|
23 |
+
using namespace Aidlux::Aidlite;
|
24 |
+
|
25 |
+
struct Args {
|
26 |
+
std::string target_model = "../../models/cutoff_yolov6m_fp16.qnn223.ctx.bin.aidem";
|
27 |
+
std::string imgs = "../test.png";
|
28 |
+
int invoke_nums = 10;
|
29 |
+
std::string model_type = "QNN";
|
30 |
+
};
|
31 |
+
|
32 |
+
Args parse_args(int argc, char* argv[]) {
|
33 |
+
Args args;
|
34 |
+
for (int i = 1; i < argc; ++i) {
|
35 |
+
std::string arg = argv[i];
|
36 |
+
if (arg == "--target_model" && i + 1 < argc) {
|
37 |
+
args.target_model = argv[++i];
|
38 |
+
} else if (arg == "--imgs" && i + 1 < argc) {
|
39 |
+
args.imgs = argv[++i];
|
40 |
+
} else if (arg == "--invoke_nums" && i + 1 < argc) {
|
41 |
+
args.invoke_nums = std::stoi(argv[++i]);
|
42 |
+
} else if (arg == "--model_type" && i + 1 < argc) {
|
43 |
+
args.model_type = argv[++i];
|
44 |
+
}
|
45 |
+
}
|
46 |
+
return args;
|
47 |
+
}
|
48 |
+
|
49 |
+
std::string to_lower(const std::string& str) {
|
50 |
+
std::string lower_str = str;
|
51 |
+
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), [](unsigned char c) {
|
52 |
+
return std::tolower(c);
|
53 |
+
});
|
54 |
+
return lower_str;
|
55 |
+
}
|
56 |
+
|
57 |
+
|
58 |
+
void concatenate(float* qnn_trans_data, float* qnn_mul_data, int batch, int num_elements, int trans_dim, int mul_dim, std::vector<float>& output) {
|
59 |
+
int out_dim = trans_dim + mul_dim + 1;
|
60 |
+
output.resize(batch * num_elements * out_dim);
|
61 |
+
for (int i = 0; i < batch * num_elements; ++i) {
|
62 |
+
std::memcpy(&output[i * out_dim], &qnn_mul_data[i * mul_dim], mul_dim * sizeof(float));
|
63 |
+
float max_val = *std::max_element(&qnn_trans_data[i * trans_dim], &qnn_trans_data[i * trans_dim + trans_dim]);
|
64 |
+
output[i * out_dim + 4] = max_val;
|
65 |
+
std::memcpy(&output[i * out_dim + 5], &qnn_trans_data[i * trans_dim], trans_dim * sizeof(float));
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
cv::Mat letterbox(cv::Mat im, cv::Size new_shape = cv::Size(640, 640),
|
70 |
+
cv::Scalar color = cv::Scalar(114, 114, 114),
|
71 |
+
bool auto_pad = true, bool scaleup = true, int stride = 32) {
|
72 |
+
// current shape [height, width]
|
73 |
+
cv::Size shape = im.size();
|
74 |
+
int height = shape.height;
|
75 |
+
int width = shape.width;
|
76 |
+
|
77 |
+
if (new_shape.width == 0) {
|
78 |
+
new_shape = cv::Size(new_shape.height, new_shape.height);
|
79 |
+
}
|
80 |
+
|
81 |
+
// Scale ratio (new / old)
|
82 |
+
float r = std::min((float)new_shape.height / height, (float)new_shape.width / width);
|
83 |
+
if (!scaleup) {
|
84 |
+
// only scale down, do not scale up (for better val mAP)
|
85 |
+
r = std::min(r, 1.0f);
|
86 |
+
}
|
87 |
+
|
88 |
+
// Compute padding
|
89 |
+
cv::Size new_unpad(round(width * r), round(height * r));
|
90 |
+
int dw = new_shape.width - new_unpad.width;
|
91 |
+
int dh = new_shape.height - new_unpad.height;
|
92 |
+
|
93 |
+
// minimum rectangle
|
94 |
+
if (auto_pad) {
|
95 |
+
dw = dw % stride;
|
96 |
+
dh = dh % stride;
|
97 |
+
}
|
98 |
+
|
99 |
+
dw /= 2; // divide padding into 2 sides
|
100 |
+
dh /= 2;
|
101 |
+
|
102 |
+
// resize
|
103 |
+
if (cv::Size(width, height) != new_unpad) {
|
104 |
+
cv::resize(im, im, new_unpad, 0, 0, cv::INTER_LINEAR);
|
105 |
+
}
|
106 |
+
|
107 |
+
int top = round(dh - 0.1);
|
108 |
+
int bottom = round(dh + 0.1);
|
109 |
+
int left = round(dw - 0.1);
|
110 |
+
int right = round(dw + 0.1);
|
111 |
+
|
112 |
+
cv::copyMakeBorder(im, im, top, bottom, left, right, cv::BORDER_CONSTANT, color);
|
113 |
+
return im;
|
114 |
+
}
|
115 |
+
cv::Scalar generate_colors(int i, bool bgr = false) {
|
116 |
+
static const std::vector<std::string> hex_colors = {
|
117 |
+
"FF3838", "FF9D97", "FF701F", "FFB21D", "CFD231", "48F90A",
|
118 |
+
"92CC17", "3DDB86", "1A9334", "00D4BB", "2C99A8", "00C2FF",
|
119 |
+
"344593", "6473FF", "0018EC", "8438FF", "520085", "CB38FF",
|
120 |
+
"FF95C8", "FF37C7"
|
121 |
+
};
|
122 |
+
|
123 |
+
int num = hex_colors.size();
|
124 |
+
std::string hex = hex_colors[i % num];
|
125 |
+
|
126 |
+
int r = std::stoi(hex.substr(0, 2), nullptr, 16);
|
127 |
+
int g = std::stoi(hex.substr(2, 2), nullptr, 16);
|
128 |
+
int b = std::stoi(hex.substr(4, 2), nullptr, 16);
|
129 |
+
|
130 |
+
if (bgr)
|
131 |
+
return cv::Scalar(b, g, r);
|
132 |
+
else
|
133 |
+
return cv::Scalar(r, g, b);
|
134 |
+
}
|
135 |
+
|
136 |
+
void draw_label(cv::Mat& input_image, std::string label, int left, int top, cv::Scalar color)
|
137 |
+
{
|
138 |
+
int baseLine;
|
139 |
+
cv::Size label_size = cv::getTextSize(label, FONT_FACE, FONT_SCALE, THICKNESS, &baseLine);
|
140 |
+
int y = top - label_size.height - baseLine;
|
141 |
+
if (y < 0) {
|
142 |
+
y = top + label_size.height + baseLine;
|
143 |
+
}
|
144 |
+
cv::Point tlc(left, y);
|
145 |
+
cv::Point brc(left + label_size.width, y + label_size.height + baseLine);
|
146 |
+
rectangle(input_image, tlc, brc, color, cv::FILLED);
|
147 |
+
putText(input_image, label, cv::Point(left, y + label_size.height), FONT_FACE, FONT_SCALE, WHITE, THICKNESS);
|
148 |
+
}
|
149 |
+
|
150 |
+
|
151 |
+
cv::Mat post_process(cv::Mat &input_image, std::vector<float> &outputs, const std::vector<std::string> &class_name)
|
152 |
+
{
|
153 |
+
// Initialize vectors to hold respective outputs while unwrapping detections.
|
154 |
+
std::vector<int> class_ids;
|
155 |
+
std::vector<float> confidences;
|
156 |
+
std::vector<cv::Rect> boxes;
|
157 |
+
|
158 |
+
// Resizing factor.
|
159 |
+
float r = std::min(INPUT_WIDTH / (float)input_image.cols, INPUT_HEIGHT / (float)input_image.rows);
|
160 |
+
int new_unpad_w = round(input_image.cols * r);
|
161 |
+
int new_unpad_h = round(input_image.rows * r);
|
162 |
+
int dw = (int)INPUT_WIDTH - new_unpad_w;
|
163 |
+
int dh = (int)INPUT_HEIGHT - new_unpad_h;
|
164 |
+
dw /= 24;
|
165 |
+
dh /= 24;
|
166 |
+
|
167 |
+
// Iterate through outputs for each box prediction
|
168 |
+
for (int i = 0; i < outputs.size(); i+=85)
|
169 |
+
{
|
170 |
+
float confidence = outputs[i+4];
|
171 |
+
if (confidence >= CONFIDENCE_THRESHOLD)
|
172 |
+
{
|
173 |
+
// Create a 1x80 Mat and store class scores of 80 classes.
|
174 |
+
cv::Mat scores(1, class_name.size(), CV_32FC1, outputs.data() + i + 5);
|
175 |
+
cv::Point class_id;
|
176 |
+
double max_class_score;
|
177 |
+
|
178 |
+
// For multi-label, check each class score
|
179 |
+
for (int c = 0; c < class_name.size(); c++) {
|
180 |
+
float class_score = scores.at<float>(0, c);
|
181 |
+
|
182 |
+
// If class score is above threshold, consider this class for the box
|
183 |
+
if (class_score > SCORE_THRESHOLD) {
|
184 |
+
// Store class ID and confidence in the pre-defined respective vectors.
|
185 |
+
confidences.push_back(confidence * class_score); // Multiply with confidence
|
186 |
+
class_ids.push_back(c); // class index
|
187 |
+
// Center and box dimension.
|
188 |
+
float cx = outputs[i];
|
189 |
+
float cy = outputs[i+1];
|
190 |
+
float w = outputs[i+2];
|
191 |
+
float h = outputs[i+3];
|
192 |
+
|
193 |
+
float x0 = (cx - 0.5f * w - dw) / r;
|
194 |
+
float y0 = (cy - 0.5f * h - dh) / r;
|
195 |
+
float x1 = (cx + 0.5f * w - dw) / r;
|
196 |
+
float y1 = (cy + 0.5f * h - dh) / r;
|
197 |
+
|
198 |
+
int left = int(x0);
|
199 |
+
int top = int(y0);
|
200 |
+
int width = int(x1 - x0);
|
201 |
+
int height = int(y1 - y0);
|
202 |
+
|
203 |
+
// Store good detections in the boxes vector.
|
204 |
+
boxes.push_back(cv::Rect(left, top, width, height));
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
// Perform Non Maximum Suppression and draw predictions.
|
211 |
+
std::vector<int> indices;
|
212 |
+
cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices);
|
213 |
+
printf("Detected {%ld} targets.\n", indices.size());
|
214 |
+
|
215 |
+
// Loop over NMS results and draw bounding boxes
|
216 |
+
for (int i = 0; i < indices.size(); i++)
|
217 |
+
{
|
218 |
+
int idx = indices[i];
|
219 |
+
cv::Rect box = boxes[idx];
|
220 |
+
|
221 |
+
int left = box.x;
|
222 |
+
int top = box.y;
|
223 |
+
int width = box.width;
|
224 |
+
int height = box.height;
|
225 |
+
cv::Scalar color = generate_colors(class_ids[idx]);
|
226 |
+
// Draw bounding box.
|
227 |
+
rectangle(input_image, cv::Point(left, top), cv::Point(left + width, top + height), color, 3*THICKNESS);
|
228 |
+
|
229 |
+
// Get the label for the class name and its confidence.
|
230 |
+
std::string label = cv::format("%.2f", confidences[idx]);
|
231 |
+
label = class_name[class_ids[idx]] + ":" + label;
|
232 |
+
// Draw class labels.
|
233 |
+
draw_label(input_image, label, left, top, color);
|
234 |
+
}
|
235 |
+
printf("Processing finished.\n");
|
236 |
+
return input_image;
|
237 |
+
}
|
238 |
+
|
239 |
+
|
240 |
+
int invoke(const Args& args) {
|
241 |
+
std::cout << "Start main ... ... Model Path: " << args.target_model << "\n"
|
242 |
+
<< "Image Path: " << args.imgs << "\n"
|
243 |
+
<< "Inference Nums: " << args.invoke_nums << "\n"
|
244 |
+
<< "Model Type: " << args.model_type << "\n";
|
245 |
+
Model* model = Model::create_instance(args.target_model);
|
246 |
+
if(model == nullptr){
|
247 |
+
printf("Create model failed !\n");
|
248 |
+
return EXIT_FAILURE;
|
249 |
+
}
|
250 |
+
Config* config = Config::create_instance();
|
251 |
+
if(config == nullptr){
|
252 |
+
printf("Create config failed !\n");
|
253 |
+
return EXIT_FAILURE;
|
254 |
+
}
|
255 |
+
config->implement_type = ImplementType::TYPE_LOCAL;
|
256 |
+
std::string model_type_lower = to_lower(args.model_type);
|
257 |
+
if (model_type_lower == "qnn"){
|
258 |
+
config->framework_type = FrameworkType::TYPE_QNN223;
|
259 |
+
} else if (model_type_lower == "snpe2" || model_type_lower == "snpe") {
|
260 |
+
config->framework_type = FrameworkType::TYPE_SNPE2;
|
261 |
+
}
|
262 |
+
config->accelerate_type = AccelerateType::TYPE_DSP;
|
263 |
+
config->is_quantify_model = 1;
|
264 |
+
|
265 |
+
std::vector<std::vector<uint32_t>> input_shapes = {{1, size, size, 3}};
|
266 |
+
std::vector<std::vector<uint32_t>> output_shapes = {{1, out_size, 80}, {1, out_size, 4}};
|
267 |
+
model->set_model_properties(input_shapes, DataType::TYPE_FLOAT32, output_shapes, DataType::TYPE_FLOAT32);
|
268 |
+
std::unique_ptr<Interpreter> fast_interpreter = InterpreterBuilder::build_interpretper_from_model_and_config(model, config);
|
269 |
+
if(fast_interpreter == nullptr){
|
270 |
+
printf("build_interpretper_from_model_and_config failed !\n");
|
271 |
+
return EXIT_FAILURE;
|
272 |
+
}
|
273 |
+
int result = fast_interpreter->init();
|
274 |
+
if(result != EXIT_SUCCESS){
|
275 |
+
printf("interpreter->init() failed !\n");
|
276 |
+
return EXIT_FAILURE;
|
277 |
+
}
|
278 |
+
// load model
|
279 |
+
fast_interpreter->load_model();
|
280 |
+
if(result != EXIT_SUCCESS){
|
281 |
+
printf("interpreter->load_model() failed !\n");
|
282 |
+
return EXIT_FAILURE;
|
283 |
+
}
|
284 |
+
printf("detect model load success!\n");
|
285 |
+
cv::Size img_size(size, size);
|
286 |
+
|
287 |
+
cv::Mat img_src = cv::imread(args.imgs);
|
288 |
+
printf("img_src cols: %d, img_src rows: %d\n", img_src.cols, img_src.rows);
|
289 |
+
cv::Mat img_ori = img_src.clone();
|
290 |
+
cv::cvtColor(img_ori, img_ori, cv::COLOR_BGR2RGB);
|
291 |
+
cv::Mat resized_img = letterbox(img_ori, img_size);
|
292 |
+
cv::Mat input_img = cv::Mat::zeros(img_size, CV_32FC3);
|
293 |
+
resized_img.convertTo(resized_img, CV_32FC3, 1.0 / 255.0);
|
294 |
+
resized_img.copyTo(input_img(cv::Rect(0, 0, resized_img.cols, resized_img.rows)));
|
295 |
+
float *qnn_trans_data = nullptr;
|
296 |
+
float *qnn_mul_data = nullptr;
|
297 |
+
|
298 |
+
std::vector<float> invoke_time;
|
299 |
+
for (int i = 0; i < args.invoke_nums; ++i) {
|
300 |
+
result = fast_interpreter->set_input_tensor(0, input_img.data);
|
301 |
+
if(result != EXIT_SUCCESS){
|
302 |
+
printf("interpreter->set_input_tensor() failed !\n");
|
303 |
+
return EXIT_FAILURE;
|
304 |
+
}
|
305 |
+
// 开始计时
|
306 |
+
auto t1 = std::chrono::high_resolution_clock::now();
|
307 |
+
result = fast_interpreter->invoke();
|
308 |
+
auto t2 = std::chrono::high_resolution_clock::now();
|
309 |
+
std::chrono::duration<double> cost_time = t2 - t1;
|
310 |
+
invoke_time.push_back(cost_time.count() * 1000);
|
311 |
+
if(result != EXIT_SUCCESS){
|
312 |
+
printf("interpreter->invoke() failed !\n");
|
313 |
+
return EXIT_FAILURE;
|
314 |
+
}
|
315 |
+
uint32_t out_data_1 = 0;
|
316 |
+
result = fast_interpreter->get_output_tensor(0, (void**)&qnn_trans_data, &out_data_1);
|
317 |
+
if(result != EXIT_SUCCESS){
|
318 |
+
printf("interpreter->get_output_tensor() 1 failed !\n");
|
319 |
+
return EXIT_FAILURE;
|
320 |
+
}
|
321 |
+
uint32_t out_data_2 = 0;
|
322 |
+
result = fast_interpreter->get_output_tensor(1, (void**)&qnn_mul_data, &out_data_2);
|
323 |
+
if(result != EXIT_SUCCESS){
|
324 |
+
printf("interpreter->get_output_tensor() 2 failed !\n");
|
325 |
+
return EXIT_FAILURE;
|
326 |
+
}
|
327 |
+
}
|
328 |
+
|
329 |
+
float max_invoke_time = *std::max_element(invoke_time.begin(), invoke_time.end());
|
330 |
+
float min_invoke_time = *std::min_element(invoke_time.begin(), invoke_time.end());
|
331 |
+
float mean_invoke_time = std::accumulate(invoke_time.begin(), invoke_time.end(), 0.0f) / args.invoke_nums;
|
332 |
+
float var_invoketime = 0.0f;
|
333 |
+
for (auto time : invoke_time) {
|
334 |
+
var_invoketime += (time - mean_invoke_time) * (time - mean_invoke_time);
|
335 |
+
}
|
336 |
+
var_invoketime /= args.invoke_nums;
|
337 |
+
printf("=======================================\n");
|
338 |
+
printf("QNN inference %d times :\n --mean_invoke_time is %f \n --max_invoke_time is %f \n --min_invoke_time is %f \n --var_invoketime is %f\n",
|
339 |
+
args.invoke_nums, mean_invoke_time, max_invoke_time, min_invoke_time, var_invoketime);
|
340 |
+
printf("=======================================\n");
|
341 |
+
|
342 |
+
std::vector<std::string> class_list = {
|
343 |
+
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
|
344 |
+
"truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter",
|
345 |
+
"bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear",
|
346 |
+
"zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
|
347 |
+
"frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
|
348 |
+
"baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
|
349 |
+
"wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
|
350 |
+
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",
|
351 |
+
"cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet",
|
352 |
+
"TV", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
|
353 |
+
"oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
|
354 |
+
"scissors", "teddy bear", "hair drier", "toothbrush"
|
355 |
+
};
|
356 |
+
|
357 |
+
// post process
|
358 |
+
std::vector<float> qnn_concat;
|
359 |
+
concatenate(qnn_trans_data, qnn_mul_data, 1, out_size, 80, 4, qnn_concat);
|
360 |
+
cv::Mat img = post_process(img_src, qnn_concat, class_list);
|
361 |
+
cv::imwrite("./results.png", img);
|
362 |
+
fast_interpreter->destory();
|
363 |
+
return 0;
|
364 |
+
}
|
365 |
+
|
366 |
+
|
367 |
+
int main(int argc, char* argv[]) {
|
368 |
+
Args args = parse_args(argc, argv);
|
369 |
+
return invoke(args);
|
370 |
+
}
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/cpp/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/models/cutoff_yolov6m_fp16.qnn223.ctx.bin.aidem
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b687e374d65441b8677cab88cb92a146fdd30b7369d7c186d2c4143be99d37f5
|
3 |
+
size 70849520
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/run_test.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, torch, cv2
|
2 |
+
import numpy as np
|
3 |
+
import time
|
4 |
+
import aidlite
|
5 |
+
import argparse
|
6 |
+
from utils import letterbox,plot_box_and_label,rescale,generate_colors,non_max_suppression
|
7 |
+
import torch
|
8 |
+
|
9 |
+
|
10 |
+
def process_image(path, img_size):
|
11 |
+
img_src = cv2.imread(path)
|
12 |
+
img_src = cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB)
|
13 |
+
image = letterbox(img_src, img_size)[0]
|
14 |
+
new_h,new_w,_=image.shape
|
15 |
+
input_img = np.zeros((img_size[0], img_size[1], 3), np.uint8)
|
16 |
+
input_img[0:new_h, 0:new_w] = image
|
17 |
+
input_img = input_img.astype(np.float32)
|
18 |
+
input_img /= 255 # 0 - 255 to 0.0 - 1.0
|
19 |
+
input_img = np.expand_dims(input_img,0)
|
20 |
+
return image,input_img, img_src
|
21 |
+
|
22 |
+
def main(args):
|
23 |
+
print("Start main ... ...")
|
24 |
+
# aidlite.set_log_level(aidlite.LogLevel.INFO)
|
25 |
+
# aidlite.log_to_stderr()
|
26 |
+
# print(f"Aidlite library version : {aidlite.get_library_version()}")
|
27 |
+
# print(f"Aidlite python library version : {aidlite.get_py_library_version()}")
|
28 |
+
|
29 |
+
size=640
|
30 |
+
out_size=8400
|
31 |
+
config = aidlite.Config.create_instance()
|
32 |
+
if config is None:
|
33 |
+
print("Create config failed !")
|
34 |
+
return False
|
35 |
+
config.implement_type = aidlite.ImplementType.TYPE_LOCAL
|
36 |
+
if args.model_type.lower()=="qnn":
|
37 |
+
config.framework_type = aidlite.FrameworkType.TYPE_QNN223
|
38 |
+
elif args.model_type.lower()=="snpe2" or args.model_type.lower()=="snpe":
|
39 |
+
config.framework_type = aidlite.FrameworkType.TYPE_SNPE2
|
40 |
+
|
41 |
+
config.accelerate_type = aidlite.AccelerateType.TYPE_DSP
|
42 |
+
config.is_quantify_model = 1
|
43 |
+
|
44 |
+
|
45 |
+
model = aidlite.Model.create_instance(args.target_model)
|
46 |
+
if model is None:
|
47 |
+
print("Create model failed !")
|
48 |
+
return False
|
49 |
+
input_shapes = [[1, size, size, 3]]
|
50 |
+
output_shapes = [[1, out_size,80],[1, out_size,4]]
|
51 |
+
model.set_model_properties(input_shapes, aidlite.DataType.TYPE_FLOAT32,
|
52 |
+
output_shapes, aidlite.DataType.TYPE_FLOAT32)
|
53 |
+
|
54 |
+
interpreter = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model, config)
|
55 |
+
if interpreter is None:
|
56 |
+
print("build_interpretper_from_model_and_config failed !")
|
57 |
+
return None
|
58 |
+
result = interpreter.init()
|
59 |
+
if result != 0:
|
60 |
+
print(f"interpreter init failed !")
|
61 |
+
return False
|
62 |
+
result = interpreter.load_model()
|
63 |
+
if result != 0:
|
64 |
+
print("interpreter load model failed !")
|
65 |
+
return False
|
66 |
+
print("detect model load success!")
|
67 |
+
|
68 |
+
# image process
|
69 |
+
img_size=[size,size]
|
70 |
+
resize_img,input_img, img_src = process_image(args.imgs, img_size)
|
71 |
+
|
72 |
+
# qnn run
|
73 |
+
invoke_time=[]
|
74 |
+
for i in range(args.invoke_nums):
|
75 |
+
result = interpreter.set_input_tensor(0, input_img.data)
|
76 |
+
if result != 0:
|
77 |
+
print("interpreter set_input_tensor() failed")
|
78 |
+
|
79 |
+
t1=time.time()
|
80 |
+
result = interpreter.invoke()
|
81 |
+
cost_time = (time.time()-t1)*1000
|
82 |
+
invoke_time.append(cost_time)
|
83 |
+
|
84 |
+
if result != 0:
|
85 |
+
print("interpreter set_input_tensor() failed")
|
86 |
+
|
87 |
+
qnn_trans = interpreter.get_output_tensor(0).reshape(1,out_size,80)
|
88 |
+
qnn_mul = interpreter.get_output_tensor(1).reshape(1,out_size,4)
|
89 |
+
|
90 |
+
result = interpreter.destory()
|
91 |
+
|
92 |
+
## time 统计
|
93 |
+
max_invoke_time = max(invoke_time)
|
94 |
+
min_invoke_time = min(invoke_time)
|
95 |
+
mean_invoke_time = sum(invoke_time)/args.invoke_nums
|
96 |
+
var_invoketime=np.var(invoke_time)
|
97 |
+
print("=======================================")
|
98 |
+
print(f"QNN inference {args.invoke_nums} times :\n --mean_invoke_time is {mean_invoke_time} \n --max_invoke_time is {max_invoke_time} \n --min_invoke_time is {min_invoke_time} \n --var_invoketime is {var_invoketime}")
|
99 |
+
print("=======================================")
|
100 |
+
|
101 |
+
# 后处理
|
102 |
+
conf_thres =0.25 #@param {type:"number"}
|
103 |
+
iou_thres =0.45 #@param {type:"number"}
|
104 |
+
max_det= 1000#@param {type:"integer"}
|
105 |
+
agnostic_nms= False #@param {type:"boolean"}
|
106 |
+
classes =None
|
107 |
+
hide_labels = False #@param {type:"boolean"}
|
108 |
+
hide_conf= False #@param {type:"boolean"}
|
109 |
+
|
110 |
+
qnn_conf = np.ones((1,out_size,1))
|
111 |
+
qnn_predict=np.concatenate((qnn_mul,qnn_conf,qnn_trans), axis=2)
|
112 |
+
pred_results =torch.from_numpy(qnn_predict.copy())
|
113 |
+
det = non_max_suppression(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)[0]
|
114 |
+
|
115 |
+
class_names=[ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
|
116 |
+
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
|
117 |
+
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
|
118 |
+
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
|
119 |
+
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
120 |
+
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
|
121 |
+
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
|
122 |
+
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
|
123 |
+
'hair drier', 'toothbrush' ]
|
124 |
+
|
125 |
+
img_ori = img_src.copy()
|
126 |
+
print(f"Detected {len(det)} targets.")
|
127 |
+
if len(det):
|
128 |
+
det[:, :4] = rescale(resize_img.shape[:2], det[:, :4], img_src.shape).round()
|
129 |
+
for *xyxy, conf, cls in reversed(det):
|
130 |
+
class_num = int(cls)
|
131 |
+
label = None if hide_labels else (class_names[class_num] if hide_conf else f'{class_names[class_num]} {conf:.2f}')
|
132 |
+
plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=generate_colors(class_num, True))
|
133 |
+
|
134 |
+
cv2.imwrite("./python/results.png",cv2.cvtColor(img_ori,cv2.COLOR_RGB2BGR))
|
135 |
+
|
136 |
+
def parser_args():
|
137 |
+
parser = argparse.ArgumentParser(description="Run model benchmarks")
|
138 |
+
parser.add_argument('--target_model',type=str,default='./models/cutoff_yolov6m_fp16.qnn223.ctx.bin',help="inference model path")
|
139 |
+
parser.add_argument('--imgs',type=str,default='./python/test.png',help="Predict images path")
|
140 |
+
parser.add_argument('--invoke_nums',type=int,default=10,help="Inference nums")
|
141 |
+
parser.add_argument('--model_type',type=str,default='QNN',help="run backend")
|
142 |
+
args = parser.parse_args()
|
143 |
+
return args
|
144 |
+
|
145 |
+
if __name__ == "__main__":
|
146 |
+
args = parser_args()
|
147 |
+
main(args)
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs8550_qnn2.23_fp16_aidlite/python/utils.py
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
import torch
|
4 |
+
import torchvision
|
5 |
+
|
6 |
+
def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
|
7 |
+
'''Resize and pad image while meeting stride-multiple constraints.'''
|
8 |
+
shape = im.shape[:2] # current shape [height, width]
|
9 |
+
if isinstance(new_shape, int):
|
10 |
+
new_shape = (new_shape, new_shape)
|
11 |
+
elif isinstance(new_shape, list) and len(new_shape) == 1:
|
12 |
+
new_shape = (new_shape[0], new_shape[0])
|
13 |
+
|
14 |
+
# Scale ratio (new / old)
|
15 |
+
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
|
16 |
+
if not scaleup: # only scale down, do not scale up (for better val mAP)
|
17 |
+
r = min(r, 1.0)
|
18 |
+
|
19 |
+
# Compute padding
|
20 |
+
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
|
21 |
+
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
|
22 |
+
|
23 |
+
if auto: # minimum rectangle
|
24 |
+
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
|
25 |
+
|
26 |
+
dw /= 2 # divide padding into 2 sides
|
27 |
+
dh /= 2
|
28 |
+
|
29 |
+
if shape[::-1] != new_unpad: # resize
|
30 |
+
im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
|
31 |
+
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
|
32 |
+
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
|
33 |
+
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
|
34 |
+
|
35 |
+
return im, r, (left, top)
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
|
40 |
+
def xywh2xyxy(x):
|
41 |
+
'''Convert boxes with shape [n, 4] from [x, y, w, h] to [x1, y1, x2, y2] where x1y1 is top-left, x2y2=bottom-right.'''
|
42 |
+
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
43 |
+
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
|
44 |
+
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
|
45 |
+
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
|
46 |
+
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
|
47 |
+
return y
|
48 |
+
|
49 |
+
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, max_det=300):
|
50 |
+
"""Runs Non-Maximum Suppression (NMS) on inference results.
|
51 |
+
This code is borrowed from: https://github.com/ultralytics/yolov5/blob/47233e1698b89fc437a4fb9463c815e9171be955/utils/general.py#L775
|
52 |
+
Args:
|
53 |
+
prediction: (tensor), with shape [N, 5 + num_classes], N is the number of bboxes.
|
54 |
+
conf_thres: (float) confidence threshold.
|
55 |
+
iou_thres: (float) iou threshold.
|
56 |
+
classes: (None or list[int]), if a list is provided, nms only keep the classes you provide.
|
57 |
+
agnostic: (bool), when it is set to True, we do class-independent nms, otherwise, different class would do nms respectively.
|
58 |
+
multi_label: (bool), when it is set to True, one box can have multi labels, otherwise, one box only huave one label.
|
59 |
+
max_det:(int), max number of output bboxes.
|
60 |
+
|
61 |
+
Returns:
|
62 |
+
list of detections, echo item is one tensor with shape (num_boxes, 6), 6 is for [xyxy, conf, cls].
|
63 |
+
"""
|
64 |
+
|
65 |
+
num_classes = prediction.shape[2] - 5 # number of classes
|
66 |
+
pred_candidates = torch.logical_and(prediction[..., 4] > conf_thres, torch.max(prediction[..., 5:], axis=-1)[0] > conf_thres) # candidates
|
67 |
+
# Check the parameters.
|
68 |
+
assert 0 <= conf_thres <= 1, f'conf_thresh must be in 0.0 to 1.0, however {conf_thres} is provided.'
|
69 |
+
assert 0 <= iou_thres <= 1, f'iou_thres must be in 0.0 to 1.0, however {iou_thres} is provided.'
|
70 |
+
|
71 |
+
# Function settings.
|
72 |
+
max_wh = 4096 # maximum box width and height
|
73 |
+
max_nms = 30000 # maximum number of boxes put into torchvision.ops.nms()
|
74 |
+
time_limit = 10.0 # quit the function when nms cost time exceed the limit time.
|
75 |
+
multi_label &= num_classes > 1 # multiple labels per box
|
76 |
+
|
77 |
+
output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0]
|
78 |
+
for img_idx, x in enumerate(prediction): # image index, image inference
|
79 |
+
x = x[pred_candidates[img_idx]] # confidence
|
80 |
+
|
81 |
+
# If no box remains, skip the next process.
|
82 |
+
if not x.shape[0]:
|
83 |
+
continue
|
84 |
+
|
85 |
+
# confidence multiply the objectness
|
86 |
+
x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf
|
87 |
+
|
88 |
+
# (center x, center y, width, height) to (x1, y1, x2, y2)
|
89 |
+
box = xywh2xyxy(x[:, :4])
|
90 |
+
|
91 |
+
# Detections matrix's shape is (n,6), each row represents (xyxy, conf, cls)
|
92 |
+
if multi_label:
|
93 |
+
box_idx, class_idx = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T
|
94 |
+
x = torch.cat((box[box_idx], x[box_idx, class_idx + 5, None], class_idx[:, None].float()), 1)
|
95 |
+
else: # Only keep the class with highest scores.
|
96 |
+
conf, class_idx = x[:, 5:].max(1, keepdim=True)
|
97 |
+
x = torch.cat((box, conf, class_idx.float()), 1)[conf.view(-1) > conf_thres]
|
98 |
+
|
99 |
+
# Filter by class, only keep boxes whose category is in classes.
|
100 |
+
if classes is not None:
|
101 |
+
x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]
|
102 |
+
|
103 |
+
# Check shape
|
104 |
+
num_box = x.shape[0] # number of boxes
|
105 |
+
if not num_box: # no boxes kept.
|
106 |
+
continue
|
107 |
+
elif num_box > max_nms: # excess max boxes' number.
|
108 |
+
x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence
|
109 |
+
|
110 |
+
# Batched NMS
|
111 |
+
class_offset = x[:, 5:6] * (0 if agnostic else max_wh) # classes
|
112 |
+
boxes, scores = x[:, :4] + class_offset, x[:, 4] # boxes (offset by class), scores
|
113 |
+
keep_box_idx = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
|
114 |
+
if keep_box_idx.shape[0] > max_det: # limit detections
|
115 |
+
keep_box_idx = keep_box_idx[:max_det]
|
116 |
+
|
117 |
+
output[img_idx] = x[keep_box_idx]
|
118 |
+
|
119 |
+
return output
|
120 |
+
|
121 |
+
def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX):
|
122 |
+
# Add one xyxy box to image with label
|
123 |
+
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
|
124 |
+
cv2.rectangle(image, p1, p2, color, thickness=lw, lineType=cv2.LINE_AA)
|
125 |
+
if label:
|
126 |
+
tf = max(lw - 1, 1) # font thickness
|
127 |
+
w, h = cv2.getTextSize(label, 0, fontScale=lw / 3, thickness=tf)[0] # text width, height
|
128 |
+
outside = p1[1] - h - 3 >= 0 # label fits outside box
|
129 |
+
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
|
130 |
+
cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA) # filled
|
131 |
+
cv2.putText(image, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), font, lw / 3, txt_color,
|
132 |
+
thickness=tf, lineType=cv2.LINE_AA)
|
133 |
+
|
134 |
+
def generate_colors(i, bgr=False):
|
135 |
+
hex = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
|
136 |
+
'2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
|
137 |
+
palette = []
|
138 |
+
for iter in hex:
|
139 |
+
h = '#' + iter
|
140 |
+
palette.append(tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)))
|
141 |
+
num = len(palette)
|
142 |
+
color = palette[int(i) % num]
|
143 |
+
return (color[2], color[1], color[0]) if bgr else color
|
144 |
+
|
145 |
+
def rescale(ori_shape, boxes, target_shape):
|
146 |
+
'''Rescale the output to the original image shape'''
|
147 |
+
ratio = min(ori_shape[0] / target_shape[0], ori_shape[1] / target_shape[1])
|
148 |
+
padding = (ori_shape[1] - target_shape[1] * ratio) / 2, (ori_shape[0] - target_shape[0] * ratio) / 2
|
149 |
+
|
150 |
+
boxes[:, [0, 2]] -= padding[0]
|
151 |
+
boxes[:, [1, 3]] -= padding[1]
|
152 |
+
boxes[:, :4] /= ratio
|
153 |
+
|
154 |
+
boxes[:, 0].clamp_(0, target_shape[1]) # x1
|
155 |
+
boxes[:, 1].clamp_(0, target_shape[0]) # y1
|
156 |
+
boxes[:, 2].clamp_(0, target_shape[1]) # x2
|
157 |
+
boxes[:, 3].clamp_(0, target_shape[0]) # y2
|
158 |
+
|
159 |
+
return boxes
|
160 |
+
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Model Information
|
2 |
+
### Source model
|
3 |
+
|
4 |
+
- Input shape: 1x3x640x640
|
5 |
+
- Number of parameters: 33.24M
|
6 |
+
- Model size: 133.20MB
|
7 |
+
- Output shape: 1x8400x85
|
8 |
+
|
9 |
+
Source model repository: [yolov6](https://github.com/meituan/YOLOv6/tree/main)
|
10 |
+
|
11 |
+
### Converted model
|
12 |
+
|
13 |
+
- Precision: INT8
|
14 |
+
- Backend: QNN2.23
|
15 |
+
- Target Device: SNM972 QCS8550
|
16 |
+
|
17 |
+
## Inference with AidLite SDK
|
18 |
+
|
19 |
+
### SDK installation
|
20 |
+
Model Farm uses AidLite SDK as the model inference SDK. For details, please refer to the [AidLite Developer Documentation](https://v2.docs.aidlux.com/en/sdk-api/aidlite-sdk/)
|
21 |
+
|
22 |
+
- install AidLite SDK
|
23 |
+
|
24 |
+
```bash
|
25 |
+
# Install the appropriate version of the aidlite sdk
|
26 |
+
sudo aid-pkg update
|
27 |
+
sudo aid-pkg install aidlite-sdk
|
28 |
+
# Download the qnn version that matches the above backend. Eg Install QNN2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
29 |
+
sudo aid-pkg install aidlite-{QNN VERSION}
|
30 |
+
# eg: Install QNN 2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
31 |
+
```
|
32 |
+
|
33 |
+
- Verify AidLite SDK
|
34 |
+
|
35 |
+
```bash
|
36 |
+
# aidlite sdk c++ check
|
37 |
+
python3 -c "import aidlite; print(aidlite.get_library_version())"
|
38 |
+
|
39 |
+
# aidlite sdk python check
|
40 |
+
python3 -c "import aidlite; print(aidlite.get_py_library_version())"
|
41 |
+
```
|
42 |
+
|
43 |
+
### Run demo
|
44 |
+
|
45 |
+
#### python
|
46 |
+
```bash
|
47 |
+
cd yolov6m/model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite
|
48 |
+
python3 python/run_test.py --target_model ./models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem --imgs ./python/test.png --invoke_nums 10
|
49 |
+
|
50 |
+
```
|
51 |
+
|
52 |
+
#### cpp
|
53 |
+
```bash
|
54 |
+
cd yolov6m/model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp
|
55 |
+
mkdir build && cd build
|
56 |
+
cmake .. && make
|
57 |
+
./run_test --target_model ../../models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem --imgs ../test.png --invoke_nums 10
|
58 |
+
```
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/CMakeLists.txt
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
cmake_minimum_required (VERSION 3.5)
|
2 |
+
project("run_test")
|
3 |
+
|
4 |
+
find_package(OpenCV REQUIRED)
|
5 |
+
|
6 |
+
message(STATUS "oPENCV Library status:")
|
7 |
+
message(STATUS ">version:${OpenCV_VERSION}")
|
8 |
+
message(STATUS "Include:${OpenCV_INCLUDE_DIRS}")
|
9 |
+
|
10 |
+
set(CMAKE_CXX_FLAGS "-Wno-error=deprecated-declarations -Wno-deprecated-declarations")
|
11 |
+
|
12 |
+
include_directories(
|
13 |
+
/usr/local/include
|
14 |
+
/usr/include/opencv4
|
15 |
+
)
|
16 |
+
|
17 |
+
link_directories(
|
18 |
+
/usr/local/lib/
|
19 |
+
)
|
20 |
+
|
21 |
+
file(GLOB SRC_LISTS
|
22 |
+
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
|
23 |
+
)
|
24 |
+
|
25 |
+
add_executable(run_test ${SRC_LISTS})
|
26 |
+
|
27 |
+
target_link_libraries(run_test
|
28 |
+
aidlite
|
29 |
+
${OpenCV_LIBS}
|
30 |
+
)
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/main.cpp
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#include <iostream>
|
2 |
+
#include <string>
|
3 |
+
#include <algorithm>
|
4 |
+
#include <cctype>
|
5 |
+
#include <opencv2/opencv.hpp>
|
6 |
+
#include <aidlux/aidlite/aidlite.hpp>
|
7 |
+
#include <vector>
|
8 |
+
#include <numeric>
|
9 |
+
|
10 |
+
const float INPUT_WIDTH = 640.0;
|
11 |
+
const float INPUT_HEIGHT = 640.0;
|
12 |
+
const float SCORE_THRESHOLD = 0.25;
|
13 |
+
const float NMS_THRESHOLD = 0.45;
|
14 |
+
const float CONFIDENCE_THRESHOLD = 0.25;
|
15 |
+
const uint32_t size = 640;
|
16 |
+
const uint32_t out_size = 8400;
|
17 |
+
|
18 |
+
const int FONT_FACE = cv::FONT_HERSHEY_SIMPLEX;
|
19 |
+
cv::Scalar WHITE = cv::Scalar(255,255,255);
|
20 |
+
|
21 |
+
const float FONT_SCALE = 1;
|
22 |
+
const int THICKNESS = 2;
|
23 |
+
using namespace Aidlux::Aidlite;
|
24 |
+
|
25 |
+
struct Args {
|
26 |
+
std::string target_model = "../../models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem";
|
27 |
+
std::string imgs = "../test.png";
|
28 |
+
int invoke_nums = 10;
|
29 |
+
std::string model_type = "QNN";
|
30 |
+
};
|
31 |
+
|
32 |
+
Args parse_args(int argc, char* argv[]) {
|
33 |
+
Args args;
|
34 |
+
for (int i = 1; i < argc; ++i) {
|
35 |
+
std::string arg = argv[i];
|
36 |
+
if (arg == "--target_model" && i + 1 < argc) {
|
37 |
+
args.target_model = argv[++i];
|
38 |
+
} else if (arg == "--imgs" && i + 1 < argc) {
|
39 |
+
args.imgs = argv[++i];
|
40 |
+
} else if (arg == "--invoke_nums" && i + 1 < argc) {
|
41 |
+
args.invoke_nums = std::stoi(argv[++i]);
|
42 |
+
} else if (arg == "--model_type" && i + 1 < argc) {
|
43 |
+
args.model_type = argv[++i];
|
44 |
+
}
|
45 |
+
}
|
46 |
+
return args;
|
47 |
+
}
|
48 |
+
|
49 |
+
std::string to_lower(const std::string& str) {
|
50 |
+
std::string lower_str = str;
|
51 |
+
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), [](unsigned char c) {
|
52 |
+
return std::tolower(c);
|
53 |
+
});
|
54 |
+
return lower_str;
|
55 |
+
}
|
56 |
+
|
57 |
+
|
58 |
+
void concatenate(float* qnn_trans_data, float* qnn_mul_data, int batch, int num_elements, int trans_dim, int mul_dim, std::vector<float>& output) {
|
59 |
+
int out_dim = trans_dim + mul_dim + 1;
|
60 |
+
output.resize(batch * num_elements * out_dim);
|
61 |
+
for (int i = 0; i < batch * num_elements; ++i) {
|
62 |
+
std::memcpy(&output[i * out_dim], &qnn_mul_data[i * mul_dim], mul_dim * sizeof(float));
|
63 |
+
float max_val = *std::max_element(&qnn_trans_data[i * trans_dim], &qnn_trans_data[i * trans_dim + trans_dim]);
|
64 |
+
output[i * out_dim + 4] = max_val;
|
65 |
+
std::memcpy(&output[i * out_dim + 5], &qnn_trans_data[i * trans_dim], trans_dim * sizeof(float));
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
cv::Mat letterbox(cv::Mat im, cv::Size new_shape = cv::Size(640, 640),
|
70 |
+
cv::Scalar color = cv::Scalar(114, 114, 114),
|
71 |
+
bool auto_pad = true, bool scaleup = true, int stride = 32) {
|
72 |
+
// current shape [height, width]
|
73 |
+
cv::Size shape = im.size();
|
74 |
+
int height = shape.height;
|
75 |
+
int width = shape.width;
|
76 |
+
|
77 |
+
if (new_shape.width == 0) {
|
78 |
+
new_shape = cv::Size(new_shape.height, new_shape.height);
|
79 |
+
}
|
80 |
+
|
81 |
+
// Scale ratio (new / old)
|
82 |
+
float r = std::min((float)new_shape.height / height, (float)new_shape.width / width);
|
83 |
+
if (!scaleup) {
|
84 |
+
// only scale down, do not scale up (for better val mAP)
|
85 |
+
r = std::min(r, 1.0f);
|
86 |
+
}
|
87 |
+
|
88 |
+
// Compute padding
|
89 |
+
cv::Size new_unpad(round(width * r), round(height * r));
|
90 |
+
int dw = new_shape.width - new_unpad.width;
|
91 |
+
int dh = new_shape.height - new_unpad.height;
|
92 |
+
|
93 |
+
// minimum rectangle
|
94 |
+
if (auto_pad) {
|
95 |
+
dw = dw % stride;
|
96 |
+
dh = dh % stride;
|
97 |
+
}
|
98 |
+
|
99 |
+
dw /= 2; // divide padding into 2 sides
|
100 |
+
dh /= 2;
|
101 |
+
|
102 |
+
// resize
|
103 |
+
if (cv::Size(width, height) != new_unpad) {
|
104 |
+
cv::resize(im, im, new_unpad, 0, 0, cv::INTER_LINEAR);
|
105 |
+
}
|
106 |
+
|
107 |
+
int top = round(dh - 0.1);
|
108 |
+
int bottom = round(dh + 0.1);
|
109 |
+
int left = round(dw - 0.1);
|
110 |
+
int right = round(dw + 0.1);
|
111 |
+
|
112 |
+
cv::copyMakeBorder(im, im, top, bottom, left, right, cv::BORDER_CONSTANT, color);
|
113 |
+
return im;
|
114 |
+
}
|
115 |
+
cv::Scalar generate_colors(int i, bool bgr = false) {
|
116 |
+
static const std::vector<std::string> hex_colors = {
|
117 |
+
"FF3838", "FF9D97", "FF701F", "FFB21D", "CFD231", "48F90A",
|
118 |
+
"92CC17", "3DDB86", "1A9334", "00D4BB", "2C99A8", "00C2FF",
|
119 |
+
"344593", "6473FF", "0018EC", "8438FF", "520085", "CB38FF",
|
120 |
+
"FF95C8", "FF37C7"
|
121 |
+
};
|
122 |
+
|
123 |
+
int num = hex_colors.size();
|
124 |
+
std::string hex = hex_colors[i % num];
|
125 |
+
|
126 |
+
int r = std::stoi(hex.substr(0, 2), nullptr, 16);
|
127 |
+
int g = std::stoi(hex.substr(2, 2), nullptr, 16);
|
128 |
+
int b = std::stoi(hex.substr(4, 2), nullptr, 16);
|
129 |
+
|
130 |
+
if (bgr)
|
131 |
+
return cv::Scalar(b, g, r);
|
132 |
+
else
|
133 |
+
return cv::Scalar(r, g, b);
|
134 |
+
}
|
135 |
+
|
136 |
+
void draw_label(cv::Mat& input_image, std::string label, int left, int top, cv::Scalar color)
|
137 |
+
{
|
138 |
+
int baseLine;
|
139 |
+
cv::Size label_size = cv::getTextSize(label, FONT_FACE, FONT_SCALE, THICKNESS, &baseLine);
|
140 |
+
int y = top - label_size.height - baseLine;
|
141 |
+
if (y < 0) {
|
142 |
+
y = top + label_size.height + baseLine;
|
143 |
+
}
|
144 |
+
cv::Point tlc(left, y);
|
145 |
+
cv::Point brc(left + label_size.width, y + label_size.height + baseLine);
|
146 |
+
rectangle(input_image, tlc, brc, color, cv::FILLED);
|
147 |
+
putText(input_image, label, cv::Point(left, y + label_size.height), FONT_FACE, FONT_SCALE, WHITE, THICKNESS);
|
148 |
+
}
|
149 |
+
|
150 |
+
|
151 |
+
cv::Mat post_process(cv::Mat &input_image, std::vector<float> &outputs, const std::vector<std::string> &class_name)
|
152 |
+
{
|
153 |
+
// Initialize vectors to hold respective outputs while unwrapping detections.
|
154 |
+
std::vector<int> class_ids;
|
155 |
+
std::vector<float> confidences;
|
156 |
+
std::vector<cv::Rect> boxes;
|
157 |
+
|
158 |
+
// Resizing factor.
|
159 |
+
float r = std::min(INPUT_WIDTH / (float)input_image.cols, INPUT_HEIGHT / (float)input_image.rows);
|
160 |
+
int new_unpad_w = round(input_image.cols * r);
|
161 |
+
int new_unpad_h = round(input_image.rows * r);
|
162 |
+
int dw = (int)INPUT_WIDTH - new_unpad_w;
|
163 |
+
int dh = (int)INPUT_HEIGHT - new_unpad_h;
|
164 |
+
dw /= 24;
|
165 |
+
dh /= 24;
|
166 |
+
|
167 |
+
// Iterate through outputs for each box prediction
|
168 |
+
for (int i = 0; i < outputs.size(); i+=85)
|
169 |
+
{
|
170 |
+
float confidence = outputs[i+4];
|
171 |
+
if (confidence >= CONFIDENCE_THRESHOLD)
|
172 |
+
{
|
173 |
+
// Create a 1x80 Mat and store class scores of 80 classes.
|
174 |
+
cv::Mat scores(1, class_name.size(), CV_32FC1, outputs.data() + i + 5);
|
175 |
+
cv::Point class_id;
|
176 |
+
double max_class_score;
|
177 |
+
|
178 |
+
// For multi-label, check each class score
|
179 |
+
for (int c = 0; c < class_name.size(); c++) {
|
180 |
+
float class_score = scores.at<float>(0, c);
|
181 |
+
|
182 |
+
// If class score is above threshold, consider this class for the box
|
183 |
+
if (class_score > SCORE_THRESHOLD) {
|
184 |
+
// Store class ID and confidence in the pre-defined respective vectors.
|
185 |
+
confidences.push_back(confidence * class_score); // Multiply with confidence
|
186 |
+
class_ids.push_back(c); // class index
|
187 |
+
// Center and box dimension.
|
188 |
+
float cx = outputs[i];
|
189 |
+
float cy = outputs[i+1];
|
190 |
+
float w = outputs[i+2];
|
191 |
+
float h = outputs[i+3];
|
192 |
+
|
193 |
+
float x0 = (cx - 0.5f * w - dw) / r;
|
194 |
+
float y0 = (cy - 0.5f * h - dh) / r;
|
195 |
+
float x1 = (cx + 0.5f * w - dw) / r;
|
196 |
+
float y1 = (cy + 0.5f * h - dh) / r;
|
197 |
+
|
198 |
+
int left = int(x0);
|
199 |
+
int top = int(y0);
|
200 |
+
int width = int(x1 - x0);
|
201 |
+
int height = int(y1 - y0);
|
202 |
+
|
203 |
+
// Store good detections in the boxes vector.
|
204 |
+
boxes.push_back(cv::Rect(left, top, width, height));
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
// Perform Non Maximum Suppression and draw predictions.
|
211 |
+
std::vector<int> indices;
|
212 |
+
cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices);
|
213 |
+
printf("Detected {%ld} targets.\n", indices.size());
|
214 |
+
|
215 |
+
// Loop over NMS results and draw bounding boxes
|
216 |
+
for (int i = 0; i < indices.size(); i++)
|
217 |
+
{
|
218 |
+
int idx = indices[i];
|
219 |
+
cv::Rect box = boxes[idx];
|
220 |
+
|
221 |
+
int left = box.x;
|
222 |
+
int top = box.y;
|
223 |
+
int width = box.width;
|
224 |
+
int height = box.height;
|
225 |
+
cv::Scalar color = generate_colors(class_ids[idx]);
|
226 |
+
// Draw bounding box.
|
227 |
+
rectangle(input_image, cv::Point(left, top), cv::Point(left + width, top + height), color, 3*THICKNESS);
|
228 |
+
|
229 |
+
// Get the label for the class name and its confidence.
|
230 |
+
std::string label = cv::format("%.2f", confidences[idx]);
|
231 |
+
label = class_name[class_ids[idx]] + ":" + label;
|
232 |
+
// Draw class labels.
|
233 |
+
draw_label(input_image, label, left, top, color);
|
234 |
+
}
|
235 |
+
printf("Processing finished.\n");
|
236 |
+
return input_image;
|
237 |
+
}
|
238 |
+
|
239 |
+
|
240 |
+
int invoke(const Args& args) {
|
241 |
+
std::cout << "Start main ... ... Model Path: " << args.target_model << "\n"
|
242 |
+
<< "Image Path: " << args.imgs << "\n"
|
243 |
+
<< "Inference Nums: " << args.invoke_nums << "\n"
|
244 |
+
<< "Model Type: " << args.model_type << "\n";
|
245 |
+
Model* model = Model::create_instance(args.target_model);
|
246 |
+
if(model == nullptr){
|
247 |
+
printf("Create model failed !\n");
|
248 |
+
return EXIT_FAILURE;
|
249 |
+
}
|
250 |
+
Config* config = Config::create_instance();
|
251 |
+
if(config == nullptr){
|
252 |
+
printf("Create config failed !\n");
|
253 |
+
return EXIT_FAILURE;
|
254 |
+
}
|
255 |
+
config->implement_type = ImplementType::TYPE_LOCAL;
|
256 |
+
std::string model_type_lower = to_lower(args.model_type);
|
257 |
+
if (model_type_lower == "qnn"){
|
258 |
+
config->framework_type = FrameworkType::TYPE_QNN223;
|
259 |
+
} else if (model_type_lower == "snpe2" || model_type_lower == "snpe") {
|
260 |
+
config->framework_type = FrameworkType::TYPE_SNPE2;
|
261 |
+
}
|
262 |
+
config->accelerate_type = AccelerateType::TYPE_DSP;
|
263 |
+
config->is_quantify_model = 1;
|
264 |
+
|
265 |
+
std::vector<std::vector<uint32_t>> input_shapes = {{1, size, size, 3}};
|
266 |
+
std::vector<std::vector<uint32_t>> output_shapes = {{1, out_size, 80}, {1, out_size, 4}};
|
267 |
+
model->set_model_properties(input_shapes, DataType::TYPE_FLOAT32, output_shapes, DataType::TYPE_FLOAT32);
|
268 |
+
std::unique_ptr<Interpreter> fast_interpreter = InterpreterBuilder::build_interpretper_from_model_and_config(model, config);
|
269 |
+
if(fast_interpreter == nullptr){
|
270 |
+
printf("build_interpretper_from_model_and_config failed !\n");
|
271 |
+
return EXIT_FAILURE;
|
272 |
+
}
|
273 |
+
int result = fast_interpreter->init();
|
274 |
+
if(result != EXIT_SUCCESS){
|
275 |
+
printf("interpreter->init() failed !\n");
|
276 |
+
return EXIT_FAILURE;
|
277 |
+
}
|
278 |
+
// load model
|
279 |
+
fast_interpreter->load_model();
|
280 |
+
if(result != EXIT_SUCCESS){
|
281 |
+
printf("interpreter->load_model() failed !\n");
|
282 |
+
return EXIT_FAILURE;
|
283 |
+
}
|
284 |
+
printf("detect model load success!\n");
|
285 |
+
cv::Size img_size(size, size);
|
286 |
+
|
287 |
+
cv::Mat img_src = cv::imread(args.imgs);
|
288 |
+
printf("img_src cols: %d, img_src rows: %d\n", img_src.cols, img_src.rows);
|
289 |
+
cv::Mat img_ori = img_src.clone();
|
290 |
+
cv::cvtColor(img_ori, img_ori, cv::COLOR_BGR2RGB);
|
291 |
+
cv::Mat resized_img = letterbox(img_ori, img_size);
|
292 |
+
cv::Mat input_img = cv::Mat::zeros(img_size, CV_32FC3);
|
293 |
+
resized_img.convertTo(resized_img, CV_32FC3, 1.0 / 255.0);
|
294 |
+
resized_img.copyTo(input_img(cv::Rect(0, 0, resized_img.cols, resized_img.rows)));
|
295 |
+
float *qnn_trans_data = nullptr;
|
296 |
+
float *qnn_mul_data = nullptr;
|
297 |
+
|
298 |
+
std::vector<float> invoke_time;
|
299 |
+
for (int i = 0; i < args.invoke_nums; ++i) {
|
300 |
+
result = fast_interpreter->set_input_tensor(0, input_img.data);
|
301 |
+
if(result != EXIT_SUCCESS){
|
302 |
+
printf("interpreter->set_input_tensor() failed !\n");
|
303 |
+
return EXIT_FAILURE;
|
304 |
+
}
|
305 |
+
// 开始计时
|
306 |
+
auto t1 = std::chrono::high_resolution_clock::now();
|
307 |
+
result = fast_interpreter->invoke();
|
308 |
+
auto t2 = std::chrono::high_resolution_clock::now();
|
309 |
+
std::chrono::duration<double> cost_time = t2 - t1;
|
310 |
+
invoke_time.push_back(cost_time.count() * 1000);
|
311 |
+
if(result != EXIT_SUCCESS){
|
312 |
+
printf("interpreter->invoke() failed !\n");
|
313 |
+
return EXIT_FAILURE;
|
314 |
+
}
|
315 |
+
uint32_t out_data_1 = 0;
|
316 |
+
result = fast_interpreter->get_output_tensor(0, (void**)&qnn_trans_data, &out_data_1);
|
317 |
+
if(result != EXIT_SUCCESS){
|
318 |
+
printf("interpreter->get_output_tensor() 1 failed !\n");
|
319 |
+
return EXIT_FAILURE;
|
320 |
+
}
|
321 |
+
uint32_t out_data_2 = 0;
|
322 |
+
result = fast_interpreter->get_output_tensor(1, (void**)&qnn_mul_data, &out_data_2);
|
323 |
+
if(result != EXIT_SUCCESS){
|
324 |
+
printf("interpreter->get_output_tensor() 2 failed !\n");
|
325 |
+
return EXIT_FAILURE;
|
326 |
+
}
|
327 |
+
}
|
328 |
+
|
329 |
+
float max_invoke_time = *std::max_element(invoke_time.begin(), invoke_time.end());
|
330 |
+
float min_invoke_time = *std::min_element(invoke_time.begin(), invoke_time.end());
|
331 |
+
float mean_invoke_time = std::accumulate(invoke_time.begin(), invoke_time.end(), 0.0f) / args.invoke_nums;
|
332 |
+
float var_invoketime = 0.0f;
|
333 |
+
for (auto time : invoke_time) {
|
334 |
+
var_invoketime += (time - mean_invoke_time) * (time - mean_invoke_time);
|
335 |
+
}
|
336 |
+
var_invoketime /= args.invoke_nums;
|
337 |
+
printf("=======================================\n");
|
338 |
+
printf("QNN inference %d times :\n --mean_invoke_time is %f \n --max_invoke_time is %f \n --min_invoke_time is %f \n --var_invoketime is %f\n",
|
339 |
+
args.invoke_nums, mean_invoke_time, max_invoke_time, min_invoke_time, var_invoketime);
|
340 |
+
printf("=======================================\n");
|
341 |
+
|
342 |
+
std::vector<std::string> class_list = {
|
343 |
+
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
|
344 |
+
"truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter",
|
345 |
+
"bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear",
|
346 |
+
"zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
|
347 |
+
"frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
|
348 |
+
"baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
|
349 |
+
"wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
|
350 |
+
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",
|
351 |
+
"cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet",
|
352 |
+
"TV", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
|
353 |
+
"oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
|
354 |
+
"scissors", "teddy bear", "hair drier", "toothbrush"
|
355 |
+
};
|
356 |
+
|
357 |
+
// post process
|
358 |
+
std::vector<float> qnn_concat;
|
359 |
+
concatenate(qnn_trans_data, qnn_mul_data, 1, out_size, 80, 4, qnn_concat);
|
360 |
+
cv::Mat img = post_process(img_src, qnn_concat, class_list);
|
361 |
+
cv::imwrite("./results.png", img);
|
362 |
+
fast_interpreter->destory();
|
363 |
+
return 0;
|
364 |
+
}
|
365 |
+
|
366 |
+
|
367 |
+
int main(int argc, char* argv[]) {
|
368 |
+
Args args = parse_args(argc, argv);
|
369 |
+
return invoke(args);
|
370 |
+
}
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/cpp/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/models/cutoff_yolov6m_w8a8.qnn223.ctx.bin.aidem
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8f0bb56df2017a4e7fcd2423204a7f15a4a596964e6cd5609f469d3bab8104c1
|
3 |
+
size 35596880
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/run_test.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, torch, cv2
|
2 |
+
import numpy as np
|
3 |
+
import time
|
4 |
+
import aidlite
|
5 |
+
import argparse
|
6 |
+
from utils import letterbox,plot_box_and_label,rescale,generate_colors,non_max_suppression
|
7 |
+
import torch
|
8 |
+
|
9 |
+
|
10 |
+
def process_image(path, img_size):
|
11 |
+
img_src = cv2.imread(path)
|
12 |
+
img_src = cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB)
|
13 |
+
image = letterbox(img_src, img_size)[0]
|
14 |
+
new_h,new_w,_=image.shape
|
15 |
+
input_img = np.zeros((img_size[0], img_size[1], 3), np.uint8)
|
16 |
+
input_img[0:new_h, 0:new_w] = image
|
17 |
+
input_img = input_img.astype(np.float32)
|
18 |
+
input_img /= 255 # 0 - 255 to 0.0 - 1.0
|
19 |
+
input_img = np.expand_dims(input_img,0)
|
20 |
+
return image,input_img, img_src
|
21 |
+
|
22 |
+
def main(args):
|
23 |
+
print("Start main ... ...")
|
24 |
+
# aidlite.set_log_level(aidlite.LogLevel.INFO)
|
25 |
+
# aidlite.log_to_stderr()
|
26 |
+
# print(f"Aidlite library version : {aidlite.get_library_version()}")
|
27 |
+
# print(f"Aidlite python library version : {aidlite.get_py_library_version()}")
|
28 |
+
|
29 |
+
size=640
|
30 |
+
out_size=8400
|
31 |
+
config = aidlite.Config.create_instance()
|
32 |
+
if config is None:
|
33 |
+
print("Create config failed !")
|
34 |
+
return False
|
35 |
+
config.implement_type = aidlite.ImplementType.TYPE_LOCAL
|
36 |
+
if args.model_type.lower()=="qnn":
|
37 |
+
config.framework_type = aidlite.FrameworkType.TYPE_QNN223
|
38 |
+
elif args.model_type.lower()=="snpe2" or args.model_type.lower()=="snpe":
|
39 |
+
config.framework_type = aidlite.FrameworkType.TYPE_SNPE2
|
40 |
+
|
41 |
+
config.accelerate_type = aidlite.AccelerateType.TYPE_DSP
|
42 |
+
config.is_quantify_model = 1
|
43 |
+
|
44 |
+
|
45 |
+
model = aidlite.Model.create_instance(args.target_model)
|
46 |
+
if model is None:
|
47 |
+
print("Create model failed !")
|
48 |
+
return False
|
49 |
+
input_shapes = [[1, size, size, 3]]
|
50 |
+
output_shapes = [[1, out_size,80],[1, out_size,4]]
|
51 |
+
model.set_model_properties(input_shapes, aidlite.DataType.TYPE_FLOAT32,
|
52 |
+
output_shapes, aidlite.DataType.TYPE_FLOAT32)
|
53 |
+
|
54 |
+
interpreter = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model, config)
|
55 |
+
if interpreter is None:
|
56 |
+
print("build_interpretper_from_model_and_config failed !")
|
57 |
+
return None
|
58 |
+
result = interpreter.init()
|
59 |
+
if result != 0:
|
60 |
+
print(f"interpreter init failed !")
|
61 |
+
return False
|
62 |
+
result = interpreter.load_model()
|
63 |
+
if result != 0:
|
64 |
+
print("interpreter load model failed !")
|
65 |
+
return False
|
66 |
+
print("detect model load success!")
|
67 |
+
|
68 |
+
# image process
|
69 |
+
img_size=[size,size]
|
70 |
+
resize_img,input_img, img_src = process_image(args.imgs, img_size)
|
71 |
+
|
72 |
+
# qnn run
|
73 |
+
invoke_time=[]
|
74 |
+
for i in range(args.invoke_nums):
|
75 |
+
result = interpreter.set_input_tensor(0, input_img.data)
|
76 |
+
if result != 0:
|
77 |
+
print("interpreter set_input_tensor() failed")
|
78 |
+
|
79 |
+
t1=time.time()
|
80 |
+
result = interpreter.invoke()
|
81 |
+
cost_time = (time.time()-t1)*1000
|
82 |
+
invoke_time.append(cost_time)
|
83 |
+
|
84 |
+
if result != 0:
|
85 |
+
print("interpreter set_input_tensor() failed")
|
86 |
+
|
87 |
+
qnn_trans = interpreter.get_output_tensor(0).reshape(1,out_size,80)
|
88 |
+
qnn_mul = interpreter.get_output_tensor(1).reshape(1,out_size,4)
|
89 |
+
|
90 |
+
result = interpreter.destory()
|
91 |
+
|
92 |
+
## time 统计
|
93 |
+
max_invoke_time = max(invoke_time)
|
94 |
+
min_invoke_time = min(invoke_time)
|
95 |
+
mean_invoke_time = sum(invoke_time)/args.invoke_nums
|
96 |
+
var_invoketime=np.var(invoke_time)
|
97 |
+
print("=======================================")
|
98 |
+
print(f"QNN inference {args.invoke_nums} times :\n --mean_invoke_time is {mean_invoke_time} \n --max_invoke_time is {max_invoke_time} \n --min_invoke_time is {min_invoke_time} \n --var_invoketime is {var_invoketime}")
|
99 |
+
print("=======================================")
|
100 |
+
|
101 |
+
# 后处理
|
102 |
+
conf_thres =0.25 #@param {type:"number"}
|
103 |
+
iou_thres =0.45 #@param {type:"number"}
|
104 |
+
max_det= 1000#@param {type:"integer"}
|
105 |
+
agnostic_nms= False #@param {type:"boolean"}
|
106 |
+
classes =None
|
107 |
+
hide_labels = False #@param {type:"boolean"}
|
108 |
+
hide_conf= False #@param {type:"boolean"}
|
109 |
+
|
110 |
+
qnn_conf = np.ones((1,out_size,1))
|
111 |
+
qnn_predict=np.concatenate((qnn_mul,qnn_conf,qnn_trans), axis=2)
|
112 |
+
pred_results =torch.from_numpy(qnn_predict.copy())
|
113 |
+
det = non_max_suppression(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)[0]
|
114 |
+
|
115 |
+
class_names=[ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
|
116 |
+
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
|
117 |
+
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
|
118 |
+
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
|
119 |
+
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
120 |
+
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
|
121 |
+
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
|
122 |
+
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
|
123 |
+
'hair drier', 'toothbrush' ]
|
124 |
+
|
125 |
+
img_ori = img_src.copy()
|
126 |
+
print(f"Detected {len(det)} targets.")
|
127 |
+
if len(det):
|
128 |
+
det[:, :4] = rescale(resize_img.shape[:2], det[:, :4], img_src.shape).round()
|
129 |
+
for *xyxy, conf, cls in reversed(det):
|
130 |
+
class_num = int(cls)
|
131 |
+
label = None if hide_labels else (class_names[class_num] if hide_conf else f'{class_names[class_num]} {conf:.2f}')
|
132 |
+
plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=generate_colors(class_num, True))
|
133 |
+
|
134 |
+
cv2.imwrite("./python/results.png",cv2.cvtColor(img_ori,cv2.COLOR_RGB2BGR))
|
135 |
+
|
136 |
+
def parser_args():
|
137 |
+
parser = argparse.ArgumentParser(description="Run model benchmarks")
|
138 |
+
parser.add_argument('--target_model',type=str,default='./models/cutoff_yolov6m_w8a8.qnn223.ctx.bin',help="inference model path")
|
139 |
+
parser.add_argument('--imgs',type=str,default='./python/test.png',help="Predict images path")
|
140 |
+
parser.add_argument('--invoke_nums',type=int,default=10,help="Inference nums")
|
141 |
+
parser.add_argument('--model_type',type=str,default='QNN',help="run backend")
|
142 |
+
args = parser.parse_args()
|
143 |
+
return args
|
144 |
+
|
145 |
+
if __name__ == "__main__":
|
146 |
+
args = parser_args()
|
147 |
+
main(args)
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs8550_qnn2.23_int8_aidlite/python/utils.py
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
import torch
|
4 |
+
import torchvision
|
5 |
+
|
6 |
+
def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
|
7 |
+
'''Resize and pad image while meeting stride-multiple constraints.'''
|
8 |
+
shape = im.shape[:2] # current shape [height, width]
|
9 |
+
if isinstance(new_shape, int):
|
10 |
+
new_shape = (new_shape, new_shape)
|
11 |
+
elif isinstance(new_shape, list) and len(new_shape) == 1:
|
12 |
+
new_shape = (new_shape[0], new_shape[0])
|
13 |
+
|
14 |
+
# Scale ratio (new / old)
|
15 |
+
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
|
16 |
+
if not scaleup: # only scale down, do not scale up (for better val mAP)
|
17 |
+
r = min(r, 1.0)
|
18 |
+
|
19 |
+
# Compute padding
|
20 |
+
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
|
21 |
+
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
|
22 |
+
|
23 |
+
if auto: # minimum rectangle
|
24 |
+
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
|
25 |
+
|
26 |
+
dw /= 2 # divide padding into 2 sides
|
27 |
+
dh /= 2
|
28 |
+
|
29 |
+
if shape[::-1] != new_unpad: # resize
|
30 |
+
im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
|
31 |
+
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
|
32 |
+
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
|
33 |
+
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
|
34 |
+
|
35 |
+
return im, r, (left, top)
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
|
40 |
+
def xywh2xyxy(x):
|
41 |
+
'''Convert boxes with shape [n, 4] from [x, y, w, h] to [x1, y1, x2, y2] where x1y1 is top-left, x2y2=bottom-right.'''
|
42 |
+
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
43 |
+
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
|
44 |
+
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
|
45 |
+
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
|
46 |
+
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
|
47 |
+
return y
|
48 |
+
|
49 |
+
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, max_det=300):
|
50 |
+
"""Runs Non-Maximum Suppression (NMS) on inference results.
|
51 |
+
This code is borrowed from: https://github.com/ultralytics/yolov5/blob/47233e1698b89fc437a4fb9463c815e9171be955/utils/general.py#L775
|
52 |
+
Args:
|
53 |
+
prediction: (tensor), with shape [N, 5 + num_classes], N is the number of bboxes.
|
54 |
+
conf_thres: (float) confidence threshold.
|
55 |
+
iou_thres: (float) iou threshold.
|
56 |
+
classes: (None or list[int]), if a list is provided, nms only keep the classes you provide.
|
57 |
+
agnostic: (bool), when it is set to True, we do class-independent nms, otherwise, different class would do nms respectively.
|
58 |
+
multi_label: (bool), when it is set to True, one box can have multi labels, otherwise, one box only huave one label.
|
59 |
+
max_det:(int), max number of output bboxes.
|
60 |
+
|
61 |
+
Returns:
|
62 |
+
list of detections, echo item is one tensor with shape (num_boxes, 6), 6 is for [xyxy, conf, cls].
|
63 |
+
"""
|
64 |
+
|
65 |
+
num_classes = prediction.shape[2] - 5 # number of classes
|
66 |
+
pred_candidates = torch.logical_and(prediction[..., 4] > conf_thres, torch.max(prediction[..., 5:], axis=-1)[0] > conf_thres) # candidates
|
67 |
+
# Check the parameters.
|
68 |
+
assert 0 <= conf_thres <= 1, f'conf_thresh must be in 0.0 to 1.0, however {conf_thres} is provided.'
|
69 |
+
assert 0 <= iou_thres <= 1, f'iou_thres must be in 0.0 to 1.0, however {iou_thres} is provided.'
|
70 |
+
|
71 |
+
# Function settings.
|
72 |
+
max_wh = 4096 # maximum box width and height
|
73 |
+
max_nms = 30000 # maximum number of boxes put into torchvision.ops.nms()
|
74 |
+
time_limit = 10.0 # quit the function when nms cost time exceed the limit time.
|
75 |
+
multi_label &= num_classes > 1 # multiple labels per box
|
76 |
+
|
77 |
+
output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0]
|
78 |
+
for img_idx, x in enumerate(prediction): # image index, image inference
|
79 |
+
x = x[pred_candidates[img_idx]] # confidence
|
80 |
+
|
81 |
+
# If no box remains, skip the next process.
|
82 |
+
if not x.shape[0]:
|
83 |
+
continue
|
84 |
+
|
85 |
+
# confidence multiply the objectness
|
86 |
+
x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf
|
87 |
+
|
88 |
+
# (center x, center y, width, height) to (x1, y1, x2, y2)
|
89 |
+
box = xywh2xyxy(x[:, :4])
|
90 |
+
|
91 |
+
# Detections matrix's shape is (n,6), each row represents (xyxy, conf, cls)
|
92 |
+
if multi_label:
|
93 |
+
box_idx, class_idx = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T
|
94 |
+
x = torch.cat((box[box_idx], x[box_idx, class_idx + 5, None], class_idx[:, None].float()), 1)
|
95 |
+
else: # Only keep the class with highest scores.
|
96 |
+
conf, class_idx = x[:, 5:].max(1, keepdim=True)
|
97 |
+
x = torch.cat((box, conf, class_idx.float()), 1)[conf.view(-1) > conf_thres]
|
98 |
+
|
99 |
+
# Filter by class, only keep boxes whose category is in classes.
|
100 |
+
if classes is not None:
|
101 |
+
x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]
|
102 |
+
|
103 |
+
# Check shape
|
104 |
+
num_box = x.shape[0] # number of boxes
|
105 |
+
if not num_box: # no boxes kept.
|
106 |
+
continue
|
107 |
+
elif num_box > max_nms: # excess max boxes' number.
|
108 |
+
x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence
|
109 |
+
|
110 |
+
# Batched NMS
|
111 |
+
class_offset = x[:, 5:6] * (0 if agnostic else max_wh) # classes
|
112 |
+
boxes, scores = x[:, :4] + class_offset, x[:, 4] # boxes (offset by class), scores
|
113 |
+
keep_box_idx = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
|
114 |
+
if keep_box_idx.shape[0] > max_det: # limit detections
|
115 |
+
keep_box_idx = keep_box_idx[:max_det]
|
116 |
+
|
117 |
+
output[img_idx] = x[keep_box_idx]
|
118 |
+
|
119 |
+
return output
|
120 |
+
|
121 |
+
def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX):
|
122 |
+
# Add one xyxy box to image with label
|
123 |
+
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
|
124 |
+
cv2.rectangle(image, p1, p2, color, thickness=lw, lineType=cv2.LINE_AA)
|
125 |
+
if label:
|
126 |
+
tf = max(lw - 1, 1) # font thickness
|
127 |
+
w, h = cv2.getTextSize(label, 0, fontScale=lw / 3, thickness=tf)[0] # text width, height
|
128 |
+
outside = p1[1] - h - 3 >= 0 # label fits outside box
|
129 |
+
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
|
130 |
+
cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA) # filled
|
131 |
+
cv2.putText(image, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), font, lw / 3, txt_color,
|
132 |
+
thickness=tf, lineType=cv2.LINE_AA)
|
133 |
+
|
134 |
+
def generate_colors(i, bgr=False):
|
135 |
+
hex = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
|
136 |
+
'2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
|
137 |
+
palette = []
|
138 |
+
for iter in hex:
|
139 |
+
h = '#' + iter
|
140 |
+
palette.append(tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)))
|
141 |
+
num = len(palette)
|
142 |
+
color = palette[int(i) % num]
|
143 |
+
return (color[2], color[1], color[0]) if bgr else color
|
144 |
+
|
145 |
+
def rescale(ori_shape, boxes, target_shape):
|
146 |
+
'''Rescale the output to the original image shape'''
|
147 |
+
ratio = min(ori_shape[0] / target_shape[0], ori_shape[1] / target_shape[1])
|
148 |
+
padding = (ori_shape[1] - target_shape[1] * ratio) / 2, (ori_shape[0] - target_shape[0] * ratio) / 2
|
149 |
+
|
150 |
+
boxes[:, [0, 2]] -= padding[0]
|
151 |
+
boxes[:, [1, 3]] -= padding[1]
|
152 |
+
boxes[:, :4] /= ratio
|
153 |
+
|
154 |
+
boxes[:, 0].clamp_(0, target_shape[1]) # x1
|
155 |
+
boxes[:, 1].clamp_(0, target_shape[0]) # y1
|
156 |
+
boxes[:, 2].clamp_(0, target_shape[1]) # x2
|
157 |
+
boxes[:, 3].clamp_(0, target_shape[0]) # y2
|
158 |
+
|
159 |
+
return boxes
|
160 |
+
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Model Information
|
2 |
+
### Source model
|
3 |
+
|
4 |
+
- Input shape: 1x3x640x640
|
5 |
+
- Number of parameters: 33.24M
|
6 |
+
- Model size: 133.20MB
|
7 |
+
- Output shape: 1x8400x85
|
8 |
+
|
9 |
+
Source model repository: [yolov6](https://github.com/meituan/YOLOv6/tree/main)
|
10 |
+
|
11 |
+
### Converted model
|
12 |
+
|
13 |
+
- Precision: W8A16
|
14 |
+
- Backend: QNN2.23
|
15 |
+
- Target Device: SNM972 QCS8550
|
16 |
+
|
17 |
+
## Inference with AidLite SDK
|
18 |
+
|
19 |
+
### SDK installation
|
20 |
+
Model Farm uses AidLite SDK as the model inference SDK. For details, please refer to the [AidLite Developer Documentation](https://v2.docs.aidlux.com/en/sdk-api/aidlite-sdk/)
|
21 |
+
|
22 |
+
- install AidLite SDK
|
23 |
+
|
24 |
+
```bash
|
25 |
+
# Install the appropriate version of the aidlite sdk
|
26 |
+
sudo aid-pkg update
|
27 |
+
sudo aid-pkg install aidlite-sdk
|
28 |
+
# Download the qnn version that matches the above backend. Eg Install QNN2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
29 |
+
sudo aid-pkg install aidlite-{QNN VERSION}
|
30 |
+
# eg: Install QNN 2.23 Aidlite: sudo aid-pkg install aidlite-qnn223
|
31 |
+
```
|
32 |
+
|
33 |
+
- Verify AidLite SDK
|
34 |
+
|
35 |
+
```bash
|
36 |
+
# aidlite sdk c++ check
|
37 |
+
python3 -c "import aidlite; print(aidlite.get_library_version())"
|
38 |
+
|
39 |
+
# aidlite sdk python check
|
40 |
+
python3 -c "import aidlite; print(aidlite.get_py_library_version())"
|
41 |
+
```
|
42 |
+
|
43 |
+
### Run demo
|
44 |
+
|
45 |
+
#### python
|
46 |
+
```bash
|
47 |
+
cd yolov6m/model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite
|
48 |
+
python3 python/run_test.py --target_model ./models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem --imgs ./python/test.png --invoke_nums 10
|
49 |
+
|
50 |
+
```
|
51 |
+
|
52 |
+
#### cpp
|
53 |
+
```bash
|
54 |
+
cd yolov6m/model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp
|
55 |
+
mkdir build && cd build
|
56 |
+
cmake .. && make
|
57 |
+
./run_test --target_model ../../models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem --imgs ../test.png --invoke_nums 10
|
58 |
+
```
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/CMakeLists.txt
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
cmake_minimum_required (VERSION 3.5)
|
2 |
+
project("run_test")
|
3 |
+
|
4 |
+
find_package(OpenCV REQUIRED)
|
5 |
+
|
6 |
+
message(STATUS "oPENCV Library status:")
|
7 |
+
message(STATUS ">version:${OpenCV_VERSION}")
|
8 |
+
message(STATUS "Include:${OpenCV_INCLUDE_DIRS}")
|
9 |
+
|
10 |
+
set(CMAKE_CXX_FLAGS "-Wno-error=deprecated-declarations -Wno-deprecated-declarations")
|
11 |
+
|
12 |
+
include_directories(
|
13 |
+
/usr/local/include
|
14 |
+
/usr/include/opencv4
|
15 |
+
)
|
16 |
+
|
17 |
+
link_directories(
|
18 |
+
/usr/local/lib/
|
19 |
+
)
|
20 |
+
|
21 |
+
file(GLOB SRC_LISTS
|
22 |
+
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
|
23 |
+
)
|
24 |
+
|
25 |
+
add_executable(run_test ${SRC_LISTS})
|
26 |
+
|
27 |
+
target_link_libraries(run_test
|
28 |
+
aidlite
|
29 |
+
${OpenCV_LIBS}
|
30 |
+
)
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/main.cpp
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#include <iostream>
|
2 |
+
#include <string>
|
3 |
+
#include <algorithm>
|
4 |
+
#include <cctype>
|
5 |
+
#include <opencv2/opencv.hpp>
|
6 |
+
#include <aidlux/aidlite/aidlite.hpp>
|
7 |
+
#include <vector>
|
8 |
+
#include <numeric>
|
9 |
+
|
10 |
+
const float INPUT_WIDTH = 640.0;
|
11 |
+
const float INPUT_HEIGHT = 640.0;
|
12 |
+
const float SCORE_THRESHOLD = 0.25;
|
13 |
+
const float NMS_THRESHOLD = 0.45;
|
14 |
+
const float CONFIDENCE_THRESHOLD = 0.25;
|
15 |
+
const uint32_t size = 640;
|
16 |
+
const uint32_t out_size = 8400;
|
17 |
+
|
18 |
+
const int FONT_FACE = cv::FONT_HERSHEY_SIMPLEX;
|
19 |
+
cv::Scalar WHITE = cv::Scalar(255,255,255);
|
20 |
+
|
21 |
+
const float FONT_SCALE = 1;
|
22 |
+
const int THICKNESS = 2;
|
23 |
+
using namespace Aidlux::Aidlite;
|
24 |
+
|
25 |
+
struct Args {
|
26 |
+
std::string target_model = "../../models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem";
|
27 |
+
std::string imgs = "../test.png";
|
28 |
+
int invoke_nums = 10;
|
29 |
+
std::string model_type = "QNN";
|
30 |
+
};
|
31 |
+
|
32 |
+
Args parse_args(int argc, char* argv[]) {
|
33 |
+
Args args;
|
34 |
+
for (int i = 1; i < argc; ++i) {
|
35 |
+
std::string arg = argv[i];
|
36 |
+
if (arg == "--target_model" && i + 1 < argc) {
|
37 |
+
args.target_model = argv[++i];
|
38 |
+
} else if (arg == "--imgs" && i + 1 < argc) {
|
39 |
+
args.imgs = argv[++i];
|
40 |
+
} else if (arg == "--invoke_nums" && i + 1 < argc) {
|
41 |
+
args.invoke_nums = std::stoi(argv[++i]);
|
42 |
+
} else if (arg == "--model_type" && i + 1 < argc) {
|
43 |
+
args.model_type = argv[++i];
|
44 |
+
}
|
45 |
+
}
|
46 |
+
return args;
|
47 |
+
}
|
48 |
+
|
49 |
+
std::string to_lower(const std::string& str) {
|
50 |
+
std::string lower_str = str;
|
51 |
+
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), [](unsigned char c) {
|
52 |
+
return std::tolower(c);
|
53 |
+
});
|
54 |
+
return lower_str;
|
55 |
+
}
|
56 |
+
|
57 |
+
|
58 |
+
void concatenate(float* qnn_trans_data, float* qnn_mul_data, int batch, int num_elements, int trans_dim, int mul_dim, std::vector<float>& output) {
|
59 |
+
int out_dim = trans_dim + mul_dim + 1;
|
60 |
+
output.resize(batch * num_elements * out_dim);
|
61 |
+
for (int i = 0; i < batch * num_elements; ++i) {
|
62 |
+
std::memcpy(&output[i * out_dim], &qnn_mul_data[i * mul_dim], mul_dim * sizeof(float));
|
63 |
+
float max_val = *std::max_element(&qnn_trans_data[i * trans_dim], &qnn_trans_data[i * trans_dim + trans_dim]);
|
64 |
+
output[i * out_dim + 4] = max_val;
|
65 |
+
std::memcpy(&output[i * out_dim + 5], &qnn_trans_data[i * trans_dim], trans_dim * sizeof(float));
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
cv::Mat letterbox(cv::Mat im, cv::Size new_shape = cv::Size(640, 640),
|
70 |
+
cv::Scalar color = cv::Scalar(114, 114, 114),
|
71 |
+
bool auto_pad = true, bool scaleup = true, int stride = 32) {
|
72 |
+
// current shape [height, width]
|
73 |
+
cv::Size shape = im.size();
|
74 |
+
int height = shape.height;
|
75 |
+
int width = shape.width;
|
76 |
+
|
77 |
+
if (new_shape.width == 0) {
|
78 |
+
new_shape = cv::Size(new_shape.height, new_shape.height);
|
79 |
+
}
|
80 |
+
|
81 |
+
// Scale ratio (new / old)
|
82 |
+
float r = std::min((float)new_shape.height / height, (float)new_shape.width / width);
|
83 |
+
if (!scaleup) {
|
84 |
+
// only scale down, do not scale up (for better val mAP)
|
85 |
+
r = std::min(r, 1.0f);
|
86 |
+
}
|
87 |
+
|
88 |
+
// Compute padding
|
89 |
+
cv::Size new_unpad(round(width * r), round(height * r));
|
90 |
+
int dw = new_shape.width - new_unpad.width;
|
91 |
+
int dh = new_shape.height - new_unpad.height;
|
92 |
+
|
93 |
+
// minimum rectangle
|
94 |
+
if (auto_pad) {
|
95 |
+
dw = dw % stride;
|
96 |
+
dh = dh % stride;
|
97 |
+
}
|
98 |
+
|
99 |
+
dw /= 2; // divide padding into 2 sides
|
100 |
+
dh /= 2;
|
101 |
+
|
102 |
+
// resize
|
103 |
+
if (cv::Size(width, height) != new_unpad) {
|
104 |
+
cv::resize(im, im, new_unpad, 0, 0, cv::INTER_LINEAR);
|
105 |
+
}
|
106 |
+
|
107 |
+
int top = round(dh - 0.1);
|
108 |
+
int bottom = round(dh + 0.1);
|
109 |
+
int left = round(dw - 0.1);
|
110 |
+
int right = round(dw + 0.1);
|
111 |
+
|
112 |
+
cv::copyMakeBorder(im, im, top, bottom, left, right, cv::BORDER_CONSTANT, color);
|
113 |
+
return im;
|
114 |
+
}
|
115 |
+
cv::Scalar generate_colors(int i, bool bgr = false) {
|
116 |
+
static const std::vector<std::string> hex_colors = {
|
117 |
+
"FF3838", "FF9D97", "FF701F", "FFB21D", "CFD231", "48F90A",
|
118 |
+
"92CC17", "3DDB86", "1A9334", "00D4BB", "2C99A8", "00C2FF",
|
119 |
+
"344593", "6473FF", "0018EC", "8438FF", "520085", "CB38FF",
|
120 |
+
"FF95C8", "FF37C7"
|
121 |
+
};
|
122 |
+
|
123 |
+
int num = hex_colors.size();
|
124 |
+
std::string hex = hex_colors[i % num];
|
125 |
+
|
126 |
+
int r = std::stoi(hex.substr(0, 2), nullptr, 16);
|
127 |
+
int g = std::stoi(hex.substr(2, 2), nullptr, 16);
|
128 |
+
int b = std::stoi(hex.substr(4, 2), nullptr, 16);
|
129 |
+
|
130 |
+
if (bgr)
|
131 |
+
return cv::Scalar(b, g, r);
|
132 |
+
else
|
133 |
+
return cv::Scalar(r, g, b);
|
134 |
+
}
|
135 |
+
|
136 |
+
void draw_label(cv::Mat& input_image, std::string label, int left, int top, cv::Scalar color)
|
137 |
+
{
|
138 |
+
int baseLine;
|
139 |
+
cv::Size label_size = cv::getTextSize(label, FONT_FACE, FONT_SCALE, THICKNESS, &baseLine);
|
140 |
+
int y = top - label_size.height - baseLine;
|
141 |
+
if (y < 0) {
|
142 |
+
y = top + label_size.height + baseLine;
|
143 |
+
}
|
144 |
+
cv::Point tlc(left, y);
|
145 |
+
cv::Point brc(left + label_size.width, y + label_size.height + baseLine);
|
146 |
+
rectangle(input_image, tlc, brc, color, cv::FILLED);
|
147 |
+
putText(input_image, label, cv::Point(left, y + label_size.height), FONT_FACE, FONT_SCALE, WHITE, THICKNESS);
|
148 |
+
}
|
149 |
+
|
150 |
+
|
151 |
+
cv::Mat post_process(cv::Mat &input_image, std::vector<float> &outputs, const std::vector<std::string> &class_name)
|
152 |
+
{
|
153 |
+
// Initialize vectors to hold respective outputs while unwrapping detections.
|
154 |
+
std::vector<int> class_ids;
|
155 |
+
std::vector<float> confidences;
|
156 |
+
std::vector<cv::Rect> boxes;
|
157 |
+
|
158 |
+
// Resizing factor.
|
159 |
+
float r = std::min(INPUT_WIDTH / (float)input_image.cols, INPUT_HEIGHT / (float)input_image.rows);
|
160 |
+
int new_unpad_w = round(input_image.cols * r);
|
161 |
+
int new_unpad_h = round(input_image.rows * r);
|
162 |
+
int dw = (int)INPUT_WIDTH - new_unpad_w;
|
163 |
+
int dh = (int)INPUT_HEIGHT - new_unpad_h;
|
164 |
+
dw /= 24;
|
165 |
+
dh /= 24;
|
166 |
+
|
167 |
+
// Iterate through outputs for each box prediction
|
168 |
+
for (int i = 0; i < outputs.size(); i+=85)
|
169 |
+
{
|
170 |
+
float confidence = outputs[i+4];
|
171 |
+
if (confidence >= CONFIDENCE_THRESHOLD)
|
172 |
+
{
|
173 |
+
// Create a 1x80 Mat and store class scores of 80 classes.
|
174 |
+
cv::Mat scores(1, class_name.size(), CV_32FC1, outputs.data() + i + 5);
|
175 |
+
cv::Point class_id;
|
176 |
+
double max_class_score;
|
177 |
+
|
178 |
+
// For multi-label, check each class score
|
179 |
+
for (int c = 0; c < class_name.size(); c++) {
|
180 |
+
float class_score = scores.at<float>(0, c);
|
181 |
+
|
182 |
+
// If class score is above threshold, consider this class for the box
|
183 |
+
if (class_score > SCORE_THRESHOLD) {
|
184 |
+
// Store class ID and confidence in the pre-defined respective vectors.
|
185 |
+
confidences.push_back(confidence * class_score); // Multiply with confidence
|
186 |
+
class_ids.push_back(c); // class index
|
187 |
+
// Center and box dimension.
|
188 |
+
float cx = outputs[i];
|
189 |
+
float cy = outputs[i+1];
|
190 |
+
float w = outputs[i+2];
|
191 |
+
float h = outputs[i+3];
|
192 |
+
|
193 |
+
float x0 = (cx - 0.5f * w - dw) / r;
|
194 |
+
float y0 = (cy - 0.5f * h - dh) / r;
|
195 |
+
float x1 = (cx + 0.5f * w - dw) / r;
|
196 |
+
float y1 = (cy + 0.5f * h - dh) / r;
|
197 |
+
|
198 |
+
int left = int(x0);
|
199 |
+
int top = int(y0);
|
200 |
+
int width = int(x1 - x0);
|
201 |
+
int height = int(y1 - y0);
|
202 |
+
|
203 |
+
// Store good detections in the boxes vector.
|
204 |
+
boxes.push_back(cv::Rect(left, top, width, height));
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
// Perform Non Maximum Suppression and draw predictions.
|
211 |
+
std::vector<int> indices;
|
212 |
+
cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, indices);
|
213 |
+
printf("Detected {%ld} targets.\n", indices.size());
|
214 |
+
|
215 |
+
// Loop over NMS results and draw bounding boxes
|
216 |
+
for (int i = 0; i < indices.size(); i++)
|
217 |
+
{
|
218 |
+
int idx = indices[i];
|
219 |
+
cv::Rect box = boxes[idx];
|
220 |
+
|
221 |
+
int left = box.x;
|
222 |
+
int top = box.y;
|
223 |
+
int width = box.width;
|
224 |
+
int height = box.height;
|
225 |
+
cv::Scalar color = generate_colors(class_ids[idx]);
|
226 |
+
// Draw bounding box.
|
227 |
+
rectangle(input_image, cv::Point(left, top), cv::Point(left + width, top + height), color, 3*THICKNESS);
|
228 |
+
|
229 |
+
// Get the label for the class name and its confidence.
|
230 |
+
std::string label = cv::format("%.2f", confidences[idx]);
|
231 |
+
label = class_name[class_ids[idx]] + ":" + label;
|
232 |
+
// Draw class labels.
|
233 |
+
draw_label(input_image, label, left, top, color);
|
234 |
+
}
|
235 |
+
printf("Processing finished.\n");
|
236 |
+
return input_image;
|
237 |
+
}
|
238 |
+
|
239 |
+
|
240 |
+
int invoke(const Args& args) {
|
241 |
+
std::cout << "Start main ... ... Model Path: " << args.target_model << "\n"
|
242 |
+
<< "Image Path: " << args.imgs << "\n"
|
243 |
+
<< "Inference Nums: " << args.invoke_nums << "\n"
|
244 |
+
<< "Model Type: " << args.model_type << "\n";
|
245 |
+
Model* model = Model::create_instance(args.target_model);
|
246 |
+
if(model == nullptr){
|
247 |
+
printf("Create model failed !\n");
|
248 |
+
return EXIT_FAILURE;
|
249 |
+
}
|
250 |
+
Config* config = Config::create_instance();
|
251 |
+
if(config == nullptr){
|
252 |
+
printf("Create config failed !\n");
|
253 |
+
return EXIT_FAILURE;
|
254 |
+
}
|
255 |
+
config->implement_type = ImplementType::TYPE_LOCAL;
|
256 |
+
std::string model_type_lower = to_lower(args.model_type);
|
257 |
+
if (model_type_lower == "qnn"){
|
258 |
+
config->framework_type = FrameworkType::TYPE_QNN223;
|
259 |
+
} else if (model_type_lower == "snpe2" || model_type_lower == "snpe") {
|
260 |
+
config->framework_type = FrameworkType::TYPE_SNPE2;
|
261 |
+
}
|
262 |
+
config->accelerate_type = AccelerateType::TYPE_DSP;
|
263 |
+
config->is_quantify_model = 1;
|
264 |
+
|
265 |
+
std::vector<std::vector<uint32_t>> input_shapes = {{1, size, size, 3}};
|
266 |
+
std::vector<std::vector<uint32_t>> output_shapes = {{1, out_size, 80}, {1, out_size, 4}};
|
267 |
+
model->set_model_properties(input_shapes, DataType::TYPE_FLOAT32, output_shapes, DataType::TYPE_FLOAT32);
|
268 |
+
std::unique_ptr<Interpreter> fast_interpreter = InterpreterBuilder::build_interpretper_from_model_and_config(model, config);
|
269 |
+
if(fast_interpreter == nullptr){
|
270 |
+
printf("build_interpretper_from_model_and_config failed !\n");
|
271 |
+
return EXIT_FAILURE;
|
272 |
+
}
|
273 |
+
int result = fast_interpreter->init();
|
274 |
+
if(result != EXIT_SUCCESS){
|
275 |
+
printf("interpreter->init() failed !\n");
|
276 |
+
return EXIT_FAILURE;
|
277 |
+
}
|
278 |
+
// load model
|
279 |
+
fast_interpreter->load_model();
|
280 |
+
if(result != EXIT_SUCCESS){
|
281 |
+
printf("interpreter->load_model() failed !\n");
|
282 |
+
return EXIT_FAILURE;
|
283 |
+
}
|
284 |
+
printf("detect model load success!\n");
|
285 |
+
cv::Size img_size(size, size);
|
286 |
+
|
287 |
+
cv::Mat img_src = cv::imread(args.imgs);
|
288 |
+
printf("img_src cols: %d, img_src rows: %d\n", img_src.cols, img_src.rows);
|
289 |
+
cv::Mat img_ori = img_src.clone();
|
290 |
+
cv::cvtColor(img_ori, img_ori, cv::COLOR_BGR2RGB);
|
291 |
+
cv::Mat resized_img = letterbox(img_ori, img_size);
|
292 |
+
cv::Mat input_img = cv::Mat::zeros(img_size, CV_32FC3);
|
293 |
+
resized_img.convertTo(resized_img, CV_32FC3, 1.0 / 255.0);
|
294 |
+
resized_img.copyTo(input_img(cv::Rect(0, 0, resized_img.cols, resized_img.rows)));
|
295 |
+
float *qnn_trans_data = nullptr;
|
296 |
+
float *qnn_mul_data = nullptr;
|
297 |
+
|
298 |
+
std::vector<float> invoke_time;
|
299 |
+
for (int i = 0; i < args.invoke_nums; ++i) {
|
300 |
+
result = fast_interpreter->set_input_tensor(0, input_img.data);
|
301 |
+
if(result != EXIT_SUCCESS){
|
302 |
+
printf("interpreter->set_input_tensor() failed !\n");
|
303 |
+
return EXIT_FAILURE;
|
304 |
+
}
|
305 |
+
// 开始计时
|
306 |
+
auto t1 = std::chrono::high_resolution_clock::now();
|
307 |
+
result = fast_interpreter->invoke();
|
308 |
+
auto t2 = std::chrono::high_resolution_clock::now();
|
309 |
+
std::chrono::duration<double> cost_time = t2 - t1;
|
310 |
+
invoke_time.push_back(cost_time.count() * 1000);
|
311 |
+
if(result != EXIT_SUCCESS){
|
312 |
+
printf("interpreter->invoke() failed !\n");
|
313 |
+
return EXIT_FAILURE;
|
314 |
+
}
|
315 |
+
uint32_t out_data_1 = 0;
|
316 |
+
result = fast_interpreter->get_output_tensor(0, (void**)&qnn_trans_data, &out_data_1);
|
317 |
+
if(result != EXIT_SUCCESS){
|
318 |
+
printf("interpreter->get_output_tensor() 1 failed !\n");
|
319 |
+
return EXIT_FAILURE;
|
320 |
+
}
|
321 |
+
uint32_t out_data_2 = 0;
|
322 |
+
result = fast_interpreter->get_output_tensor(1, (void**)&qnn_mul_data, &out_data_2);
|
323 |
+
if(result != EXIT_SUCCESS){
|
324 |
+
printf("interpreter->get_output_tensor() 2 failed !\n");
|
325 |
+
return EXIT_FAILURE;
|
326 |
+
}
|
327 |
+
}
|
328 |
+
|
329 |
+
float max_invoke_time = *std::max_element(invoke_time.begin(), invoke_time.end());
|
330 |
+
float min_invoke_time = *std::min_element(invoke_time.begin(), invoke_time.end());
|
331 |
+
float mean_invoke_time = std::accumulate(invoke_time.begin(), invoke_time.end(), 0.0f) / args.invoke_nums;
|
332 |
+
float var_invoketime = 0.0f;
|
333 |
+
for (auto time : invoke_time) {
|
334 |
+
var_invoketime += (time - mean_invoke_time) * (time - mean_invoke_time);
|
335 |
+
}
|
336 |
+
var_invoketime /= args.invoke_nums;
|
337 |
+
printf("=======================================\n");
|
338 |
+
printf("QNN inference %d times :\n --mean_invoke_time is %f \n --max_invoke_time is %f \n --min_invoke_time is %f \n --var_invoketime is %f\n",
|
339 |
+
args.invoke_nums, mean_invoke_time, max_invoke_time, min_invoke_time, var_invoketime);
|
340 |
+
printf("=======================================\n");
|
341 |
+
|
342 |
+
std::vector<std::string> class_list = {
|
343 |
+
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
|
344 |
+
"truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter",
|
345 |
+
"bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear",
|
346 |
+
"zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
|
347 |
+
"frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
|
348 |
+
"baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
|
349 |
+
"wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
|
350 |
+
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",
|
351 |
+
"cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet",
|
352 |
+
"TV", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
|
353 |
+
"oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
|
354 |
+
"scissors", "teddy bear", "hair drier", "toothbrush"
|
355 |
+
};
|
356 |
+
|
357 |
+
// post process
|
358 |
+
std::vector<float> qnn_concat;
|
359 |
+
concatenate(qnn_trans_data, qnn_mul_data, 1, out_size, 80, 4, qnn_concat);
|
360 |
+
cv::Mat img = post_process(img_src, qnn_concat, class_list);
|
361 |
+
cv::imwrite("./results.png", img);
|
362 |
+
fast_interpreter->destory();
|
363 |
+
return 0;
|
364 |
+
}
|
365 |
+
|
366 |
+
|
367 |
+
int main(int argc, char* argv[]) {
|
368 |
+
Args args = parse_args(argc, argv);
|
369 |
+
return invoke(args);
|
370 |
+
}
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/cpp/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8e920a05c31f9b5cc85cd66035ff82bcf0ffe07378413039b2b7e8c43096f2f8
|
3 |
+
size 36018768
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/run_test.py
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os, torch, cv2
|
2 |
+
import numpy as np
|
3 |
+
import time
|
4 |
+
import aidlite
|
5 |
+
import argparse
|
6 |
+
from utils import letterbox,plot_box_and_label,rescale,generate_colors,non_max_suppression
|
7 |
+
import torch
|
8 |
+
|
9 |
+
|
10 |
+
def process_image(path, img_size):
|
11 |
+
img_src = cv2.imread(path)
|
12 |
+
img_src = cv2.cvtColor(img_src,cv2.COLOR_BGR2RGB)
|
13 |
+
image = letterbox(img_src, img_size)[0]
|
14 |
+
new_h,new_w,_=image.shape
|
15 |
+
input_img = np.zeros((img_size[0], img_size[1], 3), np.uint8)
|
16 |
+
input_img[0:new_h, 0:new_w] = image
|
17 |
+
input_img = input_img.astype(np.float32)
|
18 |
+
input_img /= 255 # 0 - 255 to 0.0 - 1.0
|
19 |
+
input_img = np.expand_dims(input_img,0)
|
20 |
+
return image,input_img, img_src
|
21 |
+
|
22 |
+
def main(args):
|
23 |
+
print("Start main ... ...")
|
24 |
+
# aidlite.set_log_level(aidlite.LogLevel.INFO)
|
25 |
+
# aidlite.log_to_stderr()
|
26 |
+
# print(f"Aidlite library version : {aidlite.get_library_version()}")
|
27 |
+
# print(f"Aidlite python library version : {aidlite.get_py_library_version()}")
|
28 |
+
|
29 |
+
size=640
|
30 |
+
out_size=8400
|
31 |
+
config = aidlite.Config.create_instance()
|
32 |
+
if config is None:
|
33 |
+
print("Create config failed !")
|
34 |
+
return False
|
35 |
+
config.implement_type = aidlite.ImplementType.TYPE_LOCAL
|
36 |
+
if args.model_type.lower()=="qnn":
|
37 |
+
config.framework_type = aidlite.FrameworkType.TYPE_QNN223
|
38 |
+
elif args.model_type.lower()=="snpe2" or args.model_type.lower()=="snpe":
|
39 |
+
config.framework_type = aidlite.FrameworkType.TYPE_SNPE2
|
40 |
+
|
41 |
+
config.accelerate_type = aidlite.AccelerateType.TYPE_DSP
|
42 |
+
config.is_quantify_model = 1
|
43 |
+
|
44 |
+
|
45 |
+
model = aidlite.Model.create_instance(args.target_model)
|
46 |
+
if model is None:
|
47 |
+
print("Create model failed !")
|
48 |
+
return False
|
49 |
+
input_shapes = [[1, size, size, 3]]
|
50 |
+
output_shapes = [[1, out_size,80],[1, out_size,4]]
|
51 |
+
model.set_model_properties(input_shapes, aidlite.DataType.TYPE_FLOAT32,
|
52 |
+
output_shapes, aidlite.DataType.TYPE_FLOAT32)
|
53 |
+
|
54 |
+
interpreter = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model, config)
|
55 |
+
if interpreter is None:
|
56 |
+
print("build_interpretper_from_model_and_config failed !")
|
57 |
+
return None
|
58 |
+
result = interpreter.init()
|
59 |
+
if result != 0:
|
60 |
+
print(f"interpreter init failed !")
|
61 |
+
return False
|
62 |
+
result = interpreter.load_model()
|
63 |
+
if result != 0:
|
64 |
+
print("interpreter load model failed !")
|
65 |
+
return False
|
66 |
+
print("detect model load success!")
|
67 |
+
|
68 |
+
# image process
|
69 |
+
img_size=[size,size]
|
70 |
+
resize_img,input_img, img_src = process_image(args.imgs, img_size)
|
71 |
+
|
72 |
+
# qnn run
|
73 |
+
invoke_time=[]
|
74 |
+
for i in range(args.invoke_nums):
|
75 |
+
result = interpreter.set_input_tensor(0, input_img.data)
|
76 |
+
if result != 0:
|
77 |
+
print("interpreter set_input_tensor() failed")
|
78 |
+
|
79 |
+
t1=time.time()
|
80 |
+
result = interpreter.invoke()
|
81 |
+
cost_time = (time.time()-t1)*1000
|
82 |
+
invoke_time.append(cost_time)
|
83 |
+
|
84 |
+
if result != 0:
|
85 |
+
print("interpreter set_input_tensor() failed")
|
86 |
+
|
87 |
+
qnn_trans = interpreter.get_output_tensor(0).reshape(1,out_size,80)
|
88 |
+
qnn_mul = interpreter.get_output_tensor(1).reshape(1,out_size,4)
|
89 |
+
|
90 |
+
result = interpreter.destory()
|
91 |
+
|
92 |
+
## time 统计
|
93 |
+
max_invoke_time = max(invoke_time)
|
94 |
+
min_invoke_time = min(invoke_time)
|
95 |
+
mean_invoke_time = sum(invoke_time)/args.invoke_nums
|
96 |
+
var_invoketime=np.var(invoke_time)
|
97 |
+
print("=======================================")
|
98 |
+
print(f"QNN inference {args.invoke_nums} times :\n --mean_invoke_time is {mean_invoke_time} \n --max_invoke_time is {max_invoke_time} \n --min_invoke_time is {min_invoke_time} \n --var_invoketime is {var_invoketime}")
|
99 |
+
print("=======================================")
|
100 |
+
|
101 |
+
# 后处理
|
102 |
+
conf_thres =0.25 #@param {type:"number"}
|
103 |
+
iou_thres =0.45 #@param {type:"number"}
|
104 |
+
max_det= 1000#@param {type:"integer"}
|
105 |
+
agnostic_nms= False #@param {type:"boolean"}
|
106 |
+
classes =None
|
107 |
+
hide_labels = False #@param {type:"boolean"}
|
108 |
+
hide_conf= False #@param {type:"boolean"}
|
109 |
+
|
110 |
+
qnn_conf = np.ones((1,out_size,1))
|
111 |
+
qnn_predict=np.concatenate((qnn_mul,qnn_conf,qnn_trans), axis=2)
|
112 |
+
pred_results =torch.from_numpy(qnn_predict.copy())
|
113 |
+
det = non_max_suppression(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)[0]
|
114 |
+
|
115 |
+
class_names=[ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
|
116 |
+
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
|
117 |
+
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
|
118 |
+
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
|
119 |
+
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
120 |
+
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
|
121 |
+
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
|
122 |
+
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
|
123 |
+
'hair drier', 'toothbrush' ]
|
124 |
+
|
125 |
+
img_ori = img_src.copy()
|
126 |
+
print(f"Detected {len(det)} targets.")
|
127 |
+
if len(det):
|
128 |
+
det[:, :4] = rescale(resize_img.shape[:2], det[:, :4], img_src.shape).round()
|
129 |
+
for *xyxy, conf, cls in reversed(det):
|
130 |
+
class_num = int(cls)
|
131 |
+
label = None if hide_labels else (class_names[class_num] if hide_conf else f'{class_names[class_num]} {conf:.2f}')
|
132 |
+
plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=generate_colors(class_num, True))
|
133 |
+
|
134 |
+
cv2.imwrite("./python/results.png",cv2.cvtColor(img_ori,cv2.COLOR_RGB2BGR))
|
135 |
+
|
136 |
+
def parser_args():
|
137 |
+
parser = argparse.ArgumentParser(description="Run model benchmarks")
|
138 |
+
parser.add_argument('--target_model',type=str,default='./models/cutoff_yolov6m_w8a16.qnn223.ctx.bin.aidem',help="inference model path")
|
139 |
+
parser.add_argument('--imgs',type=str,default='./python/test.png',help="Predict images path")
|
140 |
+
parser.add_argument('--invoke_nums',type=int,default=10,help="Inference nums")
|
141 |
+
parser.add_argument('--model_type',type=str,default='QNN',help="run backend")
|
142 |
+
args = parser.parse_args()
|
143 |
+
return args
|
144 |
+
|
145 |
+
if __name__ == "__main__":
|
146 |
+
args = parser_args()
|
147 |
+
main(args)
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/test.png
ADDED
![]() |
Git LFS Details
|
model_farm_yolov6m_qcs8550_qnn2.23_w8a16_aidlite/python/utils.py
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
import torch
|
4 |
+
import torchvision
|
5 |
+
|
6 |
+
def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
|
7 |
+
'''Resize and pad image while meeting stride-multiple constraints.'''
|
8 |
+
shape = im.shape[:2] # current shape [height, width]
|
9 |
+
if isinstance(new_shape, int):
|
10 |
+
new_shape = (new_shape, new_shape)
|
11 |
+
elif isinstance(new_shape, list) and len(new_shape) == 1:
|
12 |
+
new_shape = (new_shape[0], new_shape[0])
|
13 |
+
|
14 |
+
# Scale ratio (new / old)
|
15 |
+
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
|
16 |
+
if not scaleup: # only scale down, do not scale up (for better val mAP)
|
17 |
+
r = min(r, 1.0)
|
18 |
+
|
19 |
+
# Compute padding
|
20 |
+
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
|
21 |
+
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
|
22 |
+
|
23 |
+
if auto: # minimum rectangle
|
24 |
+
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
|
25 |
+
|
26 |
+
dw /= 2 # divide padding into 2 sides
|
27 |
+
dh /= 2
|
28 |
+
|
29 |
+
if shape[::-1] != new_unpad: # resize
|
30 |
+
im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
|
31 |
+
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
|
32 |
+
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
|
33 |
+
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
|
34 |
+
|
35 |
+
return im, r, (left, top)
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
|
40 |
+
def xywh2xyxy(x):
|
41 |
+
'''Convert boxes with shape [n, 4] from [x, y, w, h] to [x1, y1, x2, y2] where x1y1 is top-left, x2y2=bottom-right.'''
|
42 |
+
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
43 |
+
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
|
44 |
+
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
|
45 |
+
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
|
46 |
+
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
|
47 |
+
return y
|
48 |
+
|
49 |
+
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, max_det=300):
|
50 |
+
"""Runs Non-Maximum Suppression (NMS) on inference results.
|
51 |
+
This code is borrowed from: https://github.com/ultralytics/yolov5/blob/47233e1698b89fc437a4fb9463c815e9171be955/utils/general.py#L775
|
52 |
+
Args:
|
53 |
+
prediction: (tensor), with shape [N, 5 + num_classes], N is the number of bboxes.
|
54 |
+
conf_thres: (float) confidence threshold.
|
55 |
+
iou_thres: (float) iou threshold.
|
56 |
+
classes: (None or list[int]), if a list is provided, nms only keep the classes you provide.
|
57 |
+
agnostic: (bool), when it is set to True, we do class-independent nms, otherwise, different class would do nms respectively.
|
58 |
+
multi_label: (bool), when it is set to True, one box can have multi labels, otherwise, one box only huave one label.
|
59 |
+
max_det:(int), max number of output bboxes.
|
60 |
+
|
61 |
+
Returns:
|
62 |
+
list of detections, echo item is one tensor with shape (num_boxes, 6), 6 is for [xyxy, conf, cls].
|
63 |
+
"""
|
64 |
+
|
65 |
+
num_classes = prediction.shape[2] - 5 # number of classes
|
66 |
+
pred_candidates = torch.logical_and(prediction[..., 4] > conf_thres, torch.max(prediction[..., 5:], axis=-1)[0] > conf_thres) # candidates
|
67 |
+
# Check the parameters.
|
68 |
+
assert 0 <= conf_thres <= 1, f'conf_thresh must be in 0.0 to 1.0, however {conf_thres} is provided.'
|
69 |
+
assert 0 <= iou_thres <= 1, f'iou_thres must be in 0.0 to 1.0, however {iou_thres} is provided.'
|
70 |
+
|
71 |
+
# Function settings.
|
72 |
+
max_wh = 4096 # maximum box width and height
|
73 |
+
max_nms = 30000 # maximum number of boxes put into torchvision.ops.nms()
|
74 |
+
time_limit = 10.0 # quit the function when nms cost time exceed the limit time.
|
75 |
+
multi_label &= num_classes > 1 # multiple labels per box
|
76 |
+
|
77 |
+
output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0]
|
78 |
+
for img_idx, x in enumerate(prediction): # image index, image inference
|
79 |
+
x = x[pred_candidates[img_idx]] # confidence
|
80 |
+
|
81 |
+
# If no box remains, skip the next process.
|
82 |
+
if not x.shape[0]:
|
83 |
+
continue
|
84 |
+
|
85 |
+
# confidence multiply the objectness
|
86 |
+
x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf
|
87 |
+
|
88 |
+
# (center x, center y, width, height) to (x1, y1, x2, y2)
|
89 |
+
box = xywh2xyxy(x[:, :4])
|
90 |
+
|
91 |
+
# Detections matrix's shape is (n,6), each row represents (xyxy, conf, cls)
|
92 |
+
if multi_label:
|
93 |
+
box_idx, class_idx = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T
|
94 |
+
x = torch.cat((box[box_idx], x[box_idx, class_idx + 5, None], class_idx[:, None].float()), 1)
|
95 |
+
else: # Only keep the class with highest scores.
|
96 |
+
conf, class_idx = x[:, 5:].max(1, keepdim=True)
|
97 |
+
x = torch.cat((box, conf, class_idx.float()), 1)[conf.view(-1) > conf_thres]
|
98 |
+
|
99 |
+
# Filter by class, only keep boxes whose category is in classes.
|
100 |
+
if classes is not None:
|
101 |
+
x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]
|
102 |
+
|
103 |
+
# Check shape
|
104 |
+
num_box = x.shape[0] # number of boxes
|
105 |
+
if not num_box: # no boxes kept.
|
106 |
+
continue
|
107 |
+
elif num_box > max_nms: # excess max boxes' number.
|
108 |
+
x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence
|
109 |
+
|
110 |
+
# Batched NMS
|
111 |
+
class_offset = x[:, 5:6] * (0 if agnostic else max_wh) # classes
|
112 |
+
boxes, scores = x[:, :4] + class_offset, x[:, 4] # boxes (offset by class), scores
|
113 |
+
keep_box_idx = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
|
114 |
+
if keep_box_idx.shape[0] > max_det: # limit detections
|
115 |
+
keep_box_idx = keep_box_idx[:max_det]
|
116 |
+
|
117 |
+
output[img_idx] = x[keep_box_idx]
|
118 |
+
|
119 |
+
return output
|
120 |
+
|
121 |
+
def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX):
|
122 |
+
# Add one xyxy box to image with label
|
123 |
+
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
|
124 |
+
cv2.rectangle(image, p1, p2, color, thickness=lw, lineType=cv2.LINE_AA)
|
125 |
+
if label:
|
126 |
+
tf = max(lw - 1, 1) # font thickness
|
127 |
+
w, h = cv2.getTextSize(label, 0, fontScale=lw / 3, thickness=tf)[0] # text width, height
|
128 |
+
outside = p1[1] - h - 3 >= 0 # label fits outside box
|
129 |
+
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
|
130 |
+
cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA) # filled
|
131 |
+
cv2.putText(image, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), font, lw / 3, txt_color,
|
132 |
+
thickness=tf, lineType=cv2.LINE_AA)
|
133 |
+
|
134 |
+
def generate_colors(i, bgr=False):
|
135 |
+
hex = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
|
136 |
+
'2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
|
137 |
+
palette = []
|
138 |
+
for iter in hex:
|
139 |
+
h = '#' + iter
|
140 |
+
palette.append(tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)))
|
141 |
+
num = len(palette)
|
142 |
+
color = palette[int(i) % num]
|
143 |
+
return (color[2], color[1], color[0]) if bgr else color
|
144 |
+
|
145 |
+
def rescale(ori_shape, boxes, target_shape):
|
146 |
+
'''Rescale the output to the original image shape'''
|
147 |
+
ratio = min(ori_shape[0] / target_shape[0], ori_shape[1] / target_shape[1])
|
148 |
+
padding = (ori_shape[1] - target_shape[1] * ratio) / 2, (ori_shape[0] - target_shape[0] * ratio) / 2
|
149 |
+
|
150 |
+
boxes[:, [0, 2]] -= padding[0]
|
151 |
+
boxes[:, [1, 3]] -= padding[1]
|
152 |
+
boxes[:, :4] /= ratio
|
153 |
+
|
154 |
+
boxes[:, 0].clamp_(0, target_shape[1]) # x1
|
155 |
+
boxes[:, 1].clamp_(0, target_shape[0]) # y1
|
156 |
+
boxes[:, 2].clamp_(0, target_shape[1]) # x2
|
157 |
+
boxes[:, 3].clamp_(0, target_shape[0]) # y2
|
158 |
+
|
159 |
+
return boxes
|
160 |
+
|