#include #include #include #include #include #include #include #include #include #include #include "interface.hpp" namespace nb = nanobind; using namespace nb::literals; namespace { using PoseArray2D = nb::ndarray, nb::c_contig>; using CountArray = nb::ndarray, nb::c_contig>; using RoomArray = nb::ndarray, nb::c_contig>; using PoseArray3D = nb::ndarray, nb::c_contig>; PoseBatch2D pose_batch_from_numpy(const PoseArray2D &poses_2d, const CountArray &person_counts) { if (poses_2d.shape(0) != person_counts.shape(0)) { throw std::invalid_argument("poses_2d and person_counts must have the same number of views."); } PoseBatch2D batch; batch.num_views = static_cast(poses_2d.shape(0)); batch.max_persons = static_cast(poses_2d.shape(1)); batch.num_joints = static_cast(poses_2d.shape(2)); batch.person_counts.assign(person_counts.data(), person_counts.data() + batch.num_views); for (size_t i = 0; i < batch.person_counts.size(); ++i) { if (batch.person_counts[i] > batch.max_persons) { throw std::invalid_argument("person_counts entries must not exceed the padded person dimension."); } } const size_t total_size = batch.num_views * batch.max_persons * batch.num_joints * 3; batch.data.resize(total_size); std::memcpy(batch.data.data(), poses_2d.data(), total_size * sizeof(float)); return batch; } std::array, 2> roomparams_from_numpy(const RoomArray &roomparams) { std::array, 2> result {}; for (size_t i = 0; i < 2; ++i) { for (size_t j = 0; j < 3; ++j) { result[i][j] = roomparams(i, j); } } return result; } PoseArray3D pose_batch_to_numpy(PoseBatch3D batch) { auto *storage = new std::vector(std::move(batch.data)); nb::capsule owner(storage, [](void *value) noexcept { delete static_cast *>(value); }); const size_t shape[3] = {batch.num_persons, batch.num_joints, 4}; return PoseArray3D(storage->data(), 3, shape, owner); } } // namespace NB_MODULE(_core, m) { nb::class_(m, "Camera") .def(nb::init<>()) .def_rw("name", &Camera::name) .def_rw("K", &Camera::K) .def_rw("DC", &Camera::DC) .def_rw("R", &Camera::R) .def_rw("T", &Camera::T) .def_rw("width", &Camera::width) .def_rw("height", &Camera::height) .def_rw("type", &Camera::type) .def("__repr__", [](const Camera &camera) { return camera.to_string(); }); nb::class_(m, "Triangulator") .def(nb::init(), "min_match_score"_a = 0.95f, "min_group_size"_a = 1) .def( "triangulate_poses", [](Triangulator &self, const PoseArray2D &poses_2d, const CountArray &person_counts, const std::vector &cameras, const RoomArray &roomparams, const std::vector &joint_names) { PoseBatch2D pose_batch = pose_batch_from_numpy(poses_2d, person_counts); auto room = roomparams_from_numpy(roomparams); PoseBatch3D poses_3d = self.triangulate_poses(pose_batch, cameras, room, joint_names); return pose_batch_to_numpy(std::move(poses_3d)); }, "poses_2d"_a, "person_counts"_a, "cameras"_a, "roomparams"_a, "joint_names"_a) .def("reset", &Triangulator::reset) .def("print_stats", &Triangulator::print_stats); }