Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/core/schema/fbs/hand.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
include "pose.fbs";

namespace core;

// Describe a HandJoint pose.
struct HandJointPose {
// The concrete pose data
pose: Pose;

// Whether the hand pose data is valid
is_valid: bool;

// The radius of each joint.
// See: https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrHandJointLocationEXT.html
radius: float32;
}

struct HandJoints {
poses: [HandJointPose:26];
}

// Describe a hand pose.
table HandPose {
// Vector of HandJointPose.
// For OpenXR, this should be 26 according to XR_HAND_JOINT_COUNT_EXT
joints: HandJoints (id: 0);

// Whether the hand pose data is active
is_active: bool (id: 1);

// The timestamp of the pose data in the XrTime format (i.e. int64)
// See: https://github.com/KhronosGroup/OpenXR-SDK/blob/release-1.0.0/include/openxr/openxr.h#L113
timestamp: int64 (id: 2);
}

root_type HandPose;
18 changes: 18 additions & 0 deletions src/core/schema/fbs/head.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include "pose.fbs";

namespace core;

// Describe a Head pose.
table HeadPose {
// The concrete pose data
pose: Pose (id: 0);

// Whether the head pose data is valid
is_valid: bool (id: 1);

// The timestamp of the pose data in the XrTime format (i.e. int64)
// See: https://github.com/KhronosGroup/OpenXR-SDK/blob/release-1.0.0/include/openxr/openxr.h#L113
timestamp: int64 (id: 2);
}

root_type HeadPose;
7 changes: 7 additions & 0 deletions src/core/schema/fbs/point.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace core;

struct Point {
x: float;
y: float;
z: float;
}
11 changes: 5 additions & 6 deletions src/core/schema/fbs/pose.fbs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
include "tensor.fbs";
include "point.fbs";
include "quaternion.fbs";

namespace core;

