23 std::string stdout_text;
30std::string Trim(
const std::string& value) {
31 std::size_t first = 0;
32 while (first < value.size() && std::isspace(
static_cast<unsigned char>(value[first]))) {
35 std::size_t last = value.size();
36 while (last > first && std::isspace(
static_cast<unsigned char>(value[last - 1]))) {
39 return value.substr(first, last - first);
46CommandResult RunCommandCapture(
const std::string& command) {
48 FILE* pipe = popen(command.c_str(),
"r");
49 if (pipe ==
nullptr) {
53 while (fgets(buffer,
static_cast<int>(
sizeof(buffer)), pipe) !=
nullptr) {
54 result.stdout_text += buffer;
56 result.exit_code = pclose(pipe);
64std::vector<std::string> SplitLines(
const std::string& text) {
65 std::vector<std::string> lines;
66 std::stringstream ss(text);
68 while (std::getline(ss, line)) {
69 lines.push_back(Trim(line));
78std::vector<float> ParseCsvFloats(
const std::string& csv) {
79 std::vector<float> out;
80 std::stringstream ss(csv);
82 while (std::getline(ss, item,
',')) {
83 const std::string trimmed = Trim(item);
84 if (!trimmed.empty()) {
85 out.push_back(std::stof(trimmed));
95void WriteManifestWithScoreThreshold(
const std::string& path,
const std::string& score_threshold_value) {
96 std::ofstream out(path);
97 out <<
"schema_version: '2.0'\n";
99 out <<
" bundle_name: 'pbod_fp16_gpu_onnx_test'\n";
100 out <<
" export_format: 'onnx_fp16_gpu'\n";
101 out <<
" backend: 'onnx'\n";
102 out <<
" precision: 'fp16'\n";
103 out <<
" device: 'cuda'\n";
105 out <<
" model: 'model.onnx'\n";
106 out <<
" checkpoint: 'checkpoints/best.pt'\n";
107 out <<
" resolved_training_config: 'config/resolved_training_config.yml'\n";
109 out <<
" enabled: false\n";
111 out <<
" - name: 'point_features'\n";
112 out <<
" dtype: 'float16'\n";
113 out <<
" shape: ['batch', 100, 1]\n";
114 out <<
" outputs:\n";
115 out <<
" - name: 'reg_logits'\n";
116 out <<
" dtype: 'float16'\n";
117 out <<
" shape: ['batch', 300, 21]\n";
118 out <<
"frozen_contract:\n";
119 out <<
" preprocessing:\n";
120 out <<
" max_num_points: 100\n";
121 out <<
" num_point_features: 1\n";
122 out <<
" point_cloud_range:\n";
123 out <<
" x: [-1.0, 1.0]\n";
124 out <<
" y: [-1.0, 1.0]\n";
125 out <<
" z: [-1.0, 1.0]\n";
126 out <<
" voxel_size:\n";
130 out <<
" point_feature_normalization:\n";
131 out <<
" type: value_threshold\n";
132 out <<
" epsilon: 1e-6\n";
133 out <<
" postprocessing:\n";
134 out <<
" grid_size:\n";
137 out <<
" num_classes: 3\n";
138 out <<
" class_names: ['car', 'pedestrian', 'truck']\n";
140 out <<
" stride: [2, 1, 2]\n";
141 out <<
" up_stride: [1, 1, 2]\n";
142 out <<
" first_up_stride: 1\n";
143 out <<
" pillar_map_size: [10, 10]\n";
144 out <<
" pillar_map_range: [[-1.0, 1.0], [-1.0, 1.0], [-1.0, 1.0]]\n";
145 out <<
"runtime_defaults:\n";
146 out <<
" preprocessing:\n";
147 out <<
" point_feature:\n";
148 out <<
" value_threshold: 1.0\n";
149 out <<
" postprocessing:\n";
150 out <<
" class_score_threshold: 0.0\n";
152 out <<
" score_threshold: " << score_threshold_value <<
"\n";
153 out <<
" iou_threshold: 0.5\n";
154 out <<
" max_num_objects: 10\n";
161 const std::string python_dir = PCOD_COMMON_PYTHON_DIR;
162 const std::string py_prefix =
"PYTHONPATH='" + python_dir +
"' python3 -c \"";
165 const auto result = RunCommandCapture(py_prefix +
"from pcod_common.manifest import SCHEMA_VERSION; print(SCHEMA_VERSION)\"");
166 assert(result.exit_code == 0);
171 const auto result = RunCommandCapture(py_prefix +
172 "from pcod_common.manifest import score_threshold_list as s; "
173 "print(','.join(str(v) for v in s(None))); "
174 "print(','.join(str(v) for v in s(0.25))); "
175 "print(','.join(str(v) for v in s([0.1, 0.2])))\"");
176 assert(result.exit_code == 0);
177 const auto lines = SplitLines(result.stdout_text);
178 assert(lines.size() == 3);
179 assert(lines[0].empty());
180 assert(lines[1] ==
"0.25");
181 assert(lines[2] ==
"0.1,0.2");
185 const std::string scalar_path =
"./test_py_cpp_score_scalar.yml";
186 const std::string list_path =
"./test_py_cpp_score_list.yml";
187 WriteManifestWithScoreThreshold(scalar_path,
"0.2");
188 WriteManifestWithScoreThreshold(list_path,
"[0.1, 0.2, 0.3]");
193 assert(scalar_manifest.runtime_defaults.postprocessing.nms_score_thresholds.size() == 1);
194 assert(std::abs(scalar_manifest.runtime_defaults.postprocessing.nms_score_thresholds[0] - 0.2f) < 1e-6f);
195 assert(list_manifest.runtime_defaults.postprocessing.nms_score_thresholds.size() == 3);
196 assert(std::abs(list_manifest.runtime_defaults.postprocessing.nms_score_thresholds[0] - 0.1f) < 1e-6f);
197 assert(std::abs(list_manifest.runtime_defaults.postprocessing.nms_score_thresholds[1] - 0.2f) < 1e-6f);
198 assert(std::abs(list_manifest.runtime_defaults.postprocessing.nms_score_thresholds[2] - 0.3f) < 1e-6f);
202 const auto torch_check = RunCommandCapture(py_prefix +
"import torch, torchvision; print('ok')\"");
203 if (torch_check.exit_code == 0) {
219 std::vector<pcod_common::BoundingBox> boxes = {a, b, c};
227 std::vector<float> cpp_kept_x;
228 for (
const auto& box : boxes) {
229 cpp_kept_x.push_back(box.center[0]);
232 const auto py_nms = RunCommandCapture(py_prefix +
233 "from pcod_common.postprocess import apply_nms; "
235 "boxes=torch.tensor([[0.0,0.0,0.0,1.0,1.0,1.0,0.0],[0.1,0.0,0.0,1.0,1.0,1.0,0.0],[10."
236 "0,0.0,0.0,1.0,1.0,1.0,0.0]],dtype=torch.float32); "
237 "scores=torch.tensor([0.9,0.8,0.7],dtype=torch.float32); "
238 "labels=torch.tensor([0,0,0],dtype=torch.long); "
239 "kept_boxes,_,_=apply_nms(boxes,scores,labels,[0.5],0.1,10,use_rotated=False); "
240 "print(','.join(str(float(v)) for v in kept_boxes[:,0].tolist()))\"");
241 assert(py_nms.exit_code == 0);
243 const auto py_kept_x = ParseCsvFloats(py_nms.stdout_text);
244 assert(py_kept_x.size() == cpp_kept_x.size());
245 for (std::size_t i = 0; i < py_kept_x.size(); ++i) {
246 assert(std::abs(py_kept_x[i] - cpp_kept_x[i]) < 1e-5f);
constexpr const char * kManifestSchemaVersion
void ApplyRotatedNms(std::vector< BoundingBox > &bboxes, const NmsConfig &config)
ModelManifest LoadModelManifest(const std::string &path)
float length
Length along the local X axis.
std::array< float, 2 > center
XY center in metres.
float width
Width along the local Y axis.
std::vector< ClassificationEntry > classification
Ranked semantic predictions.
float existence_probability
Detection confidence.
float iou_threshold
IoU above which a lower-scored box is suppressed.
std::vector< float > score_thresholds
Per-class thresholds, or one threshold shared by all classes.
int max_detections
Maximum number of boxes retained.
float internal_score_threshold
Score pivot used when rescaling class thresholds.