diff --git a/SDK/CPP/public/CMakeLists.txt b/SDK/CPP/public/CMakeLists.txt index 51c27627169c6c5a3e99e4f195538710e9c61ae3..1128913e54a4350b1863f62849cca251e3aa6ed3 100644 --- a/SDK/CPP/public/CMakeLists.txt +++ b/SDK/CPP/public/CMakeLists.txt @@ -30,14 +30,15 @@ else() endif() set(VOLTU_SRCS - voltu.cpp + src/voltu.cpp + src/types/intrinsics.cpp ) set(OPTIONAL_DEPENDENCIES) if (WITH_OPENCV) list(APPEND OPTIONAL_DEPENDENCIES ${OpenCV_LIBS}) - list(APPEND VOLTU_SRCS voltu_cv.cpp) + list(APPEND VOLTU_SRCS src/voltu_cv.cpp) set(CMAKE_REQUIRED_INCLUDES ${OpenCV_INCLUDE_DIRS}) endif() diff --git a/SDK/CPP/public/include/voltu/types/intrinsics.hpp b/SDK/CPP/public/include/voltu/types/intrinsics.hpp index cc4ee4caa188064a72ae0aad8a631ebfd4227890..8571c9ea1321c4661003cd58058c2a540fb2ea7f 100644 --- a/SDK/CPP/public/include/voltu/types/intrinsics.hpp +++ b/SDK/CPP/public/include/voltu/types/intrinsics.hpp @@ -6,9 +6,14 @@ #pragma once +#include "../defines.hpp" + +#include <Eigen/Eigen> + namespace voltu { +/** Intrinsic camera paramters */ struct Intrinsics { unsigned int width; @@ -17,12 +22,20 @@ struct Intrinsics float principle_y; float focal_x; float focal_y; + + /** Projection matrix */ + PY_API Eigen::Matrix3d matrix(); }; +/** Stereo camera intrinsic parameters. + * + * Baseline can be used to estimate depth accuracy for known depth. Assuming 1px + * disparity accuracy, accuracy of given depth z, the accuracy of depth is + * z^2/(f*T), where f is focal length and T baseline. + */ struct StereoIntrinsics : public Intrinsics { float baseline; - float doffs; float min_depth; float max_depth; }; diff --git a/SDK/CPP/public/python/README.md b/SDK/CPP/public/python/README.md index 076ec86e3fca27d3778ba90522dc39cead275fba..1b8dcfdddfc85fce9c19e55476d7e61b7245d609 100644 --- a/SDK/CPP/public/python/README.md +++ b/SDK/CPP/public/python/README.md @@ -39,7 +39,7 @@ build. Several (empty) macros are used in headers to annoate Python API details. ## Not supported (yet) in automatic binding generation: * Nested classes * Static members - * Constructors + * Constructors for non-POD structs. * Automatic documentation (Doxygen) * Generator does not verify that shared_ptr<> is used consistently/correctly * Member variables of singleton classes diff --git a/SDK/CPP/public/python/gen.py b/SDK/CPP/public/python/gen.py index c284c737e5077419463bb498cf7a82bc25cb303f..7bb7643ca62094a2a66ea8a5b1a191196df4ef6b 100755 --- a/SDK/CPP/public/python/gen.py +++ b/SDK/CPP/public/python/gen.py @@ -50,12 +50,47 @@ def print_err(msg, loc=None): msg += " " + get_loc_msg(loc) print("gen.py error: %s" % msg, file=sys.stderr) +# Helpers ====================================================================== + +def is_singleton_class(ccls): + # covers both cases PY_SINGLETON and PY_SINGLETON_OBJECT + return "PY_SINGLETON" in ccls["debug"] + +def is_member_of_singleton_class(func): + if func["parent"] is None: + return False + + return is_singleton_class(func["parent"]) + def include_in_api(data): return "PY_API" in data["debug"] def no_shared_ptr_cls(clsdata): return (clsdata["declaration_method"] == "struct") or ("PY_NO_SHARED_PTR" in clsdata["debug"]) +def rv_bind_lifetime_to_parent(func): + if "PY_RV_LIFETIME_PARENT" in func["debug"]: + if func["parent"] is None: + print_err("PY_RV_LIFETIME_PARENT can not be used for function or singleton", func) + sys.exit(2) # generated code most likely wrong and unsafe (crash/UB) + return False + + if is_member_of_singleton_class(func): + print_warn(("Return value of %s lifetime bound to parent, but " + + "parent is singleton of %s. This is possibly an " + + "error (lifetime of the return value is the lifetime " + + "of the program).") % + (func["name"], func["parent"]["name"])) + + return True + + return False + +def is_struct(clsdata): + return clsdata["declaration_method"] == "struct" + +# Code generation ============================================================== + def create_enum_bindigs(enum, parent=[], pybind_handle="", export_values=False): name_full = parent + [enum["name"]] name_py = enum["name"] @@ -76,6 +111,35 @@ def create_enum_bindigs(enum, parent=[], pybind_handle="", export_values=False): return "\n\t".join(cpp) +def create_struct_ctor(struct): + types = [] + args = [] + ctors = ["def(py::init<>())"] + + for item in struct["properties"]["public"]: + types.append(item["type"]) + arg = "py::arg(\"%s\")" % item["name"] + has_defaults = False + + if "default" in item: + has_defaults = True + arg += "=%s" % item["default"] + + args.append(arg) + + if len(struct["inherits"]) > 0 or has_defaults: + # NOTE: Structs which inherit another struct require managing state, + # as base class possibly defines its own members from base + # class(es). Move everything to class? + # NOTE: Non-POD structs which do not inherit base class but set default + # values can be supported by generating lambda which assigns + # the values. + print_warn("Only aggregate initialization supported for structs") + return ctors + + ctors.append("def(py::init<%s>(), %s)" % (", ".join(types), ", ".join(args))) + return ctors + def process_func_args(func): args = [] @@ -133,11 +197,7 @@ def create_function_bindings(func, parent=[], pybind_handle=None, bind_to_single args += process_func_args(func) - if "PY_RV_LIFETIME_PARENT" in func["debug"]: - if func["parent"] is None or bind_to_singleton is not None: - print_err("PY_RV_LIFETIME_PARENT used for function or singleton", func) - raise ValueError() - + if rv_bind_lifetime_to_parent(func): args.append("py::return_value_policy::reference_internal") cpp = "def({0})".format(", ".join(args)) @@ -160,8 +220,8 @@ def create_class_bindings(clsdata, parent=[], pybind_handle=""): cpp.append(cls_cpp.format(handle=pybind_handle, name=clsdata_name)) - if clsdata["declaration_method"] == "struct": - cpp.append(".def(py::init<>())") + if is_struct(clsdata): + cpp += ["." + ctor for ctor in create_struct_ctor(clsdata)] for method in clsdata["methods"]["public"]: if include_in_api(method): @@ -172,6 +232,7 @@ def create_class_bindings(clsdata, parent=[], pybind_handle=""): field_cpp = ".def_property_readonly(\"{name}\", &{cpp_name})" else: field_cpp = ".def_readwrite(\"{name}\", &{cpp_name})" + field_name = field["name"] field_name_cpp = "::".join(full_name + [field_name]) cpp.append(field_cpp.format(name=field_name, cpp_name=field_name_cpp)) diff --git a/SDK/CPP/public/src/types/intrinsics.cpp b/SDK/CPP/public/src/types/intrinsics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63cfbc5908b6b5a80bee72619a158f781a2f1c57 --- /dev/null +++ b/SDK/CPP/public/src/types/intrinsics.cpp @@ -0,0 +1,10 @@ +#include <voltu/types/intrinsics.hpp> +#include <Eigen/Eigen> + +Eigen::Matrix3d voltu::Intrinsics::matrix() { + Eigen::Matrix3d K; + K << focal_x, 0.0, -principle_x, + 0.0, focal_y, -principle_y, + 0.0, 0.0, 1.0; + return K; +} diff --git a/SDK/CPP/public/voltu.cpp b/SDK/CPP/public/src/voltu.cpp similarity index 100% rename from SDK/CPP/public/voltu.cpp rename to SDK/CPP/public/src/voltu.cpp diff --git a/SDK/CPP/public/voltu_cv.cpp b/SDK/CPP/public/src/voltu_cv.cpp similarity index 100% rename from SDK/CPP/public/voltu_cv.cpp rename to SDK/CPP/public/src/voltu_cv.cpp diff --git a/SDK/CPP/tests/CMakeLists.txt b/SDK/CPP/tests/CMakeLists.txt index 489f6e3380a20d296427308c61f667d5f5c35936..d7f25424e60c7d288b9e7076ef1a107648bd0251 100644 --- a/SDK/CPP/tests/CMakeLists.txt +++ b/SDK/CPP/tests/CMakeLists.txt @@ -13,3 +13,4 @@ function(add_python_test TEST_NAME TEST_SCRIPT) endfunction() add_python_test(Py_TestLoad test_load.py) +add_python_test(Py_TestIntrinsics test_intrinsics.py) diff --git a/SDK/CPP/tests/test_intrinsics.py b/SDK/CPP/tests/test_intrinsics.py new file mode 100644 index 0000000000000000000000000000000000000000..52d3fdab74dbb965e88c9b103be6ceb464144adb --- /dev/null +++ b/SDK/CPP/tests/test_intrinsics.py @@ -0,0 +1,32 @@ +import unittest +import voltu +import numpy as np + +class Intrinsics(unittest.TestCase): + + def test_default_ctor(self): + intr = voltu.Intrinsics() + self.assertIsNotNone(intr) + + def test_create(self): + fx = 2.0 + fy = 3.0 + cx = 4.0 + cy = 5.0 + + intr = voltu.Intrinsics( + width = 6, + height = 7, + focal_x = fx, + focal_y = fy, + principle_x = -cx, + principle_y = -cy + ) + + K = np.array([ + [fx , 0.0, cx], + [0.0, fy, cy], + [0.0, 0.0, 1.0], + ]) + + self.assertTrue(np.array_equal(intr.matrix(), K))