pcod-common v1.0.0
Shared preprocessing and postprocessing for point-cloud object detection
Loading...
Searching...
No Matches
test_py_cpp_contract.cpp File Reference
#include "pcod_common/model_manifest.hpp"
#include "pcod_common/nms.hpp"
#include "pcod_common/version.hpp"
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

Go to the source code of this file.

Functions

int main ()
 

Function Documentation

◆ main()

int main ( )

Run C++ and Python contract parity checks.

Definition at line 160 of file test_py_cpp_contract.cpp.

160 {
161 const std::string python_dir = PCOD_COMMON_PYTHON_DIR;
162 const std::string py_prefix = "PYTHONPATH='" + python_dir + "' python3 -c \"";
163
164 {
165 const auto result = RunCommandCapture(py_prefix + "from pcod_common.manifest import SCHEMA_VERSION; print(SCHEMA_VERSION)\"");
166 assert(result.exit_code == 0);
167 assert(Trim(result.stdout_text) == pcod_common::kManifestSchemaVersion);
168 }
169
170 {
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");
182 }
183
184 {
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]");
189
190 auto scalar_manifest = pcod_common::LoadModelManifest(scalar_path);
191 auto list_manifest = pcod_common::LoadModelManifest(list_path);
192
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);
199 }
200
201 {
202 const auto torch_check = RunCommandCapture(py_prefix + "import torch, torchvision; print('ok')\"");
203 if (torch_check.exit_code == 0) {
205 a.center = {0.0f, 0.0f};
206 a.length = 1.0f;
207 a.width = 1.0f;
208 a.existence_probability = 0.9f;
209 a.classification.push_back({0, 1.0f});
210
212 b.center = {0.1f, 0.0f};
213 b.existence_probability = 0.8f;
214
216 c.center = {10.0f, 0.0f};
217 c.existence_probability = 0.7f;
218
219 std::vector<pcod_common::BoundingBox> boxes = {a, b, c};
221 cfg.score_thresholds = {0.5f};
222 cfg.iou_threshold = 0.1f;
223 cfg.max_detections = 10;
224 cfg.internal_score_threshold = 0.5f;
226
227 std::vector<float> cpp_kept_x;
228 for (const auto& box : boxes) {
229 cpp_kept_x.push_back(box.center[0]);
230 }
231
232 const auto py_nms = RunCommandCapture(py_prefix +
233 "from pcod_common.postprocess import apply_nms; "
234 "import torch; "
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);
242
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);
247 }
248 }
249 }
250
251 return 0;
252}
constexpr const char * kManifestSchemaVersion
Definition version.hpp:10
void ApplyRotatedNms(std::vector< BoundingBox > &bboxes, const NmsConfig &config)
Definition nms.cpp:12
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.
Definition nms.hpp:15
std::vector< float > score_thresholds
Per-class thresholds, or one threshold shared by all classes.
Definition nms.hpp:14
int max_detections
Maximum number of boxes retained.
Definition nms.hpp:16
float internal_score_threshold
Score pivot used when rescaling class thresholds.
Definition nms.hpp:17

References pcod_common::ApplyRotatedNms(), pcod_common::BoundingBox::center, pcod_common::BoundingBox::classification, pcod_common::BoundingBox::existence_probability, pcod_common::NmsConfig::internal_score_threshold, pcod_common::NmsConfig::iou_threshold, pcod_common::kManifestSchemaVersion, pcod_common::BoundingBox::length, pcod_common::LoadModelManifest(), pcod_common::NmsConfig::max_detections, pcod_common::NmsConfig::score_thresholds, and pcod_common::BoundingBox::width.