// Describes a pose.
table Pose {
struct Pose {
// Position of the pose (x, y, z) in meters.
position: Tensor (id: 0);
position: Point (id: 0);

// Orientation of the pose (w, x, y, z).
orientation: Tensor (id: 1);
orientation: Quaternion (id: 1);
}

root_type Pose;
8 changes: 8 additions & 0 deletions src/core/schema/fbs/quaternion.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace core;

struct Quaternion {
x: float;
y: float;
z: float;
w: float;
}
12 changes: 6 additions & 6 deletions src/core/schema/fbs/tensor.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ struct DLDevice {

// Defines a Tensor message based on DLPack, using holoscan::Tensor as the native type.
table Tensor {
data: [ubyte];
shape: [int64];
dtype: DLDataType (native_inline);
device: DLDevice (native_inline);
ndim: uint32;
strides: [int64];
data: [ubyte] (id: 0);
shape: [int64] (id: 1);
dtype: DLDataType (native_inline, id: 2);
device: DLDevice (native_inline, id: 3);
ndim: uint32 (id: 4);
strides: [int64] (id: 5);
}

root_type Tensor;
Expand Down
4 changes: 4 additions & 0 deletions src/core/schema/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ endif()

# Schema module (tensor and pose types).
pybind11_add_module(schema_py
hand_bindings.h
head_bindings.h
pose_bindings.h
schema_module.cpp
tensor_bindings.h
)

target_link_libraries(schema_py
Expand Down
105 changes: 105 additions & 0 deletions src/core/schema/python/hand_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Python bindings for the HandPose FlatBuffer schema.
// Includes HandJointPose struct, HandJoints struct, and HandPoseT table.

#pragma once

#include <memory>
#include <array>
#include <vector>
#include <cstring>

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include <schema/hand_generated.h>

namespace py = pybind11;

namespace core {

inline void bind_hand(py::module& m) {
// Bind HandJointPose struct (pose, is_valid, radius).
py::class_<HandJointPose>(m, "HandJointPose")
.def(py::init<>())
.def(py::init<const Pose&, bool, float>(),
py::arg("pose"),
py::arg("is_valid") = false,
py::arg("radius") = 0.0f)
.def_property_readonly("pose", &HandJointPose::pose,
py::return_value_policy::reference_internal)
.def_property_readonly("is_valid", &HandJointPose::is_valid)
.def_property_readonly("radius", &HandJointPose::radius)
.def("__repr__", [](const HandJointPose& self) {
return "HandJointPose(pose=Pose(position=Point(x=" +
std::to_string(self.pose().position().x()) +
", y=" + std::to_string(self.pose().position().y()) +
", z=" + std::to_string(self.pose().position().z()) +
"), orientation=Quaternion(x=" + std::to_string(self.pose().orientation().x()) +
", y=" + std::to_string(self.pose().orientation().y()) +
", z=" + std::to_string(self.pose().orientation().z()) +
", w=" + std::to_string(self.pose().orientation().w()) +
")), is_valid=" + (self.is_valid() ? "True" : "False") +
", radius=" + std::to_string(self.radius()) + ")";
});

// Bind HandJoints struct (fixed-size array of 26 HandJointPose).
py::class_<HandJoints>(m, "HandJoints")
.def(py::init<>())
.def("poses", [](const HandJoints& self, size_t index) -> const HandJointPose* {
if (index >= 26) {
throw py::index_error("HandJoints index out of range (must be 0-25)");
}
return (*self.poses())[index];
}, py::arg("index"), py::return_value_policy::reference_internal,
"Get the HandJointPose at the specified index (0-25).")
.def("__len__", [](const HandJoints&) { return 26; })
.def("__getitem__", [](const HandJoints& self, size_t index) -> const HandJointPose* {
if (index >= 26) {
throw py::index_error("HandJoints index out of range (must be 0-25)");
}
return (*self.poses())[index];
}, py::return_value_policy::reference_internal)
.def("__repr__", [](const HandJoints&) {
return "HandJoints(poses=[...26 HandJointPose entries...])";
});

// Bind HandPoseT class (FlatBuffers object API for tables).
py::class_<HandPoseT, std::unique_ptr<HandPoseT>>(m, "HandPoseT")
.def(py::init<>())
.def_property_readonly(
"joints",
[](const HandPoseT& self) -> const HandJoints* {
return self.joints.get();
},
py::return_value_policy::reference_internal)
.def_readwrite("is_active", &HandPoseT::is_active)
.def_readwrite("timestamp", &HandPoseT::timestamp)
// Convenience method to set joints from a list of HandJointPose.
.def("set_joints", [](HandPoseT& self, const std::vector<HandJointPose>& joint_poses) {
if (joint_poses.size() != 26) {
throw std::runtime_error("HandPoseT requires exactly 26 joint poses");
}
self.joints = std::make_unique<HandJoints>();
// Copy each joint pose into the fixed-size array using memcpy.
// HandJoints is a POD struct with a fixed array, so this is safe.
auto* mutable_array = const_cast<flatbuffers::Array<HandJointPose, 26>*>(self.joints->poses());
for (size_t i = 0; i < 26; ++i) {
mutable_array->Mutate(i, joint_poses[i]);
}
}, py::arg("joint_poses"),
"Set the joints from a list of 26 HandJointPose entries.")
.def("__repr__", [](const HandPoseT& self) {
std::string joints_str = "None";
if (self.joints) {
joints_str = "HandJoints(poses=[...26 entries...])";
}
return "HandPoseT(joints=" + joints_str +
", is_active=" + (self.is_active ? "True" : "False") +
", timestamp=" + std::to_string(self.timestamp) + ")";
});
}

} // namespace core
53 changes: 53 additions & 0 deletions src/core/schema/python/head_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add new headers to cmakelists for easier access/search from IDE

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean list the header file explicitly in the CMakeLists.txt?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, along with cpp

// SPDX-License-Identifier: Apache-2.0

// Python bindings for the HeadPose FlatBuffer schema.
// HeadPoseT is a table type (mutable object-API) with pose, is_valid, and timestamp fields.

#pragma once

#include <memory>

#include "pybind11/pybind11.h"

#include <schema/head_generated.h>

namespace py = pybind11;

namespace core {

inline void bind_head(py::module& m) {
// Bind HeadPoseT class (FlatBuffers object API for tables).
py::class_<HeadPoseT, std::unique_ptr<HeadPoseT>>(m, "HeadPoseT")
.def(py::init<>())
.def_property_readonly(
"pose",
[](const HeadPoseT& self) -> const Pose* {
return self.pose.get();
},
py::return_value_policy::reference_internal)
.def_readwrite("is_valid", &HeadPoseT::is_valid)
.def_readwrite("timestamp", &HeadPoseT::timestamp)
// Convenience method to set pose from components.
.def("set_pose", [](HeadPoseT& self, const Point& position, const Quaternion& orientation) {
self.pose = std::make_unique<Pose>(position, orientation);
}, py::arg("position"), py::arg("orientation"),
"Set the pose from position and orientation components.")
.def("__repr__", [](const HeadPoseT& self) {
std::string pose_str = "None";
if (self.pose) {
pose_str = "Pose(position=Point(x=" + std::to_string(self.pose->position().x()) +
", y=" + std::to_string(self.pose->position().y()) +
", z=" + std::to_string(self.pose->position().z()) +
"), orientation=Quaternion(x=" + std::to_string(self.pose->orientation().x()) +
", y=" + std::to_string(self.pose->orientation().y()) +
", z=" + std::to_string(self.pose->orientation().z()) +
", w=" + std::to_string(self.pose->orientation().w()) + "))";
}
return "HeadPoseT(pose=" + pose_str +
", is_valid=" + (self.is_valid ? "True" : "False") +
", timestamp=" + std::to_string(self.timestamp) + ")";
});
}

} // namespace core
Loading