diff --git a/.gitignore b/.gitignore index 2f67ce1..d454704 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,8 @@ _output/ /mynteye/ /mynteye.bag /dataset*/ + +# wrappers + +/wrappers/python/third_party/numpy-opencv-converter/ +/wrappers/python/third_party/pyboostcvconverter/ diff --git a/CommonDefs.mk b/CommonDefs.mk index cf43145..2b0dc6d 100644 --- a/CommonDefs.mk +++ b/CommonDefs.mk @@ -163,7 +163,6 @@ ifneq ($(MAKE),) endif CMAKE_OPTIONS := -CMAKE_OPTIONS += -DCMAKE_INSTALL_PREFIX=$(shell pwd)/_install # CMAKE_OPTIONS += -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON CMAKE_OPTIONS_AFTER := diff --git a/Makefile b/Makefile index 3de211c..5c65f55 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,9 @@ # limitations under the License. include CommonDefs.mk +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +MKFILE_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) + .DEFAULT_GOAL := help help: @@ -77,7 +80,7 @@ init: submodules build: third_party @$(call echo,Make $@) - @$(call cmake_build,./_build) + @$(call cmake_build,./_build,..,-DCMAKE_INSTALL_PREFIX=$(MKFILE_DIR)/_install) .PHONY: build @@ -161,11 +164,30 @@ cleanros: # python +PBCVT_DIR := wrappers/python/third_party/pyboostcvconverter + +$(PBCVT_DIR): + @git clone https://github.com/Algomorph/pyboostcvconverter.git $@ + +pbcvt: $(PBCVT_DIR) + @$(call cmake_build,$(PBCVT_DIR)/_build,.., \ + -DCMAKE_INSTALL_PREFIX=$(MKFILE_DIR)/wrappers/python/_install \ + -DPYTHON_DESIRED_VERSION=2.X) + @cd $(PBCVT_DIR)/_build; make install + +.PHONY: pbcvt + +NPCV_DIR := wrappers/python/third_party/numpy-opencv-converter + +$(NPCV_DIR): + @git clone https://github.com/GarrickLin/numpy-opencv-converter.git $@ + py: python -python: install +python: install $(NPCV_DIR) @$(call echo,Make $@) @$(call cmake_build,./wrappers/python/_build) + @cd ./wrappers/python/_build; make install .PHONY: py python @@ -173,6 +195,8 @@ cleanpy: @$(call echo,Make $@) @$(call rm,./wrappers/python/_build/) @$(call rm,./wrappers/python/_output/) + @$(call rm,./wrappers/python/_install/) + @$(call rm,./$(PBCVT_DIR)/_build/) .PHONY: cleanpy @@ -206,6 +230,8 @@ cleanall: clean @$(call rm,./test/gtest/_build/) @$(call rm,./third_party/glog/_build/) @$(FIND) . -type f -name ".DS_Store" -print0 | xargs -0 rm -f + @$(call rm,./$(PBCVT_DIR)/) + @$(call rm,./$(NPCV_DIR)/) .PHONY: clean cleanlog cleanall @@ -213,6 +239,8 @@ cleanall: clean host: @$(call echo,Make $@) + @echo MKFILE_PATH: $(MKFILE_PATH) + @echo MKFILE_DIR: $(MKFILE_DIR) @echo HOST_OS: $(HOST_OS) @echo HOST_ARCH: $(HOST_ARCH) @echo HOST_NAME: $(HOST_NAME) diff --git a/src/api/api.h b/src/api/api.h index 6b136f5..39eec99 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -41,6 +41,14 @@ struct MYNTEYE_API StreamData { std::shared_ptr img; /** Frame. */ cv::Mat frame; + + bool operator==(const StreamData &other) const { + if (img && other.img) { + return img->frame_id == other.img->frame_id && + img->timestamp == other.img->timestamp; + } + return false; + } }; /** @@ -50,6 +58,14 @@ struct MYNTEYE_API StreamData { struct MYNTEYE_API MotionData { /** ImuData. */ std::shared_ptr imu; + + bool operator==(const MotionData &other) const { + if (imu && other.imu) { + return imu->frame_id == other.imu->frame_id && + imu->timestamp == other.imu->timestamp; + } + return false; + } }; } // namespace api @@ -65,7 +81,7 @@ class MYNTEYE_API API { using motion_callback_t = std::function; explicit API(std::shared_ptr device); - /*virtual*/ ~API(); + virtual ~API(); /** * Create the API instance. diff --git a/wrappers/python/CMakeLists.txt b/wrappers/python/CMakeLists.txt index 678d8f9..4cecbff 100644 --- a/wrappers/python/CMakeLists.txt +++ b/wrappers/python/CMakeLists.txt @@ -87,7 +87,7 @@ message(STATUS "Found mynteye: ${mynteye_VERSION}") if(CMAKE_VERSION VERSION_LESS "3.10" OR CMAKE_VERSION VERSION_EQUAL "3.10") find_package(Boost ${BOOST_FIND_VERSION} REQUIRED - COMPONENTS python${PYTHON_BOOST_CODE} + COMPONENTS python${PYTHON_BOOST_CODE} # numpy${PYTHON_BOOST_CODE} ) else() find_package(Boost ${BOOST_FIND_VERSION} REQUIRED COMPONENTS @@ -103,6 +103,8 @@ message(STATUS "Found PythonLibs: ${PYTHONLIBS_VERSION_STRING}") message(STATUS " PYTHON_INCLUDE_DIRS: ${PYTHON_INCLUDE_DIRS}") message(STATUS " PYTHON_LIBRARIES: ${PYTHON_LIBRARIES}") +include(${PRO_DIR}/cmake/DetectOpenCV.cmake) + #LIST(APPEND CMAKE_MODULE_PATH ${PRO_DIR}/cmake) # targets @@ -115,6 +117,14 @@ set_outdir( "${OUT_DIR}/bin" ) +set(DEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/_install") +message(STATUS "DEST_DIR: ${DEST_DIR}") + +set(CMAKE_SKIP_BUILD_RPATH FALSE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +set(CMAKE_INSTALL_RPATH "${DEST_DIR}/lib") +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + if(OS_WIN) add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS}) endif() @@ -145,4 +155,42 @@ endmacro() ## mynteye_py -add_library_py(mynteye_py SRCS src/mynteye_py.cc LINK_LIBS mynteye) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/array +) + +#set(PBCVT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/pyboostcvconverter") +# +#include_directories( +# ${PBCVT_DIR}/include +#) +# +#set(PBCVT_SRCS +# ${PBCVT_DIR}/src/pyboost_cv2_converter.cpp +# ${PBCVT_DIR}/src/pyboost_cv3_converter.cpp +# ${PBCVT_DIR}/include/pyboostcvconverter/pyboostcvconverter.hpp +#) + +set(NPCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/numpy-opencv-converter") + +include_directories( + ${NPCV_DIR} +) + +set(NPCV_SRCS + ${NPCV_DIR}/np_opencv_converter.cpp + ${NPCV_DIR}/utils/conversion.cpp +) + +add_library_py(mynteye_py + SRCS src/mynteye_py.cc ${NPCV_SRCS} + LINK_LIBS mynteye ${OpenCV_LIBS} +) + +# insall + +install(TARGETS mynteye_py + ARCHIVE DESTINATION "${DEST_DIR}/lib" + LIBRARY DESTINATION "${DEST_DIR}/lib" + RUNTIME DESTINATION "${DEST_DIR}/bin" +) diff --git a/wrappers/python/samples/mynteye.py b/wrappers/python/samples/mynteye.py index 593720b..1e70f75 100644 --- a/wrappers/python/samples/mynteye.py +++ b/wrappers/python/samples/mynteye.py @@ -22,15 +22,177 @@ import os import sys PY_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.append(os.path.join(PY_DIR, '_output/lib')) +LIB_DIR = os.path.join(PY_DIR, '_install/lib') +for root, dirs, files in os.walk(LIB_DIR): + if files: + sys.path.append(root) -import mynteye_py # pylint: disable=import-error,wrong-import-position -# glog_init = mynteye_py.glog_init.create(sys.argv) +# pylint: disable=import-error,wrong-import-position +import mynteye_py as mynteye +# glog_init = mynteye.glog_init.create(sys.argv) def main(): - # api = mynteye_py.api.create() # should glog_init - api = mynteye_py.api.create(sys.argv) # pylint: disable=unused-variable + # api = mynteye.API.create() # should glog_init + api = mynteye.API.create(sys.argv) + if not api: + sys.exit(1) + + # model + print('model: {}'.format(api.model)) + + # supports + supports_types = ( + mynteye.Stream, + mynteye.Capabilities, + mynteye.Option, + mynteye.AddOns) + for x in supports_types: + print('\nsupports({})'.format(x.__name__)) + for k, v in x.names.iteritems(): + print(' supports({}): {}'.format(k, api.supports(v))) + + # get_stream_requests + for k, v in mynteye.Capabilities.names.iteritems(): + if v != mynteye.STEREO: + continue + if not api.supports(v): + continue + print('\nget_stream_requests({})'.format(k)) + for req in api.get_stream_requests(v): + print(' {}'.format(req)) + + # config_stream_request + # print('\nconfig_stream_request({},?)'.format(mynteye.STEREO)) + # api.config_stream_request(mynteye.STEREO, req) + # print(' {}'.format(req)) + + # get_info + print() + for k, v in mynteye.Info.names.iteritems(): + print('get_info({}): {}'.format(k, api.get_info(v))) + + # get_intrinsics + print('\nget_intrinsics(LEFT)\n{}'.format(api.get_intrinsics(mynteye.LEFT))) + print('\nget_intrinsics(RIGHT)\n{}'.format(api.get_intrinsics(mynteye.RIGHT))) + # get_extrinsics + print('\nget_extrinsics(LEFT, RIGHT)\n{}'.format( + api.get_extrinsics(mynteye.LEFT, mynteye.RIGHT))) + # get_motion_intrinsics + print('\nget_motion_intrinsics()\n{}'.format(api.get_motion_intrinsics())) + # get_motion_extrinsics + print('\nget_motion_extrinsics(LEFT)\n{}'.format( + api.get_motion_extrinsics(mynteye.LEFT))) + + # api.log_option_infos() + + # get_option_info, get_option_value + print() + for k, v in mynteye.Option.names.iteritems(): + if v == mynteye.ZERO_DRIFT_CALIBRATION or v == mynteye.ERASE_CHIP: + continue + if not api.supports(v): + continue + print('get_option_info({}): {}, cur: {}'.format( + k, api.get_option_info(v), api.get_option_value(v))) + + # set_option_value + + def set_rate(frame_rate=25, imu_frequency=500): # pylint: disable=unused-variable + # FRAME_RATE values: 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 + api.set_option_value(mynteye.FRAME_RATE, frame_rate) + # IMU_FREQUENCY values: 100, 200, 250, 333, 500 + api.set_option_value(mynteye.IMU_FREQUENCY, imu_frequency) + + def set_ir(intensity=80): # pylint: disable=unused-variable + # set infrared intensity value: range [0,48], default 160 + api.set_option_value(mynteye.IR_CONTROL, intensity) + + def set_auto_exposure( # pylint: disable=unused-variable + max_gain=48, # pylint: disable=bad-continuation + max_exposure_time=240, # pylint: disable=bad-continuation + desired_brightness=192): # pylint: disable=bad-continuation + # auto-exposure: 0 + api.set_option_value(mynteye.EXPOSURE_MODE, 0) + # max_gain: range [0,48], default 48 + api.set_option_value(mynteye.MAX_GAIN, max_gain) + # max_exposure_time: range [0,240], default 240 + api.set_option_value(mynteye.MAX_EXPOSURE_TIME, max_exposure_time) + # desired_brightness: range [0,255], default 192 + api.set_option_value(mynteye.DESIRED_BRIGHTNESS, desired_brightness) + + def set_manual_exposure(gain=24, brightness=120, contrast=127): # pylint: disable=unused-variable + # manual-exposure: 1 + api.set_option_value(mynteye.EXPOSURE_MODE, 1) + # gain: range [0,48], default 24 + api.set_option_value(mynteye.GAIN, gain) + # brightness/exposure_time: range [0,240], default 120 + api.set_option_value(mynteye.BRIGHTNESS, brightness) + # contrast/black_level_calibration: range [0,255], default 127 + api.set_option_value(mynteye.CONTRAST, contrast) + + # set_rate(25, 500); + # set_ir(80); + # set_auto_exposure(48, 240, 192); + # set_manual_exposure(24, 120, 127); + + # run_option_action + # api.run_option_action(mynteye.ZERO_DRIFT_CALIBRATION) + + ############################################################################## + # get stream and motion datas + + import cv2 + import numpy as np + from util.cv_painter import Gravity, draw_text + + # api.enable_stream_data(mynteye.DISPARITY_NORMALIZED); + + api.enable_motion_datas() + + api.start(mynteye.ALL) + + # cv2.namedWindow('frame') + # cv2.namedWindow('disparity') + + fps = 0. + while True: + t = cv2.getTickCount() + + api.wait_for_streams() + + left_data = api.get_stream_data(mynteye.LEFT) + right_data = api.get_stream_data(mynteye.RIGHT) + + motion_datas = api.get_motion_datas() + + img = np.hstack((left_data.frame, right_data.frame)) + draw_text(img, '{1}x{0}'.format(*img.shape), Gravity.TOP_LEFT) + draw_text(img, '{:.1f}'.format(fps), Gravity.TOP_RIGHT) + if motion_datas: + imu = motion_datas[0].imu + draw_text(img, + 'accel: {:+8f}, {:+8f}, {:+8f}'.format(*imu.accel), + Gravity.BOTTOM_LEFT) + draw_text(img, + 'gyro: {:+8f}, {:+8f}, {:+8f}'.format(*imu.gyro), + Gravity.BOTTOM_RIGHT) + cv2.imshow('frame', img) + + # disp_data = api.get_stream_data(mynteye.DISPARITY_NORMALIZED); + # if disp_data.frame is not None: + # cv2.imshow('disparity', disp_data.frame); + + t = cv2.getTickCount() - t + fps = cv2.getTickFrequency() / t + + key = cv2.waitKey(10) & 0xFF + if key == 27 or key == ord('q'): # esc, q + break + + api.stop(mynteye.ALL) + + cv2.destroyAllWindows() if __name__ == '__main__': diff --git a/wrappers/python/samples/util/__init__.py b/wrappers/python/samples/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wrappers/python/samples/util/cv_painter.py b/wrappers/python/samples/util/cv_painter.py new file mode 100644 index 0000000..b6150a4 --- /dev/null +++ b/wrappers/python/samples/util/cv_painter.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright 2018 Slightech Co., Ltd. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=missing-docstring + +import cv2 + + +FONT_FACE = cv2.FONT_HERSHEY_SIMPLEX +FONT_SCALE = 1 +FONT_COLOR = (255, 255, 255) +THICKNESS = 1 + + +class Gravity(object): # pylint: disable=no-init + TOP_LEFT = 1 + TOP_RIGHT = 2 + BOTTOM_LEFT = 3 + BOTTOM_RIGHT = 4 + + +def draw_text(img, text, gravity, margin=10, offset_x=0, offset_y=0): + h, w = img.shape[:2] # pylint: disable=invalid-name + + # getTextSize, result: ((width, height), baseline) + x, y = cv2.getTextSize(text, FONT_FACE, FONT_SCALE, THICKNESS)[0] + + org = { + Gravity.TOP_LEFT: (margin, margin + y), + Gravity.TOP_RIGHT: (w - margin - x, margin + y), + Gravity.BOTTOM_LEFT: (margin, h - margin), + Gravity.BOTTOM_RIGHT: (w - margin - x, h - margin), + }.get(gravity, (margin, margin + y)) + + org = (org[0] + offset_x, org[1] + offset_y) + + # putText(img, text, org, fontFace, fontScale, color, thickness, lineType) + cv2.putText(img, text, org, FONT_FACE, FONT_SCALE, + FONT_COLOR, THICKNESS, cv2.LINE_AA) diff --git a/wrappers/python/src/mynteye_py.cc b/wrappers/python/src/mynteye_py.cc index d966d49..8107f55 100644 --- a/wrappers/python/src/mynteye_py.cc +++ b/wrappers/python/src/mynteye_py.cc @@ -11,34 +11,52 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include + +// #include +#include +#include +#include +#include +#include #include +#include + +#include #include #include "mynteye/api.h" #include "mynteye/glog_init.h" +#include "mynteye/utils.h" + +#include "array_indexing_suite.hpp" +#include "array_ref.hpp" + +// #define PY_ARRAY_UNIQUE_SYMBOL pbcvt_ARRAY_API +// #include "pyboostcvconverter/pyboostcvconverter.hpp" +#include "np_opencv_converter.hpp" + +#define ENUM_EXPORT_VALUES + +namespace bp = boost::python; namespace { template inline void std_vector_assign( - std::vector &l, const boost::python::object &o) { // NOLINT - l.assign( - boost::python::stl_input_iterator(o), - boost::python::stl_input_iterator()); + std::vector &l, const bp::object &o) { // NOLINT + l.assign(bp::stl_input_iterator(o), bp::stl_input_iterator()); } template -inline std::vector py_list_to_std_vector(const boost::python::object &o) { +inline std::vector py_list_to_std_vector(const bp::object &o) { return std::vector( - boost::python::stl_input_iterator(o), - boost::python::stl_input_iterator()); + bp::stl_input_iterator(o), bp::stl_input_iterator()); } template -inline boost::python::list std_vector_to_py_list(const std::vector &v) { - boost::python::list l; +inline bp::list std_vector_to_py_list(const std::vector &v) { + bp::list l; for (auto &&val : v) { l.append(val); } @@ -64,29 +82,100 @@ void del_cstrings(char **cstrings, std::size_t n) { } // namespace -using namespace boost::python; // NOLINT +MYNTEYE_BEGIN_NAMESPACE -MYNTEYE_USE_NAMESPACE +namespace python { + +// api wrapper + +struct MYNTEYE_API StreamData { + /** ImgData. */ + ImgData img; + /** Frame. */ + PyObject *frame; + + bool operator==(const StreamData &other) const { + return img.frame_id == other.img.frame_id && + img.timestamp == other.img.timestamp; + } +}; + +struct MYNTEYE_API MotionData { + /** ImuData. */ + ImuData imu; + + bool operator==(const MotionData &other) const { + return imu.frame_id == other.imu.frame_id && + imu.timestamp == other.imu.timestamp; + } +}; + +class MYNTEYE_API APIWrap : public API { + public: + explicit APIWrap(std::shared_ptr device) : API(device) {} + ~APIWrap() {} + + static std::shared_ptr Create() { + auto &&device = device::select(); + if (!device) + return nullptr; + return std::make_shared(device); + } + + static std::shared_ptr Create(int argc, char *argv[]) { + static glog_init _(argc, argv); + auto &&device = device::select(); + if (!device) + return nullptr; + return std::make_shared(device); + } + + python::StreamData GetStreamData(const Stream &stream) { + auto &&data = API::GetStreamData(stream); + // return {*data.img, pbcvt::fromMatToNDArray(data.frame)}; + return {*data.img, + fs::python::Mat_to_PyObject::convert(data.frame)}; + } + + std::vector GetStreamDatas(const Stream &stream) { + std::vector datas; + for (auto &&data : API::GetStreamDatas(stream)) { + // datas.push_back({*data.img, pbcvt::fromMatToNDArray(data.frame)}); + datas.push_back( + {*data.img, + fs::python::Mat_to_PyObject::convert(data.frame)}); + } + return datas; + } + + std::vector GetMotionDatas() { + std::vector datas; + for (auto &&data : API::GetMotionDatas()) { + datas.push_back({*data.imu}); + } + return datas; + } +}; // api create static methods -std::shared_ptr (*api_create_1)() = &API::Create; +std::shared_ptr (*api_create_1)() = &APIWrap::Create; -std::shared_ptr api_create_2(list argv) { +std::shared_ptr api_create_2(bp::list argv) { auto &&args = py_list_to_std_vector(argv); auto &&n = args.size(); if (n == 0) { - return API::Create(); + return APIWrap::Create(); } char **cstrings = new_cstrings(args, n); - auto &&api = API::Create(args.size(), cstrings); + auto &&api = APIWrap::Create(args.size(), cstrings); del_cstrings(cstrings, n); return api; } // glog_init create static methods -std::shared_ptr glog_init_create(list argv) { +std::shared_ptr glog_init_create(bp::list argv) { auto &&args = py_list_to_std_vector(argv); auto &&n = args.size(); assert(n > 0); @@ -99,16 +188,278 @@ std::shared_ptr glog_init_create(list argv) { // BOOST_PYTHON_MODULE BOOST_PYTHON_MODULE(mynteye_py) { - class_("api", no_init) + /* + Py_Initialize(); + import_array(); + bp::to_python_converter(); + pbcvt::matFromNDArrayBoostConverter(); + */ + fs::python::init_and_export_converters(); + py::scope scope = py::scope(); + + bp::class_>("DoubleArray") + .def(array_indexing_suite>()); + + bp::class_>>("Double2DArray") + .def(array_indexing_suite>>{}); + + // types.h - enums + + bp::enum_("Model") + .value("STANDARD", Model::STANDARD) +#ifdef ENUM_EXPORT_VALUES + .export_values() +#endif + ; // NOLINT + + bp::enum_("Stream") + .value("LEFT", Stream::LEFT) + .value("RIGHT", Stream::RIGHT) + .value("LEFT_RECTIFIED", Stream::LEFT_RECTIFIED) + .value("RIGHT_RECTIFIED", Stream::RIGHT_RECTIFIED) + .value("DISPARITY", Stream::DISPARITY) + .value("DISPARITY_NORMALIZED", Stream::DISPARITY_NORMALIZED) + .value("DEPTH", Stream::DEPTH) + .value("POINTS", Stream::POINTS) +#ifdef ENUM_EXPORT_VALUES + .export_values() +#endif + ; // NOLINT + + bp::enum_("Capabilities") + .value("STEREO", Capabilities::STEREO) + .value("COLOR", Capabilities::COLOR) + .value("DEPTH", Capabilities::DEPTH) + .value("POINTS", Capabilities::POINTS) + .value("FISHEYE", Capabilities::FISHEYE) + .value("INFRARED", Capabilities::INFRARED) + .value("INFRARED2", Capabilities::INFRARED2) + .value("IMU", Capabilities::IMU) +#ifdef ENUM_EXPORT_VALUES + .export_values() +#endif + ; // NOLINT + + bp::enum_("Info") + .value("DEVICE_NAME", Info::DEVICE_NAME) + .value("SERIAL_NUMBER", Info::SERIAL_NUMBER) + .value("FIRMWARE_VERSION", Info::FIRMWARE_VERSION) + .value("HARDWARE_VERSION", Info::HARDWARE_VERSION) + .value("SPEC_VERSION", Info::SPEC_VERSION) + .value("LENS_TYPE", Info::LENS_TYPE) + .value("IMU_TYPE", Info::IMU_TYPE) + .value("NOMINAL_BASELINE", Info::NOMINAL_BASELINE) +#ifdef ENUM_EXPORT_VALUES + .export_values() +#endif + ; // NOLINT + + bp::enum_