commit cc767394b80f800699af7e057990044db536ae59 Author: Robofish <1683502971@qq.com> Date: Mon Dec 15 02:33:20 2025 +0800 first_commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3089f33 --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +# https://github.com/ament/ament_lint/blob/rolling/ament_clang_format/ament_clang_format/configuration/.clang-format +--- +Language: Cpp +BasedOnStyle: Google + +AccessModifierOffset: -2 +AlignAfterOpenBracket: AlwaysBreak +BraceWrapping: + AfterClass: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterEnum: true +BreakBeforeBraces: Custom +ColumnLimit: 100 +ConstructorInitializerIndentWidth: 0 +ContinuationIndentWidth: 2 +DerivePointerAlignment: false +PointerAlignment: Middle +ReflowComments: false +... \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9c908d --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.vscode/ +.github/ +build/ +Camera/ +logs/ +assets/img_with_q/ +records/ +records_auto_buff_big/ +records_new/ +imgs/ +MvSdkLog/ +testvideos/ +watchdog.sh +records_24/ +records_25/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6aa155d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.16.3) + +project(sp_vision) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_BUILD_TYPE Release) +message(STATUS "--------------------CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}--------------------") + +find_package(OpenCV REQUIRED) +find_package(fmt REQUIRED) +find_package(Eigen3 REQUIRED) +find_package(spdlog REQUIRED) +find_package(yaml-cpp REQUIRED) +find_package(nlohmann_json REQUIRED) +set(OpenVINO_DIR "/opt/intel/openvino_2024.6.0/runtime/cmake/") +find_package(OpenVINO REQUIRED) + +include_directories(${EIGEN3_INCLUDE_DIR}) +include_directories(${OpenCV_INCLUDE_DIRS}) +include_directories(${PROJECT_SOURCE_DIR}) + + +add_subdirectory(tools) +add_subdirectory(io) +add_subdirectory(tasks/auto_aim) +add_subdirectory(tasks/auto_buff) +add_subdirectory(tasks/omniperception) + +####################src################### +add_executable(standard src/standard.cpp) +add_executable(mt_standard src/mt_standard.cpp) +add_executable(standard_mpc src/standard_mpc.cpp) +add_executable(auto_aim_debug_mpc src/auto_aim_debug_mpc.cpp) +add_executable(mt_auto_aim_debug src/mt_auto_aim_debug.cpp) +add_executable(auto_buff_debug src/auto_buff_debug.cpp) +add_executable(auto_buff_debug_mpc src/auto_buff_debug_mpc.cpp) +add_executable(uav src/uav.cpp) +add_executable(uav_debug src/uav_debug.cpp) + +target_link_libraries(standard ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim auto_buff tools io) +target_link_libraries(mt_standard ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim auto_buff tools io) +target_link_libraries(standard_mpc ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim auto_buff tools io) +target_link_libraries(auto_aim_debug_mpc ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim auto_buff tools io) +target_link_libraries(mt_auto_aim_debug ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim auto_buff tools io) +target_link_libraries(auto_buff_debug ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_buff tools io ${CERES_LIBRARIES}) +target_link_libraries(auto_buff_debug_mpc ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_buff tools io ${CERES_LIBRARIES}) +target_link_libraries(uav ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim auto_buff tools io) +target_link_libraries(uav_debug ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim tools io) + +##################calibration################ +add_executable(capture calibration/capture.cpp) +add_executable(calibrate_camera calibration/calibrate_camera.cpp) +add_executable(calibrate_handeye calibration/calibrate_handeye.cpp) +add_executable(calibrate_robotworld_handeye calibration/calibrate_robotworld_handeye.cpp) +add_executable(split_video calibration/split_video.cpp) + +target_link_libraries(capture ${OpenCV_LIBS} fmt::fmt tools io) +target_link_libraries(calibrate_camera ${OpenCV_LIBS} fmt::fmt yaml-cpp tools) +target_link_libraries(calibrate_handeye ${OpenCV_LIBS} fmt::fmt yaml-cpp tools) +target_link_libraries(calibrate_robotworld_handeye ${OpenCV_LIBS} fmt::fmt yaml-cpp tools) +target_link_libraries(split_video ${OpenCV_LIBS} fmt::fmt tools) + +##################tests################## +add_executable(auto_aim_test tests/auto_aim_test.cpp) +add_executable(auto_buff_test tests/auto_buff_test.cpp) +add_executable(camera_detect_test tests/camera_detect_test.cpp) +add_executable(camera_test tests/camera_test.cpp) +add_executable(camera_thread_test tests/camera_thread_test.cpp) +add_executable(cboard_test tests/cboard_test.cpp) +add_executable(fire_test tests/fire_test.cpp) +add_executable(detector_video_test tests/detector_video_test.cpp) +add_executable(gimbal_response_test tests/gimbal_response_test.cpp) +add_executable(multi_usbcamera_test tests/multi_usbcamera_test.cpp) +add_executable(usbcamera_detect_test tests/usbcamera_detect_test.cpp) +add_executable(usbcamera_test tests/usbcamera_test.cpp) +add_executable(handeye_test tests/handeye_test.cpp) +add_executable(dm_test tests/dm_test.cpp) +add_executable(minimum_vision_system tests/minimum_vision_system.cpp) + +target_link_libraries(auto_aim_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io auto_aim) +target_link_libraries(auto_buff_test ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_buff tools io) +target_link_libraries(camera_detect_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools auto_aim io) +target_link_libraries(camera_test ${OpenCV_LIBS} fmt::fmt tools io) +target_link_libraries(camera_thread_test ${OpenCV_LIBS} fmt::fmt auto_aim tools io) +target_link_libraries(cboard_test ${OpenCV_LIBS} fmt::fmt tools io) +target_link_libraries(fire_test ${OpenCV_LIBS} fmt::fmt tools io) +target_link_libraries(detector_video_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools auto_aim) +target_link_libraries(gimbal_response_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io) +target_link_libraries(multi_usbcamera_test ${OpenCV_LIBS} fmt::fmt tools io) +target_link_libraries(usbcamera_detect_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io auto_aim) +target_link_libraries(usbcamera_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io) +target_link_libraries(handeye_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io auto_aim) +target_link_libraries(dm_test ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io) +target_link_libraries(minimum_vision_system ${OpenCV_LIBS} fmt::fmt yaml-cpp tools io auto_aim) + +add_executable(gimbal_test tests/gimbal_test.cpp) +target_link_libraries(gimbal_test ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim tools io) + +add_executable(planner_test tests/planner_test.cpp) +target_link_libraries(planner_test ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim tools io) + +add_executable(planner_test_offline tests/planner_test_offline.cpp) +target_link_libraries(planner_test_offline ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim tools io) + +# 检查 ROS 环境 +find_package(ament_cmake QUIET) +find_package(rclcpp QUIET) +find_package(std_msgs QUIET) +find_package(rosidl_typesupport_cpp QUIET) +find_package(sp_msgs QUIET) +# 只有在找到 ROS 环境时才会添加 sentry 和 publish_test +if(ament_cmake_FOUND AND rclcpp_FOUND AND std_msgs_FOUND AND rosidl_typesupport_cpp_FOUND AND sp_msgs_FOUND) + # 编译 sentry 和 publish_test + add_executable(sentry src/sentry.cpp) + add_executable(sentry_bp src/sentry_bp.cpp) + add_executable(sentry_debug src/sentry_debug.cpp) + add_executable(sentry_multithread src/sentry_multithread.cpp) + add_executable(publish_test tests/publish_test.cpp) + add_executable(subscribe_test tests/subscribe_test.cpp) + add_executable(topic_loop_test tests/topic_loop_test.cpp) + + target_link_libraries(sentry ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim omniperception tools io) + target_link_libraries(sentry_bp ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim omniperception tools io) + target_link_libraries(sentry_debug ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim omniperception tools io) + target_link_libraries(sentry_multithread ${OpenCV_LIBS} fmt::fmt yaml-cpp auto_aim omniperception tools io) + target_link_libraries(publish_test ${OpenCV_LIBS} fmt::fmt tools io auto_aim) + target_link_libraries(subscribe_test ${OpenCV_LIBS} fmt::fmt tools io) + target_link_libraries(topic_loop_test ${OpenCV_LIBS} fmt::fmt tools io) +else() + message(STATUS "ROS2 environment not found, skipping ROS2-related code.") +endif() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c61fcfe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 TongjiSuperPower + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/best2-sim.onnx b/assets/best2-sim.onnx new file mode 100644 index 0000000..13f2589 Binary files /dev/null and b/assets/best2-sim.onnx differ diff --git a/assets/demo/demo.avi b/assets/demo/demo.avi new file mode 100644 index 0000000..95f1dbc Binary files /dev/null and b/assets/demo/demo.avi differ diff --git a/assets/demo/demo.txt b/assets/demo/demo.txt new file mode 100644 index 0000000..ba5a849 --- /dev/null +++ b/assets/demo/demo.txt @@ -0,0 +1,620 @@ +0.752677887 0.5014435722929029 0.02327065116579723 0.0019472255628188646 0.8648751522116124 +25.824433869 0.4044806118882999 -0.019203333238947136 0.007353485765453223 0.9143153683746531 +25.897933452 0.3983196747926323 -0.0436570165419713 0.018327136925283163 0.9160238084416079 +25.937844162 0.4060992801021506 -0.060038428432863424 0.024035605927579317 0.911537740008345 +25.971199735 0.41094740236919536 -0.05302595072734453 0.022424116638938174 0.9098393484722648 +26.004610259 0.40968966344416635 -0.06222787384895668 0.026676178451042067 0.9097089935174283 +26.038064416 0.4052178756282984 -0.06004722598623817 0.025247798677895384 0.9118965690167756 +26.077887404 0.40238001786745814 -0.05878320483250864 0.02484137457076296 0.9132457293412537 +26.111296109 0.40071966107416385 -0.060104792050292295 0.025001993365346215 0.91388515007574 +26.14465991 0.40002642058153615 -0.05785093696523309 0.02402514483049273 0.9143603908442461 +26.178099861 0.39929477459454044 -0.057247371909738026 0.02370218810986068 0.9147265316310269 +26.218006534 0.39592077215767296 -0.05910142357447336 0.024000578101308977 0.916066447456922 +26.258174622 0.3914227776785776 -0.06137418865489941 0.024358413852926662 0.9178388125133461 +26.297970237 0.3855702178436289 -0.06842624749883933 0.02654238434945672 0.9197548355938607 +26.331391866 0.37864028600252797 -0.07377439499650759 0.027719254342318013 0.9221824740242731 +26.364852852 0.36916508135424514 -0.08236062699653302 0.030084141809927266 0.9252182522199894 +26.398257869 0.35781284168321736 -0.09010595648555406 0.031602689105865805 0.9288983566427934 +26.438065898 0.3423988381374929 -0.094062894882146 0.031306837908086714 0.9343099535746594 +26.471630757 0.3284589912585138 -0.100292321321941 0.03340331127965669 0.9385842317772286 +26.511411364 0.30648004432504633 -0.10372794354937309 0.02920252880446784 0.9457577430129557 +26.544782875 0.2877371342097568 -0.10816547583833148 0.03136957635674919 0.9510644148072316 +26.578234048 0.27056379547459847 -0.11476815782525768 0.028758032907769022 0.9554038298390122 +26.618249467 0.24702723299386736 -0.11930861794076049 0.029647629255281208 0.9611784526943998 +26.65819223 0.22474004532143046 -0.12747277314000643 0.02684990433599021 0.9656716247120788 +26.691595323 0.20478718291793538 -0.1354562932082495 0.026679200858173283 0.9690211672530101 +26.731533578 0.18464779929792222 -0.14656507828977644 0.02557516004343661 0.9714781414057131 +26.764973543 0.16962745445697788 -0.15571367025164942 0.024602979225598506 0.9728178005178726 +26.804876967 0.15668079501547813 -0.165082270044186 0.021902446350445876 0.9735087341335167 +26.838271933 0.14948108929914852 -0.17336450566714282 0.023865026107513124 0.973154978739571 +26.878294766 0.14670414846347568 -0.17732779299413426 0.022900647578824766 0.9728865848576116 +26.918471623 0.16793725762402759 -0.1556833711631501 0.024272873455547277 0.9731241406209225 +26.958307164 0.18634764447388283 -0.14828721348713966 0.02716398992035998 0.9708488941987118 +26.991752292 0.19135093840176348 -0.1409333380258021 0.02560205317865002 0.9710134641078398 +27.031645647 0.19548121140962427 -0.13017912492072262 0.024075762752732745 0.9717308521749091 +27.065002019 0.2030810496528304 -0.11954184165675422 0.022601603668929358 0.9715744968229708 +27.098378842 0.21533477553100352 -0.10884752288991607 0.02217154034270757 0.9702018212756423 +27.138566887 0.2309530705457854 -0.10363395684749488 0.023384977600967825 0.9671472612876528 +27.178444982 0.2610574165126007 -0.07534989163557612 0.01975464577855268 0.9621752299266685 +27.211783938 0.28189139816589925 -0.06455159511978166 0.01934054677327865 0.9570769428097602 +27.251917112 0.30344756555078894 -0.05920441836657583 0.018158406704229697 0.9508336784491486 +27.285291209 0.3197638072317074 -0.052305763630237506 0.0190128093844623 0.9458613681481295 +27.325135927 0.34050741054630956 -0.04356369533756827 0.01739493407681777 0.9390709899044084 +27.358476022 0.36074398179533307 -0.043177686734943606 0.017975353795504252 0.9314914672838361 +27.391868412 0.3787823021316463 -0.03624523535416737 0.01568693148279716 0.9246427259685901 +27.42522498 0.3984670274873839 -0.04007924788396457 0.017989448735969783 0.9161299370878566 +27.465429627 0.4195406147954505 -0.03692008972117208 0.01825845120939563 0.9068016367825271 +27.505361927 0.4428245058408616 -0.03847256797862454 0.01988862267960126 0.8955617015191278 +27.538757471 0.46230490758180315 -0.03792234735216942 0.02220157131743785 0.8856315025043535 +27.578597084 0.4838649748930099 -0.04076880357753215 0.02443333342290202 0.8738510187351668 +27.612003127 0.5029620477890874 -0.04332278759451557 0.027400941729415852 0.8627870553915313 +27.645609807 0.5164711925243503 -0.04852694340232439 0.03269867674780466 0.8543028968670572 +27.685514463 0.5346476087719997 -0.052552062589177925 0.03330828442595869 0.8427815691747212 +27.725445945 0.5483503022201482 -0.05677974866673668 0.04052650103918396 0.833334032012335 +27.758780308 0.5604878399036811 -0.058961518326285545 0.04247728364422499 0.824968242449958 +27.798875869 0.571417869922125 -0.06320457473969693 0.046910106558344385 0.8168759034078068 +27.83230702 0.5793812677133586 -0.06757245519538539 0.048605247049575986 0.8107951898482002 +27.87214098 0.5939104023850323 -0.06534585789781365 0.0508035152165781 0.800262054352252 +27.912176896 0.5854053622748845 -0.0647026264982299 0.04940118779555409 0.806643449479651 +27.945738619 0.5484893523181781 -0.06296952524452691 0.04326309904847135 0.8326599387173533 +27.985561072 0.5023457335699482 -0.06007586355282575 0.036143248490428076 0.861819772441438 +28.025565824 0.48265743343532164 -0.05297786338571968 0.02931557823091408 0.873713765951813 +28.058924678 0.47321784897275215 -0.04940209718149981 0.027201154723416898 0.87913844040025 +28.098993169 0.454826580318603 -0.04068404964729429 0.020584409203952665 0.8894120934851888 +28.138865086 0.43209180787629975 -0.03808694033648821 0.018506465746383264 0.9008349267582678 +28.172422693 0.4146567620523134 -0.028721650974283558 0.012880152825570333 0.9094333060278761 +28.212409768 0.4165125396419987 -0.04340323269539244 0.021428768549504835 0.9078404439057637 +28.245832927 0.4084996986948947 -0.04176063927302493 0.019259226597509577 0.9115992142189211 +28.285655953 0.3837377360348871 -0.04162778955717806 0.01723239944012399 0.9223424101107672 +28.319103435 0.34663020129947947 -0.05575000855977064 0.020189405315549035 0.9361259680222915 +28.359132216 0.3215898402058671 -0.06694038194132015 0.02121937972090072 0.9442715170257839 +28.39893612 0.3191246191429412 -0.0856146732825442 0.02549929462302405 0.9434931855341215 +28.432387406 0.311282249380152 -0.10399925402143416 0.03281288350827445 0.9440396342631439 +28.472413896 0.3027637644227617 -0.1442911954492042 0.042430382176946085 0.9411236988497933 +28.512438655 0.2792311437803485 -0.19292151186743026 0.0530538305086665 0.9391467136024387 +28.552487544 0.25399677541556676 -0.2227387876177815 0.054100275930032804 0.9396521860312725 +28.586039756 0.2565817998246983 -0.24347813752778957 0.06245154897650981 0.9332652252040423 +28.625817307 0.29769257489780704 -0.2354296157143852 0.072967502762912 0.9222948392113202 +28.65918369 0.31398004396575024 -0.23344648938751542 0.07823336000621821 0.9169508219997666 +28.692535692 0.31150059834170446 -0.23627797301834827 0.07734464473301211 0.9171466091254031 +28.725869117 0.30290876772937103 -0.22655739560738158 0.07226151040671558 0.922873934533864 +28.765799969 0.30123134447545497 -0.187413394701377 0.05712519180985434 0.9332055556267885 +28.799252042 0.3041879950337558 -0.1657474708904943 0.05170527857763309 0.9366557551939549 +28.83281661 0.31099686973283364 -0.12893354694262157 0.03941615610368397 0.9407993697527565 +28.872692948 0.315227209620526 -0.12035272852321002 0.037414039040739464 0.9406100237263759 +28.912720524 0.3154260116678618 -0.10679714618271541 0.032446845303020014 0.9423629889593582 +28.952760607 0.31613276095486886 -0.1045108304959943 0.03200331651551979 0.9423976610179468 +28.992661348 0.3156462155986657 -0.10589643353602368 0.03231755011857671 0.9423953458591169 +29.032576927 0.314815368087299 -0.10450510154105064 0.031801552430673785 0.9428460261521774 +29.066041179 0.3148639043329818 -0.10515409891927241 0.03220150564769405 0.9427440799404091 +29.105976656 0.3148248942489635 -0.10505843638464234 0.032002530546273285 0.9427745483116193 +29.139375245 0.31452325985513124 -0.10510777300723144 0.032002366662525275 0.942869727794651 +29.172731973 0.3148245305089792 -0.10500818203126688 0.03220250915625518 0.9427734590559551 +29.206294979 0.31525741283153325 -0.10510685476651474 0.032002087084000694 0.9426246226139687 +29.246181549 0.3146354063412599 -0.10531185088281841 0.03210361266228367 0.9428060952253837 +29.279588728 0.31492782510257605 -0.10460924263489824 0.032202845247071926 0.9427832985843075 +29.319473832 0.3292795253786904 -0.05527650227395744 0.01743693655655526 0.9424518320350724 +29.359443762 0.3567154861057565 -0.023190959454469157 0.008381725248960104 0.9338875671373755 +29.392987327 0.360946593189094 -0.04470723120881977 0.014463611798745599 0.9314019670778901 +29.432980661 0.34577297778749766 -0.11129112511418768 0.03910734338753012 0.9308737556704211 +29.472866367 0.3310136170867147 -0.13852117797464425 0.046104784748342106 0.932264027719077 +29.50637431 0.32634270221054634 -0.15144298392210845 0.0499821024659049 0.9317012679865658 +29.546290163 0.32651364207192 -0.17929210883548183 0.05979575993220456 0.9261034760459471 +29.586258229 0.3207882708899325 -0.2109358301925009 0.07033174620624963 0.920681490134361 +29.626251253 0.3173645078894013 -0.22428208807123948 0.07354740778710415 0.9184596305283756 +29.659858131 0.3149299011405167 -0.2271949905143246 0.07391106915404334 0.9185525284431624 +29.699635647 0.3159754663811358 -0.2325000272672274 0.07614490269132208 0.9166816218077525 +29.733141129 0.31843723826828896 -0.22883963567773133 0.07558358435495449 0.91679728850112 +29.773035006 0.32315472577674303 -0.1966484277758075 0.06518853581097142 0.9233909647924237 +29.806553861 0.3255257062845889 -0.18414720918973623 0.0617048727427316 0.925373075346022 +29.846479871 0.324835280905318 -0.15337750844106096 0.0502745022225885 0.9318958389272088 +29.886403411 0.3248880692958557 -0.12357078512198329 0.04067624659138025 0.9367622144689576 +29.926389194 0.3264593571862328 -0.08671084772300566 0.028153929331478582 0.9408043756572922 +29.959840492 0.3283675311496364 -0.05622802944971049 0.01805900866458588 0.9427019918282487 +29.999770493 0.3307567180131652 -0.010821135472014803 0.002527329810063534 0.9436506287394117 +30.033161415 0.3320567726442118 0.0025965095322532357 -0.0018482956263382029 0.9432540175809679 +30.066523885 0.3342815614753687 0.025411818591313442 -0.01012335377226029 0.942076214985748 +30.106499966 0.3382845206792486 0.044224933480299655 -0.0165699225687323 0.9398580616207644 +30.139866396 0.3401183879702809 0.05720737205044044 -0.021252432746559503 0.9384003052268968 +30.179989005 0.34256937750165134 0.06619532394759138 -0.02430110224744672 0.9368424932266702 +30.219842067 0.344623430236308 0.059878104556614964 -0.022311130314846956 0.9365636750327987 +30.253276402 0.34522447628295433 0.05522026732492536 -0.02065964087518999 0.9366664092891513 +30.286641332 0.3451187515838374 0.04860151789103361 -0.018292598243356377 0.9371212945042965 +30.326675126 0.3438457407107739 0.04832093668524092 -0.01830201934721479 0.9376034501646922 +30.366667361 0.3429318533799306 0.03413163782197401 -0.013270694643356435 0.9386461867501932 +30.400078661 0.34099087160867003 0.009880972929028852 -0.004427041687738833 0.9400042516688052 +30.439933789 0.3337466350949278 -0.04479772856893368 0.015465943953540087 0.9414707386082178 +30.473359852 0.327502001625859 -0.04822546446444369 0.01554501461772024 0.9434909093514253 +30.506713728 0.32742515100044156 -0.05396348614326533 0.017189703551363827 0.9431782582033048 +30.540067363 0.32762673246303753 -0.0692522807086646 0.022457088218536425 0.9419981555082719 +30.580183256 0.3211706030541114 -0.13150514868757188 0.04340804157037819 0.936841278728442 +30.620118924 0.3156623157618935 -0.15152403620692897 0.048945734663551704 0.935415460593658 +30.653556381 0.31251469315972596 -0.17233735877100592 0.055033408585554396 0.9325265279178772 +30.693612598 0.30707801890832764 -0.201591008905681 0.06279686470201684 0.9279658987351438 +30.733401342 0.3070144936742045 -0.2180656699052845 0.06916985484325372 0.9237992181549487 +30.766763122 0.307516455225731 -0.2285795236638387 0.07338757315311484 0.9207601724845129 +30.800130476 0.30685308901421826 -0.2363146033564864 0.07590469062529547 0.9188226531512906 +30.833570583 0.31304294798738397 -0.18654854721808672 0.059324835261395314 0.9293461767110238 +30.873680288 0.3179267616358679 -0.1587133597722939 0.051604343820103114 0.9333111139188353 +30.913521915 0.31398354851894233 -0.15394153937504743 0.04853342238545297 0.9356072042400321 +31.080372626 0.31321944996563716 -0.1506093523781129 0.047502949787253404 0.9364581511744019 +31.113758022 0.3088298470777448 -0.0974729465717457 0.028097697739777907 0.9456921643020432 +31.15368512 0.28208488208503557 -0.085771531000141 0.022564467961138535 0.9552812196178643 +31.18702301 0.2616628145118343 -0.08315458740030784 0.01818988565662334 0.9613984679391939 +31.220453011 0.247896361568307 -0.06999324306994446 0.014566461258688601 0.9661449984614866 +31.260330085 0.23110617691853164 -0.07257634513517928 0.013824733687868419 0.9701193152676527 +31.293733821 0.2202888951997949 -0.06696486874124245 0.012200800488341431 0.973056858294296 +31.327167668 0.2112520306738455 -0.06458384143320492 0.011464277138012064 0.9752282180657049 +31.36052945 0.19972929183072463 -0.058442444889792354 0.009600770030694117 0.9780595666087617 +31.393946942 0.195298987116921 -0.06071821612280596 0.008800591361365616 0.9788228406885789 +31.427284711 0.1884055201932333 -0.056530448392185614 0.007963294597824626 0.9804306473710642 +31.467105136 0.1809636017918513 -0.058018052413492005 0.0074003701882794995 0.9817491099775655 +31.507183596 0.17685620474810448 -0.05806968583402605 0.007400621854908224 0.9824942876299052 +31.547212613 0.16466045534051527 -0.059204781976887055 0.006700193527206321 0.9845490519241901 +57.646185138 0.3648277315793616 0.0025002234819031127 -0.001300098824158909 0.9310707733014958 +57.679691503 0.35939410032381164 0.003681914957301948 -0.002094031130928149 0.9331762637295851 +57.71305164 0.3601129890279416 -0.03938227313049235 0.014928919849971368 0.9319575092505827 +57.753067919 0.36876978940305033 -0.07447976382732267 0.028640860117576963 0.926089255058918 +57.793093463 0.3755303499851984 -0.06476356468277647 0.025402052968372942 0.924195527274703 +57.833076037 0.37505547533571193 -0.0693647513044173 0.027802568360908445 0.9239853564260185 +57.872958251 0.371648935723233 -0.0685273769739429 0.02712443229489106 0.9254433166615551 +57.906291651 0.3698817896467579 -0.06704118431847518 0.026201377701737485 0.926286364522019 +57.946298377 0.36883775196533913 -0.06956436196205014 0.026960001241541888 0.9264948303164049 +57.979743529 0.36771210408958105 -0.06789539441821965 0.02590238061688819 0.9270960525216282 +58.01307696 0.3660305216376218 -0.06936753172424923 0.026583477045683517 0.9276330748309578 +58.052974633 0.3643509402540849 -0.06872859753131956 0.02640199542584913 0.9283467600282103 +58.086505113 0.36332175280535794 -0.06870977841227453 0.02625440421377999 0.928755606469408 +58.126570832 0.3621776022579473 -0.06945023327718987 0.026402468538066267 0.9291431316947484 +58.166469941 0.3597280954018862 -0.07164803946981183 0.02704955467844082 0.9299089081250711 +58.199900426 0.35647340016840284 -0.07736889913395652 0.029023119575736042 0.9306440925237048 +58.239864087 0.3514784440798335 -0.08366090281104806 0.03073943108649393 0.9319435841640515 +58.273396205 0.34623189094108586 -0.09170006766039057 0.03309975448491024 0.933069655245244 +58.313314371 0.3374310922974114 -0.10038541388923851 0.03489764152998602 0.9353315889271071 +58.353324331 0.327247779651809 -0.10840353029644155 0.03620402773962069 0.9380015105039321 +58.386708159 0.32084967489258137 -0.11230649023244715 0.036469268088100394 0.9397407785438027 +58.420110256 0.31312522948248706 -0.11724194944659518 0.037205319884909466 0.9417126313922755 +58.459890547 0.2934158767140341 -0.1388722335495908 0.040950048927314096 0.9449575226082564 +58.493474575 0.28391013881347704 -0.1474430651071003 0.04211535504946051 0.946510365764577 +58.533395611 0.2799214687055404 -0.15989369219174132 0.044500901904946585 0.9455673684538147 +58.573331218 0.2763304265699522 -0.1738098073833949 0.047902215360459445 0.9440005423582 +58.613252003 0.274635833710138 -0.1851832836280359 0.05059342615452792 0.9421903287218684 +58.646809776 0.27439578896301897 -0.19183288427053158 0.05277666438157558 0.9408090769165474 +58.68670508 0.2734499045567866 -0.1939459675217642 0.05350264945124146 0.9406102157012908 +58.726659999 0.2703606687750818 -0.19308909508172478 0.05220484193570918 0.9417517531804647 +58.760194601 0.26848646144490124 -0.18647101365306806 0.04989948326164886 0.9437444689417859 +58.800091022 0.2672177509023071 -0.1748348539570433 0.04656774141441302 0.9464982265720799 +58.840027909 0.26672276279028617 -0.15613398075798654 0.041341451714856106 0.9501431640717569 +58.873411026 0.26730627161015935 -0.1377186299679909 0.03640504803532092 0.9530244533038811 +58.913491169 0.267306647262335 -0.11594112896747992 0.0303817542261305 0.9561285269006772 +58.953486966 0.26760993558545765 -0.09298748317700264 0.023696524552685096 0.9587370468864119 +58.986896014 0.2684987185719504 -0.07613944241148507 0.018971515054979834 0.9600788014798616 +59.026930521 0.27147298641603634 -0.0537368583379652 0.013143257657024324 0.9608548394427148 +59.066903569 0.2757755204858268 -0.034311853833894984 0.007947104326678526 0.9605765989862931 +59.100260082 0.2789962725356336 -0.02058850829599111 0.004222067172057051 0.9600621685006462 +59.133635337 0.280729665148298 -0.0076084474944423655 0.0006737538540854326 0.9597564861402046 +59.167015679 0.28310252320476503 0.0024970573964317837 -0.00216577863043965 0.9590839564200719 +59.206883378 0.28586696886309737 0.01334532404459 -0.005048480852492831 0.9581630817769238 +59.240381069 0.2874075173807805 0.022003848343924756 -0.007651018322476242 0.9575250448576805 +59.280263498 0.2883515447825343 0.025640446260136892 -0.008839736495476532 0.9571404354616921 +59.31368658 0.2890182194808034 0.02821762825440461 -0.009516449346823212 0.9568603197205283 +59.347298376 0.2889153881977819 0.026502940620754204 -0.009000426936712078 0.9569453928599765 +59.387020221 0.28713766647345373 0.023147914945962314 -0.008216010472606861 0.9575743478695261 +59.420461592 0.28566612440829653 0.017049436942301356 -0.006449856983485678 0.9581558231365015 +59.460346171 0.2847212276456365 0.007801813220703021 -0.0037008916980609337 0.9585714671525907 +59.493884887 0.28489390344878146 0.0004908958586388112 -0.001553619322662375 0.958557671225875 +59.533705066 0.2830206185816189 -0.019982702000005712 0.0040246109056272066 0.9588971913534396 +59.567245962 0.28142431079927405 -0.03430018739554989 0.008371431959058652 0.9589336648396168 +59.60708867 0.280308255851015 -0.049880761174041555 0.01276013983733152 0.9585282312993145 +59.640467299 0.2787649308114513 -0.06437196433450137 0.016457017644675297 0.958058208110367 +59.673882936 0.2771935994115264 -0.07916682342006238 0.020737205304306502 0.9573224591698588 +59.713885138 0.2746909690270775 -0.09632564400615011 0.02518388503298312 0.9563639546618281 +59.753815901 0.2741171465659534 -0.11080133999916107 0.029125424141915345 0.9548479264686516 +59.787474612 0.27420963270161186 -0.12284048621704814 0.03281320968003316 0.9532274574046325 +59.827247797 0.2746074941695586 -0.13359023495548616 0.0361976740755462 0.951543010936434 +59.860695477 0.2750936394584554 -0.1406197928985947 0.03807247857911732 0.9503157631805864 +59.90054314 0.2747201705490309 -0.14527095340460258 0.03940289304562001 0.9496697268050954 +59.934049841 0.2750193586155686 -0.148949280473019 0.040502850996111006 0.9489667977829562 +59.973959253 0.27412126356538025 -0.15448185688784744 0.042073137352098666 0.9482735575070906 +60.007318902 0.2740216010757454 -0.15461521604839573 0.042205345545121356 0.9482747523358459 +60.047302748 0.27431062504513654 -0.1480072023983457 0.040102043145115986 0.949336281388818 +60.087423516 0.27462680180856897 -0.13146965567843455 0.034409525993149566 0.9518990670680993 +60.120785661 0.27542878501884666 -0.13352188907119297 0.03580374184340854 0.9513301117866041 +60.154181941 0.2742236561334814 -0.13677340081460995 0.0367031662293901 0.9511820545169732 +60.1940437 0.2750566903523599 -0.10277152530977768 0.026483886164914996 0.9555524237065759 +60.227515539 0.27754067726615284 -0.062063430532637034 0.015683429985613236 0.9585788090068819 +60.267432144 0.2774233745832361 -0.0598201423342465 0.014705573070768592 0.9587708630984884 +60.307483302 0.2770237550885866 -0.04626981951180268 0.01089239699100195 0.9596865626894827 +60.347437713 0.27681844922520893 -0.029758710987930707 0.006486096043002889 0.9604394285154105 +60.381027699 0.277412988347121 -0.019566489315750134 0.0035415591196058336 0.960544972268961 +60.420908029 0.2785889332634186 0.01006048009946837 -0.005028237337328633 0.9603445786969375 +60.46097436 0.28091585121888696 0.017085858088799973 -0.007085293787664801 0.9595541446939199 +60.50093872 0.2822986613948781 0.021347961278119 -0.007982704177122624 0.9590557891791012 +60.534317552 0.28345973360606685 0.02364509443060165 -0.008600484591643576 0.9586540150638991 +60.567686078 0.28392580334411344 0.024721410965704366 -0.008800799821867556 0.9584871078724847 +60.601134895 0.28369488034782964 0.026191995964036263 -0.009400105294010322 0.9585107366286056 +60.64098427 0.2845070080118677 0.02567377340725567 -0.00941870582720424 0.958283834639669 +60.681035995 0.2852300783730423 0.012649299246872954 -0.005412466437362554 0.9583603199355323 +60.721056573 0.28443123442982315 0.007414061375186405 -0.004000247986348545 0.9586594299284287 +60.760950423 0.2845019932975103 -0.0008613114356811632 -0.001579706486831008 0.9586737601915314 +60.794346753 0.2848613354630318 -0.024229759872585188 0.005311685742634968 0.9582477363862157 +60.827683646 0.284014202386103 -0.032295726940009144 0.007338011729121775 0.9582479180505772 +60.861059918 0.2845095607832369 -0.045307837181211605 0.01156023760536584 0.9575321773284687 +60.894418265 0.2844159262635979 -0.06201152709322005 0.016353332129045144 0.9565535635412497 +60.927853232 0.28329829541775264 -0.07787573172405092 0.020758259805666403 0.9556393361889619 +60.967822384 0.28230882822833464 -0.09547560242921625 0.026050133702126322 0.954205179916366 +61.007852874 0.28180987820301423 -0.10726356102589757 0.029254292491293066 0.9530067719560443 +61.047793891 0.28092241133537277 -0.12055656628538143 0.033044621526746 0.9515549201823047 +61.081297135 0.28131808585239404 -0.13162044158457847 0.03630638562412605 0.9498515885612542 +61.121126465 0.2827108332999337 -0.14174546260265558 0.03957752898837435 0.9478483147453451 +61.154576236 0.283791753554629 -0.14830510399666721 0.0416675929157472 0.9464310056435885 +61.194722847 0.2854004613278448 -0.15143880308621713 0.04312906788465369 0.9453849740169706 +61.234615585 0.2865944179029341 -0.15324757293495053 0.04403714683332024 0.9446901876886443 +61.268036279 0.2866302970700691 -0.15064630796320014 0.04337631566709277 0.9451281701111915 +61.307955206 0.2860798252793477 -0.14874561154348634 0.04223665641173193 0.9456474720898108 +61.341304937 0.28563435663316866 -0.14678701439683384 0.0414955630347826 0.9461208722798826 +61.374700703 0.28531184809918114 -0.12376644738366559 0.03455950353679075 0.9497813730283127 +61.408128753 0.285867897914429 -0.11584382382246575 0.03246127675785646 0.9506871298883902 +61.441511647 0.28593200766477295 -0.10514477186184161 0.02907645558108238 0.9520199702074295 +61.481396216 0.28582490808609917 -0.0905523414362669 0.024889725185378877 0.9536691758456803 +61.514803789 0.2871992313567452 -0.07691973477045909 0.020965768695799105 0.9545472185564899 +61.554684279 0.28852221790137245 -0.05970025097906658 0.016157145254836726 0.9554735770640252 +61.588075195 0.28921573339151685 -0.05026073817264798 0.013231578080056611 0.9558519985325442 +61.621566899 0.28902458829576755 -0.03861862231306294 0.00967663376576401 0.956493466851743 +61.661394915 0.28920964147309014 -0.028077574915538837 0.006492429401688862 0.9568318982069594 +61.694815738 0.28931478822671375 -0.022769474428963054 0.005223037165444572 0.9569489141173259 +61.734797182 0.2886196224288099 -0.018122548407617643 0.0037215693259306628 0.9572650817354705 +61.774938242 0.2877273086742173 -0.020184227380927657 0.004100389174710778 0.9574908770409997 +61.814938254 0.28860733071930167 -0.020200513099549178 0.004400111764258237 0.95722431380636 +61.84847218 0.28875919295377667 -0.02466856135980777 0.0056226707181293 0.9570673832803606 +61.888382085 0.2868986877286922 -0.035891258963021734 0.008758331470854126 0.9572482708991699 +61.928269187 0.2848074621127248 -0.04726902322872832 0.01215780260011114 0.9573413898938286 +61.968318182 0.28381385518873375 -0.057337360004308904 0.014956490296768757 0.9570467209852649 +62.008424942 0.2834565449505991 -0.07891109639227538 0.021222548942302456 0.9554972681317461 +62.041827025 0.2827147524815781 -0.09533837404096494 0.026079160563548875 0.9540979197906292 +62.081712008 0.2835224224197247 -0.11310352924741736 0.03136085550925628 0.9517558113291231 +62.121640667 0.28516155946180044 -0.13185773876604248 0.03695805643671476 0.9486466801698306 +62.161637419 0.2885637616404501 -0.14642537987815493 0.0419411640547271 0.9452679526743653 +62.195031152 0.2918918358347289 -0.15595855502800993 0.04568280976347906 0.9425439863362987 +62.228512856 0.2947915046390717 -0.16447734235562703 0.049135778232572196 0.9400164083364405 +62.268405621 0.2973761095573238 -0.17183142899332057 0.05231330381434036 0.9377124973672327 +62.308522313 0.29812810348935065 -0.18173747143537178 0.05543336138117255 0.9354241112095116 +62.341934684 0.29801463716053067 -0.18514483088932002 0.056202760430945714 0.9347459105837181 +62.381822543 0.2979245286258951 -0.18514487503268026 0.05616388755648625 0.9347769617543609 +62.421928382 0.2995523883022208 -0.18661466857341408 0.056872414930442684 0.9339212282391112 +62.461770589 0.3007365446583887 -0.18039157304908446 0.05517915728209339 0.9348645204955892 +62.495282167 0.30022041504782776 -0.16600290707915755 0.049898563917465326 0.9379876707871769 +62.535280767 0.2978254459637583 -0.1628123460742277 0.048302961138273384 0.9393928718313593 +62.575271511 0.2927147726873752 -0.15690826899251742 0.045402425878007635 0.9421446155961101 +62.608663599 0.286404822110078 -0.1521919670442699 0.04302991809568021 0.9449647131972545 +62.648664754 0.2792963135003527 -0.14883539420674555 0.040629007611222547 0.9477293276231155 +62.688583807 0.27650900443530846 -0.14507150809035033 0.03900127006501638 0.949197518401677 +62.722013011 0.2757161863938473 -0.1437084366514177 0.03850226034154197 0.9496557511773573 +62.76204976 0.27562033538357833 -0.14227281164287617 0.03830282599851614 0.9499077698970579 +62.801974933 0.275125992387055 -0.14077764179702965 0.03790358092137181 0.9502897781395117 +62.835410443 0.2737120039082373 -0.14101231850801518 0.037403267462409705 0.9506830494590017 +62.875370115 0.27308301603726287 -0.13395448039718916 0.035370276453400545 0.9519615575626577 +62.915414692 0.2658904368289764 -0.12701364460125542 0.03250157604817458 0.9550463115694375 +62.948840307 0.26166074146074464 -0.12881301053463154 0.03180321222827083 0.9559965587737133 +62.98220057 0.2612686416605179 -0.1278545635588067 0.03105491294941914 0.9562570260362844 +63.022028478 0.26644026275902405 -0.12811085776695608 0.03264199356153755 0.9547416900709038 +63.055412647 0.26884135554383154 -0.1278137534498672 0.03280352983689863 0.9541026665976002 +63.095569362 0.27101884188485453 -0.12780888558259929 0.03340232221016288 0.9534662872805177 +63.135472554 0.2700953628187982 -0.12709006175872004 0.03308657714580149 0.9538353577000181 +63.175458795 0.26795909854581584 -0.129350491791601 0.03340080623526233 0.9541230308102929 +63.208911137 0.26435129431231846 -0.13191133229816376 0.03330286099718616 0.954782023844253 +63.248792856 0.2628103336504036 -0.13040512750385322 0.03262339091024838 0.9554375676164218 +63.282240414 0.26311020327320933 -0.13050506091658615 0.03275020240941291 0.9553370474606495 +63.315635148 0.26339183339590566 -0.13045815523136886 0.03287699510500992 0.9552614903924668 +63.355570487 0.26351772633217535 -0.13035070600370108 0.03282317673298574 0.9552432886035959 +63.389101183 0.25931271446282833 -0.14020103785137325 0.03480109946876105 0.954929038504476 +63.428970599 0.2551229596991654 -0.1395125553823347 0.03401113070096952 0.9561860516204315 +63.46898607 0.2560003571369712 -0.13890487248862282 0.0341925765899839 0.956033535630838 +63.502432967 0.2572087388153544 -0.13940473635637793 0.034301165401892125 0.9556324681646682 +63.536008704 0.2577122609524792 -0.13840658484991508 0.03420162718112064 0.9556454659145871 +63.575632014 0.25821791987023207 -0.1386262079074321 0.03450239440558873 0.9554663076840424 +63.60921073 0.25822738085573405 -0.13841467664769014 0.03420362674386562 0.955505159055915 +63.649086326 0.25810946063986767 -0.13840429321403241 0.03430125726442255 0.9555350237946283 +63.682468557 0.2581284147957332 -0.1385494421287691 0.034403787171535145 0.9555051820838569 +63.715828042 0.2580193803659286 -0.13833647818762831 0.034228658435329226 0.9555717749598635 +63.755707572 0.2578230534211806 -0.13858943437308835 0.034303067231755226 0.9555854443131813 +63.789079711 0.2569187840801675 -0.14606903584460204 0.036091826850644285 0.9546486029889636 +63.822464671 0.24362501333806216 -0.17087707394123441 0.040672210528031806 0.953831038375285 +63.855833243 0.23758004409366493 -0.16754055393694606 0.03816990524571669 0.9560486095217577 +63.895896852 0.23794496569337004 -0.17022896998692733 0.03940256580395416 0.9554327443030388 +63.929355883 0.23881305941020078 -0.17830975080753264 0.04149134794591114 0.9536521501405671 +63.9692235 0.23912270819126671 -0.17639606135460537 0.040795027248123984 0.9539604424281342 +64.009199932 0.2403130344044474 -0.1769099918860005 0.04130198195602294 0.9535442551621433 +64.042571409 0.2409098335335081 -0.17726412159570387 0.041701702193222445 0.9533104695407204 +64.082558493 0.24111473617741602 -0.1762107694502725 0.04160254261709045 0.9534582723830295 +64.115950647 0.24021800141042882 -0.1773132874690634 0.041803132635120414 0.9534714510603781 +64.155900702 0.24082688018823045 -0.1763196801378116 0.04170465491631732 0.9535064267917729 +64.18924529 0.2409076773069191 -0.1766128860660676 0.041901335322374056 0.9534231262079821 +64.222754713 0.2404164794523016 -0.1765327440663529 0.041823510363253796 0.9535653625531181 +64.262702332 0.24062498750884803 -0.17609446986537078 0.0417043307527804 0.9535990257260457 +64.30275806 0.24092503341152474 -0.17671836199176597 0.041904354088596454 0.9533990633092841 +64.342809314 0.24083320204607145 -0.1759146992861333 0.04170348470853757 0.953579680325913 +64.382700693 0.2405170352223008 -0.17641249485743807 0.04170295371629915 0.9535675388127394 +64.416184431 0.24056521230446204 -0.17590893625023213 0.04170211848570028 0.9536484457545273 +64.456125522 0.24061449264735138 -0.17631061950842913 0.041802517841476663 0.9535574344939715 +64.496032895 0.24062297337195407 -0.17611681467498388 0.04180399121757141 0.9535910436831182 +64.529717853 0.24070870538774838 -0.1763063762353969 0.041801511779010724 0.9535344851982471 +64.569622489 0.24071819416869888 -0.17611331114710377 0.041703152043351656 0.9535720737010984 +64.609475668 0.24070890132552572 -0.17630651974943992 0.04178206820719111 0.9535352613788483 +64.643018115 0.240713953388724 -0.17621021432111827 0.04170241735068463 0.9535552744335203 +237.224652599 -0.7398448860850116 0.02880199027778505 0.02600172727781124 0.6716575764989666 +237.26459653 -0.7372147911859329 0.029190583961903285 0.025722260357686447 0.6745373427666818 +237.304577638 -0.719774213481204 0.024585925767784924 0.021717065753797272 0.6934327529881326 +237.338274005 -0.7065618911623412 0.02311168683405692 0.021108277492017914 0.7069586865652762 +237.377909861 -0.7029157775106044 0.0202262565466694 0.017676291691649994 0.7107656836010039 +237.411334506 -0.7065108557314751 0.01991394273749358 0.019157448950281628 0.707162525709574 +237.444759512 -0.7066496268974564 0.021287525981304834 0.020379923502217845 0.7069496479674665 +237.484615802 -0.7085189490191697 0.02103443639616331 0.020916670756841563 0.7050680422843719 +237.518174235 -0.7032733386680614 0.019396886854743087 0.02049211851203453 0.7103593773421388 +237.558096176 -0.7011389792290881 0.022573860527453183 0.025281284609096007 0.7122186527148762 +237.591439633 -0.7116380918435294 0.020814186798997172 0.02342727837692802 0.7018469623023575 +237.624809877 -0.7142359766518366 0.0244222614330849 0.02459588541984571 0.6990461824680148 +237.664758513 -0.6883823330947716 0.02078155641383355 0.023101642546811355 0.7246821403261273 +237.698193782 -0.6418943843669256 0.018006441479894 0.02266905587123676 0.7662463580920589 +237.731617528 -0.6067771801183071 0.01863711239024718 0.023073335359475672 0.7943184077716052 +237.771560356 -0.5912876062541214 0.02099286459940562 0.022800858396953828 0.8058649931486939 +237.811430199 -0.6152911676118697 0.029743209425535877 0.027543034522966206 0.787256947763027 +237.844900693 -0.6516273881691241 0.02733493544917675 0.02600113227053243 0.757600481396709 +237.885046453 -0.6636923427193527 0.026877123905247095 0.027106730051557944 0.747031003113118 +237.924951961 -0.6568099397047276 0.02603639084756669 0.026168119409953988 0.7531520689630026 +237.965042646 -0.6534747112605533 0.02540290429448739 0.025666812022707472 0.7560864427808057 +237.998432626 -0.6509603119699333 0.023112637992068807 0.02398122870212651 0.75838076114508 +238.038373687 -0.6518610896148515 0.024282998066971213 0.026792872097090355 0.7574758067792048 +238.071807186 -0.6523312880969664 0.023697992628947867 0.02406036089311443 0.7571812165847778 +238.111745652 -0.6541333862571794 0.023704463170307794 0.026628799095168607 0.7555385618671443 +238.151660619 -0.6551582775796915 0.026902393019224084 0.025929784086878592 0.7545671201860434 +238.185055706 -0.6539363742338972 0.027039839756743497 0.028388807721200768 0.7555330178859974 +238.224994003 -0.652245341533551 0.02883335334408795 0.02866763586874644 0.7569166525030437 +238.258337377 -0.6492631197767772 0.027507326320962183 0.024420982651544156 0.7596738536390019 +238.291777781 -0.6463246800306472 0.030203130788683277 0.028002902717984494 0.7619501403065756 +238.331816336 -0.6417429834184132 0.02973726885534526 0.02591589327909385 0.7659046967800928 +238.371776423 -0.6364593181664243 0.02849356837761892 0.027223983453827823 0.7703028674522419 +238.411753748 -0.6290274181709601 0.0316022530027864 0.02614681523754289 0.776300295534697 +238.445094107 -0.621693604302577 0.031118363054238585 0.026102653773446564 0.7822067254352133 +238.478469514 -0.617013808729699 0.03326661036518229 0.025589466936078956 0.7858323432218917 +238.518569694 -0.6131338818246268 0.03307181434644744 0.026436454278204645 0.78884359155646 +238.558465278 -0.6292927738169665 0.030655855388338288 0.021713493844222557 0.776259845372176 +238.591893193 -0.6378941040430138 0.03199957415778331 0.021558830862241703 0.769156912530034 +238.631944176 -0.6271538701270035 0.03323997536894822 0.026351955926561813 0.7777394818581849 +238.665330098 -0.5905127439272005 0.03290102473176915 0.023453149952717144 0.8060162353131998 +238.70531927 -0.5733654448877479 0.03278302238490831 0.025601998148518296 0.8182431654115963 +238.858595665 -0.6130995875387844 0.03594102126302643 0.022020851374104526 0.7888803590248035 +238.891960809 -0.5880713019710265 0.03769197803191883 0.02311304540425028 0.8075996816011534 +238.925379419 -0.5734855512032803 0.03682319432298265 0.022278843954877052 0.818084364862791 +238.965553319 -0.5569114281884976 0.03934392474727085 0.02327021473008288 0.8293130975964054 +239.005490219 -0.5481361669675943 0.04184904788369592 0.025048255195668417 0.8349658583229344 +239.04536653 -0.5485901390509745 0.040902715418226786 0.023647850247542943 0.8347554171049852 +239.078977707 -0.5491597841097845 0.040132500891461004 0.0229317141831777 0.8344381644995109 +239.119176823 -0.5473186385746069 0.04160296761048178 0.022751179345660618 0.8355799691191871 +239.158778348 -0.5445705381193571 0.04157381085158515 0.022324953055749167 0.837386496030793 +239.192218698 -0.550261828806127 0.04060233999774576 0.022601435837753075 0.8336981137340519 +239.225675996 -0.5547888547499716 0.039830711121631945 0.020129149871999628 0.8307933909355507 +239.259023645 -0.5568821236861582 0.04090318897651194 0.02436319252774728 0.8292259428528524 +239.298827899 -0.5666330578628691 0.03909509744202021 0.020062966517381382 0.8227976838005171 +239.332263476 -0.5756114966686485 0.040498957890201776 0.02240038010407117 0.816412556422229 +239.372266675 -0.5810681018589907 0.034107145619721294 0.01710974908846781 0.8129599129759072 +239.405604515 -0.5936616049119721 0.04081131010694034 0.021719302649958704 0.8033857154026927 +239.438948668 -0.627638922925641 0.04232908794896426 0.020738562164078395 0.7770762786120272 +239.472315909 -0.6432891548836509 0.040580441231301535 0.020878436195524163 0.7642619851206658 +239.505813017 -0.6415426694839114 0.040304059466855356 0.021763165689755417 0.7657185844950543 +239.54560886 -0.6325430527964304 0.036403340116676834 0.019860130466871854 0.7734142863984094 +239.578950703 -0.6249371039304736 0.038236719767479194 0.02133236741719445 0.7794462774898561 +239.612357077 -0.6262389018208373 0.038079984561546563 0.021600614098840124 0.7784010316620714 +239.64572434 -0.6294433047169053 0.03891086160143383 0.020487595873406907 0.7758010888184054 +239.679065633 -0.6327580022048975 0.03972190072611971 0.02002299569503784 0.7730708640815328 +239.712583156 -0.635157093496259 0.04406319601429506 0.02342248780517333 0.7707692835105762 +239.752536358 -0.636784906242236 0.04345734208913995 0.020565576239353314 0.7695410968067642 +239.792381657 -0.639159534914996 0.04638397560371163 0.022496762458775955 0.7673444542142165 +239.825895897 -0.6423973545820866 0.04518489070093774 0.023649082539650847 0.7646729270566037 +239.865988228 -0.6435476610183528 0.046117123960465585 0.023415442678233086 0.7636565562604728 +239.905810086 -0.6479926071669341 0.04708001288789257 0.025347004697747352 0.7597674531041243 +239.939230309 -0.6538293515860074 0.04695267676296676 0.02326001572880971 0.7548255406500957 +239.972745715 -0.6557733073619862 0.04713364913710568 0.023501531331391216 0.7531184943258411 +240.012586389 -0.6582987223720739 0.04786685633500892 0.02444960263871173 0.7508353835020748 +240.05252711 -0.6700262578599798 0.04300530392524151 0.017322349472025912 0.7408881790265104 +240.085969655 -0.7001708375552325 0.044196381582831165 0.019186952256127054 0.7123477654596175 +240.119389433 -0.7210171826308943 0.043655057963665735 0.02040226519295533 0.6912396153579402 +240.159243469 -0.7292553754727438 0.04315211551348799 0.022799589687022344 0.682498843208486 +240.199351733 -0.7328958141178934 0.03984653537153811 0.020058966915709527 0.6788767318988252 +240.239255958 -0.7392580145346286 0.040533371904498364 0.01993244109385431 0.6719057459944888 +240.272685278 -0.7440371721386948 0.04237320511099688 0.02124227062326568 0.6664547725864403 +240.306045977 -0.7491917310420214 0.040609613752215425 0.017808660763861464 0.6608672037640793 +240.346166128 -0.7535554430214993 0.040696543331009145 0.01900139803239348 0.6558482541917708 +240.386025549 -0.7563236059040115 0.04090972361520709 0.01872382837338613 0.652648769183797 +240.419548684 -0.7572022343054041 0.03799379700621332 0.02113207278048279 0.6517320640047987 +240.459425203 -0.7588321903106341 0.03810428629021577 0.01760080560694561 0.6499322902862001 +240.492855212 -0.7606906238959252 0.03776501398651325 0.01996377356278154 0.6477075159210062 +240.532733447 -0.7616749154204283 0.03730218337940114 0.017870713111560747 0.6466378491453291 +240.566139425 -0.7619988154362971 0.03600061610258555 0.019400489625791807 0.646285836078893 +240.599512374 -0.7639172030025358 0.0369748529559444 0.016244060771824553 0.6440492975660405 +240.63955173 -0.7619946257186881 0.03763024234044501 0.018282317981459534 0.6462305409731581 +240.673226409 -0.7627946315747083 0.03747640716029538 0.018868154977860513 0.645278127379886 +240.712977483 -0.7874763771384581 0.036703365723037104 0.019842236049353017 0.6149309750387045 +240.752841427 -0.7932514643598876 0.03650291347851922 0.019138810554269938 0.6074976193608018 +240.786276718 -0.7873101904571339 0.039632257426235176 0.022001393176875588 0.6148885158074824 +240.826387453 -0.7807397286608538 0.03861306972099767 0.019150943750093352 0.6233680680711665 +240.866407073 -0.7780167852440415 0.04018876600093528 0.01977046881212255 0.6266449341762831 +240.90626368 -0.7770544034990937 0.040793166763709254 0.02110658808902222 0.6277554328616332 +240.939623332 -0.773756269311581 0.03873000636236827 0.022850093583271003 0.6318853499895617 +240.972990291 -0.7705633609395449 0.040243112752171024 0.021782805146814233 0.6357185761431742 +241.006524855 -0.7649289126254382 0.0406568845388112 0.024278660844678453 0.6423716393154758 +241.046515904 -0.7585653724527895 0.041864354220823456 0.02236362999526692 0.6498660012761625 +241.086410399 -0.7590491325849261 0.0423037432165785 0.025020245235346746 0.6491754731660002 +241.126376736 -0.7608761501913889 0.04051535304488624 0.02207799609793322 0.6472546271181681 +241.159723766 -0.763111740906569 0.042180996459945566 0.024841424643631663 0.6444099146117063 +241.193190873 -0.7657056982338764 0.0412532437792336 0.02205006662221007 0.6414879173700928 +241.226680258 -0.7800509523526011 0.04082885668824386 0.021754262779086094 0.6240034200597536 +241.266542382 -0.7878570149977546 0.04050294899629278 0.02490181308660963 0.6140201419712802 +241.306530322 -0.7937301074622842 0.040448216200342994 0.023779360466155745 0.6064577481819666 +241.34648746 -0.7953611626425902 0.04038263266062048 0.0270381090412026 0.6041844127405975 +241.379869979 -0.7979080359031728 0.04169295130797545 0.025582584389363262 0.6007911412698537 +241.419866053 -0.8020346459463255 0.04031729865444801 0.027801714424672327 0.5952663326663196 +241.453324024 -0.8021744889231099 0.040203733268556315 0.02767496999175672 0.595091459515404 +241.486664985 -0.8021993865822173 0.04015406314749203 0.027802583829647017 0.5950553013899271 +241.519999899 -0.8006753898767827 0.040994413663027834 0.027608897116518285 0.5970562175323998 +241.553356735 -0.800629081395388 0.04167112845056958 0.02740393024429167 0.5970809121758955 +241.593333504 -0.8000552981777835 0.039282366786158714 0.02970205294485021 0.5979014998878972 +241.626691213 -0.799600529840944 0.04018235251939279 0.025221509036599372 0.5986553655535093 +241.660145009 -0.8006425422975403 0.04079847331279591 0.02910947405960326 0.5970424126966609 +241.700003673 -0.8004875900298837 0.04099529348393601 0.026387724774763914 0.5973631157859731 +242.720558703 -0.794450343530368 0.021729453520980112 0.03624393879652909 0.6058571278896314 +242.760454466 -0.7950640377780425 0.024836050594316345 0.034944504321129055 0.6050084528671501 +242.794103122 -0.7819008218387371 0.02060918070083504 0.03545406344966234 0.6220525507251421 +242.83403055 -0.7629413999181803 0.028900595538859982 0.04050008098162014 0.6445501681862077 +242.873829245 -0.7580740197433834 0.02470217157341355 0.03871078036053453 0.6505498126919601 +242.907301163 -0.758829264307354 0.02860248727435542 0.03957688868944677 0.6494564767820422 +242.947372815 -0.7623713427443437 0.02649413019336018 0.03832578769088851 0.6454604022133013 +242.987461621 -0.7637815838681922 0.02730291638025618 0.03640388850700824 0.6438687752970303 +243.027253869 -0.7616177267712572 0.027439704419571852 0.036416624517971244 0.6464203975338854 +243.060659886 -0.7604374493482504 0.030908341136354635 0.03497066186737446 0.6477318989253837 +243.09401031 -0.7735665648355493 0.033803728624669155 0.0376081341625404 0.6316943136869367 +243.127388229 -0.791929356078706 0.031169605853374218 0.03485865020635539 0.6088195341457948 +243.16074622 -0.7959355246960755 0.030774438423373705 0.034264996897322 0.6036269414581621 +243.194248473 -0.7929810467862657 0.03296087825419788 0.035888063432670286 0.6072945634908344 +243.233985182 -0.78504977820596 0.034002198937717595 0.03440189434575279 0.617541258436711 +243.267587731 -0.7785228240679044 0.03312143077675334 0.033703783679774224 0.6258348329985337 +243.307450961 -0.7743803674115186 0.03242764746757066 0.037178671653781824 0.6307941348977342 +243.340870149 -0.7748086221488291 0.028920749638342725 0.03282099712563151 0.6306805621167253 +243.374236749 -0.7759793421386333 0.03359608267896157 0.03408935798259871 0.6289398059234736 +243.407665296 -0.7772879387144771 0.032521657399209696 0.03450307063968944 0.6273558322399183 +243.447583812 -0.7788086853502265 0.03432244926460159 0.03554459571356544 0.6253123881829706 +243.487506729 -0.7818954694066684 0.03255672619385175 0.0353467846351223 0.6215546149107273 +243.520864347 -0.784278968627682 0.032986754940516415 0.03357024721393656 0.6186205718118247 +243.554311235 -0.7868957125355791 0.03698578020564404 0.036168606278630576 0.6149138326428871 +243.594197731 -0.7902765231685532 0.035827896450185366 0.03318934555182752 0.6108009873161794 +243.627636256 -0.7934598347217816 0.035382648985657204 0.03566265088442944 0.606545739549736 +243.661027148 -0.8052147827536443 0.038174259153005104 0.03720337355739193 0.5905825840380581 +243.700991524 -0.8291901340181825 0.037995973170304405 0.03515571618560939 0.5565645544671588 +243.734370962 -0.83509730954713 0.04003248448780763 0.036922347441520044 0.5473998758058738 +243.774402322 -0.8198314283616239 0.03764953561266079 0.032976020427303285 0.5704134672458818 +243.814480121 -0.8054633799247986 0.038357166197690716 0.03364830924751279 0.5904449700744229 +243.847913394 -0.8028464662978672 0.03550362042066799 0.03260220003156211 0.5942340793350336 +243.887739277 -0.8105031136087074 0.04011207707689189 0.03300265080081505 0.5834265584829745 +243.92769101 -0.8185163084845994 0.037888173152325906 0.032675454248351755 0.5723004925470424 +243.961154419 -0.823418853455404 0.03692683561043423 0.03360259745697052 0.5652332846092247 +244.001043379 -0.8268167768205759 0.034092419986228 0.032927749161793134 0.5604707733706146 +244.03450406 -0.8301858796962555 0.03593063620207311 0.034086782132382455 0.5552823478366015 +244.074515133 -0.8322518736899639 0.03527525947168676 0.03269428096774672 0.5523074857366608 +244.114529925 -0.8349697708900148 0.03588213317955448 0.03596343772328037 0.5479457868854224 +244.154597694 -0.834974971113073 0.035574076353461645 0.034544866154355945 0.5480492084919919 +244.188142254 -0.8341544866192296 0.03522830925146127 0.03421536706050674 0.5493401199049707 +244.2278879 -0.8339667080706517 0.037112152092703826 0.03520268466225661 0.5494387945799042 +244.261326426 -0.8338501520547195 0.03582483636170743 0.03121369300777566 0.549942006385032 +244.294858025 -0.8336788367480741 0.03572444280238121 0.03482435768593056 0.5499914776217899 +244.33459512 -0.8333759764707798 0.032271374510706746 0.031734460715658185 0.5508502194169032 +244.374535448 -0.8338381907472637 0.03687101017793684 0.034501580212018 0.5496944980829462 +244.407988351 -0.8334485181807082 0.03500440691503399 0.030701787266795948 0.5506320543680081 +244.441348514 -0.8343814765829702 0.03518358394791308 0.03570219247145967 0.5489034709386829 +244.474893758 -0.8336025385320821 0.0354563778747961 0.03449439015292214 0.5501452445207959 +244.514606905 -0.8334164674511001 0.03513605823197383 0.033270832259868106 0.5505229340490589 +261.742808571 -0.492390337246879 0.02761474303904702 0.02918992189195911 0.8694464504572628 +261.776378882 -0.48682071379739567 0.029633345065584637 0.031439369275732446 0.8724328189250805 +261.816359221 -0.46278316432593464 0.042966379369456555 0.039929856322520406 0.8845288235181586 +261.856255245 -0.4364826657557189 0.039018122632218266 0.03889695019679967 0.8980242178618716 +261.889617012 -0.40534723562500025 0.05057737482168146 0.040945473304809334 0.9118437453550706 +261.92301596 -0.4110476949285202 0.05790648721927345 0.04770811563288436 0.9085210877765215 +261.963055751 -0.4528477920897629 0.053507990588702134 0.04520407847818043 0.888832022055644 +262.00313968 -0.4702518066407381 0.06310344281097734 0.05529647924120107 0.8785348560188441 +262.036483884 -0.4761414437583875 0.060736790013897105 0.05157378623604043 0.8757513987700879 +262.076405694 -0.4812953535919647 0.06598484871490432 0.05747814962520306 0.8721794796178821 +262.109740578 -0.4876601820802172 0.0667707860672644 0.058342405950877044 0.8685190686503734 +262.143299317 -0.4919022542838608 0.07434590779148013 0.0632902792169759 0.8651584818874252 +262.183013211 -0.4984287966872071 0.07269072338000834 0.06320437753256815 0.8615625340206912 +262.216442071 -0.5067808238927949 0.08285415137576507 0.0707274172226619 0.8551643225645895 +262.256639628 -0.5185293751509472 0.0817992261845819 0.07073329780268191 0.8481939485065483 +262.29649362 -0.5277778670740825 0.0909772739298654 0.0791509258262726 0.8407786805074605 +262.329980071 -0.5379426006928065 0.09243346559147012 0.08101851306186507 0.8339723096961043 +262.369948234 -0.5494691869768229 0.0989374779731067 0.08267584488145745 0.8255057193554068 +262.409808927 -0.5576234483276423 0.1040292906370292 0.08986680595659953 0.8186317571117704 +262.449800633 -0.5633853831281898 0.10419232251821237 0.0906687360471637 0.8145674007160939 +262.489861551 -0.5671783877908197 0.10166763029185129 0.090148851798643 0.8123087798942176 +262.529911177 -0.5756197916112045 0.10696082555957338 0.09720520317847695 0.8048430814612989 +262.56997375 -0.5687882709979508 0.09596812028204671 0.08556701867675583 0.8123720256012412 +262.609853522 -0.5615109406583646 0.08894432496118791 0.08335492231888024 0.8184414013864222 +262.643276813 -0.5621995622183242 0.09236672514071978 0.08203435753785487 0.8177226941400458 +262.68331509 -0.5652046223756785 0.09149095540931225 0.0792894395324198 0.8160185811010021 +262.716713959 -0.5792666486301181 0.07541814343364919 0.07346740403770038 0.8083098378529203 +262.756684576 -0.6327026201780289 0.07314176725312356 0.07318043642833083 0.7674518226098013 +262.796721622 -0.6867117743077357 0.07253510126562135 0.07413523648881502 0.7194925745428198 +262.836624835 -0.7026505013220113 0.07255152169512319 0.07050692745189623 0.7043062706471819 +262.869991225 -0.6961192155330221 0.07078602036596023 0.07135737268391958 0.7108554722656977 +262.903445924 -0.6896244783031741 0.06977794592203947 0.0636563069737576 0.7179811917943524 +262.936843485 -0.6974285771125298 0.06741535042046969 0.061791339797198495 0.7107955969760537 +262.976781499 -0.7175640009150057 0.07108412266598935 0.05977200750756993 0.6902725977569812 +263.01015257 -0.7339481096858896 0.0708928757550175 0.05636305072004498 0.6731400886659645 +263.050089341 -0.7488010480670951 0.07039084133393367 0.04925384282377272 0.6572033009936971 +263.083433029 -0.7650276565384505 0.06608428793880212 0.046159723792380945 0.6389325719653987 +263.123580477 -0.759769112365214 0.07120439637288271 0.051619252283097564 0.6442175739815563 +263.163652675 -0.7395908309264311 0.08969193491507335 0.03875318628266474 0.6659271357841506 +263.203570002 -0.7138426882345714 0.08041083482311241 0.03470125541199424 0.6948082735330102 +263.243520895 -0.6905059060909539 0.0680043831659756 0.033929833028546276 0.719323129027825 +263.276978085 -0.6735392545108225 0.07094116690038248 0.05053340002649777 0.7340017704022106 +263.310351264 -0.6535198118300644 0.08169482521562137 0.048827371442678846 0.7509019236069282 +263.350348329 -0.6285031016449124 0.06540252568789343 0.033638237266481574 0.7743221744532267 +263.390215901 -0.597597759975782 0.08041849222986115 0.045151379258440816 0.7964741906241932 +263.423631479 -0.5855794838506132 0.06666940243972289 0.03496655077367687 0.8071116398606649 +263.463657246 -0.5845601318882496 0.07226772360141404 0.034338932523008195 0.8073956069024912 +263.497184693 -0.589325291486152 0.06999637263091353 0.035476477527406215 0.8040756358549386 +263.537050353 -0.5931609242298242 0.07129197916553609 0.03660375961357539 0.8010853490456764 +263.577229306 -0.590704120917637 0.0705189697772078 0.036731944054572675 0.8029610704875143 +263.617137181 -0.5877951213932916 0.06688175186599517 0.03978050132459503 0.8052584915714612 +263.650505531 -0.5885396442081441 0.06680449997128976 0.03910127750133417 0.80475420848648 +263.690427283 -0.5860902186568708 0.06565280929277087 0.040128839421852636 0.8065839326898123 +263.730486075 -0.5916245608673145 0.06052850817983245 0.03809646185305764 0.8030350791032479 +263.764034955 -0.5954060180692134 0.06401254344918918 0.04038745934692126 0.7998518119349546 +263.803941131 -0.6009381353312887 0.06158764547490287 0.038086154078189126 0.7960086458682116 +263.843968779 -0.601188718705783 0.06170522943653155 0.03720362711233978 0.7958520461057882 +263.883795881 -0.6041257681638235 0.06243057055249998 0.03910166782851432 0.793476867762374 +263.917149503 -0.6057301358239046 0.0590029354690612 0.03650181601052091 0.7926394347928458 +263.950528031 -0.6080634013365945 0.06048437618307217 0.03908286242362899 0.7906156272535295 +263.98395776 -0.6115813643241973 0.0615029372596799 0.03869753532337942 0.7878376255800946 +264.02402125 -0.6096917676270661 0.06497343686839953 0.04135971642311708 0.7888876820224101 +264.057500051 -0.6052202428728259 0.06308817907674807 0.03760298037333147 0.7926628256357054 +264.097336955 -0.5926158386131634 0.06392423657699592 0.04070258960181896 0.80191237613755 +264.137350425 -0.5713310415499188 0.06501045153289282 0.03998309797578019 0.8171632848025134 +264.177556814 -0.5624531659050787 0.06614190727716705 0.0404084276423265 0.8231882185992262 +264.217318908 -0.5494980844410502 0.06670474225761372 0.03930034106594068 0.8319001236615261 +264.250654226 -0.5335587415490124 0.06444960489227361 0.040048110162840045 0.8423523411365634 +264.284025954 -0.5079092288151696 0.06365611557014426 0.035546367802650525 0.8583196199382481 +264.317373401 -0.48606852198966877 0.0629840680658517 0.03542041845945029 0.8709281216362305 +264.350790037 -0.4737681769746858 0.06021818663254122 0.030406992407089835 0.8780620133552792 +264.390799451 -0.46115125378317784 0.059743582610039564 0.029793664094503413 0.8848065116458765 +264.424157809 -0.45145793664761047 0.056941380067110955 0.026838732954425747 0.8900691507334337 +264.464123518 -0.4375945786764074 0.05598087255134386 0.02476207947184981 0.8970863760205243 +264.497628602 -0.4232633740128462 0.0557021561900418 0.02447444228530421 0.9039614967961107 +264.537516814 -0.40976446304231784 0.051697068436898794 0.020471949480633984 0.9104951384972542 +264.570953282 -0.394404762834615 0.05649619527227564 0.022199131234126868 0.9169298018635768 +347.143468116 0.4730519346282629 -0.06221664456394384 -0.021076123747344584 -0.8785822404826131 +347.18332169 0.4648592034940948 -0.06187319382945125 -0.013535347229064071 -0.8831163135100425 +347.216678041 0.44476200771752367 -0.0750702575895775 -0.02079571159351462 -0.8922548690233253 +347.250059703 0.4365460120904825 -0.04296919106067979 -0.015423656303356275 -0.8985228649142565 +347.28996064 0.43028404268218046 -0.06387543895312994 -0.04280942086964046 -0.899412544051077 +347.323446715 0.44809170396511877 -0.04104020887525186 -0.05126545982605537 -0.8915724192245604 +347.356899921 0.48925795075881234 -0.04803711142677635 -0.059733227690227084 -0.8687640848094301 +347.396893155 0.5239674490283242 -0.03998553782005044 -0.053927633452822386 -0.84908838142691 +347.43697453 0.5315155721034144 -0.021464538394438206 -0.058758469653814725 -0.8447354097272327 +347.470330691 0.5037041385344577 -0.030576215470486272 -0.06966944538685836 -0.8605192643109448 +347.510236723 0.4545578437293535 -0.029488655136588736 -0.0791907512372356 -0.8866997298075816 +347.543585399 0.43450533757488047 -0.034308086723568595 -0.08974978294096142 -0.8955294764921797 +347.583513418 0.393449902051833 -0.014749737676544522 -0.08293897946586651 -0.9154784243765862 +347.616963521 0.38354208486947444 -0.02777757660563286 -0.077284434012173 -0.919864659409912 +347.65041836 0.38698802213408134 -0.03715615252394913 -0.08295334299492432 -0.9175938284122966 +347.690161084 0.3983786712713217 -0.007501916893747665 -0.07418855541829539 -0.9141849997478422 +347.723539363 0.40259613132462235 -0.021110313238303816 -0.07665253807862202 -0.9119183615453379 +347.756932275 0.40113316061560916 -0.013092739112825628 -0.07180545208523943 -0.9131071923317052 +347.796937718 0.3819278328053421 -0.022587278562296493 -0.060477766673134876 -0.9219345883054795 +349.31776045 0.40910432265814306 0.01678086795843859 -0.005840281520681834 -0.9123146095315708 +349.357902582 0.40562481581795595 0.02008156672452735 -0.004100331383478491 -0.9138098416810159 +349.3977756 0.37812767583828616 0.020411226141690005 -0.002022521124874527 -0.9255262027737672 +349.431331935 0.3660248648532596 0.01859780583343839 -0.004765823635339921 -0.930407011394697 +349.470994107 0.3639449660241751 0.01633510706619165 -0.004409753837888614 -0.9312667609519237 +349.504374961 0.36648487325138274 0.008994882729107818 -0.006562673605771192 -0.9303573835240689 +349.537824176 0.36488204611016994 0.00864716930228317 -0.007275381984313243 -0.9309851705083685 +349.577759027 0.3635893202800476 0.004373257049036545 -0.008436823042478997 -0.9315108699408859 +349.61780063 0.3554795000826525 0.005240242412714433 -0.005117332223630488 -0.9346553791592689 +349.657798937 0.3473621577382386 0.0036648960411413793 -0.005554370365892558 -0.9377074431176715 +349.691153707 0.34494037906991837 0.004945084104685389 -0.002184370522349425 -0.9386090291253968 diff --git a/assets/standard_fanblade.jpg b/assets/standard_fanblade.jpg new file mode 100755 index 0000000..d924c5c Binary files /dev/null and b/assets/standard_fanblade.jpg differ diff --git a/assets/tiny_resnet.onnx b/assets/tiny_resnet.onnx new file mode 100644 index 0000000..1c2e6ec Binary files /dev/null and b/assets/tiny_resnet.onnx differ diff --git a/assets/yolo11.bin b/assets/yolo11.bin new file mode 100644 index 0000000..9ce81f1 Binary files /dev/null and b/assets/yolo11.bin differ diff --git a/assets/yolo11.xml b/assets/yolo11.xml new file mode 100644 index 0000000..e3e3391 --- /dev/null +++ b/assets/yolo11.xml @@ -0,0 +1,23449 @@ + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + 1 + 2 + 8400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + 16 + 3 + 3 + 3 + + + + + + + + 16 + 3 + 3 + 3 + + + + + 16 + 3 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 3 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 3 + 3 + 3 + + + + + + + + 1 + 3 + 640 + 640 + + + 16 + 3 + 3 + 3 + + + + + 1 + 16 + 320 + 320 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 320 + 320 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 320 + 320 + + + + + + + 1 + 16 + 320 + 320 + + + + + 1 + 16 + 320 + 320 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 320 + 320 + + + + + + + + + 1 + 16 + 320 + 320 + + + + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 16 + 3 + 3 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 16 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 320 + 320 + + + 32 + 16 + 3 + 3 + + + + + 1 + 32 + 160 + 160 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 160 + 160 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 160 + 160 + + + + + + + 1 + 32 + 160 + 160 + + + + + 1 + 32 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + 32 + 32 + 1 + 1 + + + + + + + + 32 + 32 + 1 + 1 + + + + + 32 + 32 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 160 + 160 + + + 32 + 32 + 1 + 1 + + + + + 1 + 32 + 160 + 160 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 160 + 160 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 160 + 160 + + + + + + + 1 + 32 + 160 + 160 + + + + + 1 + 32 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 32 + 160 + 160 + + + + 2 + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + + + + + + 8 + 16 + 3 + 3 + + + + + + + + 8 + 16 + 3 + 3 + + + + + 8 + 16 + 3 + 3 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 3 + 3 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 160 + 160 + + + 8 + 16 + 3 + 3 + + + + + 1 + 8 + 160 + 160 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 160 + 160 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 160 + 160 + + + + + + + 1 + 8 + 160 + 160 + + + + + 1 + 8 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 160 + 160 + + + + + + + + + 1 + 8 + 160 + 160 + + + + + + + + 16 + 8 + 3 + 3 + + + + + + + + 16 + 8 + 3 + 3 + + + + + 16 + 8 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 8 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 8 + 3 + 3 + + + + + + + + 1 + 8 + 160 + 160 + + + 16 + 8 + 3 + 3 + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 160 + 160 + + + + + + + 1 + 16 + 160 + 160 + + + + + 1 + 16 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + + + 1 + 16 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + + + 1 + 48 + 160 + 160 + + + + + + + + 64 + 48 + 1 + 1 + + + + + + + + 64 + 48 + 1 + 1 + + + + + 64 + 48 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 48 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 160 + 160 + + + 64 + 48 + 1 + 1 + + + + + 1 + 64 + 160 + 160 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 160 + 160 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 160 + 160 + + + + + + + 1 + 64 + 160 + 160 + + + + + 1 + 64 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 160 + 160 + + + + + + + + + 1 + 64 + 160 + 160 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 160 + 160 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 64 + 80 + 80 + + + + 2 + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 32 + 3 + 3 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 32 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 80 + 80 + + + 16 + 32 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 16 + 3 + 3 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 16 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 80 + 80 + + + 32 + 16 + 3 + 3 + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 80 + 80 + + + + + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 96 + 80 + 80 + + + + + + + + 128 + 96 + 1 + 1 + + + + + + + + 128 + 96 + 1 + 1 + + + + + 128 + 96 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 96 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 80 + 80 + + + 128 + 96 + 1 + 1 + + + + + 1 + 128 + 80 + 80 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 80 + 80 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 80 + 80 + + + + + + + 1 + 128 + 80 + 80 + + + + + 1 + 128 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 80 + 80 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 40 + 40 + + + + 2 + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 256 + 128 + 3 + 3 + + + + + + + + 256 + 128 + 3 + 3 + + + + + 256 + 128 + 3 + 3 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 128 + 3 + 3 + + + 256 + 1 + 1 + 1 + + + + + 256 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 256 + 128 + 3 + 3 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 20 + 20 + + + + 2 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 384 + 20 + 20 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 20 + 20 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 128 + 256 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 512 + 20 + 20 + + + + + + + + 256 + 512 + 1 + 1 + + + + + + + + 256 + 512 + 1 + 1 + + + + + 256 + 512 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 512 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 512 + 1 + 1 + + + + + + + + 1 + 512 + 20 + 20 + + + 256 + 512 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 20 + 20 + + + + 2 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 256 + 128 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 4 + + + + + + + + 1 + 256 + 20 + 20 + + + 4 + + + + + 1 + 2 + 128 + 400 + + + + + + + + + + + + + + 3 + + + + + + + 1 + 2 + 128 + 400 + + + + 3 + + + + + 1 + 2 + 32 + 400 + + + 1 + 2 + 32 + 400 + + + 1 + 2 + 64 + 400 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 2 + 32 + 400 + + + 1 + 1 + 1 + 1 + + + + + 1 + 2 + 32 + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 32 + 400 + + + + + + + + + 1 + 2 + 32 + 400 + + + + + + + + 1 + 2 + 32 + 400 + + + 1 + 2 + 32 + 400 + + + + + 1 + 2 + 400 + 400 + + + + + + + + 1 + 2 + 400 + 400 + + + + + 1 + 2 + 400 + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 400 + 400 + + + + + + + + + 1 + 2 + 400 + 400 + + + + + + + + 1 + 2 + 64 + 400 + + + 1 + 2 + 400 + 400 + + + + + 1 + 2 + 64 + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 64 + 400 + + + + + + + + + 1 + 2 + 64 + 400 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 4 + + + + + + + + 2 + + + + + + + + + + + + + + 4 + + + 2 + + + + + + 2 + + + + + + + + 1 + 256 + 20 + 20 + + + + + 4 + + + + + + + + 2 + + + + + + + + + + + + + + 4 + + + 2 + + + + + + 2 + + + + + + + + 2 + + + 2 + + + + + 4 + + + + + + + + 1 + 2 + 64 + 400 + + + 4 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 2 + 64 + 400 + + + 4 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + 128 + 1 + 1 + 1 + 1 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 1 + 1 + 3 + 3 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 256 + 128 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 128 + 256 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 1 + 256 + 20 + 20 + + + 2 + + + 2 + + + + + 1 + 256 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 40 + 40 + + + + + + + + + 1 + 256 + 40 + 40 + + + + + + + + 1 + 256 + 40 + 40 + + + 1 + 128 + 40 + 40 + + + + + 1 + 384 + 40 + 40 + + + + + + + + 128 + 384 + 1 + 1 + + + + + + + + 128 + 384 + 1 + 1 + + + + + 128 + 384 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 384 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 40 + 40 + + + 128 + 384 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 40 + 40 + + + + 2 + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 64 + 3 + 3 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 32 + 3 + 3 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 32 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 64 + 32 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 1 + 128 + 40 + 40 + + + 2 + + + 2 + + + + + 1 + 128 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + 1 + 128 + 80 + 80 + + + 1 + 128 + 80 + 80 + + + + + 1 + 256 + 80 + 80 + + + + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 80 + 80 + + + 64 + 256 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 64 + 80 + 80 + + + + 2 + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 32 + 3 + 3 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 32 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 80 + 80 + + + 16 + 32 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 16 + 3 + 3 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 16 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 80 + 80 + + + 32 + 16 + 3 + 3 + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 80 + 80 + + + + + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 96 + 80 + 80 + + + + + + + + 64 + 96 + 1 + 1 + + + + + + + + 64 + 96 + 1 + 1 + + + + + 64 + 96 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 96 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 80 + 80 + + + 64 + 96 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 38 + 64 + 1 + 1 + + + + + + + + 38 + 64 + 1 + 1 + + + + + 38 + 64 + 1 + 1 + + + + + + + + 38 + 1 + 1 + 1 + + + + + + + + 38 + 64 + 1 + 1 + + + 38 + 1 + 1 + 1 + + + + + 38 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 38 + 64 + 1 + 1 + + + + + 1 + 38 + 80 + 80 + + + + + + + + 1 + 38 + 1 + 1 + + + + + + + + 1 + 38 + 80 + 80 + + + 1 + 38 + 1 + 1 + + + + + 1 + 38 + 80 + 80 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 38 + 80 + 80 + + + + + 1 + 102 + 80 + 80 + + + + + + + + 3 + + + + + + + + 1 + 102 + 80 + 80 + + + 3 + + + + + 1 + 102 + 6400 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 128 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 40 + 40 + + + + 2 + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 64 + 3 + 3 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 32 + 3 + 3 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 32 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 64 + 32 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 128 + 3 + 3 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 64 + 128 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + 128 + 1 + 1 + 1 + 1 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 128 + 1 + 1 + 3 + 3 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 38 + 64 + 1 + 1 + + + + + + + + 38 + 64 + 1 + 1 + + + + + 38 + 64 + 1 + 1 + + + + + + + + 38 + 1 + 1 + 1 + + + + + + + + 38 + 64 + 1 + 1 + + + 38 + 1 + 1 + 1 + + + + + 38 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 38 + 64 + 1 + 1 + + + + + 1 + 38 + 40 + 40 + + + + + + + + 1 + 38 + 1 + 1 + + + + + + + + 1 + 38 + 40 + 40 + + + 1 + 38 + 1 + 1 + + + + + 1 + 38 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 38 + 40 + 40 + + + + + 1 + 102 + 40 + 40 + + + + + + + + 3 + + + + + + + + 1 + 102 + 40 + 40 + + + 3 + + + + + 1 + 102 + 1600 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 256 + 20 + 20 + + + + + 1 + 384 + 20 + 20 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 20 + 20 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 20 + 20 + + + + 2 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 384 + 20 + 20 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 20 + 20 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 256 + 3 + 3 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 20 + 20 + + + 64 + 256 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 256 + 1 + 1 + 3 + 3 + + + + + + + + 256 + 1 + 1 + 3 + 3 + + + + + 256 + 1 + 1 + 3 + 3 + + + + + + + + 256 + 1 + 1 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 3 + 3 + + + 256 + 1 + 1 + 1 + 1 + + + + + 256 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 1 + 1 + 3 + 3 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 64 + 256 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 38 + 64 + 1 + 1 + + + + + + + + 38 + 64 + 1 + 1 + + + + + 38 + 64 + 1 + 1 + + + + + + + + 38 + 1 + 1 + 1 + + + + + + + + 38 + 64 + 1 + 1 + + + 38 + 1 + 1 + 1 + + + + + 38 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 38 + 64 + 1 + 1 + + + + + 1 + 38 + 20 + 20 + + + + + + + + 1 + 38 + 1 + 1 + + + + + + + + 1 + 38 + 20 + 20 + + + 1 + 38 + 1 + 1 + + + + + 1 + 38 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 38 + 20 + 20 + + + + + 1 + 102 + 20 + 20 + + + + + + + + 3 + + + + + + + + 1 + 102 + 20 + 20 + + + 3 + + + + + 1 + 102 + 400 + + + + + + + + 1 + 102 + 6400 + + + 1 + 102 + 1600 + + + 1 + 102 + 400 + + + + + 1 + 102 + 8400 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 102 + 8400 + + + + 2 + + + + + 1 + 64 + 8400 + + + 1 + 38 + 8400 + + + + + + + + 4 + + + + + + + + 1 + 64 + 8400 + + + 4 + + + + + 1 + 4 + 16 + 8400 + + + + + + + + 4 + + + + + + + 1 + 4 + 16 + 8400 + + + 4 + + + + + 1 + 16 + 4 + 8400 + + + + + + + + 1 + 16 + 4 + 8400 + + + + + 1 + 16 + 4 + 8400 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 4 + 8400 + + + 1 + 16 + 1 + 1 + + + + + 1 + 1 + 4 + 8400 + + + + + + + + 3 + + + + + + + + 1 + 1 + 4 + 8400 + + + 3 + + + + + 1 + 4 + 8400 + + + + + + + + 1 + 4 + 8400 + + + + + 3 + + + + + + + + 1 + + + + + + + + 3 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 1 + + + + + + + + 1 + + + + + 1 + + + + + + + + + 1 + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + 1 + + + + + 2 + + + + + + + 1 + 4 + 8400 + + + + 2 + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 1 + 1 + + + + + + + + 1 + 2 + 8400 + + + 1 + 1 + 1 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 4 + 8400 + + + + + + + + 1 + 1 + 8400 + + + + + + + + 1 + 4 + 8400 + + + 1 + 1 + 8400 + + + + + 1 + 4 + 8400 + + + + + + + 1 + 38 + 8400 + + + + + 1 + 38 + 8400 + + + + + + + + 16 + 64 + 3 + 3 + + + + + + + + 16 + 64 + 3 + 3 + + + + + 16 + 64 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 64 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 16 + 64 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 80 + 80 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 80 + 80 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 80 + 80 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 80 + 80 + + + + + + + + + 1 + 8 + 80 + 80 + + + + + + + + 3 + + + + + + + + 1 + 8 + 80 + 80 + + + 3 + + + + + 1 + 8 + 6400 + + + + + + + + 16 + 128 + 3 + 3 + + + + + + + + 16 + 128 + 3 + 3 + + + + + 16 + 128 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 128 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 16 + 128 + 3 + 3 + + + + + 1 + 16 + 40 + 40 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 40 + 40 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 40 + 40 + + + + + + + 1 + 16 + 40 + 40 + + + + + 1 + 16 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 40 + 40 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 40 + 40 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 40 + 40 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 40 + 40 + + + + + + + 1 + 16 + 40 + 40 + + + + + 1 + 16 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 40 + 40 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 40 + 40 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 40 + 40 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 40 + 40 + + + + + + + + + 1 + 8 + 40 + 40 + + + + + + + + 3 + + + + + + + + 1 + 8 + 40 + 40 + + + 3 + + + + + 1 + 8 + 1600 + + + + + + + + 16 + 256 + 3 + 3 + + + + + + + + 16 + 256 + 3 + 3 + + + + + 16 + 256 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 256 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 20 + 20 + + + 16 + 256 + 3 + 3 + + + + + 1 + 16 + 20 + 20 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 20 + 20 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 20 + 20 + + + + + + + 1 + 16 + 20 + 20 + + + + + 1 + 16 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 20 + 20 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 20 + 20 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 20 + 20 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 20 + 20 + + + + + + + 1 + 16 + 20 + 20 + + + + + 1 + 16 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 20 + 20 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 20 + 20 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 20 + 20 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 20 + 20 + + + + + + + + + 1 + 8 + 20 + 20 + + + + + + + + 3 + + + + + + + + 1 + 8 + 20 + 20 + + + 3 + + + + + 1 + 8 + 400 + + + + + + + + 1 + 8 + 6400 + + + 1 + 8 + 1600 + + + 1 + 8 + 400 + + + + + 1 + 8 + 8400 + + + + + + + + 4 + + + + + + + + 1 + 8 + 8400 + + + 4 + + + + + 1 + 4 + 2 + 8400 + + + + + + + + 3 + + + + + + + + 3 + + + + + + + + 3 + + + + + + + + 1 + 4 + 2 + 8400 + + + 3 + + + 3 + + + 3 + + + + + 1 + 4 + 2 + 8400 + + + + + + + + 1 + 1 + 1 + 8400 + + + + + + + + 1 + 4 + 2 + 8400 + + + 1 + 1 + 1 + 8400 + + + + + 1 + 4 + 2 + 8400 + + + + + + + + 1 + 1 + 2 + 8400 + + + + + + + + 1 + 4 + 2 + 8400 + + + 1 + 1 + 2 + 8400 + + + + + 1 + 4 + 2 + 8400 + + + + + + + + 1 + 3 + 640 + 640 + + + + + 4 + + + + + + + + 1 + + + + + + + + + + + + + + 4 + + + 1 + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + 3 + + + + + + + + 1 + 4 + 2 + 8400 + + + 3 + + + + + 1 + 8 + 8400 + + + + + + + + 1 + 4 + 8400 + + + 1 + 38 + 8400 + + + 1 + 8 + 8400 + + + + + 1 + 50 + 8400 + + + + + + + 1 + 50 + 8400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/yolo11_buff_int8.bin b/assets/yolo11_buff_int8.bin new file mode 100644 index 0000000..14ee67d Binary files /dev/null and b/assets/yolo11_buff_int8.bin differ diff --git a/assets/yolo11_buff_int8.xml b/assets/yolo11_buff_int8.xml new file mode 100644 index 0000000..0a7ad3d --- /dev/null +++ b/assets/yolo11_buff_int8.xml @@ -0,0 +1,23068 @@ + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + 1 + 2 + 8400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + 16 + 3 + 3 + 3 + + + + + + + + 16 + 3 + 3 + 3 + + + + + 16 + 3 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 3 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 3 + 3 + 3 + + + + + + + + 1 + 3 + 640 + 640 + + + 16 + 3 + 3 + 3 + + + + + 1 + 16 + 320 + 320 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 320 + 320 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 320 + 320 + + + + + + + 1 + 16 + 320 + 320 + + + + + 1 + 16 + 320 + 320 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 320 + 320 + + + + + + + + + 1 + 16 + 320 + 320 + + + + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 16 + 3 + 3 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 16 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 320 + 320 + + + 32 + 16 + 3 + 3 + + + + + 1 + 32 + 160 + 160 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 160 + 160 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 160 + 160 + + + + + + + 1 + 32 + 160 + 160 + + + + + 1 + 32 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + 32 + 32 + 1 + 1 + + + + + + + + 32 + 32 + 1 + 1 + + + + + 32 + 32 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 160 + 160 + + + 32 + 32 + 1 + 1 + + + + + 1 + 32 + 160 + 160 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 160 + 160 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 160 + 160 + + + + + + + 1 + 32 + 160 + 160 + + + + + 1 + 32 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + + 1 + 32 + 160 + 160 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 32 + 160 + 160 + + + + 2 + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + + + + + + 8 + 16 + 3 + 3 + + + + + + + + 8 + 16 + 3 + 3 + + + + + 8 + 16 + 3 + 3 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 3 + 3 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 160 + 160 + + + 8 + 16 + 3 + 3 + + + + + 1 + 8 + 160 + 160 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 160 + 160 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 160 + 160 + + + + + + + 1 + 8 + 160 + 160 + + + + + 1 + 8 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 160 + 160 + + + + + + + + + 1 + 8 + 160 + 160 + + + + + + + + 16 + 8 + 3 + 3 + + + + + + + + 16 + 8 + 3 + 3 + + + + + 16 + 8 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 8 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 8 + 3 + 3 + + + + + + + + 1 + 8 + 160 + 160 + + + 16 + 8 + 3 + 3 + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 160 + 160 + + + + + + + 1 + 16 + 160 + 160 + + + + + 1 + 16 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + + + 1 + 16 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 160 + 160 + + + + + 1 + 48 + 160 + 160 + + + + + + + + 64 + 48 + 1 + 1 + + + + + + + + 64 + 48 + 1 + 1 + + + + + 64 + 48 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 48 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 160 + 160 + + + 64 + 48 + 1 + 1 + + + + + 1 + 64 + 160 + 160 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 160 + 160 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 160 + 160 + + + + + + + 1 + 64 + 160 + 160 + + + + + 1 + 64 + 160 + 160 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 160 + 160 + + + + + + + + + 1 + 64 + 160 + 160 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 160 + 160 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 64 + 80 + 80 + + + + 2 + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 32 + 3 + 3 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 32 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 80 + 80 + + + 16 + 32 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 16 + 3 + 3 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 16 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 80 + 80 + + + 32 + 16 + 3 + 3 + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 80 + 80 + + + + + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 96 + 80 + 80 + + + + + + + + 128 + 96 + 1 + 1 + + + + + + + + 128 + 96 + 1 + 1 + + + + + 128 + 96 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 96 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 80 + 80 + + + 128 + 96 + 1 + 1 + + + + + 1 + 128 + 80 + 80 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 80 + 80 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 80 + 80 + + + + + + + 1 + 128 + 80 + 80 + + + + + 1 + 128 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 80 + 80 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 40 + 40 + + + + 2 + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 256 + 128 + 3 + 3 + + + + + + + + 256 + 128 + 3 + 3 + + + + + 256 + 128 + 3 + 3 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 128 + 3 + 3 + + + 256 + 1 + 1 + 1 + + + + + 256 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 256 + 128 + 3 + 3 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 20 + 20 + + + + 2 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 384 + 20 + 20 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 20 + 20 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 128 + 256 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 512 + 20 + 20 + + + + + + + + 256 + 512 + 1 + 1 + + + + + + + + 256 + 512 + 1 + 1 + + + + + 256 + 512 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 512 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 512 + 1 + 1 + + + + + + + + 1 + 512 + 20 + 20 + + + 256 + 512 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 20 + 20 + + + + 2 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 256 + 128 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 4 + + + + + + + + 1 + 256 + 20 + 20 + + + 4 + + + + + 1 + 2 + 128 + 400 + + + + + + + + + + + + + + 3 + + + + + + + 1 + 2 + 128 + 400 + + + + 3 + + + + + 1 + 2 + 32 + 400 + + + 1 + 2 + 32 + 400 + + + 1 + 2 + 64 + 400 + + + + + + + + 1 + 2 + 32 + 400 + + + 1 + 2 + 32 + 400 + + + + + 1 + 2 + 400 + 400 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 2 + 400 + 400 + + + 1 + 1 + 1 + 1 + + + + + 1 + 2 + 400 + 400 + + + + + + + + 1 + 2 + 400 + 400 + + + + + 1 + 2 + 400 + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 400 + 400 + + + + + + + + + 1 + 2 + 400 + 400 + + + + + + + + 1 + 2 + 64 + 400 + + + 1 + 2 + 400 + 400 + + + + + 1 + 2 + 64 + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 64 + 400 + + + + + + + + + 1 + 2 + 64 + 400 + + + + + + + + 4 + + + + + + + + 1 + 2 + 64 + 400 + + + 4 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 4 + + + + + + + + 1 + 2 + 64 + 400 + + + 4 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + 128 + 1 + 1 + 1 + 1 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 1 + 1 + 3 + 3 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 128 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 256 + 128 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 256 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 128 + 256 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 256 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 1 + 256 + 20 + 20 + + + 2 + + + 2 + + + + + 1 + 256 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 40 + 40 + + + + + + + + + 1 + 256 + 40 + 40 + + + + + + + + 1 + 256 + 40 + 40 + + + 1 + 128 + 40 + 40 + + + + + 1 + 384 + 40 + 40 + + + + + + + + 128 + 384 + 1 + 1 + + + + + + + + 128 + 384 + 1 + 1 + + + + + 128 + 384 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 384 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 40 + 40 + + + 128 + 384 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 40 + 40 + + + + 2 + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 64 + 3 + 3 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 32 + 3 + 3 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 32 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 64 + 32 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 1 + 128 + 40 + 40 + + + 2 + + + 2 + + + + + 1 + 128 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + + 1 + 128 + 80 + 80 + + + + + + + + 1 + 128 + 80 + 80 + + + 1 + 128 + 80 + 80 + + + + + 1 + 256 + 80 + 80 + + + + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 80 + 80 + + + 64 + 256 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 64 + 80 + 80 + + + + 2 + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 32 + 3 + 3 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 32 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 80 + 80 + + + 16 + 32 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 16 + 3 + 3 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 16 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 80 + 80 + + + 32 + 16 + 3 + 3 + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 80 + 80 + + + + + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 32 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + + 1 + 32 + 80 + 80 + + + + + + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + 1 + 32 + 80 + 80 + + + + + 1 + 96 + 80 + 80 + + + + + + + + 64 + 96 + 1 + 1 + + + + + + + + 64 + 96 + 1 + 1 + + + + + 64 + 96 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 96 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 80 + 80 + + + 64 + 96 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 80 + 80 + + + + + + + 1 + 64 + 80 + 80 + + + + + 1 + 64 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + + 1 + 64 + 80 + 80 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + 1 + 1 + 1 + 1 + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 64 + 1 + 1 + + + + + 1 + 1 + 80 + 80 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 1 + 80 + 80 + + + 1 + 1 + 1 + 1 + + + + + 1 + 1 + 80 + 80 + + + + + + + + 1 + 64 + 80 + 80 + + + 1 + 1 + 80 + 80 + + + + + 1 + 65 + 80 + 80 + + + + + + + + 3 + + + + + + + + 1 + 65 + 80 + 80 + + + 3 + + + + + 1 + 65 + 6400 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 128 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 40 + 40 + + + + 2 + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 64 + 3 + 3 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 64 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 32 + 64 + 3 + 3 + + + + + 1 + 32 + 40 + 40 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 40 + 40 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 40 + 40 + + + + + + + 1 + 32 + 40 + 40 + + + + + 1 + 32 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + + 1 + 32 + 40 + 40 + + + + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 32 + 3 + 3 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 32 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 40 + 40 + + + 64 + 32 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 40 + 40 + + + + + 1 + 192 + 40 + 40 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 40 + 40 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 128 + 3 + 3 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 64 + 128 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 3 + 3 + + + 128 + 1 + 1 + 1 + 1 + + + + + 128 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 128 + 1 + 1 + 3 + 3 + + + + + 1 + 128 + 40 + 40 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 40 + 40 + + + + + + + 1 + 128 + 40 + 40 + + + + + 1 + 128 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + + 1 + 128 + 40 + 40 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 40 + 40 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 40 + 40 + + + + + + + 1 + 64 + 40 + 40 + + + + + 1 + 64 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + + 1 + 64 + 40 + 40 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + 1 + 1 + 1 + 1 + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 64 + 1 + 1 + + + + + 1 + 1 + 40 + 40 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 1 + 40 + 40 + + + 1 + 1 + 1 + 1 + + + + + 1 + 1 + 40 + 40 + + + + + + + + 1 + 64 + 40 + 40 + + + 1 + 1 + 40 + 40 + + + + + 1 + 65 + 40 + 40 + + + + + + + + 3 + + + + + + + + 1 + 65 + 40 + 40 + + + 3 + + + + + 1 + 65 + 1600 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 256 + 20 + 20 + + + + + 1 + 384 + 20 + 20 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 20 + 20 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 20 + 20 + + + + 2 + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 64 + 128 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 128 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 20 + 20 + + + + + + + 1 + 128 + 20 + 20 + + + + + 1 + 128 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + + 1 + 128 + 20 + 20 + + + + + + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + 1 + 128 + 20 + 20 + + + + + 1 + 384 + 20 + 20 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 20 + 20 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 256 + 3 + 3 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 20 + 20 + + + 64 + 256 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 256 + 1 + 1 + 3 + 3 + + + + + + + + 256 + 1 + 1 + 3 + 3 + + + + + 256 + 1 + 1 + 3 + 3 + + + + + + + + 256 + 1 + 1 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 3 + 3 + + + 256 + 1 + 1 + 1 + 1 + + + + + 256 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 256 + 20 + 20 + + + 256 + 1 + 1 + 3 + 3 + + + + + 1 + 256 + 20 + 20 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 20 + 20 + + + + + + + 1 + 256 + 20 + 20 + + + + + 1 + 256 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + + 1 + 256 + 20 + 20 + + + + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 20 + 20 + + + 64 + 256 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 20 + 20 + + + + + + + 1 + 64 + 20 + 20 + + + + + 1 + 64 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + + 1 + 64 + 20 + 20 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + 1 + 1 + 1 + 1 + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 64 + 1 + 1 + + + + + 1 + 1 + 20 + 20 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 1 + 20 + 20 + + + 1 + 1 + 1 + 1 + + + + + 1 + 1 + 20 + 20 + + + + + + + + 1 + 64 + 20 + 20 + + + 1 + 1 + 20 + 20 + + + + + 1 + 65 + 20 + 20 + + + + + + + + 3 + + + + + + + + 1 + 65 + 20 + 20 + + + 3 + + + + + 1 + 65 + 400 + + + + + + + + 1 + 65 + 6400 + + + 1 + 65 + 1600 + + + 1 + 65 + 400 + + + + + 1 + 65 + 8400 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 65 + 8400 + + + + 2 + + + + + 1 + 64 + 8400 + + + 1 + 1 + 8400 + + + + + + + + 4 + + + + + + + + 1 + 64 + 8400 + + + 4 + + + + + 1 + 4 + 16 + 8400 + + + + + + + + 4 + + + + + + + 1 + 4 + 16 + 8400 + + + 4 + + + + + 1 + 16 + 4 + 8400 + + + + + + + + 1 + 16 + 4 + 8400 + + + + + 1 + 16 + 4 + 8400 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 4 + 8400 + + + 1 + 16 + 1 + 1 + + + + + 1 + 1 + 4 + 8400 + + + + + + + + 3 + + + + + + + + 1 + 1 + 4 + 8400 + + + 3 + + + + + 1 + 4 + 8400 + + + + + + + + 1 + 4 + 8400 + + + + + 3 + + + + + + + + 1 + + + + + + + + 3 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 1 + + + + + + + + 1 + + + + + 1 + + + + + + + + + 1 + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + 1 + + + + + 2 + + + + + + + 1 + 4 + 8400 + + + + 2 + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 1 + 1 + + + + + + + + 1 + 2 + 8400 + + + 1 + 1 + 1 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 2 + 8400 + + + + + + + + 1 + 2 + 8400 + + + 1 + 2 + 8400 + + + + + 1 + 4 + 8400 + + + + + + + + 1 + 1 + 8400 + + + + + + + + 1 + 4 + 8400 + + + 1 + 1 + 8400 + + + + + 1 + 4 + 8400 + + + + + + + 1 + 1 + 8400 + + + + + 1 + 1 + 8400 + + + + + + + + 16 + 64 + 3 + 3 + + + + + + + + 16 + 64 + 3 + 3 + + + + + 16 + 64 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 64 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 80 + 80 + + + 16 + 64 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 80 + 80 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + + 1 + 16 + 80 + 80 + + + + + + + + 12 + 16 + 1 + 1 + + + + + + + + 12 + 16 + 1 + 1 + + + + + 12 + 16 + 1 + 1 + + + + + + + + 12 + 1 + 1 + 1 + + + + + + + + 12 + 16 + 1 + 1 + + + 12 + 1 + 1 + 1 + + + + + 12 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 12 + 16 + 1 + 1 + + + + + 1 + 12 + 80 + 80 + + + + + + + + 1 + 12 + 1 + 1 + + + + + + + + 1 + 12 + 80 + 80 + + + 1 + 12 + 1 + 1 + + + + + 1 + 12 + 80 + 80 + + + + + + + + 3 + + + + + + + + 1 + 12 + 80 + 80 + + + 3 + + + + + 1 + 12 + 6400 + + + + + + + + 16 + 128 + 3 + 3 + + + + + + + + 16 + 128 + 3 + 3 + + + + + 16 + 128 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 128 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 40 + 40 + + + 16 + 128 + 3 + 3 + + + + + 1 + 16 + 40 + 40 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 40 + 40 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 40 + 40 + + + + + + + 1 + 16 + 40 + 40 + + + + + 1 + 16 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 40 + 40 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 40 + 40 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 40 + 40 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 40 + 40 + + + + + + + 1 + 16 + 40 + 40 + + + + + 1 + 16 + 40 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + + 1 + 16 + 40 + 40 + + + + + + + + 12 + 16 + 1 + 1 + + + + + + + + 12 + 16 + 1 + 1 + + + + + 12 + 16 + 1 + 1 + + + + + + + + 12 + 1 + 1 + 1 + + + + + + + + 12 + 16 + 1 + 1 + + + 12 + 1 + 1 + 1 + + + + + 12 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 40 + 40 + + + 12 + 16 + 1 + 1 + + + + + 1 + 12 + 40 + 40 + + + + + + + + 1 + 12 + 1 + 1 + + + + + + + + 1 + 12 + 40 + 40 + + + 1 + 12 + 1 + 1 + + + + + 1 + 12 + 40 + 40 + + + + + + + + 3 + + + + + + + + 1 + 12 + 40 + 40 + + + 3 + + + + + 1 + 12 + 1600 + + + + + + + + 16 + 256 + 3 + 3 + + + + + + + + 16 + 256 + 3 + 3 + + + + + 16 + 256 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 256 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 20 + 20 + + + 16 + 256 + 3 + 3 + + + + + 1 + 16 + 20 + 20 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 20 + 20 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 20 + 20 + + + + + + + 1 + 16 + 20 + 20 + + + + + 1 + 16 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 20 + 20 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 20 + 20 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 20 + 20 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 20 + 20 + + + + + + + 1 + 16 + 20 + 20 + + + + + 1 + 16 + 20 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + + 1 + 16 + 20 + 20 + + + + + + + + 12 + 16 + 1 + 1 + + + + + + + + 12 + 16 + 1 + 1 + + + + + 12 + 16 + 1 + 1 + + + + + + + + 12 + 1 + 1 + 1 + + + + + + + + 12 + 16 + 1 + 1 + + + 12 + 1 + 1 + 1 + + + + + 12 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 20 + 20 + + + 12 + 16 + 1 + 1 + + + + + 1 + 12 + 20 + 20 + + + + + + + + 1 + 12 + 1 + 1 + + + + + + + + 1 + 12 + 20 + 20 + + + 1 + 12 + 1 + 1 + + + + + 1 + 12 + 20 + 20 + + + + + + + + 3 + + + + + + + + 1 + 12 + 20 + 20 + + + 3 + + + + + 1 + 12 + 400 + + + + + + + + 1 + 12 + 6400 + + + 1 + 12 + 1600 + + + 1 + 12 + 400 + + + + + 1 + 12 + 8400 + + + + + + + + 4 + + + + + + + + 1 + 12 + 8400 + + + 4 + + + + + 1 + 6 + 2 + 8400 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + 1 + 6 + 2 + 8400 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + 6 + 2 + 8400 + + + + + + + + 1 + 1 + 1 + 8400 + + + + + + + + 1 + 6 + 2 + 8400 + + + 1 + 1 + 1 + 8400 + + + + + 1 + 6 + 2 + 8400 + + + + + + + + 1 + 1 + 2 + 8400 + + + + + + + + 1 + 6 + 2 + 8400 + + + 1 + 1 + 2 + 8400 + + + + + 1 + 6 + 2 + 8400 + + + + + + + + 3 + + + + + + + + 1 + 6 + 2 + 8400 + + + 3 + + + + + 1 + 12 + 8400 + + + + + + + + 1 + 4 + 8400 + + + 1 + 1 + 8400 + + + 1 + 12 + 8400 + + + + + 1 + 17 + 8400 + + + + + + + 1 + 17 + 8400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/yolov5.bin b/assets/yolov5.bin new file mode 100644 index 0000000..e5f8427 Binary files /dev/null and b/assets/yolov5.bin differ diff --git a/assets/yolov5.xml b/assets/yolov5.xml new file mode 100644 index 0000000..88b2f68 --- /dev/null +++ b/assets/yolov5.xml @@ -0,0 +1,12795 @@ + + + + + + + + + + + 1 + 3 + 640 + 640 + + + + + + + + 8 + 3 + 3 + 3 + + + + + + + + + + + 8 + 3 + 3 + 3 + + + + + 8 + 3 + 3 + 3 + + + + + + + + 1 + 3 + 640 + 640 + + + 8 + 3 + 3 + 3 + + + + + 1 + 8 + 320 + 320 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 320 + 320 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 320 + 320 + + + + + + + 1 + 8 + 320 + 320 + + + + + 1 + 8 + 320 + 320 + + + + + + + + 16 + 8 + 1 + 1 + + + + + + + + + + + 16 + 8 + 1 + 1 + + + + + 16 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 320 + 320 + + + 16 + 8 + 1 + 1 + + + + + 1 + 16 + 320 + 320 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 320 + 320 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 320 + 320 + + + + + + + 1 + 16 + 320 + 320 + + + + + 1 + 16 + 320 + 320 + + + + + + + + 16 + 1 + 1 + 3 + 3 + + + + + + + + + + + 16 + 1 + 1 + 3 + 3 + + + + + 16 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 16 + 320 + 320 + + + 16 + 1 + 1 + 3 + 3 + + + + + 1 + 16 + 160 + 160 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 160 + 160 + + + + + + + + 2 + + + + + + + + 1 + 16 + 160 + 160 + + + 2 + + + + + 1 + 16 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 16 + 1 + 1 + + + 2 + + + + + 1 + 16 + + + + + + + + 4 + 16 + + + + + + + + + + + 4 + 16 + + + + + 4 + 16 + + + + + + + + 1 + 16 + + + 4 + 16 + + + + + 1 + 4 + + + + + + + + 1 + 4 + + + + + + + + + + + 1 + 4 + + + + + 1 + 4 + + + + + + + + 1 + 4 + + + 1 + 4 + + + + + 1 + 4 + + + + + + + 1 + 4 + + + + + 1 + 4 + + + + + + + + 16 + 4 + + + + + + + + + + + 16 + 4 + + + + + 16 + 4 + + + + + + + + 1 + 4 + + + 16 + 4 + + + + + 1 + 16 + + + + + + + + 1 + 16 + + + + + + + + + + + 1 + 16 + + + + + 1 + 16 + + + + + + + + 1 + 16 + + + 1 + 16 + + + + + 1 + 16 + + + + + + + 1 + 16 + + + + + 1 + 16 + + + + + + + + + + + 4 + + + + + + + + 1 + 16 + + + 4 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 160 + 160 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 160 + 160 + + + + + + + 1 + 16 + 160 + 160 + + + + + 1 + 16 + 160 + 160 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 160 + 160 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 160 + 160 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 160 + 160 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 160 + 160 + + + + + + + + 72 + 8 + 1 + 1 + + + + + + + + + + + 72 + 8 + 1 + 1 + + + + + 72 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 160 + 160 + + + 72 + 8 + 1 + 1 + + + + + 1 + 72 + 160 + 160 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 160 + 160 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 160 + 160 + + + + + + + 1 + 72 + 160 + 160 + + + + + 1 + 72 + 160 + 160 + + + + + + + + 72 + 1 + 1 + 3 + 3 + + + + + + + + + + + 72 + 1 + 1 + 3 + 3 + + + + + 72 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 72 + 160 + 160 + + + 72 + 1 + 1 + 3 + 3 + + + + + 1 + 72 + 80 + 80 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 80 + 80 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 80 + 80 + + + + + + + 1 + 72 + 80 + 80 + + + + + 1 + 72 + 80 + 80 + + + + + + + + 16 + 72 + 1 + 1 + + + + + + + + + + + 16 + 72 + 1 + 1 + + + + + 16 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 80 + 80 + + + 16 + 72 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 88 + 16 + 1 + 1 + + + + + + + + + + + 88 + 16 + 1 + 1 + + + + + 88 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 88 + 16 + 1 + 1 + + + + + 1 + 88 + 80 + 80 + + + + + + + + 1 + 88 + 1 + 1 + + + + + + + + + + + 1 + 88 + 1 + 1 + + + + + 1 + 88 + 1 + 1 + + + + + + + + 1 + 88 + 80 + 80 + + + 1 + 88 + 1 + 1 + + + + + 1 + 88 + 80 + 80 + + + + + + + 1 + 88 + 80 + 80 + + + + + 1 + 88 + 80 + 80 + + + + + + + + 88 + 1 + 1 + 3 + 3 + + + + + + + + + + + 88 + 1 + 1 + 3 + 3 + + + + + 88 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 88 + 80 + 80 + + + 88 + 1 + 1 + 3 + 3 + + + + + 1 + 88 + 80 + 80 + + + + + + + + 1 + 88 + 1 + 1 + + + + + + + + + + + 1 + 88 + 1 + 1 + + + + + 1 + 88 + 1 + 1 + + + + + + + + 1 + 88 + 80 + 80 + + + 1 + 88 + 1 + 1 + + + + + 1 + 88 + 80 + 80 + + + + + + + 1 + 88 + 80 + 80 + + + + + 1 + 88 + 80 + 80 + + + + + + + + 16 + 88 + 1 + 1 + + + + + + + + + + + 16 + 88 + 1 + 1 + + + + + 16 + 88 + 1 + 1 + + + + + + + + 1 + 88 + 80 + 80 + + + 16 + 88 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 1 + 16 + 80 + 80 + + + 1 + 16 + 80 + 80 + + + + + 1 + 16 + 80 + 80 + + + + + + + + 96 + 16 + 1 + 1 + + + + + + + + + + + 96 + 16 + 1 + 1 + + + + + 96 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 80 + 80 + + + 96 + 16 + 1 + 1 + + + + + 1 + 96 + 80 + 80 + + + + + + + + 1 + 96 + 1 + 1 + + + + + + + + + + + 1 + 96 + 1 + 1 + + + + + 1 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 80 + 80 + + + 1 + 96 + 1 + 1 + + + + + 1 + 96 + 80 + 80 + + + + + + + 1 + 96 + 80 + 80 + + + + + 1 + 96 + 80 + 80 + + + + + + + + 96 + 1 + 1 + 5 + 5 + + + + + + + + + + + 96 + 1 + 1 + 5 + 5 + + + + + 96 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 96 + 80 + 80 + + + 96 + 1 + 1 + 5 + 5 + + + + + 1 + 96 + 40 + 40 + + + + + + + + 1 + 96 + 1 + 1 + + + + + + + + + + + 1 + 96 + 1 + 1 + + + + + 1 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 40 + 40 + + + 1 + 96 + 1 + 1 + + + + + 1 + 96 + 40 + 40 + + + + + + + + 2 + + + + + + + + 1 + 96 + 40 + 40 + + + 2 + + + + + 1 + 96 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 96 + 1 + 1 + + + 2 + + + + + 1 + 96 + + + + + + + + 24 + 96 + + + + + + + + + + + 24 + 96 + + + + + 24 + 96 + + + + + + + + 1 + 96 + + + 24 + 96 + + + + + 1 + 24 + + + + + + + + 1 + 24 + + + + + + + + + + + 1 + 24 + + + + + 1 + 24 + + + + + + + + 1 + 24 + + + 1 + 24 + + + + + 1 + 24 + + + + + + + 1 + 24 + + + + + 1 + 24 + + + + + + + + 96 + 24 + + + + + + + + + + + 96 + 24 + + + + + 96 + 24 + + + + + + + + 1 + 24 + + + 96 + 24 + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + + + + + + + 1 + 96 + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + 1 + 96 + + + + + 1 + 96 + + + + + + + 1 + 96 + + + + + 1 + 96 + + + + + + + + + + + 4 + + + + + + + + 1 + 96 + + + 4 + + + + + 1 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 40 + 40 + + + 1 + 96 + 1 + 1 + + + + + 1 + 96 + 40 + 40 + + + + + + + 1 + 96 + 40 + 40 + + + + + 1 + 96 + 40 + 40 + + + + + + + + 24 + 96 + 1 + 1 + + + + + + + + + + + 24 + 96 + 1 + 1 + + + + + 24 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 40 + 40 + + + 24 + 96 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 240 + 24 + 1 + 1 + + + + + + + + + + + 240 + 24 + 1 + 1 + + + + + 240 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 240 + 24 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + 1 + 240 + 40 + 40 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 240 + 1 + 1 + 5 + 5 + + + + + + + + + + + 240 + 1 + 1 + 5 + 5 + + + + + 240 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 240 + 40 + 40 + + + 240 + 1 + 1 + 5 + 5 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 2 + + + + + + + + 1 + 240 + 40 + 40 + + + 2 + + + + + 1 + 240 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 240 + 1 + 1 + + + 2 + + + + + 1 + 240 + + + + + + + + 60 + 240 + + + + + + + + + + + 60 + 240 + + + + + 60 + 240 + + + + + + + + 1 + 240 + + + 60 + 240 + + + + + 1 + 60 + + + + + + + + 1 + 60 + + + + + + + + + + + 1 + 60 + + + + + 1 + 60 + + + + + + + + 1 + 60 + + + 1 + 60 + + + + + 1 + 60 + + + + + + + 1 + 60 + + + + + 1 + 60 + + + + + + + + 240 + 60 + + + + + + + + + + + 240 + 60 + + + + + 240 + 60 + + + + + + + + 1 + 60 + + + 240 + 60 + + + + + 1 + 240 + + + + + + + + 1 + 240 + + + + + + + + + + + 1 + 240 + + + + + 1 + 240 + + + + + + + + 1 + 240 + + + 1 + 240 + + + + + 1 + 240 + + + + + + + 1 + 240 + + + + + 1 + 240 + + + + + + + + + + + 4 + + + + + + + + 1 + 240 + + + 4 + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + 1 + 240 + 40 + 40 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 24 + 240 + 1 + 1 + + + + + + + + + + + 24 + 240 + 1 + 1 + + + + + 24 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 24 + 240 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 40 + 40 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 240 + 24 + 1 + 1 + + + + + + + + + + + 240 + 24 + 1 + 1 + + + + + 240 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 240 + 24 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + 1 + 240 + 40 + 40 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 240 + 1 + 1 + 5 + 5 + + + + + + + + + + + 240 + 1 + 1 + 5 + 5 + + + + + 240 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 240 + 40 + 40 + + + 240 + 1 + 1 + 5 + 5 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 2 + + + + + + + + 1 + 240 + 40 + 40 + + + 2 + + + + + 1 + 240 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 240 + 1 + 1 + + + 2 + + + + + 1 + 240 + + + + + + + + 60 + 240 + + + + + + + + + + + 60 + 240 + + + + + 60 + 240 + + + + + + + + 1 + 240 + + + 60 + 240 + + + + + 1 + 60 + + + + + + + + 1 + 60 + + + + + + + + + + + 1 + 60 + + + + + 1 + 60 + + + + + + + + 1 + 60 + + + 1 + 60 + + + + + 1 + 60 + + + + + + + 1 + 60 + + + + + 1 + 60 + + + + + + + + 240 + 60 + + + + + + + + + + + 240 + 60 + + + + + 240 + 60 + + + + + + + + 1 + 60 + + + 240 + 60 + + + + + 1 + 240 + + + + + + + + 1 + 240 + + + + + + + + + + + 1 + 240 + + + + + 1 + 240 + + + + + + + + 1 + 240 + + + 1 + 240 + + + + + 1 + 240 + + + + + + + 1 + 240 + + + + + 1 + 240 + + + + + + + + + + + 4 + + + + + + + + 1 + 240 + + + 4 + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 40 + 40 + + + + + + + 1 + 240 + 40 + 40 + + + + + 1 + 240 + 40 + 40 + + + + + + + + 24 + 240 + 1 + 1 + + + + + + + + + + + 24 + 240 + 1 + 1 + + + + + 24 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 40 + 40 + + + 24 + 240 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 40 + 40 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 120 + 24 + 1 + 1 + + + + + + + + + + + 120 + 24 + 1 + 1 + + + + + 120 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 120 + 24 + 1 + 1 + + + + + 1 + 120 + 40 + 40 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 40 + 40 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 40 + 40 + + + + + + + 1 + 120 + 40 + 40 + + + + + 1 + 120 + 40 + 40 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 120 + 40 + 40 + + + 120 + 1 + 1 + 5 + 5 + + + + + 1 + 120 + 40 + 40 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 40 + 40 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 40 + 40 + + + + + + + + 2 + + + + + + + + 1 + 120 + 40 + 40 + + + 2 + + + + + 1 + 120 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 120 + 1 + 1 + + + 2 + + + + + 1 + 120 + + + + + + + + 30 + 120 + + + + + + + + + + + 30 + 120 + + + + + 30 + 120 + + + + + + + + 1 + 120 + + + 30 + 120 + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + + + + + + + 1 + 30 + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + 1 + 30 + + + + + 1 + 30 + + + + + + + 1 + 30 + + + + + 1 + 30 + + + + + + + + 120 + 30 + + + + + + + + + + + 120 + 30 + + + + + 120 + 30 + + + + + + + + 1 + 30 + + + 120 + 30 + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + + + + + + + + + 1 + 120 + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + 1 + 120 + + + + + 1 + 120 + + + + + + + 1 + 120 + + + + + 1 + 120 + + + + + + + + + + + 4 + + + + + + + + 1 + 120 + + + 4 + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 40 + 40 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 40 + 40 + + + + + + + 1 + 120 + 40 + 40 + + + + + 1 + 120 + 40 + 40 + + + + + + + + 24 + 120 + 1 + 1 + + + + + + + + + + + 24 + 120 + 1 + 1 + + + + + 24 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 40 + 40 + + + 24 + 120 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 40 + 40 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 144 + 24 + 1 + 1 + + + + + + + + + + + 144 + 24 + 1 + 1 + + + + + 144 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 144 + 24 + 1 + 1 + + + + + 1 + 144 + 40 + 40 + + + + + + + + 1 + 144 + 1 + 1 + + + + + + + + + + + 1 + 144 + 1 + 1 + + + + + 1 + 144 + 1 + 1 + + + + + + + + 1 + 144 + 40 + 40 + + + 1 + 144 + 1 + 1 + + + + + 1 + 144 + 40 + 40 + + + + + + + 1 + 144 + 40 + 40 + + + + + 1 + 144 + 40 + 40 + + + + + + + + 144 + 1 + 1 + 5 + 5 + + + + + + + + + + + 144 + 1 + 1 + 5 + 5 + + + + + 144 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 144 + 40 + 40 + + + 144 + 1 + 1 + 5 + 5 + + + + + 1 + 144 + 40 + 40 + + + + + + + + 1 + 144 + 1 + 1 + + + + + + + + + + + 1 + 144 + 1 + 1 + + + + + 1 + 144 + 1 + 1 + + + + + + + + 1 + 144 + 40 + 40 + + + 1 + 144 + 1 + 1 + + + + + 1 + 144 + 40 + 40 + + + + + + + + 2 + + + + + + + + 1 + 144 + 40 + 40 + + + 2 + + + + + 1 + 144 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 144 + 1 + 1 + + + 2 + + + + + 1 + 144 + + + + + + + + 36 + 144 + + + + + + + + + + + 36 + 144 + + + + + 36 + 144 + + + + + + + + 1 + 144 + + + 36 + 144 + + + + + 1 + 36 + + + + + + + + 1 + 36 + + + + + + + + + + + 1 + 36 + + + + + 1 + 36 + + + + + + + + 1 + 36 + + + 1 + 36 + + + + + 1 + 36 + + + + + + + 1 + 36 + + + + + 1 + 36 + + + + + + + + 144 + 36 + + + + + + + + + + + 144 + 36 + + + + + 144 + 36 + + + + + + + + 1 + 36 + + + 144 + 36 + + + + + 1 + 144 + + + + + + + + 1 + 144 + + + + + + + + + + + 1 + 144 + + + + + 1 + 144 + + + + + + + + 1 + 144 + + + 1 + 144 + + + + + 1 + 144 + + + + + + + 1 + 144 + + + + + 1 + 144 + + + + + + + + + + + 4 + + + + + + + + 1 + 144 + + + 4 + + + + + 1 + 144 + 1 + 1 + + + + + + + + 1 + 144 + 40 + 40 + + + 1 + 144 + 1 + 1 + + + + + 1 + 144 + 40 + 40 + + + + + + + 1 + 144 + 40 + 40 + + + + + 1 + 144 + 40 + 40 + + + + + + + + 24 + 144 + 1 + 1 + + + + + + + + + + + 24 + 144 + 1 + 1 + + + + + 24 + 144 + 1 + 1 + + + + + + + + 1 + 144 + 40 + 40 + + + 24 + 144 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 1 + 24 + 40 + 40 + + + 1 + 24 + 40 + 40 + + + + + 1 + 24 + 40 + 40 + + + + + + + + 288 + 24 + 1 + 1 + + + + + + + + + + + 288 + 24 + 1 + 1 + + + + + 288 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 40 + 40 + + + 288 + 24 + 1 + 1 + + + + + 1 + 288 + 40 + 40 + + + + + + + + 1 + 288 + 1 + 1 + + + + + + + + + + + 1 + 288 + 1 + 1 + + + + + 1 + 288 + 1 + 1 + + + + + + + + 1 + 288 + 40 + 40 + + + 1 + 288 + 1 + 1 + + + + + 1 + 288 + 40 + 40 + + + + + + + 1 + 288 + 40 + 40 + + + + + 1 + 288 + 40 + 40 + + + + + + + + 288 + 1 + 1 + 5 + 5 + + + + + + + + + + + 288 + 1 + 1 + 5 + 5 + + + + + 288 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 288 + 40 + 40 + + + 288 + 1 + 1 + 5 + 5 + + + + + 1 + 288 + 20 + 20 + + + + + + + + 1 + 288 + 1 + 1 + + + + + + + + + + + 1 + 288 + 1 + 1 + + + + + 1 + 288 + 1 + 1 + + + + + + + + 1 + 288 + 20 + 20 + + + 1 + 288 + 1 + 1 + + + + + 1 + 288 + 20 + 20 + + + + + + + + 2 + + + + + + + + 1 + 288 + 20 + 20 + + + 2 + + + + + 1 + 288 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 288 + 1 + 1 + + + 2 + + + + + 1 + 288 + + + + + + + + 72 + 288 + + + + + + + + + + + 72 + 288 + + + + + 72 + 288 + + + + + + + + 1 + 288 + + + 72 + 288 + + + + + 1 + 72 + + + + + + + + 1 + 72 + + + + + + + + + + + 1 + 72 + + + + + 1 + 72 + + + + + + + + 1 + 72 + + + 1 + 72 + + + + + 1 + 72 + + + + + + + 1 + 72 + + + + + 1 + 72 + + + + + + + + 288 + 72 + + + + + + + + + + + 288 + 72 + + + + + 288 + 72 + + + + + + + + 1 + 72 + + + 288 + 72 + + + + + 1 + 288 + + + + + + + + 1 + 288 + + + + + + + + + + + 1 + 288 + + + + + 1 + 288 + + + + + + + + 1 + 288 + + + 1 + 288 + + + + + 1 + 288 + + + + + + + 1 + 288 + + + + + 1 + 288 + + + + + + + + + + + 4 + + + + + + + + 1 + 288 + + + 4 + + + + + 1 + 288 + 1 + 1 + + + + + + + + 1 + 288 + 20 + 20 + + + 1 + 288 + 1 + 1 + + + + + 1 + 288 + 20 + 20 + + + + + + + 1 + 288 + 20 + 20 + + + + + 1 + 288 + 20 + 20 + + + + + + + + 48 + 288 + 1 + 1 + + + + + + + + + + + 48 + 288 + 1 + 1 + + + + + 48 + 288 + 1 + 1 + + + + + + + + 1 + 288 + 20 + 20 + + + 48 + 288 + 1 + 1 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 1 + 48 + 1 + 1 + + + + + + + + + + + 1 + 48 + 1 + 1 + + + + + 1 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 20 + 20 + + + 1 + 48 + 1 + 1 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 576 + 48 + 1 + 1 + + + + + + + + + + + 576 + 48 + 1 + 1 + + + + + 576 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 20 + 20 + + + 576 + 48 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 1 + 576 + 1 + 1 + + + + + + + + + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + 1 + 576 + 20 + 20 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 576 + 1 + 1 + 5 + 5 + + + + + + + + + + + 576 + 1 + 1 + 5 + 5 + + + + + 576 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 576 + 20 + 20 + + + 576 + 1 + 1 + 5 + 5 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 1 + 576 + 1 + 1 + + + + + + + + + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 2 + + + + + + + + 1 + 576 + 20 + 20 + + + 2 + + + + + 1 + 576 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 576 + 1 + 1 + + + 2 + + + + + 1 + 576 + + + + + + + + 144 + 576 + + + + + + + + + + + 144 + 576 + + + + + 144 + 576 + + + + + + + + 1 + 576 + + + 144 + 576 + + + + + 1 + 144 + + + + + + + + 1 + 144 + + + + + + + + + + + 1 + 144 + + + + + 1 + 144 + + + + + + + + 1 + 144 + + + 1 + 144 + + + + + 1 + 144 + + + + + + + 1 + 144 + + + + + 1 + 144 + + + + + + + + 576 + 144 + + + + + + + + + + + 576 + 144 + + + + + 576 + 144 + + + + + + + + 1 + 144 + + + 576 + 144 + + + + + 1 + 576 + + + + + + + + 1 + 576 + + + + + + + + + + + 1 + 576 + + + + + 1 + 576 + + + + + + + + 1 + 576 + + + 1 + 576 + + + + + 1 + 576 + + + + + + + 1 + 576 + + + + + 1 + 576 + + + + + + + + + + + 4 + + + + + + + + 1 + 576 + + + 4 + + + + + 1 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + 1 + 576 + 20 + 20 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 48 + 576 + 1 + 1 + + + + + + + + + + + 48 + 576 + 1 + 1 + + + + + 48 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 48 + 576 + 1 + 1 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 1 + 48 + 1 + 1 + + + + + + + + + + + 1 + 48 + 1 + 1 + + + + + 1 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 20 + 20 + + + 1 + 48 + 1 + 1 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 1 + 48 + 20 + 20 + + + 1 + 48 + 20 + 20 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 576 + 48 + 1 + 1 + + + + + + + + + + + 576 + 48 + 1 + 1 + + + + + 576 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 20 + 20 + + + 576 + 48 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 1 + 576 + 1 + 1 + + + + + + + + + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + 1 + 576 + 20 + 20 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 576 + 1 + 1 + 5 + 5 + + + + + + + + + + + 576 + 1 + 1 + 5 + 5 + + + + + 576 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 576 + 20 + 20 + + + 576 + 1 + 1 + 5 + 5 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 1 + 576 + 1 + 1 + + + + + + + + + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 2 + + + + + + + + 1 + 576 + 20 + 20 + + + 2 + + + + + 1 + 576 + 1 + 1 + + + + + + + + + + + 2 + + + + + + + + 1 + 576 + 1 + 1 + + + 2 + + + + + 1 + 576 + + + + + + + + 144 + 576 + + + + + + + + + + + 144 + 576 + + + + + 144 + 576 + + + + + + + + 1 + 576 + + + 144 + 576 + + + + + 1 + 144 + + + + + + + + 1 + 144 + + + + + + + + + + + 1 + 144 + + + + + 1 + 144 + + + + + + + + 1 + 144 + + + 1 + 144 + + + + + 1 + 144 + + + + + + + 1 + 144 + + + + + 1 + 144 + + + + + + + + 576 + 144 + + + + + + + + + + + 576 + 144 + + + + + 576 + 144 + + + + + + + + 1 + 144 + + + 576 + 144 + + + + + 1 + 576 + + + + + + + + 1 + 576 + + + + + + + + + + + 1 + 576 + + + + + 1 + 576 + + + + + + + + 1 + 576 + + + 1 + 576 + + + + + 1 + 576 + + + + + + + 1 + 576 + + + + + 1 + 576 + + + + + + + + + + + 4 + + + + + + + + 1 + 576 + + + 4 + + + + + 1 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 1 + 576 + 1 + 1 + + + + + 1 + 576 + 20 + 20 + + + + + + + 1 + 576 + 20 + 20 + + + + + 1 + 576 + 20 + 20 + + + + + + + + 48 + 576 + 1 + 1 + + + + + + + + + + + 48 + 576 + 1 + 1 + + + + + 48 + 576 + 1 + 1 + + + + + + + + 1 + 576 + 20 + 20 + + + 48 + 576 + 1 + 1 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 1 + 48 + 1 + 1 + + + + + + + + + + + 1 + 48 + 1 + 1 + + + + + 1 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 20 + 20 + + + 1 + 48 + 1 + 1 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 1 + 48 + 20 + 20 + + + 1 + 48 + 20 + 20 + + + + + 1 + 48 + 20 + 20 + + + + + + + + 112 + 48 + 1 + 1 + + + + + + + + + + + 112 + 48 + 1 + 1 + + + + + 112 + 48 + 1 + 1 + + + + + + + + 1 + 48 + 20 + 20 + + + 112 + 48 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + 1 + 112 + 20 + 20 + + + + + 1 + 112 + 20 + 20 + + + + + + + + + + + 4 + + + + + + + + 1 + 112 + 20 + 20 + + + 4 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 1 + 112 + 40 + 40 + + + 1 + 24 + 40 + 40 + + + + + 1 + 136 + 40 + 40 + + + + + + + + 56 + 136 + 1 + 1 + + + + + + + + + + + 56 + 136 + 1 + 1 + + + + + 56 + 136 + 1 + 1 + + + + + + + + 1 + 136 + 40 + 40 + + + 56 + 136 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 56 + 56 + 1 + 1 + + + + + + + + + + + 56 + 56 + 1 + 1 + + + + + 56 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 56 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 56 + 56 + 3 + 3 + + + + + + + + + + + 56 + 56 + 3 + 3 + + + + + 56 + 56 + 3 + 3 + + + + + + + + 1 + 56 + 40 + 40 + + + 56 + 56 + 3 + 3 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 56 + 136 + 1 + 1 + + + + + + + + + + + 56 + 136 + 1 + 1 + + + + + 56 + 136 + 1 + 1 + + + + + + + + 1 + 136 + 40 + 40 + + + 56 + 136 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 40 + 40 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 112 + 112 + 1 + 1 + + + + + + + + + + + 112 + 112 + 1 + 1 + + + + + 112 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 112 + 112 + 1 + 1 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 40 + 40 + + + + + + + 1 + 112 + 40 + 40 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 56 + 112 + 1 + 1 + + + + + + + + + + + 56 + 112 + 1 + 1 + + + + + 56 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 56 + 112 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + + + + 4 + + + + + + + + 1 + 56 + 40 + 40 + + + 4 + + + + + 1 + 56 + 80 + 80 + + + + + + + + 1 + 56 + 80 + 80 + + + 1 + 16 + 80 + 80 + + + + + 1 + 72 + 80 + 80 + + + + + + + + 28 + 72 + 1 + 1 + + + + + + + + + + + 28 + 72 + 1 + 1 + + + + + 28 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 80 + 80 + + + 28 + 72 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 1 + 28 + 1 + 1 + + + + + + + + + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 1 + 1 + + + + + + + + 1 + 28 + 80 + 80 + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + 1 + 28 + 80 + 80 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 28 + 28 + 1 + 1 + + + + + + + + + + + 28 + 28 + 1 + 1 + + + + + 28 + 28 + 1 + 1 + + + + + + + + 1 + 28 + 80 + 80 + + + 28 + 28 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 1 + 28 + 1 + 1 + + + + + + + + + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 1 + 1 + + + + + + + + 1 + 28 + 80 + 80 + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + 1 + 28 + 80 + 80 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 28 + 28 + 3 + 3 + + + + + + + + + + + 28 + 28 + 3 + 3 + + + + + 28 + 28 + 3 + 3 + + + + + + + + 1 + 28 + 80 + 80 + + + 28 + 28 + 3 + 3 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 1 + 28 + 1 + 1 + + + + + + + + + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 1 + 1 + + + + + + + + 1 + 28 + 80 + 80 + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + 1 + 28 + 80 + 80 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 28 + 72 + 1 + 1 + + + + + + + + + + + 28 + 72 + 1 + 1 + + + + + 28 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 80 + 80 + + + 28 + 72 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 1 + 28 + 1 + 1 + + + + + + + + + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 1 + 1 + + + + + + + + 1 + 28 + 80 + 80 + + + 1 + 28 + 1 + 1 + + + + + 1 + 28 + 80 + 80 + + + + + + + 1 + 28 + 80 + 80 + + + + + 1 + 28 + 80 + 80 + + + + + + + + 1 + 28 + 80 + 80 + + + 1 + 28 + 80 + 80 + + + + + 1 + 56 + 80 + 80 + + + + + + + + 56 + 56 + 1 + 1 + + + + + + + + + + + 56 + 56 + 1 + 1 + + + + + 56 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 80 + 80 + + + 56 + 56 + 1 + 1 + + + + + 1 + 56 + 80 + 80 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 80 + 80 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 80 + 80 + + + + + + + 1 + 56 + 80 + 80 + + + + + 1 + 56 + 80 + 80 + + + + + + + + 66 + 56 + 1 + 1 + + + + + + + + + + + 66 + 56 + 1 + 1 + + + + + 66 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 80 + 80 + + + 66 + 56 + 1 + 1 + + + + + 1 + 66 + 80 + 80 + + + + + + + + 1 + 66 + 1 + 1 + + + + + + + + + + + 1 + 66 + 1 + 1 + + + + + 1 + 66 + 1 + 1 + + + + + + + + 1 + 66 + 80 + 80 + + + 1 + 66 + 1 + 1 + + + + + 1 + 66 + 80 + 80 + + + + + + + + + + + 5 + + + + + + + + 1 + 66 + 80 + 80 + + + 5 + + + + + 1 + 3 + 22 + 80 + 80 + + + + + + + + 5 + + + + + + + 1 + 3 + 22 + 80 + 80 + + + 5 + + + + + 1 + 3 + 80 + 80 + 22 + + + + + + + + 1 + 3 + 1 + 1 + 22 + + + + + + + + + + + 1 + 3 + 1 + 1 + 22 + + + + + 1 + 3 + 1 + 1 + 22 + + + + + + + + 1 + 3 + 80 + 80 + 22 + + + 1 + 3 + 1 + 1 + 22 + + + + + 1 + 3 + 80 + 80 + 22 + + + + + + + + 1 + 1 + 80 + 80 + 22 + + + + + + + + + + + 1 + 1 + 80 + 80 + 22 + + + + + 1 + 1 + 80 + 80 + 22 + + + + + + + + 1 + 3 + 80 + 80 + 22 + + + 1 + 1 + 80 + 80 + 22 + + + + + 1 + 3 + 80 + 80 + 22 + + + + + + + + + + + 3 + + + + + + + + 1 + 3 + 80 + 80 + 22 + + + 3 + + + + + 1 + 19200 + 22 + + + + + + + + 56 + 56 + 3 + 3 + + + + + + + + + + + 56 + 56 + 3 + 3 + + + + + 56 + 56 + 3 + 3 + + + + + + + + 1 + 56 + 80 + 80 + + + 56 + 56 + 3 + 3 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 40 + 40 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 56 + 112 + 1 + 1 + + + + + + + + + + + 56 + 112 + 1 + 1 + + + + + 56 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 56 + 112 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 56 + 56 + 1 + 1 + + + + + + + + + + + 56 + 56 + 1 + 1 + + + + + 56 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 56 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 56 + 56 + 3 + 3 + + + + + + + + + + + 56 + 56 + 3 + 3 + + + + + 56 + 56 + 3 + 3 + + + + + + + + 1 + 56 + 40 + 40 + + + 56 + 56 + 3 + 3 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 56 + 112 + 1 + 1 + + + + + + + + + + + 56 + 112 + 1 + 1 + + + + + 56 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 56 + 112 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 1 + 1 + + + + + + + + + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 1 + 1 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 1 + 1 + + + + + 1 + 56 + 40 + 40 + + + + + + + 1 + 56 + 40 + 40 + + + + + 1 + 56 + 40 + 40 + + + + + + + + 1 + 56 + 40 + 40 + + + 1 + 56 + 40 + 40 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 112 + 112 + 1 + 1 + + + + + + + + + + + 112 + 112 + 1 + 1 + + + + + 112 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 112 + 112 + 1 + 1 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 40 + 40 + + + + + + + 1 + 112 + 40 + 40 + + + + + 1 + 112 + 40 + 40 + + + + + + + + 66 + 112 + 1 + 1 + + + + + + + + + + + 66 + 112 + 1 + 1 + + + + + 66 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 40 + 40 + + + 66 + 112 + 1 + 1 + + + + + 1 + 66 + 40 + 40 + + + + + + + + 1 + 66 + 1 + 1 + + + + + + + + + + + 1 + 66 + 1 + 1 + + + + + 1 + 66 + 1 + 1 + + + + + + + + 1 + 66 + 40 + 40 + + + 1 + 66 + 1 + 1 + + + + + 1 + 66 + 40 + 40 + + + + + + + + + + + 5 + + + + + + + + 1 + 66 + 40 + 40 + + + 5 + + + + + 1 + 3 + 22 + 40 + 40 + + + + + + + + 5 + + + + + + + 1 + 3 + 22 + 40 + 40 + + + 5 + + + + + 1 + 3 + 40 + 40 + 22 + + + + + + + + 1 + 3 + 1 + 1 + 22 + + + + + + + + + + + 1 + 3 + 1 + 1 + 22 + + + + + 1 + 3 + 1 + 1 + 22 + + + + + + + + 1 + 3 + 40 + 40 + 22 + + + 1 + 3 + 1 + 1 + 22 + + + + + 1 + 3 + 40 + 40 + 22 + + + + + + + + 1 + 1 + 40 + 40 + 22 + + + + + + + + + + + 1 + 1 + 40 + 40 + 22 + + + + + 1 + 1 + 40 + 40 + 22 + + + + + + + + 1 + 3 + 40 + 40 + 22 + + + 1 + 1 + 40 + 40 + 22 + + + + + 1 + 3 + 40 + 40 + 22 + + + + + + + + + + + 3 + + + + + + + + 1 + 3 + 40 + 40 + 22 + + + 3 + + + + + 1 + 4800 + 22 + + + + + + + + 112 + 112 + 3 + 3 + + + + + + + + + + + 112 + 112 + 3 + 3 + + + + + 112 + 112 + 3 + 3 + + + + + + + + 1 + 112 + 40 + 40 + + + 112 + 112 + 3 + 3 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + 1 + 112 + 20 + 20 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 20 + 20 + + + + + 1 + 224 + 20 + 20 + + + + + + + + 112 + 224 + 1 + 1 + + + + + + + + + + + 112 + 224 + 1 + 1 + + + + + 112 + 224 + 1 + 1 + + + + + + + + 1 + 224 + 20 + 20 + + + 112 + 224 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + 1 + 112 + 20 + 20 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 112 + 112 + 1 + 1 + + + + + + + + + + + 112 + 112 + 1 + 1 + + + + + 112 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 112 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + 1 + 112 + 20 + 20 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 112 + 112 + 3 + 3 + + + + + + + + + + + 112 + 112 + 3 + 3 + + + + + 112 + 112 + 3 + 3 + + + + + + + + 1 + 112 + 20 + 20 + + + 112 + 112 + 3 + 3 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + 1 + 112 + 20 + 20 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 112 + 224 + 1 + 1 + + + + + + + + + + + 112 + 224 + 1 + 1 + + + + + 112 + 224 + 1 + 1 + + + + + + + + 1 + 224 + 20 + 20 + + + 112 + 224 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 1 + 1 + + + + + + + + + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 1 + 1 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 1 + 1 + + + + + 1 + 112 + 20 + 20 + + + + + + + 1 + 112 + 20 + 20 + + + + + 1 + 112 + 20 + 20 + + + + + + + + 1 + 112 + 20 + 20 + + + 1 + 112 + 20 + 20 + + + + + 1 + 224 + 20 + 20 + + + + + + + + 224 + 224 + 1 + 1 + + + + + + + + + + + 224 + 224 + 1 + 1 + + + + + 224 + 224 + 1 + 1 + + + + + + + + 1 + 224 + 20 + 20 + + + 224 + 224 + 1 + 1 + + + + + 1 + 224 + 20 + 20 + + + + + + + + 1 + 224 + 1 + 1 + + + + + + + + + + + 1 + 224 + 1 + 1 + + + + + 1 + 224 + 1 + 1 + + + + + + + + 1 + 224 + 20 + 20 + + + 1 + 224 + 1 + 1 + + + + + 1 + 224 + 20 + 20 + + + + + + + 1 + 224 + 20 + 20 + + + + + 1 + 224 + 20 + 20 + + + + + + + + 66 + 224 + 1 + 1 + + + + + + + + + + + 66 + 224 + 1 + 1 + + + + + 66 + 224 + 1 + 1 + + + + + + + + 1 + 224 + 20 + 20 + + + 66 + 224 + 1 + 1 + + + + + 1 + 66 + 20 + 20 + + + + + + + + 1 + 66 + 1 + 1 + + + + + + + + + + + 1 + 66 + 1 + 1 + + + + + 1 + 66 + 1 + 1 + + + + + + + + 1 + 66 + 20 + 20 + + + 1 + 66 + 1 + 1 + + + + + 1 + 66 + 20 + 20 + + + + + + + + + + + 5 + + + + + + + + 1 + 66 + 20 + 20 + + + 5 + + + + + 1 + 3 + 22 + 20 + 20 + + + + + + + + 5 + + + + + + + 1 + 3 + 22 + 20 + 20 + + + 5 + + + + + 1 + 3 + 20 + 20 + 22 + + + + + + + + 1 + 3 + 1 + 1 + 22 + + + + + + + + + + + 1 + 3 + 1 + 1 + 22 + + + + + 1 + 3 + 1 + 1 + 22 + + + + + + + + 1 + 3 + 20 + 20 + 22 + + + 1 + 3 + 1 + 1 + 22 + + + + + 1 + 3 + 20 + 20 + 22 + + + + + + + + 1 + 1 + 20 + 20 + 22 + + + + + + + + + + + 1 + 1 + 20 + 20 + 22 + + + + + 1 + 1 + 20 + 20 + 22 + + + + + + + + 1 + 3 + 20 + 20 + 22 + + + 1 + 1 + 20 + 20 + 22 + + + + + 1 + 3 + 20 + 20 + 22 + + + + + + + + + + + 3 + + + + + + + + 1 + 3 + 20 + 20 + 22 + + + 3 + + + + + 1 + 1200 + 22 + + + + + + + + 1 + 19200 + 22 + + + 1 + 4800 + 22 + + + 1 + 1200 + 22 + + + + + 1 + 25200 + 22 + + + + + + + 1 + 25200 + 22 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/yolov8.bin b/assets/yolov8.bin new file mode 100644 index 0000000..ee0d394 Binary files /dev/null and b/assets/yolov8.bin differ diff --git a/assets/yolov8.xml b/assets/yolov8.xml new file mode 100644 index 0000000..31801c4 --- /dev/null +++ b/assets/yolov8.xml @@ -0,0 +1,25211 @@ + + + + + + + + 1 + 3 + 416 + 416 + + + + + + + + 1 + 2 + 3549 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 3 + 416 + 416 + + + + + + + + + 1 + 3 + 416 + 416 + + + + + + + + 8 + 3 + 3 + 3 + + + + + + + + 8 + 3 + 3 + 3 + + + + + 8 + 3 + 3 + 3 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 3 + 3 + 3 + + + 8 + 1 + 1 + 1 + + + + + 8 + 3 + 3 + 3 + + + + + + + + 1 + 3 + 416 + 416 + + + 8 + 3 + 3 + 3 + + + + + 1 + 8 + 208 + 208 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 208 + 208 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 208 + 208 + + + + + + + 1 + 8 + 208 + 208 + + + + + 1 + 8 + 208 + 208 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 208 + 208 + + + + + + + + + 1 + 8 + 208 + 208 + + + + + + + + 16 + 8 + 1 + 1 + + + + + + + + 16 + 8 + 1 + 1 + + + + + 16 + 8 + 1 + 1 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 8 + 1 + 1 + + + 16 + 1 + 1 + 1 + + + + + 16 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 208 + 208 + + + 16 + 8 + 1 + 1 + + + + + 1 + 16 + 208 + 208 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 208 + 208 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 208 + 208 + + + + + + + 1 + 16 + 208 + 208 + + + + + 1 + 16 + 208 + 208 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 208 + 208 + + + 1 + 16 + 1 + 1 + + + 1 + 16 + 1 + 1 + + + 1 + 16 + 1 + 1 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 208 + 208 + + + + + + + + 16 + 1 + 1 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 3 + 3 + + + + + 16 + 1 + 1 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + 1 + + + + + + + + 16 + 1 + 1 + 3 + 3 + + + 16 + 1 + 1 + 1 + 1 + + + + + 16 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 16 + 208 + 208 + + + 16 + 1 + 1 + 3 + 3 + + + + + 1 + 16 + 208 + 208 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 208 + 208 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 208 + 208 + + + + + + + 1 + 16 + 208 + 208 + + + + + 1 + 16 + 208 + 208 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 208 + 208 + + + + + + + + + 1 + 16 + 208 + 208 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 208 + 208 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 208 + 208 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 208 + 208 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 208 + 208 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 208 + 208 + + + + + + + + + 1 + 8 + 208 + 208 + + + + + + + + 1 + 8 + 208 + 208 + + + 1 + 8 + 208 + 208 + + + + + 1 + 8 + 208 + 208 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 208 + 208 + + + + + + + + + 1 + 8 + 208 + 208 + + + + + + + + 64 + 8 + 1 + 1 + + + + + + + + 64 + 8 + 1 + 1 + + + + + 64 + 8 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 8 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 208 + 208 + + + 64 + 8 + 1 + 1 + + + + + 1 + 64 + 208 + 208 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 208 + 208 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 208 + 208 + + + + + + + 1 + 64 + 208 + 208 + + + + + 1 + 64 + 208 + 208 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 208 + 208 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 208 + 208 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 3 + 3 + + + 64 + 1 + 1 + 1 + 1 + + + + + 64 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 64 + 208 + 208 + + + 64 + 1 + 1 + 3 + 3 + + + + + 1 + 64 + 104 + 104 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 104 + 104 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 104 + 104 + + + + + + + 1 + 64 + 104 + 104 + + + + + 1 + 64 + 104 + 104 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 104 + 104 + + + + + + + + + 1 + 64 + 104 + 104 + + + + + + + + 8 + 64 + 1 + 1 + + + + + + + + 8 + 64 + 1 + 1 + + + + + 8 + 64 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 64 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 104 + 104 + + + 8 + 64 + 1 + 1 + + + + + 1 + 8 + 104 + 104 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 104 + 104 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 104 + 104 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 104 + 104 + + + + + + + + + 1 + 8 + 104 + 104 + + + + + + + + 72 + 8 + 1 + 1 + + + + + + + + 72 + 8 + 1 + 1 + + + + + 72 + 8 + 1 + 1 + + + + + + + + 72 + 1 + 1 + 1 + + + + + + + + 72 + 8 + 1 + 1 + + + 72 + 1 + 1 + 1 + + + + + 72 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 104 + 104 + + + 72 + 8 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 104 + 104 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + 1 + 72 + 104 + 104 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 104 + 104 + + + 1 + 72 + 1 + 1 + + + 1 + 72 + 1 + 1 + + + 1 + 72 + 1 + 1 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 72 + 1 + 1 + 3 + 3 + + + + + + + + 72 + 1 + 1 + 3 + 3 + + + + + 72 + 1 + 1 + 3 + 3 + + + + + + + + 72 + 1 + 1 + 1 + 1 + + + + + + + + 72 + 1 + 1 + 3 + 3 + + + 72 + 1 + 1 + 1 + 1 + + + + + 72 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 72 + 104 + 104 + + + 72 + 1 + 1 + 3 + 3 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 104 + 104 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + 1 + 72 + 104 + 104 + + + + + 1 + 72 + 104 + 104 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 72 + 104 + 104 + + + + + + + + + 1 + 72 + 104 + 104 + + + + + + + + 8 + 72 + 1 + 1 + + + + + + + + 8 + 72 + 1 + 1 + + + + + 8 + 72 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 72 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 104 + 104 + + + 8 + 72 + 1 + 1 + + + + + 1 + 8 + 104 + 104 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 104 + 104 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 104 + 104 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 104 + 104 + + + + + + + + + 1 + 8 + 104 + 104 + + + + + + + + 1 + 8 + 104 + 104 + + + 1 + 8 + 104 + 104 + + + + + 1 + 8 + 104 + 104 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 8 + 104 + 104 + + + + + + + + + 1 + 8 + 104 + 104 + + + + + + + + 72 + 8 + 1 + 1 + + + + + + + + 72 + 8 + 1 + 1 + + + + + 72 + 8 + 1 + 1 + + + + + + + + 72 + 1 + 1 + 1 + + + + + + + + 72 + 8 + 1 + 1 + + + 72 + 1 + 1 + 1 + + + + + 72 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 104 + 104 + + + 72 + 8 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 104 + 104 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + 1 + 72 + 104 + 104 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 104 + 104 + + + 1 + 72 + 1 + 1 + + + 1 + 72 + 1 + 1 + + + 1 + 72 + 1 + 1 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 104 + 104 + + + + + + + + 72 + 1 + 1 + 5 + 5 + + + + + + + + 72 + 1 + 1 + 5 + 5 + + + + + 72 + 1 + 1 + 5 + 5 + + + + + + + + 72 + 1 + 1 + 1 + 1 + + + + + + + + 72 + 1 + 1 + 5 + 5 + + + 72 + 1 + 1 + 1 + 1 + + + + + 72 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 72 + 104 + 104 + + + 72 + 1 + 1 + 5 + 5 + + + + + 1 + 72 + 52 + 52 + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 52 + 52 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 72 + 52 + 52 + + + + + + + + + 1 + 72 + 52 + 52 + + + + + + + + 2 + + + + + + + + 1 + 72 + 52 + 52 + + + 2 + + + + + 1 + 72 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + + 1 + 72 + 1 + 1 + + + + + + + + 2 + + + + + + + + 1 + 72 + 1 + 1 + + + 2 + + + + + 1 + 72 + + + + + + + + 18 + 72 + + + + + + + + 18 + 72 + + + + + 18 + 72 + + + + + + + + 18 + 1 + + + + + + + + 18 + 72 + + + 18 + 1 + + + + + 18 + 72 + + + + + + + + 1 + 72 + + + 18 + 72 + + + + + 1 + 18 + + + + + + + + 1 + 18 + + + + + + + + 1 + 18 + + + 1 + 18 + + + + + 1 + 18 + + + + + + + 1 + 18 + + + + + 1 + 18 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 18 + + + + + + + + + 1 + 18 + + + + + + + + 72 + 18 + + + + + + + + 72 + 18 + + + + + 72 + 18 + + + + + + + + 72 + 1 + + + + + + + + 72 + 18 + + + 72 + 1 + + + + + 72 + 18 + + + + + + + + 1 + 18 + + + 72 + 18 + + + + + 1 + 72 + + + + + + + + 1 + 72 + + + + + + + + 1 + 72 + + + 1 + 72 + + + + + 1 + 72 + + + + + + + 1 + 72 + + + + + 1 + 72 + + + + + + + + 4 + + + + + + + + 1 + 72 + + + 4 + + + + + 1 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 52 + 52 + + + 1 + 72 + 1 + 1 + + + + + 1 + 72 + 52 + 52 + + + + + + + 1 + 72 + 52 + 52 + + + + + 1 + 72 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 72 + 52 + 52 + + + + + + + + + 1 + 72 + 52 + 52 + + + + + + + + 16 + 72 + 1 + 1 + + + + + + + + 16 + 72 + 1 + 1 + + + + + 16 + 72 + 1 + 1 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 72 + 1 + 1 + + + 16 + 1 + 1 + 1 + + + + + 16 + 72 + 1 + 1 + + + + + + + + 1 + 72 + 52 + 52 + + + 16 + 72 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 120 + 16 + 1 + 1 + + + + + + + + 120 + 16 + 1 + 1 + + + + + 120 + 16 + 1 + 1 + + + + + + + + 120 + 1 + 1 + 1 + + + + + + + + 120 + 16 + 1 + 1 + + + 120 + 1 + 1 + 1 + + + + + 120 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 120 + 16 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + 1 + 120 + 52 + 52 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + 1 + 120 + 1 + 1 + + + 1 + 120 + 1 + 1 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 120 + 1 + 1 + 1 + 1 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + 120 + 1 + 1 + 1 + 1 + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 120 + 52 + 52 + + + 120 + 1 + 1 + 5 + 5 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + 2 + + + + + + + + 1 + 120 + 52 + 52 + + + 2 + + + + + 1 + 120 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 2 + + + + + + + + 1 + 120 + 1 + 1 + + + 2 + + + + + 1 + 120 + + + + + + + + 30 + 120 + + + + + + + + 30 + 120 + + + + + 30 + 120 + + + + + + + + 30 + 1 + + + + + + + + 30 + 120 + + + 30 + 1 + + + + + 30 + 120 + + + + + + + + 1 + 120 + + + 30 + 120 + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + 1 + 30 + + + + + 1 + 30 + + + + + + + 1 + 30 + + + + + 1 + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 30 + + + + + + + + + 1 + 30 + + + + + + + + 120 + 30 + + + + + + + + 120 + 30 + + + + + 120 + 30 + + + + + + + + 120 + 1 + + + + + + + + 120 + 30 + + + 120 + 1 + + + + + 120 + 30 + + + + + + + + 1 + 30 + + + 120 + 30 + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + 1 + 120 + + + + + 1 + 120 + + + + + + + 1 + 120 + + + + + 1 + 120 + + + + + + + + 4 + + + + + + + + 1 + 120 + + + 4 + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + 1 + 120 + 52 + 52 + + + + + 1 + 120 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + 16 + 120 + 1 + 1 + + + + + + + + 16 + 120 + 1 + 1 + + + + + 16 + 120 + 1 + 1 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 120 + 1 + 1 + + + 16 + 1 + 1 + 1 + + + + + 16 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 16 + 120 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 52 + 52 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 120 + 16 + 1 + 1 + + + + + + + + 120 + 16 + 1 + 1 + + + + + 120 + 16 + 1 + 1 + + + + + + + + 120 + 1 + 1 + 1 + + + + + + + + 120 + 16 + 1 + 1 + + + 120 + 1 + 1 + 1 + + + + + 120 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 120 + 16 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + 1 + 120 + 52 + 52 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + 1 + 120 + 1 + 1 + + + 1 + 120 + 1 + 1 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 120 + 1 + 1 + 1 + 1 + + + + + + + + 120 + 1 + 1 + 5 + 5 + + + 120 + 1 + 1 + 1 + 1 + + + + + 120 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 120 + 52 + 52 + + + 120 + 1 + 1 + 5 + 5 + + + + + 1 + 120 + 52 + 52 + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + 2 + + + + + + + + 1 + 120 + 52 + 52 + + + 2 + + + + + 1 + 120 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 1 + 1 + + + 2 + + + + + 1 + 120 + + + + + + + + 30 + 120 + + + + + + + + 30 + 120 + + + + + 30 + 120 + + + + + + + + 30 + 1 + + + + + + + + 30 + 120 + + + 30 + 1 + + + + + 30 + 120 + + + + + + + + 1 + 120 + + + 30 + 120 + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + 1 + 30 + + + + + 1 + 30 + + + + + + + 1 + 30 + + + + + 1 + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 30 + + + + + + + + + 1 + 30 + + + + + + + + 120 + 30 + + + + + + + + 120 + 30 + + + + + 120 + 30 + + + + + + + + 120 + 1 + + + + + + + + 120 + 30 + + + 120 + 1 + + + + + 120 + 30 + + + + + + + + 1 + 30 + + + 120 + 30 + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + 1 + 120 + + + + + 1 + 120 + + + + + + + 1 + 120 + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + 4 + + + + + 1 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 1 + 120 + 1 + 1 + + + + + 1 + 120 + 52 + 52 + + + + + + + 1 + 120 + 52 + 52 + + + + + 1 + 120 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + + 1 + 120 + 52 + 52 + + + + + + + + 16 + 120 + 1 + 1 + + + + + + + + 16 + 120 + 1 + 1 + + + + + 16 + 120 + 1 + 1 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 120 + 1 + 1 + + + 16 + 1 + 1 + 1 + + + + + 16 + 120 + 1 + 1 + + + + + + + + 1 + 120 + 52 + 52 + + + 16 + 120 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 52 + 52 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 240 + 16 + 1 + 1 + + + + + + + + 240 + 16 + 1 + 1 + + + + + 240 + 16 + 1 + 1 + + + + + + + + 240 + 1 + 1 + 1 + + + + + + + + 240 + 16 + 1 + 1 + + + 240 + 1 + 1 + 1 + + + + + 240 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 240 + 16 + 1 + 1 + + + + + 1 + 240 + 52 + 52 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 52 + 52 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 52 + 52 + + + + + + + 1 + 240 + 52 + 52 + + + + + 1 + 240 + 52 + 52 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 52 + 52 + + + 1 + 240 + 1 + 1 + + + 1 + 240 + 1 + 1 + + + 1 + 240 + 1 + 1 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 52 + 52 + + + + + + + + 240 + 1 + 1 + 3 + 3 + + + + + + + + 240 + 1 + 1 + 3 + 3 + + + + + 240 + 1 + 1 + 3 + 3 + + + + + + + + 240 + 1 + 1 + 1 + 1 + + + + + + + + 240 + 1 + 1 + 3 + 3 + + + 240 + 1 + 1 + 1 + 1 + + + + + 240 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 240 + 52 + 52 + + + 240 + 1 + 1 + 3 + 3 + + + + + 1 + 240 + 26 + 26 + + + + + + + + 1 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 26 + 26 + + + 1 + 240 + 1 + 1 + + + + + 1 + 240 + 26 + 26 + + + + + + + 1 + 240 + 26 + 26 + + + + + 1 + 240 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 240 + 26 + 26 + + + + + + + + + 1 + 240 + 26 + 26 + + + + + + + + 24 + 240 + 1 + 1 + + + + + + + + 24 + 240 + 1 + 1 + + + + + 24 + 240 + 1 + 1 + + + + + + + + 24 + 1 + 1 + 1 + + + + + + + + 24 + 240 + 1 + 1 + + + 24 + 1 + 1 + 1 + + + + + 24 + 240 + 1 + 1 + + + + + + + + 1 + 240 + 26 + 26 + + + 24 + 240 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 200 + 24 + 1 + 1 + + + + + + + + 200 + 24 + 1 + 1 + + + + + 200 + 24 + 1 + 1 + + + + + + + + 200 + 1 + 1 + 1 + + + + + + + + 200 + 24 + 1 + 1 + + + 200 + 1 + 1 + 1 + + + + + 200 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 200 + 24 + 1 + 1 + + + + + 1 + 200 + 26 + 26 + + + + + + + + 1 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 26 + 26 + + + 1 + 200 + 1 + 1 + + + + + 1 + 200 + 26 + 26 + + + + + + + 1 + 200 + 26 + 26 + + + + + 1 + 200 + 26 + 26 + + + + + + + + 1 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 26 + 26 + + + 1 + 200 + 1 + 1 + + + 1 + 200 + 1 + 1 + + + 1 + 200 + 1 + 1 + + + 1 + 200 + 1 + 1 + + + + + 1 + 200 + 26 + 26 + + + + + + + + 200 + 1 + 1 + 3 + 3 + + + + + + + + 200 + 1 + 1 + 3 + 3 + + + + + 200 + 1 + 1 + 3 + 3 + + + + + + + + 200 + 1 + 1 + 1 + 1 + + + + + + + + 200 + 1 + 1 + 3 + 3 + + + 200 + 1 + 1 + 1 + 1 + + + + + 200 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 200 + 26 + 26 + + + 200 + 1 + 1 + 3 + 3 + + + + + 1 + 200 + 26 + 26 + + + + + + + + 1 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 26 + 26 + + + 1 + 200 + 1 + 1 + + + + + 1 + 200 + 26 + 26 + + + + + + + 1 + 200 + 26 + 26 + + + + + 1 + 200 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 200 + 26 + 26 + + + + + + + + + 1 + 200 + 26 + 26 + + + + + + + + 24 + 200 + 1 + 1 + + + + + + + + 24 + 200 + 1 + 1 + + + + + 24 + 200 + 1 + 1 + + + + + + + + 24 + 1 + 1 + 1 + + + + + + + + 24 + 200 + 1 + 1 + + + 24 + 1 + 1 + 1 + + + + + 24 + 200 + 1 + 1 + + + + + + + + 1 + 200 + 26 + 26 + + + 24 + 200 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 26 + 26 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 184 + 24 + 1 + 1 + + + + + + + + 184 + 24 + 1 + 1 + + + + + 184 + 24 + 1 + 1 + + + + + + + + 184 + 1 + 1 + 1 + + + + + + + + 184 + 24 + 1 + 1 + + + 184 + 1 + 1 + 1 + + + + + 184 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 184 + 24 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 1 + 184 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + 1 + 184 + 26 + 26 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 1 + 184 + 1 + 1 + + + 1 + 184 + 1 + 1 + + + 1 + 184 + 1 + 1 + + + 1 + 184 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 184 + 1 + 1 + 3 + 3 + + + + + + + + 184 + 1 + 1 + 3 + 3 + + + + + 184 + 1 + 1 + 3 + 3 + + + + + + + + 184 + 1 + 1 + 1 + 1 + + + + + + + + 184 + 1 + 1 + 3 + 3 + + + 184 + 1 + 1 + 1 + 1 + + + + + 184 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 184 + 26 + 26 + + + 184 + 1 + 1 + 3 + 3 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 1 + 184 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + 1 + 184 + 26 + 26 + + + + + 1 + 184 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 184 + 26 + 26 + + + + + + + + + 1 + 184 + 26 + 26 + + + + + + + + 24 + 184 + 1 + 1 + + + + + + + + 24 + 184 + 1 + 1 + + + + + 24 + 184 + 1 + 1 + + + + + + + + 24 + 1 + 1 + 1 + + + + + + + + 24 + 184 + 1 + 1 + + + 24 + 1 + 1 + 1 + + + + + 24 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 24 + 184 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 26 + 26 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 184 + 24 + 1 + 1 + + + + + + + + 184 + 24 + 1 + 1 + + + + + 184 + 24 + 1 + 1 + + + + + + + + 184 + 1 + 1 + 1 + + + + + + + + 184 + 24 + 1 + 1 + + + 184 + 1 + 1 + 1 + + + + + 184 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 184 + 24 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 1 + 184 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + 1 + 184 + 26 + 26 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 1 + 184 + 1 + 1 + + + 1 + 184 + 1 + 1 + + + 1 + 184 + 1 + 1 + + + 1 + 184 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 184 + 1 + 1 + 3 + 3 + + + + + + + + 184 + 1 + 1 + 3 + 3 + + + + + 184 + 1 + 1 + 3 + 3 + + + + + + + + 184 + 1 + 1 + 1 + 1 + + + + + + + + 184 + 1 + 1 + 3 + 3 + + + 184 + 1 + 1 + 1 + 1 + + + + + 184 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 184 + 26 + 26 + + + 184 + 1 + 1 + 3 + 3 + + + + + 1 + 184 + 26 + 26 + + + + + + + + 1 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 1 + 184 + 1 + 1 + + + + + 1 + 184 + 26 + 26 + + + + + + + 1 + 184 + 26 + 26 + + + + + 1 + 184 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 184 + 26 + 26 + + + + + + + + + 1 + 184 + 26 + 26 + + + + + + + + 24 + 184 + 1 + 1 + + + + + + + + 24 + 184 + 1 + 1 + + + + + 24 + 184 + 1 + 1 + + + + + + + + 24 + 1 + 1 + 1 + + + + + + + + 24 + 184 + 1 + 1 + + + 24 + 1 + 1 + 1 + + + + + 24 + 184 + 1 + 1 + + + + + + + + 1 + 184 + 26 + 26 + + + 24 + 184 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 1 + 1 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 1 + 24 + 26 + 26 + + + 1 + 24 + 26 + 26 + + + + + 1 + 24 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + + 1 + 24 + 26 + 26 + + + + + + + + 480 + 24 + 1 + 1 + + + + + + + + 480 + 24 + 1 + 1 + + + + + 480 + 24 + 1 + 1 + + + + + + + + 480 + 1 + 1 + 1 + + + + + + + + 480 + 24 + 1 + 1 + + + 480 + 1 + 1 + 1 + + + + + 480 + 24 + 1 + 1 + + + + + + + + 1 + 24 + 26 + 26 + + + 480 + 24 + 1 + 1 + + + + + 1 + 480 + 26 + 26 + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 26 + 26 + + + 1 + 480 + 1 + 1 + + + + + 1 + 480 + 26 + 26 + + + + + + + 1 + 480 + 26 + 26 + + + + + 1 + 480 + 26 + 26 + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 26 + 26 + + + 1 + 480 + 1 + 1 + + + 1 + 480 + 1 + 1 + + + 1 + 480 + 1 + 1 + + + 1 + 480 + 1 + 1 + + + + + 1 + 480 + 26 + 26 + + + + + + + + 480 + 1 + 1 + 3 + 3 + + + + + + + + 480 + 1 + 1 + 3 + 3 + + + + + 480 + 1 + 1 + 3 + 3 + + + + + + + + 480 + 1 + 1 + 1 + 1 + + + + + + + + 480 + 1 + 1 + 3 + 3 + + + 480 + 1 + 1 + 1 + 1 + + + + + 480 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 480 + 26 + 26 + + + 480 + 1 + 1 + 3 + 3 + + + + + 1 + 480 + 26 + 26 + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 26 + 26 + + + 1 + 480 + 1 + 1 + + + + + 1 + 480 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 480 + 26 + 26 + + + + + + + + + 1 + 480 + 26 + 26 + + + + + + + + 2 + + + + + + + + 1 + 480 + 26 + 26 + + + 2 + + + + + 1 + 480 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + + 1 + 480 + 1 + 1 + + + + + + + + 2 + + + + + + + + 1 + 480 + 1 + 1 + + + 2 + + + + + 1 + 480 + + + + + + + + 120 + 480 + + + + + + + + 120 + 480 + + + + + 120 + 480 + + + + + + + + 120 + 1 + + + + + + + + 120 + 480 + + + 120 + 1 + + + + + 120 + 480 + + + + + + + + 1 + 480 + + + 120 + 480 + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + + + + + + 1 + 120 + + + 1 + 120 + + + + + 1 + 120 + + + + + + + 1 + 120 + + + + + 1 + 120 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 120 + + + + + + + + + 1 + 120 + + + + + + + + 480 + 120 + + + + + + + + 480 + 120 + + + + + 480 + 120 + + + + + + + + 480 + 1 + + + + + + + + 480 + 120 + + + 480 + 1 + + + + + 480 + 120 + + + + + + + + 1 + 120 + + + 480 + 120 + + + + + 1 + 480 + + + + + + + + 1 + 480 + + + + + + + + 1 + 480 + + + 1 + 480 + + + + + 1 + 480 + + + + + + + 1 + 480 + + + + + 1 + 480 + + + + + + + + 4 + + + + + + + + 1 + 480 + + + 4 + + + + + 1 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 26 + 26 + + + 1 + 480 + 1 + 1 + + + + + 1 + 480 + 26 + 26 + + + + + + + 1 + 480 + 26 + 26 + + + + + 1 + 480 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 480 + 26 + 26 + + + + + + + + + 1 + 480 + 26 + 26 + + + + + + + + 32 + 480 + 1 + 1 + + + + + + + + 32 + 480 + 1 + 1 + + + + + 32 + 480 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 480 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 480 + 1 + 1 + + + + + + + + 1 + 480 + 26 + 26 + + + 32 + 480 + 1 + 1 + + + + + 1 + 32 + 26 + 26 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 26 + 26 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 26 + 26 + + + + + + + + + 1 + 32 + 26 + 26 + + + + + + + + 672 + 32 + 1 + 1 + + + + + + + + 672 + 32 + 1 + 1 + + + + + 672 + 32 + 1 + 1 + + + + + + + + 672 + 1 + 1 + 1 + + + + + + + + 672 + 32 + 1 + 1 + + + 672 + 1 + 1 + 1 + + + + + 672 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 26 + 26 + + + 672 + 32 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + 1 + 672 + 26 + 26 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 672 + 1 + 1 + 3 + 3 + + + + + + + + 672 + 1 + 1 + 3 + 3 + + + + + 672 + 1 + 1 + 3 + 3 + + + + + + + + 672 + 1 + 1 + 1 + 1 + + + + + + + + 672 + 1 + 1 + 3 + 3 + + + 672 + 1 + 1 + 1 + 1 + + + + + 672 + 1 + 1 + 3 + 3 + + + + + + + + 1 + 672 + 26 + 26 + + + 672 + 1 + 1 + 3 + 3 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + 2 + + + + + + + + 1 + 672 + 26 + 26 + + + 2 + + + + + 1 + 672 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 2 + + + + + + + + 1 + 672 + 1 + 1 + + + 2 + + + + + 1 + 672 + + + + + + + + 168 + 672 + + + + + + + + 168 + 672 + + + + + 168 + 672 + + + + + + + + 168 + 1 + + + + + + + + 168 + 672 + + + 168 + 1 + + + + + 168 + 672 + + + + + + + + 1 + 672 + + + 168 + 672 + + + + + 1 + 168 + + + + + + + + 1 + 168 + + + + + + + + 1 + 168 + + + 1 + 168 + + + + + 1 + 168 + + + + + + + 1 + 168 + + + + + 1 + 168 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 168 + + + + + + + + + 1 + 168 + + + + + + + + 672 + 168 + + + + + + + + 672 + 168 + + + + + 672 + 168 + + + + + + + + 672 + 1 + + + + + + + + 672 + 168 + + + 672 + 1 + + + + + 672 + 168 + + + + + + + + 1 + 168 + + + 672 + 168 + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + 1 + 672 + + + + + 1 + 672 + + + + + + + 1 + 672 + + + + + 1 + 672 + + + + + + + + 4 + + + + + + + + 1 + 672 + + + 4 + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + 1 + 672 + 26 + 26 + + + + + 1 + 672 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + 32 + 672 + 1 + 1 + + + + + + + + 32 + 672 + 1 + 1 + + + + + 32 + 672 + 1 + 1 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 672 + 1 + 1 + + + 32 + 1 + 1 + 1 + + + + + 32 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 32 + 672 + 1 + 1 + + + + + 1 + 32 + 26 + 26 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 26 + 26 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 26 + 26 + + + + + + + + + 1 + 32 + 26 + 26 + + + + + + + + 1 + 32 + 26 + 26 + + + 1 + 32 + 26 + 26 + + + + + 1 + 32 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 26 + 26 + + + + + + + + + 1 + 32 + 26 + 26 + + + + + + + + 672 + 32 + 1 + 1 + + + + + + + + 672 + 32 + 1 + 1 + + + + + 672 + 32 + 1 + 1 + + + + + + + + 672 + 1 + 1 + 1 + + + + + + + + 672 + 32 + 1 + 1 + + + 672 + 1 + 1 + 1 + + + + + 672 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 26 + 26 + + + 672 + 32 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + 1 + 672 + 26 + 26 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 672 + 1 + 1 + 5 + 5 + + + + + + + + 672 + 1 + 1 + 5 + 5 + + + + + 672 + 1 + 1 + 5 + 5 + + + + + + + + 672 + 1 + 1 + 1 + 1 + + + + + + + + 672 + 1 + 1 + 5 + 5 + + + 672 + 1 + 1 + 1 + 1 + + + + + 672 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 672 + 26 + 26 + + + 672 + 1 + 1 + 5 + 5 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + 2 + + + + + + + + 1 + 672 + 26 + 26 + + + 2 + + + + + 1 + 672 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + 2 + + + + + 1 + 672 + + + + + + + + 168 + 672 + + + + + + + + 168 + 672 + + + + + 168 + 672 + + + + + + + + 168 + 1 + + + + + + + + 168 + 672 + + + 168 + 1 + + + + + 168 + 672 + + + + + + + + 1 + 672 + + + 168 + 672 + + + + + 1 + 168 + + + + + + + + 1 + 168 + + + + + + + + 1 + 168 + + + 1 + 168 + + + + + 1 + 168 + + + + + + + 1 + 168 + + + + + 1 + 168 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 168 + + + + + + + + + 1 + 168 + + + + + + + + 672 + 168 + + + + + + + + 672 + 168 + + + + + 672 + 168 + + + + + + + + 672 + 1 + + + + + + + + 672 + 168 + + + 672 + 1 + + + + + 672 + 168 + + + + + + + + 1 + 168 + + + 672 + 168 + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + 1 + 672 + + + + + 1 + 672 + + + + + + + 1 + 672 + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + 4 + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + 1 + 672 + 26 + 26 + + + + + 1 + 672 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + + 1 + 672 + 26 + 26 + + + + + + + + 40 + 672 + 1 + 1 + + + + + + + + 40 + 672 + 1 + 1 + + + + + 40 + 672 + 1 + 1 + + + + + + + + 40 + 1 + 1 + 1 + + + + + + + + 40 + 672 + 1 + 1 + + + 40 + 1 + 1 + 1 + + + + + 40 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 40 + 672 + 1 + 1 + + + + + 1 + 40 + 26 + 26 + + + + + + + + 1 + 40 + 1 + 1 + + + + + + + + 1 + 40 + 26 + 26 + + + 1 + 40 + 1 + 1 + + + + + 1 + 40 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 40 + 26 + 26 + + + + + + + + + 1 + 40 + 26 + 26 + + + + + + + + 672 + 40 + 1 + 1 + + + + + + + + 672 + 40 + 1 + 1 + + + + + 672 + 40 + 1 + 1 + + + + + + + + 672 + 1 + 1 + 1 + + + + + + + + 672 + 40 + 1 + 1 + + + 672 + 1 + 1 + 1 + + + + + 672 + 40 + 1 + 1 + + + + + + + + 1 + 40 + 26 + 26 + + + 672 + 40 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + 1 + 672 + 26 + 26 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 26 + 26 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 26 + 26 + + + + + + + + 672 + 1 + 1 + 5 + 5 + + + + + + + + 672 + 1 + 1 + 5 + 5 + + + + + 672 + 1 + 1 + 5 + 5 + + + + + + + + 672 + 1 + 1 + 1 + 1 + + + + + + + + 672 + 1 + 1 + 5 + 5 + + + 672 + 1 + 1 + 1 + 1 + + + + + 672 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 672 + 26 + 26 + + + 672 + 1 + 1 + 5 + 5 + + + + + 1 + 672 + 13 + 13 + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 13 + 13 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 13 + 13 + + + + + + + + + 1 + 672 + 13 + 13 + + + + + + + + 2 + + + + + + + + 1 + 672 + 13 + 13 + + + 2 + + + + + 1 + 672 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 1 + 1 + + + 2 + + + + + 1 + 672 + + + + + + + + 168 + 672 + + + + + + + + 168 + 672 + + + + + 168 + 672 + + + + + + + + 168 + 1 + + + + + + + + 168 + 672 + + + 168 + 1 + + + + + 168 + 672 + + + + + + + + 1 + 672 + + + 168 + 672 + + + + + 1 + 168 + + + + + + + + 1 + 168 + + + + + + + + 1 + 168 + + + 1 + 168 + + + + + 1 + 168 + + + + + + + 1 + 168 + + + + + 1 + 168 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 168 + + + + + + + + + 1 + 168 + + + + + + + + 672 + 168 + + + + + + + + 672 + 168 + + + + + 672 + 168 + + + + + + + + 672 + 1 + + + + + + + + 672 + 168 + + + 672 + 1 + + + + + 672 + 168 + + + + + + + + 1 + 168 + + + 672 + 168 + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + 1 + 672 + + + + + 1 + 672 + + + + + + + 1 + 672 + + + + + 1 + 672 + + + + + + + + 1 + 672 + + + 4 + + + + + 1 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 13 + 13 + + + 1 + 672 + 1 + 1 + + + + + 1 + 672 + 13 + 13 + + + + + + + 1 + 672 + 13 + 13 + + + + + 1 + 672 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 672 + 13 + 13 + + + + + + + + + 1 + 672 + 13 + 13 + + + + + + + + 40 + 672 + 1 + 1 + + + + + + + + 40 + 672 + 1 + 1 + + + + + 40 + 672 + 1 + 1 + + + + + + + + 40 + 1 + 1 + 1 + + + + + + + + 40 + 672 + 1 + 1 + + + 40 + 1 + 1 + 1 + + + + + 40 + 672 + 1 + 1 + + + + + + + + 1 + 672 + 13 + 13 + + + 40 + 672 + 1 + 1 + + + + + 1 + 40 + 13 + 13 + + + + + + + + 1 + 40 + 1 + 1 + + + + + + + + 1 + 40 + 13 + 13 + + + 1 + 40 + 1 + 1 + + + + + 1 + 40 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 40 + 13 + 13 + + + + + + + + + 1 + 40 + 13 + 13 + + + + + + + + 960 + 40 + 1 + 1 + + + + + + + + 960 + 40 + 1 + 1 + + + + + 960 + 40 + 1 + 1 + + + + + + + + 960 + 1 + 1 + 1 + + + + + + + + 960 + 40 + 1 + 1 + + + 960 + 1 + 1 + 1 + + + + + 960 + 40 + 1 + 1 + + + + + + + + 1 + 40 + 13 + 13 + + + 960 + 40 + 1 + 1 + + + + + 1 + 960 + 13 + 13 + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 13 + 13 + + + 1 + 960 + 1 + 1 + + + + + 1 + 960 + 13 + 13 + + + + + + + 1 + 960 + 13 + 13 + + + + + 1 + 960 + 13 + 13 + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 13 + 13 + + + 1 + 960 + 1 + 1 + + + 1 + 960 + 1 + 1 + + + 1 + 960 + 1 + 1 + + + 1 + 960 + 1 + 1 + + + + + 1 + 960 + 13 + 13 + + + + + + + + 960 + 1 + 1 + 5 + 5 + + + + + + + + 960 + 1 + 1 + 5 + 5 + + + + + 960 + 1 + 1 + 5 + 5 + + + + + + + + 960 + 1 + 1 + 1 + 1 + + + + + + + + 960 + 1 + 1 + 5 + 5 + + + 960 + 1 + 1 + 1 + 1 + + + + + 960 + 1 + 1 + 5 + 5 + + + + + + + + 1 + 960 + 13 + 13 + + + 960 + 1 + 1 + 5 + 5 + + + + + 1 + 960 + 13 + 13 + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 13 + 13 + + + 1 + 960 + 1 + 1 + + + + + 1 + 960 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 960 + 13 + 13 + + + + + + + + + 1 + 960 + 13 + 13 + + + + + + + + 2 + + + + + + + + 1 + 960 + 13 + 13 + + + 2 + + + + + 1 + 960 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + + 1 + 960 + 1 + 1 + + + + + + + + 2 + + + + + + + + 1 + 960 + 1 + 1 + + + 2 + + + + + 1 + 960 + + + + + + + + 240 + 960 + + + + + + + + 240 + 960 + + + + + 240 + 960 + + + + + + + + 240 + 1 + + + + + + + + 240 + 960 + + + 240 + 1 + + + + + 240 + 960 + + + + + + + + 1 + 960 + + + 240 + 960 + + + + + 1 + 240 + + + + + + + + 1 + 240 + + + + + + + + 1 + 240 + + + 1 + 240 + + + + + 1 + 240 + + + + + + + 1 + 240 + + + + + 1 + 240 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 240 + + + + + + + + + 1 + 240 + + + + + + + + 960 + 240 + + + + + + + + 960 + 240 + + + + + 960 + 240 + + + + + + + + 960 + 1 + + + + + + + + 960 + 240 + + + 960 + 1 + + + + + 960 + 240 + + + + + + + + 1 + 240 + + + 960 + 240 + + + + + 1 + 960 + + + + + + + + 1 + 960 + + + + + + + + 1 + 960 + + + 1 + 960 + + + + + 1 + 960 + + + + + + + 1 + 960 + + + + + 1 + 960 + + + + + + + + 4 + + + + + + + + 1 + 960 + + + 4 + + + + + 1 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 13 + 13 + + + 1 + 960 + 1 + 1 + + + + + 1 + 960 + 13 + 13 + + + + + + + 1 + 960 + 13 + 13 + + + + + 1 + 960 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 960 + 13 + 13 + + + + + + + + + 1 + 960 + 13 + 13 + + + + + + + + 40 + 960 + 1 + 1 + + + + + + + + 40 + 960 + 1 + 1 + + + + + 40 + 960 + 1 + 1 + + + + + + + + 40 + 1 + 1 + 1 + + + + + + + + 40 + 960 + 1 + 1 + + + 40 + 1 + 1 + 1 + + + + + 40 + 960 + 1 + 1 + + + + + + + + 1 + 960 + 13 + 13 + + + 40 + 960 + 1 + 1 + + + + + 1 + 40 + 13 + 13 + + + + + + + + 1 + 40 + 1 + 1 + + + + + + + + 1 + 40 + 13 + 13 + + + 1 + 40 + 1 + 1 + + + + + 1 + 40 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 40 + 13 + 13 + + + + + + + + + 1 + 40 + 13 + 13 + + + + + + + + 1 + 40 + 13 + 13 + + + 1 + 40 + 13 + 13 + + + + + 1 + 40 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 40 + 13 + 13 + + + + + + + + + 1 + 40 + 13 + 13 + + + + + + + + 4 + + + + + + + + 1 + 40 + 13 + 13 + + + 4 + + + + + 1 + 40 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 40 + 26 + 26 + + + + + + + + + 1 + 40 + 26 + 26 + + + + + + + + 1 + 40 + 26 + 26 + + + 1 + 40 + 26 + 26 + + + + + 1 + 80 + 26 + 26 + + + + + + + + 128 + 80 + 1 + 1 + + + + + + + + 128 + 80 + 1 + 1 + + + + + 128 + 80 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 80 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 80 + 1 + 1 + + + + + + + + 1 + 80 + 26 + 26 + + + 128 + 80 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 26 + 26 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + 1 + 128 + 26 + 26 + + + + + 1 + 128 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 128 + 26 + 26 + + + + 2 + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 26 + 26 + + + + + 1 + 192 + 26 + 26 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 26 + 26 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 26 + 26 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + 1 + 128 + 26 + 26 + + + + + 1 + 128 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + 1 + 128 + 26 + 26 + + + 4 + + + + + 1 + 128 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 52 + 52 + + + + + + + + + 1 + 128 + 52 + 52 + + + + + + + + 1 + 128 + 52 + 52 + + + 1 + 16 + 52 + 52 + + + + + 1 + 144 + 52 + 52 + + + + + + + + 64 + 144 + 1 + 1 + + + + + + + + 64 + 144 + 1 + 1 + + + + + 64 + 144 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 144 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 144 + 1 + 1 + + + + + + + + 1 + 144 + 52 + 52 + + + 64 + 144 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + 1 + 64 + 52 + 52 + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 64 + 52 + 52 + + + + 2 + + + + + 1 + 32 + 52 + 52 + + + 1 + 32 + 52 + 52 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 52 + 52 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 52 + 52 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 52 + 52 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 52 + 52 + + + + + + + 1 + 32 + 52 + 52 + + + + + 1 + 32 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 52 + 52 + + + + + + + + + 1 + 32 + 52 + 52 + + + + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 32 + 3 + 3 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 32 + 1 + 1 + 1 + + + + + + + + 32 + 32 + 3 + 3 + + + 32 + 1 + 1 + 1 + + + + + 32 + 32 + 3 + 3 + + + + + + + + 1 + 32 + 52 + 52 + + + 32 + 32 + 3 + 3 + + + + + 1 + 32 + 52 + 52 + + + + + + + + 1 + 32 + 1 + 1 + + + + + + + + 1 + 32 + 52 + 52 + + + 1 + 32 + 1 + 1 + + + + + 1 + 32 + 52 + 52 + + + + + + + 1 + 32 + 52 + 52 + + + + + 1 + 32 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 32 + 52 + 52 + + + + + + + + + 1 + 32 + 52 + 52 + + + + + + + + 1 + 32 + 52 + 52 + + + 1 + 32 + 52 + 52 + + + 1 + 32 + 52 + 52 + + + + + 1 + 96 + 52 + 52 + + + + + + + + 64 + 96 + 1 + 1 + + + + + + + + 64 + 96 + 1 + 1 + + + + + 64 + 96 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 96 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 96 + 1 + 1 + + + + + + + + 1 + 96 + 52 + 52 + + + 64 + 96 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + 1 + 64 + 52 + 52 + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 52 + 52 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + 1 + 64 + 52 + 52 + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 52 + 52 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + 1 + 64 + 52 + 52 + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 52 + 52 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + 1 + 64 + 52 + 52 + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 52 + 52 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 52 + 52 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 52 + 52 + + + + + + + 1 + 64 + 52 + 52 + + + + + 1 + 64 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + + 1 + 64 + 52 + 52 + + + + + + + + 2 + 64 + 1 + 1 + + + + + + + + 2 + 64 + 1 + 1 + + + + + 2 + 64 + 1 + 1 + + + + + + + + 2 + 1 + 1 + 1 + + + + + + + + 2 + 64 + 1 + 1 + + + 2 + 1 + 1 + 1 + + + + + 2 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 52 + 52 + + + 2 + 64 + 1 + 1 + + + + + 1 + 2 + 52 + 52 + + + + + + + + 1 + 2 + 1 + 1 + + + + + + + + 1 + 2 + 52 + 52 + + + 1 + 2 + 1 + 1 + + + + + 1 + 2 + 52 + 52 + + + + + + + + 1 + 64 + 52 + 52 + + + 1 + 2 + 52 + 52 + + + + + 1 + 66 + 52 + 52 + + + + + + + + 3 + + + + + + + + 1 + 66 + 52 + 52 + + + 3 + + + + + 1 + 66 + 2704 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 52 + 52 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 128 + 26 + 26 + + + + + 1 + 192 + 26 + 26 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 26 + 26 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 26 + 26 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + 1 + 128 + 26 + 26 + + + + + 1 + 128 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + + + + + + 1 + 128 + 26 + 26 + + + + 2 + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 26 + 26 + + + + + 1 + 192 + 26 + 26 + + + + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 192 + 1 + 1 + + + 128 + 1 + 1 + 1 + + + + + 128 + 192 + 1 + 1 + + + + + + + + 1 + 192 + 26 + 26 + + + 128 + 192 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 26 + 26 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 26 + 26 + + + + + + + 1 + 128 + 26 + 26 + + + + + 1 + 128 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + + 1 + 128 + 26 + 26 + + + + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 128 + 3 + 3 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 26 + 26 + + + 64 + 128 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 128 + 3 + 3 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 128 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 26 + 26 + + + 64 + 128 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 26 + 26 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 26 + 26 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 26 + 26 + + + + + + + 1 + 64 + 26 + 26 + + + + + 1 + 64 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + + 1 + 64 + 26 + 26 + + + + + + + + 2 + 64 + 1 + 1 + + + + + + + + 2 + 64 + 1 + 1 + + + + + 2 + 64 + 1 + 1 + + + + + + + + 2 + 1 + 1 + 1 + + + + + + + + 2 + 64 + 1 + 1 + + + 2 + 1 + 1 + 1 + + + + + 2 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 26 + 26 + + + 2 + 64 + 1 + 1 + + + + + 1 + 2 + 26 + 26 + + + + + + + + 1 + 2 + 1 + 1 + + + + + + + + 1 + 2 + 26 + 26 + + + 1 + 2 + 1 + 1 + + + + + 1 + 2 + 26 + 26 + + + + + + + + 1 + 64 + 26 + 26 + + + 1 + 2 + 26 + 26 + + + + + 1 + 66 + 26 + 26 + + + + + + + + 1 + 66 + 26 + 26 + + + 3 + + + + + 1 + 66 + 676 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 26 + 26 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 13 + 13 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 13 + 13 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 13 + 13 + + + + + + + 1 + 128 + 13 + 13 + + + + + 1 + 128 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 13 + 13 + + + + + + + + + 1 + 128 + 13 + 13 + + + + + + + + 1 + 128 + 13 + 13 + + + 1 + 40 + 13 + 13 + + + + + 1 + 168 + 13 + 13 + + + + + + + + 256 + 168 + 1 + 1 + + + + + + + + 256 + 168 + 1 + 1 + + + + + 256 + 168 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 168 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 168 + 1 + 1 + + + + + + + + 1 + 168 + 13 + 13 + + + 256 + 168 + 1 + 1 + + + + + 1 + 256 + 13 + 13 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 13 + 13 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 13 + 13 + + + + + + + 1 + 256 + 13 + 13 + + + + + 1 + 256 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 13 + 13 + + + + + + + + + 1 + 256 + 13 + 13 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 256 + 13 + 13 + + + + 2 + + + + + 1 + 128 + 13 + 13 + + + 1 + 128 + 13 + 13 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 13 + 13 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 13 + 13 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 13 + 13 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 13 + 13 + + + + + + + 1 + 128 + 13 + 13 + + + + + 1 + 128 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 13 + 13 + + + + + + + + + 1 + 128 + 13 + 13 + + + + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 128 + 3 + 3 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 128 + 1 + 1 + 1 + + + + + + + + 128 + 128 + 3 + 3 + + + 128 + 1 + 1 + 1 + + + + + 128 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 13 + 13 + + + 128 + 128 + 3 + 3 + + + + + 1 + 128 + 13 + 13 + + + + + + + + 1 + 128 + 1 + 1 + + + + + + + + 1 + 128 + 13 + 13 + + + 1 + 128 + 1 + 1 + + + + + 1 + 128 + 13 + 13 + + + + + + + 1 + 128 + 13 + 13 + + + + + 1 + 128 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 128 + 13 + 13 + + + + + + + + + 1 + 128 + 13 + 13 + + + + + + + + 1 + 128 + 13 + 13 + + + 1 + 128 + 13 + 13 + + + 1 + 128 + 13 + 13 + + + + + 1 + 384 + 13 + 13 + + + + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 256 + 1 + 1 + 1 + + + + + + + + 256 + 384 + 1 + 1 + + + 256 + 1 + 1 + 1 + + + + + 256 + 384 + 1 + 1 + + + + + + + + 1 + 384 + 13 + 13 + + + 256 + 384 + 1 + 1 + + + + + 1 + 256 + 13 + 13 + + + + + + + + 1 + 256 + 1 + 1 + + + + + + + + 1 + 256 + 13 + 13 + + + 1 + 256 + 1 + 1 + + + + + 1 + 256 + 13 + 13 + + + + + + + 1 + 256 + 13 + 13 + + + + + 1 + 256 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 256 + 13 + 13 + + + + + + + + + 1 + 256 + 13 + 13 + + + + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 256 + 3 + 3 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 13 + 13 + + + 64 + 256 + 3 + 3 + + + + + 1 + 64 + 13 + 13 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 13 + 13 + + + + + + + 1 + 64 + 13 + 13 + + + + + 1 + 64 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 13 + 13 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 13 + 13 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 13 + 13 + + + + + + + 1 + 64 + 13 + 13 + + + + + 1 + 64 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 1 + 1 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 64 + 64 + 1 + 1 + + + + + 1 + 64 + 13 + 13 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 13 + 13 + + + + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 256 + 3 + 3 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 256 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 13 + 13 + + + 64 + 256 + 3 + 3 + + + + + 1 + 64 + 13 + 13 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 13 + 13 + + + + + + + 1 + 64 + 13 + 13 + + + + + 1 + 64 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 64 + 3 + 3 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 64 + 1 + 1 + 1 + + + + + + + + 64 + 64 + 3 + 3 + + + 64 + 1 + 1 + 1 + + + + + 64 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 13 + 13 + + + 64 + 64 + 3 + 3 + + + + + 1 + 64 + 13 + 13 + + + + + + + + 1 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 1 + 64 + 1 + 1 + + + + + 1 + 64 + 13 + 13 + + + + + + + 1 + 64 + 13 + 13 + + + + + 1 + 64 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + + 1 + 64 + 13 + 13 + + + + + + + + 2 + 64 + 1 + 1 + + + + + + + + 2 + 64 + 1 + 1 + + + + + 2 + 64 + 1 + 1 + + + + + + + + 2 + 1 + 1 + 1 + + + + + + + + 2 + 64 + 1 + 1 + + + 2 + 1 + 1 + 1 + + + + + 2 + 64 + 1 + 1 + + + + + + + + 1 + 64 + 13 + 13 + + + 2 + 64 + 1 + 1 + + + + + 1 + 2 + 13 + 13 + + + + + + + + 1 + 2 + 1 + 1 + + + + + + + + 1 + 2 + 13 + 13 + + + 1 + 2 + 1 + 1 + + + + + 1 + 2 + 13 + 13 + + + + + + + + 1 + 64 + 13 + 13 + + + 1 + 2 + 13 + 13 + + + + + 1 + 66 + 13 + 13 + + + + + + + + 1 + 66 + 13 + 13 + + + 3 + + + + + 1 + 66 + 169 + + + + + + + + 1 + 66 + 2704 + + + 1 + 66 + 676 + + + 1 + 66 + 169 + + + + + 1 + 66 + 3549 + + + + + + + + + + + + + + 2 + + + + + + + 1 + 66 + 3549 + + + + 2 + + + + + 1 + 64 + 3549 + + + 1 + 2 + 3549 + + + + + + + + 4 + + + + + + + + 1 + 64 + 3549 + + + 4 + + + + + 1 + 4 + 16 + 3549 + + + + + + + + 4 + + + + + + + 1 + 4 + 16 + 3549 + + + 4 + + + + + 1 + 16 + 4 + 3549 + + + + + + + + 1 + 16 + 4 + 3549 + + + + + 1 + 16 + 4 + 3549 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 4 + 3549 + + + + + + + + + 1 + 16 + 4 + 3549 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 16 + 1 + 1 + + + 1 + 1 + 1 + 1 + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 4 + 3549 + + + 1 + 16 + 1 + 1 + + + + + 1 + 1 + 4 + 3549 + + + + + + + + 3 + + + + + + + + 1 + 1 + 4 + 3549 + + + 3 + + + + + 1 + 4 + 3549 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 1 + 4 + 3549 + + + 2 + + + 2 + + + 2 + + + + + 1 + 2 + 3549 + + + + + + + + 1 + 2 + 3549 + + + 1 + 2 + 3549 + + + + + 1 + 2 + 3549 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 3549 + + + + + + + + + 1 + 2 + 3549 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 4 + 3549 + + + + + + + + + 1 + 4 + 3549 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + 1 + 4 + 3549 + + + 2 + + + 2 + + + 2 + + + + + 1 + 2 + 3549 + + + + + + + + 1 + 2 + 3549 + + + 1 + 2 + 3549 + + + + + 1 + 2 + 3549 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 3549 + + + + + + + + + 1 + 2 + 3549 + + + + + + + + 1 + 2 + 3549 + + + 1 + 2 + 3549 + + + + + 1 + 2 + 3549 + + + + + + + + 1 + 1 + 1 + + + + + + + + 1 + 2 + 3549 + + + 1 + 1 + 1 + + + + + 1 + 2 + 3549 + + + + + + + + 1 + 2 + 3549 + + + 1 + 2 + 3549 + + + + + 1 + 2 + 3549 + + + + + + + + 1 + 2 + 3549 + + + 1 + 2 + 3549 + + + + + 1 + 4 + 3549 + + + + + + + + 1 + 1 + 3549 + + + + + + + + 1 + 4 + 3549 + + + 1 + 1 + 3549 + + + + + 1 + 4 + 3549 + + + + + + + 1 + 2 + 3549 + + + + + 1 + 2 + 3549 + + + + + + + + 16 + 64 + 3 + 3 + + + + + + + + 16 + 64 + 3 + 3 + + + + + 16 + 64 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 64 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 64 + 3 + 3 + + + + + + + + 1 + 64 + 52 + 52 + + + 16 + 64 + 3 + 3 + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + 1 + 16 + 52 + 52 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 52 + 52 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 52 + 52 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 52 + 52 + + + + + + + 1 + 16 + 52 + 52 + + + + + 1 + 16 + 52 + 52 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + + 1 + 16 + 52 + 52 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 52 + 52 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 52 + 52 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 52 + 52 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 52 + 52 + + + + + + + + 3 + + + + + + + + 1 + 8 + 52 + 52 + + + 3 + + + + + 1 + 8 + 2704 + + + + + + + + 16 + 128 + 3 + 3 + + + + + + + + 16 + 128 + 3 + 3 + + + + + 16 + 128 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 128 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 128 + 3 + 3 + + + + + + + + 1 + 128 + 26 + 26 + + + 16 + 128 + 3 + 3 + + + + + 1 + 16 + 26 + 26 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 26 + 26 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 26 + 26 + + + + + + + 1 + 16 + 26 + 26 + + + + + 1 + 16 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 26 + 26 + + + + + + + + + 1 + 16 + 26 + 26 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 26 + 26 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 26 + 26 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 26 + 26 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 26 + 26 + + + + + + + 1 + 16 + 26 + 26 + + + + + 1 + 16 + 26 + 26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 26 + 26 + + + + + + + + + 1 + 16 + 26 + 26 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 26 + 26 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 26 + 26 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 26 + 26 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 26 + 26 + + + + + + + + 1 + 8 + 26 + 26 + + + 3 + + + + + 1 + 8 + 676 + + + + + + + + 16 + 256 + 3 + 3 + + + + + + + + 16 + 256 + 3 + 3 + + + + + 16 + 256 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 256 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 256 + 3 + 3 + + + + + + + + 1 + 256 + 13 + 13 + + + 16 + 256 + 3 + 3 + + + + + 1 + 16 + 13 + 13 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 13 + 13 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 13 + 13 + + + + + + + 1 + 16 + 13 + 13 + + + + + 1 + 16 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 13 + 13 + + + + + + + + + 1 + 16 + 13 + 13 + + + + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 16 + 3 + 3 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 16 + 1 + 1 + 1 + + + + + + + + 16 + 16 + 3 + 3 + + + 16 + 1 + 1 + 1 + + + + + 16 + 16 + 3 + 3 + + + + + + + + 1 + 16 + 13 + 13 + + + 16 + 16 + 3 + 3 + + + + + 1 + 16 + 13 + 13 + + + + + + + + 1 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 13 + 13 + + + 1 + 16 + 1 + 1 + + + + + 1 + 16 + 13 + 13 + + + + + + + 1 + 16 + 13 + 13 + + + + + 1 + 16 + 13 + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16 + 13 + 13 + + + + + + + + + 1 + 16 + 13 + 13 + + + + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 8 + 1 + 1 + 1 + + + + + + + + 8 + 16 + 1 + 1 + + + 8 + 1 + 1 + 1 + + + + + 8 + 16 + 1 + 1 + + + + + + + + 1 + 16 + 13 + 13 + + + 8 + 16 + 1 + 1 + + + + + 1 + 8 + 13 + 13 + + + + + + + + 1 + 8 + 1 + 1 + + + + + + + + 1 + 8 + 13 + 13 + + + 1 + 8 + 1 + 1 + + + + + 1 + 8 + 13 + 13 + + + + + + + + 1 + 8 + 13 + 13 + + + 3 + + + + + 1 + 8 + 169 + + + + + + + + 1 + 8 + 2704 + + + 1 + 8 + 676 + + + 1 + 8 + 169 + + + + + 1 + 8 + 3549 + + + + + + + + 4 + + + + + + + + 1 + 8 + 3549 + + + 4 + + + + + 1 + 4 + 2 + 3549 + + + + + + + + 3 + + + + + + + + 3 + + + + + + + + 3 + + + + + + + + 1 + 4 + 2 + 3549 + + + 3 + + + 3 + + + 3 + + + + + 1 + 4 + 2 + 3549 + + + + + + + + 1 + 1 + 1 + 3549 + + + + + + + + 1 + 4 + 2 + 3549 + + + 1 + 1 + 1 + 3549 + + + + + 1 + 4 + 2 + 3549 + + + + + + + + 1 + 1 + 2 + 3549 + + + + + + + + 1 + 4 + 2 + 3549 + + + 1 + 1 + 2 + 3549 + + + + + 1 + 4 + 2 + 3549 + + + + + + + + 1 + 4 + 2 + 3549 + + + 3 + + + + + 1 + 8 + 3549 + + + + + + + + 1 + 4 + 3549 + + + 1 + 2 + 3549 + + + 1 + 8 + 3549 + + + + + 1 + 14 + 3549 + + + + + + + 1 + 14 + 3549 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/autostart.sh b/autostart.sh new file mode 100755 index 0000000..15f2266 --- /dev/null +++ b/autostart.sh @@ -0,0 +1,8 @@ +sleep 5 +cd ~/Desktop/sp_vision_25/ +screen \ + -L \ + -Logfile logs/$(date "+%Y-%m-%d_%H-%M-%S").screenlog \ + -d \ + -m \ + bash -c "./watchdog.sh" diff --git a/buff_layout.xml b/buff_layout.xml new file mode 100644 index 0000000..e3d606e --- /dev/null +++ b/buff_layout.xml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/calibration/calibrate_camera.cpp b/calibration/calibrate_camera.cpp new file mode 100644 index 0000000..c43db7a --- /dev/null +++ b/calibration/calibrate_camera.cpp @@ -0,0 +1,130 @@ +#include +#include + +#include +#include + +#include "tools/img_tools.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{config-path c | configs/calibration.yaml | yaml配置文件路径 }" + "{@input-folder | assets/img_with_q | 输入文件夹路径 }"; + +std::vector centers_3d(const cv::Size & pattern_size, const float center_distance) +{ + std::vector centers_3d; + + for (int i = 0; i < pattern_size.height; i++) + for (int j = 0; j < pattern_size.width; j++) + centers_3d.push_back({j * center_distance, i * center_distance, 0}); + + return centers_3d; +} + +void load( + const std::string & input_folder, const std::string & config_path, cv::Size & img_size, + std::vector> & obj_points, + std::vector> & img_points) +{ + // 读取yaml参数 + auto yaml = YAML::LoadFile(config_path); + auto pattern_cols = yaml["pattern_cols"].as(); + auto pattern_rows = yaml["pattern_rows"].as(); + auto center_distance_mm = yaml["center_distance_mm"].as(); + cv::Size pattern_size(pattern_cols, pattern_rows); + + for (int i = 1; true; i++) { + // 读取图片 + auto img_path = fmt::format("{}/{}.jpg", input_folder, i); + auto img = cv::imread(img_path); + if (img.empty()) break; + + // 设置图片尺寸 + img_size = img.size(); + + // 识别标定板 + std::vector centers_2d; + auto success = cv::findCirclesGrid(img, pattern_size, centers_2d, cv::CALIB_CB_SYMMETRIC_GRID); + + // 显示识别结果 + auto drawing = img.clone(); + cv::drawChessboardCorners(drawing, pattern_size, centers_2d, success); + cv::resize(drawing, drawing, {}, 0.5, 0.5); // 缩小图片尺寸便于显示完全 + cv::imshow("Press any to continue", drawing); + cv::waitKey(0); + + // 输出识别结果 + fmt::print("[{}] {}\n", success ? "success" : "failure", img_path); + if (!success) continue; + + // 记录所需的数据 + img_points.emplace_back(centers_2d); + obj_points.emplace_back(centers_3d(pattern_size, center_distance_mm)); + } +} + +void print_yaml(const cv::Mat & camera_matrix, const cv::Mat & distort_coeffs, double error) +{ + YAML::Emitter result; + std::vector camera_matrix_data( + camera_matrix.begin(), camera_matrix.end()); + std::vector distort_coeffs_data( + distort_coeffs.begin(), distort_coeffs.end()); + + result << YAML::BeginMap; + result << YAML::Comment(fmt::format("重投影误差: {:.4f}px", error)); + result << YAML::Key << "camera_matrix"; + result << YAML::Value << YAML::Flow << camera_matrix_data; + result << YAML::Key << "distort_coeffs"; + result << YAML::Value << YAML::Flow << distort_coeffs_data; + result << YAML::Newline; + result << YAML::EndMap; + + fmt::print("\n{}\n", result.c_str()); +} + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto input_folder = cli.get(0); + auto config_path = cli.get("config-path"); + + // 从输入文件夹中加载标定所需的数据 + cv::Size img_size; + std::vector> obj_points; + std::vector> img_points; + load(input_folder, config_path, img_size, obj_points, img_points); + + // 相机标定 + cv::Mat camera_matrix, distort_coeffs; + std::vector rvecs, tvecs; + auto criteria = cv::TermCriteria( + cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 100, + DBL_EPSILON); // 默认迭代次数(30)有时会导致结果发散,故设为100 + cv::calibrateCamera( + obj_points, img_points, img_size, camera_matrix, distort_coeffs, rvecs, tvecs, cv::CALIB_FIX_K3, + criteria); // 由于视场角较小,不需要考虑k3 + + // 重投影误差 + double error_sum = 0; + size_t total_points = 0; + for (size_t i = 0; i < obj_points.size(); i++) { + std::vector reprojected_points; + cv::projectPoints( + obj_points[i], rvecs[i], tvecs[i], camera_matrix, distort_coeffs, reprojected_points); + + total_points += reprojected_points.size(); + for (size_t j = 0; j < reprojected_points.size(); j++) + error_sum += cv::norm(img_points[i][j] - reprojected_points[j]); + } + auto error = error_sum / total_points; + + // 输出yaml + print_yaml(camera_matrix, distort_coeffs, error); +} diff --git a/calibration/calibrate_handeye.cpp b/calibration/calibrate_handeye.cpp new file mode 100644 index 0000000..ecfa79a --- /dev/null +++ b/calibration/calibrate_handeye.cpp @@ -0,0 +1,168 @@ +#include +#include + +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include +#include + +#include "tools/img_tools.hpp" +#include "tools/math_tools.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{config-path c | configs/calibration.yaml | yaml配置文件路径 }" + "{@input-folder | assets/img_with_q | 输入文件夹路径 }"; + +std::vector centers_3d(const cv::Size & pattern_size, const float center_distance) +{ + std::vector centers_3d; + + for (int i = 0; i < pattern_size.height; i++) + for (int j = 0; j < pattern_size.width; j++) + centers_3d.push_back({j * center_distance, i * center_distance, 0}); + + return centers_3d; +} + +Eigen::Quaterniond read_q(const std::string & q_path) +{ + std::ifstream q_file(q_path); + double w, x, y, z; + q_file >> w >> x >> y >> z; + return {w, x, y, z}; +} + +void load( + const std::string & input_folder, const std::string & config_path, + std::vector & R_gimbal2imubody_data, std::vector & R_gimbal2world_list, + std::vector & t_gimbal2world_list, std::vector & rvecs, + std::vector & tvecs) +{ + // 读取yaml参数 + auto yaml = YAML::LoadFile(config_path); + auto pattern_cols = yaml["pattern_cols"].as(); + auto pattern_rows = yaml["pattern_rows"].as(); + auto center_distance_mm = yaml["center_distance_mm"].as(); + R_gimbal2imubody_data = yaml["R_gimbal2imubody"].as>(); + auto camera_matrix_data = yaml["camera_matrix"].as>(); + auto distort_coeffs_data = yaml["distort_coeffs"].as>(); + + cv::Size pattern_size(pattern_cols, pattern_rows); + Eigen::Matrix R_gimbal2imubody(R_gimbal2imubody_data.data()); + cv::Matx33d camera_matrix(camera_matrix_data.data()); + cv::Mat distort_coeffs(distort_coeffs_data); + + for (int i = 1; true; i++) { + // 读取图片和对应四元数 + auto img_path = fmt::format("{}/{}.jpg", input_folder, i); + auto q_path = fmt::format("{}/{}.txt", input_folder, i); + auto img = cv::imread(img_path); + Eigen::Quaterniond q = read_q(q_path); + if (img.empty()) break; + + // 计算云台的欧拉角 + Eigen::Matrix3d R_imubody2imuabs = q.toRotationMatrix(); + Eigen::Matrix3d R_gimbal2world = + R_gimbal2imubody.transpose() * R_imubody2imuabs * R_gimbal2imubody; + Eigen::Vector3d ypr = tools::eulers(R_gimbal2world, 2, 1, 0) * 57.3; // degree + + // 在图片上显示云台的欧拉角,用来检验R_gimbal2imubody是否正确 + auto drawing = img.clone(); + tools::draw_text(drawing, fmt::format("yaw {:.2f}", ypr[0]), {40, 40}, {0, 0, 255}); + tools::draw_text(drawing, fmt::format("pitch {:.2f}", ypr[1]), {40, 80}, {0, 0, 255}); + tools::draw_text(drawing, fmt::format("roll {:.2f}", ypr[2]), {40, 120}, {0, 0, 255}); + + // 识别标定板 + std::vector centers_2d; + auto success = cv::findCirclesGrid(img, pattern_size, centers_2d); // 默认是对称圆点图案 + + // 显示识别结果 + cv::drawChessboardCorners(drawing, pattern_size, centers_2d, success); + cv::resize(drawing, drawing, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("Press any to continue", drawing); + cv::waitKey(0); + + // 输出识别结果 + fmt::print("[{}] {}\n", success ? "success" : "failure", img_path); + if (!success) continue; + + // 计算所需的数据 + cv::Mat t_gimbal2world = (cv::Mat_(3, 1) << 0, 0, 0); + cv::Mat R_gimbal2world_cv; + cv::eigen2cv(R_gimbal2world, R_gimbal2world_cv); + cv::Mat rvec, tvec; + auto centers_3d_ = centers_3d(pattern_size, center_distance_mm); + cv::solvePnP( + centers_3d_, centers_2d, camera_matrix, distort_coeffs, rvec, tvec, false, cv::SOLVEPNP_IPPE); + + // 记录所需的数据 + R_gimbal2world_list.emplace_back(R_gimbal2world_cv); + t_gimbal2world_list.emplace_back(t_gimbal2world); + rvecs.emplace_back(rvec); + tvecs.emplace_back(tvec); + } +} + +void print_yaml( + const std::vector & R_gimbal2imubody_data, const cv::Mat & R_camera2gimbal, + const cv::Mat & t_camera2gimbal, const Eigen::Vector3d & ypr) +{ + YAML::Emitter result; + std::vector R_camera2gimbal_data( + R_camera2gimbal.begin(), R_camera2gimbal.end()); + std::vector t_camera2gimbal_data( + t_camera2gimbal.begin(), t_camera2gimbal.end()); + + result << YAML::BeginMap; + result << YAML::Key << "R_gimbal2imubody"; + result << YAML::Value << YAML::Flow << R_gimbal2imubody_data; + result << YAML::Newline; + result << YAML::Newline; + result << YAML::Comment(fmt::format( + "相机同理想情况的偏角: yaw{:.2f} pitch{:.2f} roll{:.2f} degree", ypr[0], ypr[1], ypr[2])); + result << YAML::Key << "R_camera2gimbal"; + result << YAML::Value << YAML::Flow << R_camera2gimbal_data; + result << YAML::Key << "t_camera2gimbal"; + result << YAML::Value << YAML::Flow << t_camera2gimbal_data; + result << YAML::Newline; + result << YAML::EndMap; + + fmt::print("\n{}\n", result.c_str()); +} + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto input_folder = cli.get(0); + auto config_path = cli.get("config-path"); + + // 从输入文件夹中加载标定所需的数据 + std::vector R_gimbal2imubody_data; + std::vector R_gimbal2world_list, t_gimbal2world_list; + std::vector rvecs, tvecs; + load( + input_folder, config_path, R_gimbal2imubody_data, R_gimbal2world_list, t_gimbal2world_list, + rvecs, tvecs); + + // 手眼标定 + cv::Mat R_camera2gimbal, t_camera2gimbal; + cv::calibrateHandEye( + R_gimbal2world_list, t_gimbal2world_list, rvecs, tvecs, R_camera2gimbal, t_camera2gimbal); + t_camera2gimbal /= 1e3; // mm to m + + // 计算相机同理想情况的偏角 + Eigen::Matrix3d R_camera2gimbal_eigen; + cv::cv2eigen(R_camera2gimbal, R_camera2gimbal_eigen); + Eigen::Matrix3d R_gimbal2ideal{{0, -1, 0}, {0, 0, -1}, {1, 0, 0}}; + Eigen::Matrix3d R_camera2ideal = R_gimbal2ideal * R_camera2gimbal_eigen; + Eigen::Vector3d ypr = tools::eulers(R_camera2ideal, 1, 0, 2) * 57.3; // degree + + // 输出yaml + print_yaml(R_gimbal2imubody_data, R_camera2gimbal, t_camera2gimbal, ypr); +} diff --git a/calibration/calibrate_robotworld_handeye.cpp b/calibration/calibrate_robotworld_handeye.cpp new file mode 100644 index 0000000..e367abf --- /dev/null +++ b/calibration/calibrate_robotworld_handeye.cpp @@ -0,0 +1,204 @@ +#include +#include + +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include +#include + +#include "tools/img_tools.hpp" +#include "tools/math_tools.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{config-path c | configs/calibration.yaml | yaml配置文件路径 }" + "{@input-folder | assets/img_with_q | 输入文件夹路径 }"; + +std::vector centers_3d(const cv::Size & pattern_size, const float center_distance) +{ + std::vector centers_3d; + + for (int i = 0; i < pattern_size.height; i++) { + for (int j = 0; j < pattern_size.width; j++) { + float x = 0; + float y = (-j + 0.5 * pattern_size.width) * center_distance; + float z = (-i + 0.5 * pattern_size.height) * center_distance; + centers_3d.push_back({x, y, z}); + } + } + + return centers_3d; +} + +Eigen::Quaterniond read_q(const std::string & q_path) +{ + std::ifstream q_file(q_path); + double w, x, y, z; + q_file >> w >> x >> y >> z; + return {w, x, y, z}; +} + +void load( + const std::string & input_folder, const std::string & config_path, + std::vector & R_gimbal2imubody_data, std::vector & R_world2gimbal_list, + std::vector & t_world2gimbal_list, std::vector & rvecs, + std::vector & tvecs) +{ + // 读取yaml参数 + auto yaml = YAML::LoadFile(config_path); + auto pattern_cols = yaml["pattern_cols"].as(); + auto pattern_rows = yaml["pattern_rows"].as(); + auto center_distance_mm = yaml["center_distance_mm"].as(); + R_gimbal2imubody_data = yaml["R_gimbal2imubody"].as>(); + auto camera_matrix_data = yaml["camera_matrix"].as>(); + auto distort_coeffs_data = yaml["distort_coeffs"].as>(); + + cv::Size pattern_size(pattern_cols, pattern_rows); + Eigen::Matrix R_gimbal2imubody(R_gimbal2imubody_data.data()); + cv::Matx33d camera_matrix(camera_matrix_data.data()); + cv::Mat distort_coeffs(distort_coeffs_data); + + for (int i = 1; true; i++) { + // 读取图片和对应四元数 + auto img_path = fmt::format("{}/{}.jpg", input_folder, i); + auto q_path = fmt::format("{}/{}.txt", input_folder, i); + auto img = cv::imread(img_path); + Eigen::Quaterniond q = read_q(q_path); + if (img.empty()) break; + + // 计算云台的欧拉角 + Eigen::Matrix3d R_imubody2imuabs = q.toRotationMatrix(); + Eigen::Matrix3d R_gimbal2world = + R_gimbal2imubody.transpose() * R_imubody2imuabs * R_gimbal2imubody; + Eigen::Vector3d ypr = tools::eulers(R_gimbal2world, 2, 1, 0) * 57.3; // degree + + // 在图片上显示云台的欧拉角,用来检验R_gimbal2imubody是否正确 + auto drawing = img.clone(); + tools::draw_text(drawing, fmt::format("yaw {:.2f}", ypr[0]), {40, 40}, {0, 0, 255}); + tools::draw_text(drawing, fmt::format("pitch {:.2f}", ypr[1]), {40, 80}, {0, 0, 255}); + tools::draw_text(drawing, fmt::format("roll {:.2f}", ypr[2]), {40, 120}, {0, 0, 255}); + + // 识别标定板 + std::vector centers_2d; + auto success = cv::findCirclesGrid(img, pattern_size, centers_2d); // 默认是对称圆点图案 + + // 显示识别结果 + cv::drawChessboardCorners(drawing, pattern_size, centers_2d, success); + cv::resize(drawing, drawing, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("Press any to continue", drawing); + cv::waitKey(0); + + // 输出识别结果 + fmt::print("[{}] {}\n", success ? "success" : "failure", img_path); + if (!success) continue; + + // 计算所需的数据 + Eigen::Matrix3d R_world2gimbal = R_gimbal2world.transpose(); + cv::Mat t_world2gimbal = (cv::Mat_(3, 1) << 0, 0, 0); + cv::Mat R_world2gimbal_cv; + cv::eigen2cv(R_world2gimbal, R_world2gimbal_cv); + cv::Mat rvec, tvec; + auto centers_3d_ = centers_3d(pattern_size, center_distance_mm); + cv::solvePnP( + centers_3d_, centers_2d, camera_matrix, distort_coeffs, rvec, tvec, false, cv::SOLVEPNP_IPPE); + + // 记录所需的数据 + R_world2gimbal_list.emplace_back(R_world2gimbal_cv); + t_world2gimbal_list.emplace_back(t_world2gimbal); + rvecs.emplace_back(rvec); + tvecs.emplace_back(tvec); + } +} + +void print_yaml( + const std::vector & R_gimbal2imubody_data, const cv::Mat & R_camera2gimbal, + const cv::Mat & t_camera2gimbal, const Eigen::Vector3d & camera_ypr, double distance, + const Eigen::Vector3d & board_ypr) +{ + YAML::Emitter result; + std::vector R_camera2gimbal_data( + R_camera2gimbal.begin(), R_camera2gimbal.end()); + std::vector t_camera2gimbal_data( + t_camera2gimbal.begin(), t_camera2gimbal.end()); + + result << YAML::BeginMap; + result << YAML::Key << "R_gimbal2imubody"; + result << YAML::Value << YAML::Flow << R_gimbal2imubody_data; + result << YAML::Newline; + result << YAML::Newline; + result << YAML::Comment(fmt::format( + "相机同理想情况的偏角: yaw{:.2f} pitch{:.2f} roll{:.2f} degree", camera_ypr[0], camera_ypr[1], + camera_ypr[2])); + result << YAML::Newline; + result << YAML::Comment(fmt::format("标定板到世界坐标系原点的水平距离: {:.2f} m", distance)); + result << YAML::Newline; + result << YAML::Comment(fmt::format( + "标定板同竖直摆放时的偏角: yaw{:.2f} pitch{:.2f} roll{:.2f} degree", board_ypr[0], board_ypr[1], + board_ypr[2])); + result << YAML::Key << "R_camera2gimbal"; + result << YAML::Value << YAML::Flow << R_camera2gimbal_data; + result << YAML::Key << "t_camera2gimbal"; + result << YAML::Value << YAML::Flow << t_camera2gimbal_data; + result << YAML::Newline; + result << YAML::EndMap; + + fmt::print("\n{}\n", result.c_str()); +} + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto input_folder = cli.get(0); + auto config_path = cli.get("config-path"); + + // 从输入文件夹中加载标定所需的数据 + std::vector R_gimbal2imubody_data; + std::vector R_world2gimbal_list, t_world2gimbal_list; + std::vector rvecs, tvecs; + load( + input_folder, config_path, R_gimbal2imubody_data, R_world2gimbal_list, t_world2gimbal_list, + rvecs, tvecs); + + // 手眼标定 + cv::Mat R_gimbal2camera, t_gimbal2camera; + cv::Mat R_world2board, t_world2board; + cv::calibrateRobotWorldHandEye( + rvecs, tvecs, R_world2gimbal_list, t_world2gimbal_list, R_world2board, t_world2board, + R_gimbal2camera, t_gimbal2camera); + t_gimbal2camera /= 1e3; // mm to m + t_world2board /= 1e3; // mm to m + + // 计算所需的数据 + cv::Mat R_camera2gimbal, t_camera2gimbal; + cv::Mat R_board2world, t_board2world; + cv::transpose(R_gimbal2camera, R_camera2gimbal); + cv::transpose(R_world2board, R_board2world); + t_camera2gimbal = -R_camera2gimbal * t_gimbal2camera; + t_board2world = -R_board2world * t_world2board; + + // 计算相机同理想情况的偏角 + Eigen::Matrix3d R_camera2gimbal_eigen; + cv::cv2eigen(R_camera2gimbal, R_camera2gimbal_eigen); + Eigen::Matrix3d R_gimbal2ideal{{0, -1, 0}, {0, 0, -1}, {1, 0, 0}}; + Eigen::Matrix3d R_camera2ideal = R_gimbal2ideal * R_camera2gimbal_eigen; + Eigen::Vector3d camera_ypr = tools::eulers(R_camera2ideal, 1, 0, 2) * 57.3; // degree + + // 计算标定板到世界坐标系原点的水平距离 + auto x = t_board2world.at(0); + auto y = t_board2world.at(1); + auto distance = std::sqrt(x * x + y * y); + + // 计算标定板同竖直摆放时的偏角 + Eigen::Matrix3d R_board2world_eigen; + cv::cv2eigen(R_board2world, R_board2world_eigen); + Eigen::Vector3d board_ypr = tools::eulers(R_board2world_eigen, 2, 1, 0) * 57.3; // degree + + // 输出yaml + print_yaml( + R_gimbal2imubody_data, R_camera2gimbal, t_camera2gimbal, camera_ypr, distance, board_ypr); +} diff --git a/calibration/capture.cpp b/calibration/capture.cpp new file mode 100644 index 0000000..c33a70e --- /dev/null +++ b/calibration/capture.cpp @@ -0,0 +1,93 @@ +#include + +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path c | configs/calibration.yaml | yaml配置文件路径 }" + "{output-folder o | assets/img_with_q | 输出文件夹路径 }"; + +void write_q(const std::string q_path, const Eigen::Quaterniond & q) +{ + std::ofstream q_file(q_path); + Eigen::Vector4d xyzw = q.coeffs(); + // 输出顺序为wxyz + q_file << fmt::format("{} {} {} {}", xyzw[3], xyzw[0], xyzw[1], xyzw[2]); + q_file.close(); +} + +void capture_loop( + const std::string & config_path, const std::string & can, const std::string & output_folder) +{ + io::CBoard cboard(config_path); + io::Camera camera(config_path); + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + + int count = 0; + while (true) { + camera.read(img, timestamp); + Eigen::Quaterniond q = cboard.imu_at(timestamp); + + // 在图像上显示欧拉角,用来判断imuabs系的xyz正方向,同时判断imu是否存在零漂 + auto img_with_ypr = img.clone(); + Eigen::Vector3d zyx = tools::eulers(q, 2, 1, 0) * 57.3; // degree + tools::draw_text(img_with_ypr, fmt::format("Z {:.2f}", zyx[0]), {40, 40}, {0, 0, 255}); + tools::draw_text(img_with_ypr, fmt::format("Y {:.2f}", zyx[1]), {40, 80}, {0, 0, 255}); + tools::draw_text(img_with_ypr, fmt::format("X {:.2f}", zyx[2]), {40, 120}, {0, 0, 255}); + + std::vector centers_2d; + auto success = cv::findCirclesGrid(img, cv::Size(10, 7), centers_2d); // 默认是对称圆点图案 + cv::drawChessboardCorners(img_with_ypr, cv::Size(10, 7), centers_2d, success); // 显示识别结果 + cv::resize(img_with_ypr, img_with_ypr, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + + // 按“s”保存图片和对应四元数,按“q”退出程序 + cv::imshow("Press s to save, q to quit", img_with_ypr); + auto key = cv::waitKey(1); + if (key == 'q') + break; + else if (key != 's') + continue; + + // 保存图片和四元数 + count++; + auto img_path = fmt::format("{}/{}.jpg", output_folder, count); + auto q_path = fmt::format("{}/{}.txt", output_folder, count); + cv::imwrite(img_path, img); + write_q(q_path, q); + tools::logger()->info("[{}] Saved in {}", count, output_folder); + } + + // 离开该作用域时,camera和cboard会自动关闭 +} + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + auto output_folder = cli.get("output-folder"); + + // 新建输出文件夹 + std::filesystem::create_directory(output_folder); + + tools::logger()->info("默认标定板尺寸为10列7行"); + // 主循环,保存图片和对应四元数 + capture_loop(config_path, "can0", output_folder); + + tools::logger()->warn("注意四元数输出顺序为wxyz"); + + return 0; +} diff --git a/calibration/split_video.cpp b/calibration/split_video.cpp new file mode 100644 index 0000000..d6c16fe --- /dev/null +++ b/calibration/split_video.cpp @@ -0,0 +1,89 @@ +#include + +#include +#include +#include +#include +#include + +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +// 定义命令行参数 +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{start-index s | | 视频起始帧下标 }" + "{end-index e | | 视频结束帧下标 }" + "{output-path p | records/Big/2024-05-14_11-6-26 | avi和txt文件的路径}" + "{@input-path | | avi和txt文件的路径}"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto input_path = cli.get(0); + auto output_path = cli.get("output-path"); + auto start_index = cli.get("start-index"); + std::cout << start_index << std::endl; + auto end_index = cli.get("end-index"); + + // 初始化绘图器和退出器 + tools::Exiter exiter; + + // 构造视频和文本文件路径 + auto video_path = fmt::format("{}.avi", input_path); + auto text_path = fmt::format("{}.txt", input_path); + cv::VideoCapture video(video_path); + std::ifstream text(text_path); + + // 设置视频起始帧 + video.set(cv::CAP_PROP_POS_FRAMES, start_index); + for (int i = 0; i < start_index; i++) { + double t, w, x, y, z; + text >> t >> w >> x >> y >> z; + } + // 获取原始视频的参数 + int frameWidth = video.get(cv::CAP_PROP_FRAME_WIDTH); + int frameHeight = video.get(cv::CAP_PROP_FRAME_HEIGHT); + double fps = video.get(cv::CAP_PROP_FPS); + int fourcc = static_cast(video.get(cv::CAP_PROP_FOURCC)); + + // 创建输出文件 + auto outvideo_path = fmt::format("{}.avi", output_path); + auto outtext_path = fmt::format("{}.txt", output_path); + cv::VideoWriter outvideo(outvideo_path, fourcc, fps, cv::Size(frameWidth, frameHeight)); + std::ofstream outtext(outtext_path); + + std::string line; + cv::Mat img; + + // 循环处理视频帧 + for (int frame_count = start_index; !exiter.exit(); frame_count++) { + if (end_index > 0 && frame_count > end_index) break; + + video.read(img); + if (img.empty()) break; + + outvideo.write(img); + + getline(text, line); + outtext << line << std::endl; + + cv::resize(img, img, cv::Size(img.size().width * 0.8, img.size().height * 0.8)); + cv::imshow("result", img); + + int key = cv::waitKey(1); + if (key == 'q') break; + } + cv::destroyAllWindows(); + text.close(); // 关闭文件 + outtext.close(); + + return 0; +} diff --git a/configs/ascento.yaml b/configs/ascento.yaml new file mode 100644 index 0000000..f56078c --- /dev/null +++ b/configs/ascento.yaml @@ -0,0 +1,85 @@ +# enemy_color: "red" +enemy_color: "blue" + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: GPU +min_confidence: 0.8 +use_traditional: false + +#####-----ROI-----##### +roi: + x: 500 + y: 400 + width: 500 + height: 500 + +use_roi: false + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 +outpost_max_temp_lost_count: 75 + +#####-----aimer参数-----##### +yaw_offset: 1.5 # degree +pitch_offset: -0.5 # degree +comming_angle: 60 # degree +leaving_angle: 20 # degree +decision_speed: 7 # rad/s +high_speed_delay_time: 0.050 # s +low_speed_delay_time: 0.015 # s + +#####-----shooter参数-----##### +first_tolerance: 100 # 近距离射击容差,degree +second_tolerance: 100 # 远距离射击容差,degree +judge_distance: 2 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +camera_name: "hikrobot" +exposure_ms: 2 +gain: 16 +vid_pid: "2bdf:0001" + +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.1488px +camera_matrix: [1775.1300101814929, 0, 710.22041588791421, 0, 1777.9427175568535, 599.38373307949621, 0, 0, 1] +distort_coeffs: [-0.081907189655237952, 0.14019999270205855, -0.0012264127665053185, 0.0014292255962000792, 0] + +# 相机同理想情况的偏角: yaw1.81 pitch0.16 roll-0.00 degree +R_camera2gimbal: [-0.03152275773856239, 0.0027095830669845509, 0.99949936163269204, -0.99950303430107212, -9.8404785402239625e-05, -0.031522606799691932, 1.2942398579498703e-05, -0.99999632423129503, 0.0027113384873522772] +t_camera2gimbal: [0.13895789074180098, 0.066620637390426771, 0.028079650211170731] + + +#####-----cboard参数-----##### +quaternion_canid: 0x100 +bullet_speed_canid: 0x101 +send_canid: 0xff +can_interface: "can0" + +#####-----gimbal参数-----##### +com_port: "/dev/gimbal" + +#####-----buff_detector参数-----##### +model: "assets/yolo11_buff_int8.xml" + +#####-----buff_aimer参数-----##### +fire_gap_time: 0.520 # s +predict_time: 0.100 # s diff --git a/configs/calibration.yaml b/configs/calibration.yaml new file mode 100644 index 0000000..b080281 --- /dev/null +++ b/configs/calibration.yaml @@ -0,0 +1,16 @@ +pattern_cols: 10 +pattern_rows: 7 +center_distance_mm: 40 + +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +camera_name: "hikrobot" +exposure_ms: 3 +gain: 10.0 +vid_pid: "2bdf:0001" + +#####-----cboard参数-----##### +quaternion_canid: 0x01 +bullet_speed_canid: 0x110 +send_canid: 0xff +can_interface: "can0" \ No newline at end of file diff --git a/configs/camera.yaml b/configs/camera.yaml new file mode 100644 index 0000000..e1c9cdd --- /dev/null +++ b/configs/camera.yaml @@ -0,0 +1,9 @@ +camera_name: "mindvision" +exposure_ms: 2 +gamma: 0.5 +vid_pid: "f622:d13a" + +# camera_name: "hikrobot" +# exposure_ms: 3 +# gain: 10.0 +# vid_pid: "2bdf:0001" \ No newline at end of file diff --git a/configs/demo.yaml b/configs/demo.yaml new file mode 100644 index 0000000..8724e58 --- /dev/null +++ b/configs/demo.yaml @@ -0,0 +1,121 @@ +# enemy_color: "red" +enemy_color: "blue" + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: CPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +use_roi: false + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 +outpost_max_temp_lost_count: 75 + +#####-----aimer参数-----##### +yaw_offset: -1 # degree -2.5 +pitch_offset: -1.4 # degree 2 +comming_angle: 60 # degree +leaving_angle: 20 # degree +decision_speed: 8 # rad/s +high_speed_delay_time: 0.030 # s +low_speed_delay_time: 0.015 # s + +#####-----shooter参数-----##### +first_tolerance: 3 # 近距离射击容差,degree +second_tolerance: 2 # 远距离射击容差,degree +judge_distance: 2 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +camera_name: "hikrobot" +exposure_ms: 2 +gain: 16 +vid_pid: "2bdf:0001" + +# -1 0 0 +# 0 -1 0 +# 0 0 1 +R_gimbal2imubody: [-1, 0, 0, 0, -1, 0, 0, 0, 1] + +# 重投影误差: 0.2697px +camera_matrix: [1818.3669452465165, 0, 751.06226574703498, 0, 1822.494494078506, 530.43671556112133, 0, 0, 1] +distort_coeffs: [-0.077944626599568856, 0.15447826031486889, -0.0025714394278524674, 0.00083016311301273629, 0] + +# 相机同理想情况的偏角: yaw0.46 pitch0.61 roll-1.53 degree +R_camera2gimbal: [-0.0083195760046954614, 0.010498791137270739, 0.99991027599468041, -0.99960756138647755, -0.026835747568381807, -0.0080352891314148939, 0.026748978935305992, -0.99958472279097077, 0.010717933047771133] +t_camera2gimbal: [0.094969301833534511, 0.095006290298006682, 0.050987066291756609] + +#####-----cboard参数-----##### +quaternion_canid: 0x100 +bullet_speed_canid: 0x101 +send_canid: 0xff +can_interface: "can0" + +#####-----gimbal参数-----##### +com_port: "/dev/gimbal" + +#####-----planner-----##### +fire_thresh: 0.003 + +max_yaw_acc: 50 +Q_yaw: [9e6, 0] +R_yaw: [1] + +max_pitch_acc: 100 +Q_pitch: [9e6,0] +R_pitch: [1] + +#####-----buff_detector参数-----##### +detect: + contrast: 1 + brightness: + blue: -120 + red: -100 + brightness_threshold: + blue: 120 + red: 40 + morphology_size: + blue: 3 + red: 3 + dilate_size: 2 + # canny_low_threshold: 100 + # canny_high_threshold: 180 + # approx_epsilon: 1.0 + R_contours_min_area: 200 + R_contours_max_area: 600 + fanblades_head_contours_min_area: 3000 + fanblades_head_contours_max_area: 8000 + fanblades_body_contours_min_area: 1000 + fanblades_body_contours_max_area: 4000 + standard_fanblade_path: ./assets/standard_fanblade.jpg + +#####-----buff_aimer参数-----##### +aim_time: 0.150 # s +wait_time: 0.300 # s +command_fire_gap: 0.080 # s +predict_time: 0.150 # s \ No newline at end of file diff --git a/configs/example.yaml b/configs/example.yaml new file mode 100644 index 0000000..9f6f1b3 --- /dev/null +++ b/configs/example.yaml @@ -0,0 +1,83 @@ +# enemy_color: "red" +enemy_color: "blue" + + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: CPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +#####-----USB相机参数-----##### +image_width: 1920 +image_height: 1080 +fov_h: 87.7 +fov_v: 56.7 +usb_frame_rate: 60 +usb_exposure: 250 #1-80000______250 +usb_gamma: 160 +usb_gain: 10 #0-96 + +#####-----工业相机参数-----##### +camera_name: "mindvision" +exposure_ms: 2 +gamma: 0.5 +vid_pid: "f622:d13a" + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----cboard参数-----##### +quaternion_canid: 0x01 +bullet_speed_canid: 0x101 +send_canid: 0xff +can_interface: "can0" + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 + +#####-----aimer参数-----##### +yaw_offset: -2.5 # degree -2.5 +pitch_offset: 3 # degree 2 +comming_angle: 60 # degree +leaving_angle: 20 # degree + +#####-----shooter参数-----##### +first_tolerance: 3 # 近距离射击容差,degree +second_tolerance: 2 # 远距离射击容差,degree +judge_distance: 2 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +#####-----工业相机标定参数-----##### +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.1944px +camera_matrix: [1295.4665175006589, 0, 655.71498478800618, 0, 1297.2591156991834, 506.21320670125334, 0, 0, 1] +distort_coeffs: [-0.48599095566724387, 0.23999516531343185, -0.00029018466701866776, -0.00083639963030895752, 0] + +# 相机同理想情况的偏角: yaw-2.64 pitch-3.20 roll-0.77 degree +# 标定板到世界坐标系原点的水平距离: 1.45 m +# 标定板同竖直摆放时的偏角: yaw90.85 pitch8.54 roll-0.32 degree +R_camera2gimbal: [0.046736929626455238, -0.055211941324114208, 0.99738021884550865, -0.99881598162461238, -0.016078770676278314, 0.0459141370909011, 0.013501639172863072, -0.99834518813322415, -0.055898041744621867] +t_camera2gimbal: [0.27420934670256336, 0.0098763306562371159, 0.043798385984197351] diff --git a/configs/mvs.yaml b/configs/mvs.yaml new file mode 100644 index 0000000..f19a870 --- /dev/null +++ b/configs/mvs.yaml @@ -0,0 +1,124 @@ +# enemy_color: "red" +enemy_color: "blue" + + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: GPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +use_roi: false + +#####-----USB相机参数-----##### +image_width: 640 +image_height: 360 +new_image_width: 1280 +new_image_height: 720 +fov_h: 57.7 #87.7 +fov_v: 56.7 +new_fov_h: 37 #67 +new_fov_v: 40.9 +usb_frame_rate: 60 +usb_exposure: 305 #1-80000______250 +new_usb_exposure: 320 +usb_gamma: 160 +usb_gain: 10 #0-96 + +#####-----工业相机参数-----##### +camera_name: "hikrobot" +exposure_ms: 2 +gain: 16 +vid_pid: "2bdf:0001" + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----cboard参数-----##### +quaternion_canid: 0x01 +bullet_speed_canid: 0x110 +send_canid: 0xff +can_interface: "can0" + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 +outpost_max_temp_lost_count: 75 + +#####-----aimer参数-----##### +yaw_offset: -2.5 # degree -2.5 +pitch_offset: 0 # degree 2 +comming_angle: 70 # degree +leaving_angle: 30 # degree +decision_speed: 7 # rad/s +high_speed_delay_time: 0.066 # s +low_speed_delay_time: 0.015 # s + +#####-----shooter参数-----##### +first_tolerance: 3 # 近距离射击容差,degree +second_tolerance: 2 # 远距离射击容差,degree +judge_distance: 2 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +#####-----decider参数-----##### +mode: 1 + +#####-----工业相机标定参数-----##### +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.1322px +camera_matrix: [1776.9477196851155, 0, 756.31235265560952, 0, 1776.0591253569607, 566.16539069551641, 0, 0, 1] +distort_coeffs: [-0.08382326954462313, 0.097449270330296239, -0.0012558283068959985, 0.0037372210254148081, 0] + +R_camera2gimbal: [0, 0, 1, -1, 0, 0, 0, -1, 0] +t_camera2gimbal: [0.1, 0, 0.05] + + +#####-----buff_detector参数-----##### +detect: + contrast: 1 + brightness: + blue: -120 + red: -100 + brightness_threshold: + blue: 120 + red: 80 + morphology_size: + blue: 3 + red: 2 + dilate_size: 1 + # canny_low_threshold: 100 + # canny_high_threshold: 180 + # approx_epsilon: 1.0 + R_contours_min_area: 1 + R_contours_max_area: 600 + fanblades_head_contours_min_area: 300 + fanblades_head_contours_max_area: 8000 + fanblades_body_contours_min_area: 1000 + fanblades_body_contours_max_area: 4000 + standard_fanblade_path: ./assets/standard_fanblade.jpg + +#####-----buff_aimer参数-----##### +aim_time: 0.200 # s +wait_time: 0.050 # s +predict_time: 0.300 # s diff --git a/configs/sentry.yaml b/configs/sentry.yaml new file mode 100644 index 0000000..b6b2cd4 --- /dev/null +++ b/configs/sentry.yaml @@ -0,0 +1,96 @@ +# enemy_color: "red" +enemy_color: "blue" + + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: GPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +use_roi: false + +#####-----USB相机参数-----##### +image_width: 1280 +image_height: 720 +fov_h: 57.7 #87.7 +fov_v: 56.7 +new_fov_h: 27 #67 +new_fov_v: 40.9 +usb_frame_rate: 120 +usb_exposure: 500 #1-80000______250 +usb_gamma: 160 +usb_gain: 10 #0-96 + +#####-----工业相机参数-----##### +camera_name: "hikrobot" +exposure_ms: 0.8 +gain: 16.9 +vid_pid: "2bdf:0001" + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----cboard参数-----##### +quaternion_canid: 0x01 +bullet_speed_canid: 0x110 +send_canid: 0xff +can_interface: "can0" + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 25 +outpost_max_temp_lost_count: 75 + +#####-----aimer参数-----##### +yaw_offset: -0.8 # degree -2.5 +pitch_offset: -1 # degree 2 +comming_angle: 60 # degree +leaving_angle: 20 # degree +left_yaw_offset: -1 +right_yaw_offset: -0.6 +decision_speed: 10 # rad/s +high_speed_delay_time: 0.026 # s +low_speed_delay_time: 0.010 # s + +#####-----shooter参数-----##### +first_tolerance: 5 # 近距离射击容差,degree +second_tolerance: 2 # 远距离射击容差,degree +judge_distance: 3 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +#####-----decider参数-----##### +mode: 1 + +#####-----工业相机标定参数-----##### +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.1833px +camera_matrix: [2414.9359264386621, 0, 717.26243105567414, 0, 2418.0489262208148, 582.68540529942845, 0, 0, 1] +distort_coeffs: [-0.0209453389287673, 0.15028138841073832, -0.0006517722113234505, -0.0016861906197686788, 0] + +# 相机同理想情况的偏角: yaw-1.11 pitch0.01 roll-0.06 degree +# 标定板到世界坐标系原点的水平距离: 1.20 m +# 标定板同竖直摆放时的偏角: yaw123.89 pitch14.05 roll-0.86 degree +R_camera2gimbal: [0.01928451708725664, 0.00012696140743255463, 0.99981402834802846, -0.99981344688553653, -0.0010834913551969569, 0.019284643459122196, 0.0010857382619952124, -0.99999940496346484, 0.00010604311483339372] +t_camera2gimbal: [0.13089617453251859, 0.0038468007459533785, 0.094139945222010288] diff --git a/configs/standard3.yaml b/configs/standard3.yaml new file mode 100644 index 0000000..6edfa36 --- /dev/null +++ b/configs/standard3.yaml @@ -0,0 +1,103 @@ +enemy_color: "red" +# enemy_color: "blue" + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: GPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +use_roi: false + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 +outpost_max_temp_lost_count: 75 + +#####-----aimer参数-----##### +yaw_offset: 2 # degree -2.5 +pitch_offset: 6.5 # degree 2 +comming_angle: 55 # degree +leaving_angle: 20 # degree +decision_speed: 7 # rad/s +high_speed_delay_time: 0.0 # s +low_speed_delay_time: 0.0 # s planner use this value + +#####-----shooter参数-----##### +first_tolerance: 3 # 近距离射击容差,degree +second_tolerance: 2 # 远距离射击容差,degree +judge_distance: 2 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +camera_name: "hikrobot" +exposure_ms: 2.5 +gain: 16.9 +vid_pid: "2bdf:0001" + +# 1 0 0 +# 0 1 0 +# 0 0 1 +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.1820px +camera_matrix: [1785.4881526822305, 0, 672.4806478241826, 0, 1785.026019470562, 559.89603224794314, 0, 0, 1] +distort_coeffs: [-0.076005079619881746, 0.11182817466388446, 0.0005362204787722057, -0.0027546300984895122, 0] + +# 相机同理想情况的偏角: yaw1.44 pitch-7.28 roll0.96 degree +# 标定板到世界坐标系原点的水平距离: 1.13 m +# 标定板同竖直摆放时的偏角: yaw7.61 pitch13.92 roll-0.46 degree +R_camera2gimbal: [-0.027182119030230909, -0.12616154330853446, 0.99163723074269183, -0.99949106557517331, 0.019998323121329122, -0.024853106601381177, -0.016695575474690555, -0.99180811252093692, -0.12664093215554434] +t_camera2gimbal: [0.13160669975045827, 0.10377721766577375, 0.024908271912914642] + +#####-----cboard参数-----##### +quaternion_canid: 0x100 +bullet_speed_canid: 0x101 +send_canid: 0xff +can_interface: "can0" + +#####-----gimbal参数-----##### +com_port: "/dev/gimbal" +yaw_kp: 0 +yaw_kd: 0 +pitch_kp: 0 +pitch_kd: 0 + +#####-----planner-----##### +fire_thresh: 0.0035 + +max_yaw_acc: 50 +Q_yaw: [9e6, 0] +R_yaw: [1] + +max_pitch_acc: 100 +Q_pitch: [9e6, 0] +R_pitch: [1] + +#####-----buff_detector参数-----##### +model: "assets/yolo11_buff_int8.xml" + +#####-----buff_aimer参数-----##### +fire_gap_time: 0.700 # s +predict_time: 0.120 # s \ No newline at end of file diff --git a/configs/standard4.yaml b/configs/standard4.yaml new file mode 100644 index 0000000..72475b0 --- /dev/null +++ b/configs/standard4.yaml @@ -0,0 +1,99 @@ +enemy_color: "red" +# enemy_color: "blue" + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: GPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +use_roi: false + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 +outpost_max_temp_lost_count: 75 + +#####-----aimer参数-----##### +yaw_offset: -2 # degree -2.5 +pitch_offset: 0 # degree 2 +comming_angle: 60 # degree +leaving_angle: 20 # degree +decision_speed: 8 # rad/s +high_speed_delay_time: 0.015 # s +low_speed_delay_time: 0.015 # s + +#####-----shooter参数-----##### +first_tolerance: 3 # 近距离射击容差,degree +second_tolerance: 2 # 远距离射击容差,degree +judge_distance: 2 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +camera_name: "hikrobot" +exposure_ms: 2 +gain: 16 +vid_pid: "2bdf:0001" + +# 1 0 0 +# 0 1 0 +# 0 0 1 + +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.3145px +camera_matrix: [1851.7070167840545, 0, 721.12585328714192, 0, 1851.8175594364079, 571.69879709276688, 0, 0, 1] +distort_coeffs: [-0.093662536083526302, 0.18945726820633155, -0.00040424349861928674, -0.0040568403852123142, 0] + +# 相机同理想情况的偏角: yaw-1.61 pitch-0.82 roll-0.61 degree +R_camera2gimbal: [0.02823004230930648, -0.014076983590428133, 0.99950232778328707, -0.99954530259354468, -0.010995644138609872, 0.028076393521179785, 0.010594940981141165, -0.99984045444409364, -0.014380990321749998] +t_camera2gimbal: [0.045517325957791413, 0.10544338092767802, 0.034649710880185793] + +#####-----cboard参数-----##### +quaternion_canid: 0x100 +bullet_speed_canid: 0x101 +send_canid: 0xff +can_interface: "can0" + +#####-----gimbal参数-----##### +com_port: "/dev/gimbal" + +#####-----planner-----##### +fire_thresh: 0.003 + +max_yaw_acc: 50 +Q_yaw: [9e6, 0] +R_yaw: [1] + +max_pitch_acc: 100 +Q_pitch: [9e6,0] +R_pitch: [1] + +#####-----buff_detector参数-----##### +model: "assets/yolo11_buff_int8.xml" + +#####-----buff_aimer参数-----##### +fire_gap_time: 0.520 # s +predict_time: 0.100 # s \ No newline at end of file diff --git a/configs/uav.yaml b/configs/uav.yaml new file mode 100644 index 0000000..ce71637 --- /dev/null +++ b/configs/uav.yaml @@ -0,0 +1,105 @@ +enemy_color: "red" +# enemy_color: "blue" + + +#####-----神经网络参数-----##### +yolo_name: yolov5 +classify_model: assets/tiny_resnet.onnx +yolo11_model_path: assets/yolo11.xml +yolov8_model_path: assets/yolov8.xml +yolov5_model_path: assets/yolov5.xml +device: CPU +min_confidence: 0.8 +use_traditional: true + +#####-----ROI-----##### +roi: + x: 420 + y: 50 + width: 600 + height: 600 + +use_roi: false + +#####-----工业相机参数-----##### +camera_name: "mindvision" +exposure_ms: 8 +gamma: 0.6 +vid_pid: "f622:d13a" + +#####-----USB相机参数-----##### +image_width: 640 +image_height: 360 +new_image_width: 1280 +new_image_height: 720 +fov_h: 87.7 +fov_v: 56.7 +new_fov_h: 67 +new_fov_v: 40.9 +usb_frame_rate: 100 +usb_exposure: 315 #1-80000______250 +usb_gamma: 160 +usb_gain: 10 #0-96 + + +#####-----传统方法参数-----##### +threshold: 150 +max_angle_error: 45 # degree +min_lightbar_ratio: 1.5 +max_lightbar_ratio: 20 +min_lightbar_length: 8 +min_armor_ratio: 1 +max_armor_ratio: 5 +max_side_ratio: 1.5 +max_rectangular_error: 25 # degree +min_confidence: 0.8 + +#####-----cboard参数-----##### +quaternion_canid: 0x001 +bullet_speed_canid: 0x110 +send_canid: 0xff +can_interface: "can0" + +#####-----tracker参数-----##### +min_detect_count: 5 +max_temp_lost_count: 15 +outpost_max_temp_lost_count: 150 + +#####-----aimer参数-----##### +yaw_offset: 0 # degree -2.5 +pitch_offset: 1.5 # degree 2 +comming_angle: 70 # degree +leaving_angle: 30 # degree +min_spin_speed: 2 # rad/s +decision_speed: 12 # rad/s +high_speed_delay_time: 0.005 # s +low_speed_delay_time: 0.005 # s + +#####-----decider参数-----##### +mode: 1 + +#####-----shooter参数-----##### +first_tolerance: 2 # 近距离射击容差,degree +second_tolerance: 0.8 # 远距离射击容差,degree +judge_distance: 0.5 #距离判断阈值 +auto_fire: true # 是否由自瞄控制射击 + +#####-----工业相机标定参数-----##### +R_gimbal2imubody: [1, 0, 0, 0, 1, 0, 0, 0, 1] + +# 重投影误差: 0.1032px +camera_matrix: [2924.6190997571712, 0, 647.0245571651617, 0, 2927.4258594148396, 388.18625585771758, 0, 0, 1] +distort_coeffs: [-0.58886594170687334, 0.32512251112647716, 0.012447953238733123, -0.0032411418907421475, 0] +# 相机同理想情况的偏角: yaw0.02 pitch1.94 roll1.40 degree +# 标定板到世界坐标系原点的水平距离: 4.12 m +# 标定板同竖直摆放时的偏角: yaw-87.29 pitch7.65 roll-0.51 degree +R_camera2gimbal: [0.00046423063676173171, 0.033770528223625203, 0.99942950522456209, -0.99970300483287733, 0.024367462669785803, -0.00035901413901253575, -0.02436568525675643, -0.99913251282626891, 0.03377181066266309] +t_camera2gimbal: [-0.034955523715399906, -0.000074403211899371, 0.001248173910106823] + + +#####-----buff_detector参数-----##### +model: "assets/yolo11_buff_int8.xml" + +#####-----buff_aimer参数-----##### +fire_gap_time: 0.600 # s +predict_time: 0.090 # s \ No newline at end of file diff --git a/io/CMakeLists.txt b/io/CMakeLists.txt new file mode 100644 index 0000000..b85f1a8 --- /dev/null +++ b/io/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.16) + +find_package(yaml-cpp REQUIRED) + +add_subdirectory(serial) + +# 创建目标 io +add_library(io STATIC + hikrobot/hikrobot.cpp + mindvision/mindvision.cpp + usbcamera/usbcamera.cpp + camera.cpp + cboard.cpp + dm_imu/dm_imu.cpp + gimbal/gimbal.cpp +) + +# hikrobot +target_include_directories(io PUBLIC hikrobot/include) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + target_link_directories(io PUBLIC hikrobot/lib/amd64) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + target_link_directories(io PUBLIC hikrobot/lib/arm64) +else() + message(FATAL_ERROR "Unsupported architecture: ${CMAKE_HOST_SYSTEM_PROCESSOR}!") +endif() + +# mindvision +target_include_directories(io PUBLIC mindvision/include) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + target_link_directories(io PUBLIC mindvision/lib/amd64) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + target_link_directories(io PUBLIC mindvision/lib/arm64) +else() + message(FATAL_ERROR "Unsupported architecture: ${CMAKE_HOST_SYSTEM_PROCESSOR}!") +endif() + +target_link_libraries(io MvCameraControl MVSDK usb-1.0 yaml-cpp serial) + +################## 检查 ROS 环境 ################## +find_package(ament_cmake QUIET) +find_package(rclcpp QUIET) +find_package(std_msgs QUIET) +find_package(rosidl_typesupport_cpp QUIET) +find_package(sp_msgs QUIET) + +# 如果找不到 ROS 环境,则跳过 ROS 相关的部分 +if(NOT ament_cmake_FOUND OR NOT rclcpp_FOUND OR NOT std_msgs_FOUND OR NOT rosidl_typesupport_cpp_FOUND OR NOT sp_msgs_FOUND) + message(WARNING "ROS2 not found, skipping ROS2 specific code.") +else() + message(STATUS "ROS2 environment found, compiling ROS2-related code.") + + # 添加 ROS2 相关的文件 + target_sources(io PRIVATE ros2/publish2nav.cpp ros2/ros2.cpp ros2/subscribe2nav.cpp) + + # 设置 ROS2 依赖 + include_directories(${std_msgs_INCLUDE_DIRS}) + ament_target_dependencies(io rclcpp std_msgs sp_msgs) +endif() diff --git a/io/camera.cpp b/io/camera.cpp new file mode 100644 index 0000000..82d522a --- /dev/null +++ b/io/camera.cpp @@ -0,0 +1,39 @@ +#include "camera.hpp" + +#include + +#include "hikrobot/hikrobot.hpp" +#include "mindvision/mindvision.hpp" +#include "tools/yaml.hpp" + +namespace io +{ +Camera::Camera(const std::string & config_path) +{ + auto yaml = tools::load(config_path); + auto camera_name = tools::read(yaml, "camera_name"); + auto exposure_ms = tools::read(yaml, "exposure_ms"); + + if (camera_name == "mindvision") { + auto gamma = tools::read(yaml, "gamma"); + auto vid_pid = tools::read(yaml, "vid_pid"); + camera_ = std::make_unique(exposure_ms, gamma, vid_pid); + } + + else if (camera_name == "hikrobot") { + auto gain = tools::read(yaml, "gain"); + auto vid_pid = tools::read(yaml, "vid_pid"); + camera_ = std::make_unique(exposure_ms, gain, vid_pid); + } + + else { + throw std::runtime_error("Unknow camera_name: " + camera_name + "!"); + } +} + +void Camera::read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) +{ + camera_->read(img, timestamp); +} + +} // namespace io \ No newline at end of file diff --git a/io/camera.hpp b/io/camera.hpp new file mode 100644 index 0000000..b509b9a --- /dev/null +++ b/io/camera.hpp @@ -0,0 +1,30 @@ +#ifndef IO__CAMERA_HPP +#define IO__CAMERA_HPP + +#include +#include +#include +#include + +namespace io +{ +class CameraBase +{ +public: + virtual ~CameraBase() = default; + virtual void read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) = 0; +}; + +class Camera +{ +public: + Camera(const std::string & config_path); + void read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp); + +private: + std::unique_ptr camera_; +}; + +} // namespace io + +#endif // IO__CAMERA_HPP \ No newline at end of file diff --git a/io/cboard.cpp b/io/cboard.cpp new file mode 100644 index 0000000..93aaf00 --- /dev/null +++ b/io/cboard.cpp @@ -0,0 +1,121 @@ +#include "cboard.hpp" + +#include "tools/math_tools.hpp" +#include "tools/yaml.hpp" + +namespace io +{ +CBoard::CBoard(const std::string & config_path) +: mode(Mode::idle), + shoot_mode(ShootMode::left_shoot), + bullet_speed(0), + queue_(5000), + can_(read_yaml(config_path), std::bind(&CBoard::callback, this, std::placeholders::_1)) +// 注意: callback的运行会早于Cboard构造函数的完成 +{ + tools::logger()->info("[Cboard] Waiting for q..."); + queue_.pop(data_ahead_); + queue_.pop(data_behind_); + tools::logger()->info("[Cboard] Opened."); +} + +Eigen::Quaterniond CBoard::imu_at(std::chrono::steady_clock::time_point timestamp) +{ + if (data_behind_.timestamp < timestamp) data_ahead_ = data_behind_; + + while (true) { + queue_.pop(data_behind_); + if (data_behind_.timestamp > timestamp) break; + data_ahead_ = data_behind_; + } + + Eigen::Quaterniond q_a = data_ahead_.q.normalized(); + Eigen::Quaterniond q_b = data_behind_.q.normalized(); + auto t_a = data_ahead_.timestamp; + auto t_b = data_behind_.timestamp; + auto t_c = timestamp; + std::chrono::duration t_ab = t_b - t_a; + std::chrono::duration t_ac = t_c - t_a; + + // 四元数插值 + auto k = t_ac / t_ab; + Eigen::Quaterniond q_c = q_a.slerp(k, q_b).normalized(); + + return q_c; +} + +void CBoard::send(Command command) const +{ + can_frame frame; + frame.can_id = send_canid_; + frame.can_dlc = 8; + frame.data[0] = (command.control) ? 1 : 0; + frame.data[1] = (command.shoot) ? 1 : 0; + frame.data[2] = (int16_t)(command.yaw * 1e4) >> 8; + frame.data[3] = (int16_t)(command.yaw * 1e4); + frame.data[4] = (int16_t)(command.pitch * 1e4) >> 8; + frame.data[5] = (int16_t)(command.pitch * 1e4); + frame.data[6] = (int16_t)(command.horizon_distance * 1e4) >> 8; + frame.data[7] = (int16_t)(command.horizon_distance * 1e4); + + try { + can_.write(&frame); + } catch (const std::exception & e) { + tools::logger()->warn("{}", e.what()); + } +} + +void CBoard::callback(const can_frame & frame) +{ + auto timestamp = std::chrono::steady_clock::now(); + + if (frame.can_id == quaternion_canid_) { + auto x = (int16_t)(frame.data[0] << 8 | frame.data[1]) / 1e4; + auto y = (int16_t)(frame.data[2] << 8 | frame.data[3]) / 1e4; + auto z = (int16_t)(frame.data[4] << 8 | frame.data[5]) / 1e4; + auto w = (int16_t)(frame.data[6] << 8 | frame.data[7]) / 1e4; + + if (std::abs(x * x + y * y + z * z + w * w - 1) > 1e-2) { + tools::logger()->warn("Invalid q: {} {} {} {}", w, x, y, z); + return; + } + + queue_.push({{w, x, y, z}, timestamp}); + } + + else if (frame.can_id == bullet_speed_canid_) { + bullet_speed = (int16_t)(frame.data[0] << 8 | frame.data[1]) / 1e2; + mode = Mode(frame.data[2]); + shoot_mode = ShootMode(frame.data[3]); + ft_angle = (int16_t)(frame.data[4] << 8 | frame.data[5]) / 1e4; + + // 限制日志输出频率为1Hz + static auto last_log_time = std::chrono::steady_clock::time_point::min(); + auto now = std::chrono::steady_clock::now(); + + if (bullet_speed > 0 && tools::delta_time(now, last_log_time) >= 1.0) { + tools::logger()->info( + "[CBoard] Bullet speed: {:.2f} m/s, Mode: {}, Shoot mode: {}, FT angle: {:.2f} rad", + bullet_speed, MODES[mode], SHOOT_MODES[shoot_mode], ft_angle); + last_log_time = now; + } + } +} + +// 实现方式有待改进 +std::string CBoard::read_yaml(const std::string & config_path) +{ + auto yaml = tools::load(config_path); + + quaternion_canid_ = tools::read(yaml, "quaternion_canid"); + bullet_speed_canid_ = tools::read(yaml, "bullet_speed_canid"); + send_canid_ = tools::read(yaml, "send_canid"); + + if (!yaml["can_interface"]) { + throw std::runtime_error("Missing 'can_interface' in YAML configuration."); + } + + return yaml["can_interface"].as(); +} + +} // namespace io \ No newline at end of file diff --git a/io/cboard.hpp b/io/cboard.hpp new file mode 100644 index 0000000..3a2a377 --- /dev/null +++ b/io/cboard.hpp @@ -0,0 +1,72 @@ +#ifndef IO__CBOARD_HPP +#define IO__CBOARD_HPP + +#include +#include +#include +#include +#include +#include + +#include "io/command.hpp" +#include "io/socketcan.hpp" +#include "tools/logger.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace io +{ +enum Mode +{ + idle, + auto_aim, + small_buff, + big_buff, + outpost +}; +const std::vector MODES = {"idle", "auto_aim", "small_buff", "big_buff", "outpost"}; + +// 哨兵专有 +enum ShootMode +{ + left_shoot, + right_shoot, + both_shoot +}; +const std::vector SHOOT_MODES = {"left_shoot", "right_shoot", "both_shoot"}; + +class CBoard +{ +public: + double bullet_speed; + Mode mode; + ShootMode shoot_mode; + double ft_angle; //无人机专有 + + CBoard(const std::string & config_path); + + Eigen::Quaterniond imu_at(std::chrono::steady_clock::time_point timestamp); + + void send(Command command) const; + +private: + struct IMUData + { + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point timestamp; + }; + + tools::ThreadSafeQueue queue_; // 必须在can_之前初始化,否则存在死锁的可能 + SocketCAN can_; + IMUData data_ahead_; + IMUData data_behind_; + + int quaternion_canid_, bullet_speed_canid_, send_canid_; + + void callback(const can_frame & frame); + + std::string read_yaml(const std::string & config_path); +}; + +} // namespace io + +#endif // IO__CBOARD_HPP \ No newline at end of file diff --git a/io/command.hpp b/io/command.hpp new file mode 100644 index 0000000..797ba29 --- /dev/null +++ b/io/command.hpp @@ -0,0 +1,17 @@ +#ifndef IO__COMMAND_HPP +#define IO__COMMAND_HPP + +namespace io +{ +struct Command +{ + bool control; + bool shoot; + double yaw; + double pitch; + double horizon_distance = 0; //无人机专有 +}; + +} // namespace io + +#endif // IO__COMMAND_HPP \ No newline at end of file diff --git a/io/dm_imu/dm_imu.cpp b/io/dm_imu/dm_imu.cpp new file mode 100644 index 0000000..38bfbd7 --- /dev/null +++ b/io/dm_imu/dm_imu.cpp @@ -0,0 +1,131 @@ +#include "dm_imu.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "tools/crc.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +namespace io +{ +DM_IMU::DM_IMU() : queue_(5000) +{ + init_serial(); + rec_thread_ = std::thread(&DM_IMU::get_imu_data_thread, this); + queue_.pop(data_ahead_); + queue_.pop(data_behind_); + tools::logger()->info("[DM_IMU] initialized"); +} + +DM_IMU::~DM_IMU() +{ + stop_thread_ = true; + if (rec_thread_.joinable()) { + rec_thread_.join(); + } + if (serial_.isOpen()) { + serial_.close(); + } +} + +void DM_IMU::init_serial() +{ + try { + serial_.setPort("/dev/ttyACM0"); + serial_.setBaudrate(921600); + serial_.setFlowcontrol(serial::flowcontrol_none); + serial_.setParity(serial::parity_none); //default is parity_none + serial_.setStopbits(serial::stopbits_one); + serial_.setBytesize(serial::eightbits); + serial::Timeout time_out = serial::Timeout::simpleTimeout(20); + serial_.setTimeout(time_out); + serial_.open(); + usleep(1000000); //1s + + tools::logger()->info("[DM_IMU] serial port opened"); + } + + catch (serial::IOException & e) { + tools::logger()->warn("[DM_IMU] failed to open serial port "); + exit(0); + } +} + +void DM_IMU::get_imu_data_thread() +{ + while (!stop_thread_) { + if (!serial_.isOpen()) { + tools::logger()->warn("In get_imu_data_thread,imu serial port unopen"); + } + + serial_.read((uint8_t *)(&receive_data.FrameHeader1), 4); + + if ( + receive_data.FrameHeader1 == 0x55 && receive_data.flag1 == 0xAA && + receive_data.slave_id1 == 0x01 && receive_data.reg_acc == 0x01) + + { + serial_.read((uint8_t *)(&receive_data.accx_u32), 57 - 4); + + if (tools::get_crc16((uint8_t *)(&receive_data.FrameHeader1), 16) == receive_data.crc1) { + data.accx = *((float *)(&receive_data.accx_u32)); + data.accy = *((float *)(&receive_data.accy_u32)); + data.accz = *((float *)(&receive_data.accz_u32)); + } + if (tools::get_crc16((uint8_t *)(&receive_data.FrameHeader2), 16) == receive_data.crc2) { + data.gyrox = *((float *)(&receive_data.gyrox_u32)); + data.gyroy = *((float *)(&receive_data.gyroy_u32)); + data.gyroz = *((float *)(&receive_data.gyroz_u32)); + } + if (tools::get_crc16((uint8_t *)(&receive_data.FrameHeader3), 16) == receive_data.crc3) { + data.roll = *((float *)(&receive_data.roll_u32)); + data.pitch = *((float *)(&receive_data.pitch_u32)); + data.yaw = *((float *)(&receive_data.yaw_u32)); + // tools::logger()->debug( + // "yaw: {:.2f}, pitch: {:.2f}, roll: {:.2f}", static_cast(data.yaw), + // static_cast(data.pitch), static_cast(data.roll)); + } + auto timestamp = std::chrono::steady_clock::now(); + Eigen::Quaterniond q = Eigen::AngleAxisd(data.yaw * M_PI / 180, Eigen::Vector3d::UnitZ()) * + Eigen::AngleAxisd(data.pitch * M_PI / 180, Eigen::Vector3d::UnitY()) * + Eigen::AngleAxisd(data.roll * M_PI / 180, Eigen::Vector3d::UnitX()); + q.normalize(); + queue_.push({q, timestamp}); + } else { + tools::logger()->info("[DM_IMU] failed to get correct data"); + } + } +} + +Eigen::Quaterniond DM_IMU::imu_at(std::chrono::steady_clock::time_point timestamp) +{ + if (data_behind_.timestamp < timestamp) data_ahead_ = data_behind_; + + while (true) { + queue_.pop(data_behind_); + if (data_behind_.timestamp > timestamp) break; + data_ahead_ = data_behind_; + } + + Eigen::Quaterniond q_a = data_ahead_.q.normalized(); + Eigen::Quaterniond q_b = data_behind_.q.normalized(); + auto t_a = data_ahead_.timestamp; + auto t_b = data_behind_.timestamp; + auto t_c = timestamp; + std::chrono::duration t_ab = t_b - t_a; + std::chrono::duration t_ac = t_c - t_a; + + // 四元数插值 + auto k = t_ac / t_ab; + Eigen::Quaterniond q_c = q_a.slerp(k, q_b).normalized(); + + return q_c; +} + +} // namespace io diff --git a/io/dm_imu/dm_imu.hpp b/io/dm_imu/dm_imu.hpp new file mode 100644 index 0000000..4752ae4 --- /dev/null +++ b/io/dm_imu/dm_imu.hpp @@ -0,0 +1,96 @@ +#ifndef IO__Dm_Imu_HPP +#define IO__Dm_Imu_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tools/thread_safe_queue.hpp" + +namespace io +{ + +struct __attribute__((packed)) IMU_Receive_Frame +{ + uint8_t FrameHeader1; + uint8_t flag1; + uint8_t slave_id1; + uint8_t reg_acc; + uint32_t accx_u32; + uint32_t accy_u32; + uint32_t accz_u32; + uint16_t crc1; + uint8_t FrameEnd1; + + uint8_t FrameHeader2; + uint8_t flag2; + uint8_t slave_id2; + uint8_t reg_gyro; + uint32_t gyrox_u32; + uint32_t gyroy_u32; + uint32_t gyroz_u32; + uint16_t crc2; + uint8_t FrameEnd2; + + uint8_t FrameHeader3; + uint8_t flag3; + uint8_t slave_id3; + uint8_t reg_euler; //r-p-y + uint32_t roll_u32; + uint32_t pitch_u32; + uint32_t yaw_u32; + uint16_t crc3; + uint8_t FrameEnd3; +}; + +typedef struct +{ + float accx; + float accy; + float accz; + float gyrox; + float gyroy; + float gyroz; + float roll; + float pitch; + float yaw; +} IMU_Data; + +class DM_IMU +{ +public: + DM_IMU(); + ~DM_IMU(); + + Eigen::Quaterniond imu_at(std::chrono::steady_clock::time_point timestamp); + +private: + struct IMUData + { + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point timestamp; + }; + + void init_serial(); + void get_imu_data_thread(); + + serial::Serial serial_; + std::thread rec_thread_; + + tools::ThreadSafeQueue queue_; + IMUData data_ahead_, data_behind_; + + std::atomic stop_thread_{false}; + IMU_Receive_Frame receive_data{}; //receive data frame + IMU_Data data{}; +}; + +} // namespace io + +#endif diff --git a/io/gimbal/gimbal.cpp b/io/gimbal/gimbal.cpp new file mode 100644 index 0000000..a525193 --- /dev/null +++ b/io/gimbal/gimbal.cpp @@ -0,0 +1,223 @@ +#include "gimbal.hpp" + +#include "tools/crc.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/yaml.hpp" + +namespace io +{ +Gimbal::Gimbal(const std::string & config_path) +{ + auto yaml = tools::load(config_path); + auto com_port = tools::read(yaml, "com_port"); + + try { + serial_.setPort(com_port); + serial_.open(); + } catch (const std::exception & e) { + tools::logger()->error("[Gimbal] Failed to open serial: {}", e.what()); + exit(1); + } + + thread_ = std::thread(&Gimbal::read_thread, this); + + queue_.pop(); + tools::logger()->info("[Gimbal] First q received."); +} + +Gimbal::~Gimbal() +{ + quit_ = true; + if (thread_.joinable()) thread_.join(); + serial_.close(); +} + +GimbalMode Gimbal::mode() const +{ + std::lock_guard lock(mutex_); + return mode_; +} + +GimbalState Gimbal::state() const +{ + std::lock_guard lock(mutex_); + return state_; +} + +std::string Gimbal::str(GimbalMode mode) const +{ + switch (mode) { + case GimbalMode::IDLE: + return "IDLE"; + case GimbalMode::AUTO_AIM: + return "AUTO_AIM"; + case GimbalMode::SMALL_BUFF: + return "SMALL_BUFF"; + case GimbalMode::BIG_BUFF: + return "BIG_BUFF"; + default: + return "INVALID"; + } +} + +Eigen::Quaterniond Gimbal::q(std::chrono::steady_clock::time_point t) +{ + while (true) { + auto [q_a, t_a] = queue_.pop(); + auto [q_b, t_b] = queue_.front(); + auto t_ab = tools::delta_time(t_a, t_b); + auto t_ac = tools::delta_time(t_a, t); + auto k = t_ac / t_ab; + Eigen::Quaterniond q_c = q_a.slerp(k, q_b).normalized(); + if (t < t_a) return q_c; + if (!(t_a < t && t <= t_b)) continue; + + return q_c; + } +} + +void Gimbal::send(io::VisionToGimbal VisionToGimbal) +{ + tx_data_.mode = VisionToGimbal.mode; + tx_data_.yaw = VisionToGimbal.yaw; + tx_data_.yaw_vel = VisionToGimbal.yaw_vel; + tx_data_.yaw_acc = VisionToGimbal.yaw_acc; + tx_data_.pitch = VisionToGimbal.pitch; + tx_data_.pitch_vel = VisionToGimbal.pitch_vel; + tx_data_.pitch_acc = VisionToGimbal.pitch_acc; + tx_data_.crc16 = tools::get_crc16( + reinterpret_cast(&tx_data_), sizeof(tx_data_) - sizeof(tx_data_.crc16)); + + try { + serial_.write(reinterpret_cast(&tx_data_), sizeof(tx_data_)); + } catch (const std::exception & e) { + tools::logger()->warn("[Gimbal] Failed to write serial: {}", e.what()); + } +} + +void Gimbal::send( + bool control, bool fire, float yaw, float yaw_vel, float yaw_acc, float pitch, float pitch_vel, + float pitch_acc) +{ + tx_data_.mode = control ? (fire ? 2 : 1) : 0; + tx_data_.yaw = yaw; + tx_data_.yaw_vel = yaw_vel; + tx_data_.yaw_acc = yaw_acc; + tx_data_.pitch = pitch; + tx_data_.pitch_vel = pitch_vel; + tx_data_.pitch_acc = pitch_acc; + tx_data_.crc16 = tools::get_crc16( + reinterpret_cast(&tx_data_), sizeof(tx_data_) - sizeof(tx_data_.crc16)); + + try { + serial_.write(reinterpret_cast(&tx_data_), sizeof(tx_data_)); + } catch (const std::exception & e) { + tools::logger()->warn("[Gimbal] Failed to write serial: {}", e.what()); + } +} + +bool Gimbal::read(uint8_t * buffer, size_t size) +{ + try { + return serial_.read(buffer, size) == size; + } catch (const std::exception & e) { + // tools::logger()->warn("[Gimbal] Failed to read serial: {}", e.what()); + return false; + } +} + +void Gimbal::read_thread() +{ + tools::logger()->info("[Gimbal] read_thread started."); + int error_count = 0; + + while (!quit_) { + if (error_count > 5000) { + error_count = 0; + tools::logger()->warn("[Gimbal] Too many errors, attempting to reconnect..."); + reconnect(); + continue; + } + + if (!read(reinterpret_cast(&rx_data_), sizeof(rx_data_.head))) { + error_count++; + continue; + } + + if (rx_data_.head[0] != 'S' || rx_data_.head[1] != 'P') continue; + + auto t = std::chrono::steady_clock::now(); + + if (!read( + reinterpret_cast(&rx_data_) + sizeof(rx_data_.head), + sizeof(rx_data_) - sizeof(rx_data_.head))) { + error_count++; + continue; + } + + if (!tools::check_crc16(reinterpret_cast(&rx_data_), sizeof(rx_data_))) { + tools::logger()->debug("[Gimbal] CRC16 check failed."); + continue; + } + + error_count = 0; + Eigen::Quaterniond q(rx_data_.q[0], rx_data_.q[1], rx_data_.q[2], rx_data_.q[3]); + queue_.push({q, t}); + + std::lock_guard lock(mutex_); + + state_.yaw = rx_data_.yaw; + state_.yaw_vel = rx_data_.yaw_vel; + state_.pitch = rx_data_.pitch; + state_.pitch_vel = rx_data_.pitch_vel; + state_.bullet_speed = rx_data_.bullet_speed; + state_.bullet_count = rx_data_.bullet_count; + + switch (rx_data_.mode) { + case 0: + mode_ = GimbalMode::IDLE; + break; + case 1: + mode_ = GimbalMode::AUTO_AIM; + break; + case 2: + mode_ = GimbalMode::SMALL_BUFF; + break; + case 3: + mode_ = GimbalMode::BIG_BUFF; + break; + default: + mode_ = GimbalMode::IDLE; + tools::logger()->warn("[Gimbal] Invalid mode: {}", rx_data_.mode); + break; + } + } + + tools::logger()->info("[Gimbal] read_thread stopped."); +} + +void Gimbal::reconnect() +{ + int max_retry_count = 10; + for (int i = 0; i < max_retry_count && !quit_; ++i) { + tools::logger()->warn("[Gimbal] Reconnecting serial, attempt {}/{}...", i + 1, max_retry_count); + try { + serial_.close(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } catch (...) { + } + + try { + serial_.open(); // 尝试重新打开 + queue_.clear(); + tools::logger()->info("[Gimbal] Reconnected serial successfully."); + break; + } catch (const std::exception & e) { + tools::logger()->warn("[Gimbal] Reconnect failed: {}", e.what()); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } +} + +} // namespace io \ No newline at end of file diff --git a/io/gimbal/gimbal.hpp b/io/gimbal/gimbal.hpp new file mode 100644 index 0000000..52170f0 --- /dev/null +++ b/io/gimbal/gimbal.hpp @@ -0,0 +1,106 @@ +#ifndef IO__GIMBAL_HPP +#define IO__GIMBAL_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "serial/serial.h" +#include "tools/thread_safe_queue.hpp" + +namespace io +{ +struct __attribute__((packed)) GimbalToVision +{ + uint8_t head[2] = {'M', 'R'}; + uint8_t mode; // 0: 空闲, 1: 自瞄, 2: 小符, 3: 大符 + float q[4]; // wxyz顺序 + float yaw; + float yaw_vel; + float pitch; + float pitch_vel; + float bullet_speed; + uint16_t bullet_count; // 子弹累计发送次数 + uint16_t crc16; +}; + +static_assert(sizeof(GimbalToVision) <= 64); + +struct __attribute__((packed)) VisionToGimbal +{ + uint8_t head[2] = {'M', 'R'}; + uint8_t mode; // 0: 不控制, 1: 控制云台但不开火,2: 控制云台且开火 + float yaw; + float yaw_vel; + float yaw_acc; + float pitch; + float pitch_vel; + float pitch_acc; + uint16_t crc16; +}; + +static_assert(sizeof(VisionToGimbal) <= 64); + +enum class GimbalMode +{ + IDLE, // 空闲 + AUTO_AIM, // 自瞄 + SMALL_BUFF, // 小符 + BIG_BUFF // 大符 +}; + +struct GimbalState +{ + float yaw; + float yaw_vel; + float pitch; + float pitch_vel; + float bullet_speed; + uint16_t bullet_count; +}; + +class Gimbal +{ +public: + Gimbal(const std::string & config_path); + + ~Gimbal(); + + GimbalMode mode() const; + GimbalState state() const; + std::string str(GimbalMode mode) const; + Eigen::Quaterniond q(std::chrono::steady_clock::time_point t); + + void send( + bool control, bool fire, float yaw, float yaw_vel, float yaw_acc, float pitch, float pitch_vel, + float pitch_acc); + + void send(io::VisionToGimbal VisionToGimbal); + +private: + serial::Serial serial_; + + std::thread thread_; + std::atomic quit_ = false; + mutable std::mutex mutex_; + + GimbalToVision rx_data_; + VisionToGimbal tx_data_; + + GimbalMode mode_ = GimbalMode::IDLE; + GimbalState state_; + tools::ThreadSafeQueue> + queue_{1000}; + + bool read(uint8_t * buffer, size_t size); + void read_thread(); + void reconnect(); +}; + +} // namespace io + +#endif // IO__GIMBAL_HPP \ No newline at end of file diff --git a/io/hikrobot/hikrobot.cpp b/io/hikrobot/hikrobot.cpp new file mode 100644 index 0000000..92d7927 --- /dev/null +++ b/io/hikrobot/hikrobot.cpp @@ -0,0 +1,246 @@ +#include "hikrobot.hpp" + +#include + +#include "tools/logger.hpp" + +using namespace std::chrono_literals; + +namespace io +{ +HikRobot::HikRobot(double exposure_ms, double gain, const std::string & vid_pid) +: exposure_us_(exposure_ms * 1e3), gain_(gain), queue_(1), daemon_quit_(false), vid_(-1), pid_(-1) +{ + set_vid_pid(vid_pid); + if (libusb_init(NULL)) tools::logger()->warn("Unable to init libusb!"); + + daemon_thread_ = std::thread{[this] { + tools::logger()->info("HikRobot's daemon thread started."); + + capture_start(); + + while (!daemon_quit_) { + std::this_thread::sleep_for(100ms); + + if (capturing_) continue; + + capture_stop(); + reset_usb(); + capture_start(); + } + + capture_stop(); + + tools::logger()->info("HikRobot's daemon thread stopped."); + }}; +} + +HikRobot::~HikRobot() +{ + daemon_quit_ = true; + if (daemon_thread_.joinable()) daemon_thread_.join(); + tools::logger()->info("HikRobot destructed."); +} + +void HikRobot::read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) +{ + CameraData data; + queue_.pop(data); + + img = data.img; + timestamp = data.timestamp; +} + +void HikRobot::capture_start() +{ + capturing_ = false; + capture_quit_ = false; + + unsigned int ret; + + MV_CC_DEVICE_INFO_LIST device_list; + ret = MV_CC_EnumDevices(MV_USB_DEVICE, &device_list); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_EnumDevices failed: {:#x}", ret); + return; + } + + if (device_list.nDeviceNum == 0) { + tools::logger()->warn("Not found camera!"); + return; + } + + ret = MV_CC_CreateHandle(&handle_, device_list.pDeviceInfo[0]); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_CreateHandle failed: {:#x}", ret); + return; + } + + ret = MV_CC_OpenDevice(handle_); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_OpenDevice failed: {:#x}", ret); + return; + } + + set_enum_value("BalanceWhiteAuto", MV_BALANCEWHITE_AUTO_CONTINUOUS); + set_enum_value("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF); + set_enum_value("GainAuto", MV_GAIN_MODE_OFF); + set_float_value("ExposureTime", exposure_us_); + set_float_value("Gain", gain_); + MV_CC_SetFrameRate(handle_, 150); + + ret = MV_CC_StartGrabbing(handle_); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_StartGrabbing failed: {:#x}", ret); + return; + } + + capture_thread_ = std::thread{[this] { + tools::logger()->info("HikRobot's capture thread started."); + + capturing_ = true; + + MV_FRAME_OUT raw; + MV_CC_PIXEL_CONVERT_PARAM cvt_param; + + while (!capture_quit_) { + std::this_thread::sleep_for(1ms); + + unsigned int ret; + unsigned int nMsec = 100; + + ret = MV_CC_GetImageBuffer(handle_, &raw, nMsec); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_GetImageBuffer failed: {:#x}", ret); + break; + } + + auto timestamp = std::chrono::steady_clock::now(); + cv::Mat img(cv::Size(raw.stFrameInfo.nWidth, raw.stFrameInfo.nHeight), CV_8U, raw.pBufAddr); + + cvt_param.nWidth = raw.stFrameInfo.nWidth; + cvt_param.nHeight = raw.stFrameInfo.nHeight; + + cvt_param.pSrcData = raw.pBufAddr; + cvt_param.nSrcDataLen = raw.stFrameInfo.nFrameLen; + cvt_param.enSrcPixelType = raw.stFrameInfo.enPixelType; + + cvt_param.pDstBuffer = img.data; + cvt_param.nDstBufferSize = img.total() * img.elemSize(); + cvt_param.enDstPixelType = PixelType_Gvsp_BGR8_Packed; + + // ret = MV_CC_ConvertPixelType(handle_, &cvt_param); + const auto & frame_info = raw.stFrameInfo; + auto pixel_type = frame_info.enPixelType; + cv::Mat dst_image; + const static std::unordered_map type_map = { + {PixelType_Gvsp_BayerGR8, cv::COLOR_BayerGR2RGB}, + {PixelType_Gvsp_BayerRG8, cv::COLOR_BayerRG2RGB}, + {PixelType_Gvsp_BayerGB8, cv::COLOR_BayerGB2RGB}, + {PixelType_Gvsp_BayerBG8, cv::COLOR_BayerBG2RGB}}; + cv::cvtColor(img, dst_image, type_map.at(pixel_type)); + img = dst_image; + + queue_.push({img, timestamp}); + + ret = MV_CC_FreeImageBuffer(handle_, &raw); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_FreeImageBuffer failed: {:#x}", ret); + break; + } + } + + capturing_ = false; + tools::logger()->info("HikRobot's capture thread stopped."); + }}; +} + +void HikRobot::capture_stop() +{ + capture_quit_ = true; + if (capture_thread_.joinable()) capture_thread_.join(); + + unsigned int ret; + + ret = MV_CC_StopGrabbing(handle_); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_StopGrabbing failed: {:#x}", ret); + return; + } + + ret = MV_CC_CloseDevice(handle_); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_CloseDevice failed: {:#x}", ret); + return; + } + + ret = MV_CC_DestroyHandle(handle_); + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_DestroyHandle failed: {:#x}", ret); + return; + } +} + +void HikRobot::set_float_value(const std::string & name, double value) +{ + unsigned int ret; + + ret = MV_CC_SetFloatValue(handle_, name.c_str(), value); + + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_SetFloatValue(\"{}\", {}) failed: {:#x}", name, value, ret); + return; + } +} + +void HikRobot::set_enum_value(const std::string & name, unsigned int value) +{ + unsigned int ret; + + ret = MV_CC_SetEnumValue(handle_, name.c_str(), value); + + if (ret != MV_OK) { + tools::logger()->warn("MV_CC_SetEnumValue(\"{}\", {}) failed: {:#x}", name, value, ret); + return; + } +} + +void HikRobot::set_vid_pid(const std::string & vid_pid) +{ + auto index = vid_pid.find(':'); + if (index == std::string::npos) { + tools::logger()->warn("Invalid vid_pid: \"{}\"", vid_pid); + return; + } + + auto vid_str = vid_pid.substr(0, index); + auto pid_str = vid_pid.substr(index + 1); + + try { + vid_ = std::stoi(vid_str, 0, 16); + pid_ = std::stoi(pid_str, 0, 16); + } catch (const std::exception &) { + tools::logger()->warn("Invalid vid_pid: \"{}\"", vid_pid); + } +} + +void HikRobot::reset_usb() const +{ + if (vid_ == -1 || pid_ == -1) return; + + // https://github.com/ralight/usb-reset/blob/master/usb-reset.c + auto handle = libusb_open_device_with_vid_pid(NULL, vid_, pid_); + if (!handle) { + tools::logger()->warn("Unable to open usb!"); + return; + } + + if (libusb_reset_device(handle)) + tools::logger()->warn("Unable to reset usb!"); + else + tools::logger()->info("Reset usb successfully :)"); + + libusb_close(handle); +} + +} // namespace io \ No newline at end of file diff --git a/io/hikrobot/hikrobot.hpp b/io/hikrobot/hikrobot.hpp new file mode 100644 index 0000000..5ac0e8e --- /dev/null +++ b/io/hikrobot/hikrobot.hpp @@ -0,0 +1,56 @@ +#ifndef IO__HIKROBOT_HPP +#define IO__HIKROBOT_HPP + +#include +#include +#include +#include +#include + +#include "MvCameraControl.h" +#include "io/camera.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace io +{ +class HikRobot : public CameraBase +{ +public: + HikRobot(double exposure_ms, double gain, const std::string & vid_pid); + ~HikRobot() override; + void read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) override; + +private: + struct CameraData + { + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + }; + + double exposure_us_; + double gain_; + + std::thread daemon_thread_; + std::atomic daemon_quit_; + + void * handle_; + std::thread capture_thread_; + std::atomic capturing_; + std::atomic capture_quit_; + tools::ThreadSafeQueue queue_; + + int vid_, pid_; + + void capture_start(); + void capture_stop(); + + void set_float_value(const std::string & name, double value); + void set_enum_value(const std::string & name, unsigned int value); + + void set_vid_pid(const std::string & vid_pid); + void reset_usb() const; +}; + +} // namespace io + +#endif // IO__HIKROBOT_HPP \ No newline at end of file diff --git a/io/hikrobot/include/CameraParams.h b/io/hikrobot/include/CameraParams.h new file mode 100644 index 0000000..8856b5b --- /dev/null +++ b/io/hikrobot/include/CameraParams.h @@ -0,0 +1,1341 @@ + +#ifndef _MV_CAMERA_PARAMS_H_ +#define _MV_CAMERA_PARAMS_H_ + +#include "PixelType.h" + +#ifndef __cplusplus +typedef char bool; +#define true 1 +#define false 0 +#endif + +/// \~chinese 排序方式 \~english The Method of Sorting +typedef enum _MV_SORT_METHOD_ +{ + SortMethod_SerialNumber = + 0, ///< \~chinese 按序列号排序 \~english Sorting by SerialNumber + SortMethod_UserID = + 1, ///< \~chinese 按用户自定义名字排序 \~english Sorting by UserID + SortMethod_CurrentIP_ASC = + 2, ///< \~chinese 按当前IP地址排序(升序) \~english Sorting by current IP(Ascending) + SortMethod_CurrentIP_DESC = + 3, ///< \~chinese 按当前IP地址排序(降序) \~english Sorting by current IP(Descending) + +} MV_SORT_METHOD; + +/// \~chinese GigE设备信息 \~english GigE device info +typedef struct _MV_GIGE_DEVICE_INFO_ +{ + unsigned int + nIpCfgOption; ///< [OUT] \~chinese IP配置选项 \~english IP Configuration Options + unsigned int + nIpCfgCurrent; ///< [OUT] \~chinese 当前IP配置 \~english IP Configuration + unsigned int nCurrentIp; ///< [OUT] \~chinese 当前IP地址 \~english Current Ip + unsigned int + nCurrentSubNetMask; ///< [OUT] \~chinese 当前子网掩码 \~english Curtent Subnet Mask + unsigned int + nDefultGateWay; ///< [OUT] \~chinese 当前网关 \~english Current Gateway + unsigned char + chManufacturerName[32]; ///< [OUT] \~chinese 制造商名称 \~english Manufacturer Name + unsigned char chModelName[32]; ///< [OUT] \~chinese 型号名称 \~english Model Name + unsigned char + chDeviceVersion[32]; ///< [OUT] \~chinese 设备版本 \~english Device Version + unsigned char chManufacturerSpecificInfo + [48]; ///< [OUT] \~chinese 制造商的具体信息 \~english Manufacturer Specific Information + unsigned char + chSerialNumber[16]; ///< [OUT] \~chinese 序列号 \~english Serial Number + unsigned char + chUserDefinedName[16]; ///< [OUT] \~chinese 用户自定义名称 \~english User Defined Name + unsigned int nNetExport; ///< [OUT] \~chinese 网口IP地址 \~english NetWork IP Address + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MV_GIGE_DEVICE_INFO; + +///< \~chinese 最大的数据信息大小 \~english Maximum data information size +#define INFO_MAX_BUFFER_SIZE 64 + +/// \~chinese USB设备信息 \~english USB device info +typedef struct _MV_USB3_DEVICE_INFO_ +{ + unsigned char + CrtlInEndPoint; ///< [OUT] \~chinese 控制输入端点 \~english Control input endpoint + unsigned char + CrtlOutEndPoint; ///< [OUT] \~chinese 控制输出端点 \~english Control output endpoint + unsigned char StreamEndPoint; ///< [OUT] \~chinese 流端点 \~english Flow endpoint + unsigned char EventEndPoint; ///< [OUT] \~chinese 事件端点 \~english Event endpoint + unsigned short idVendor; ///< [OUT] \~chinese 供应商ID号 \~english Vendor ID Number + unsigned short idProduct; ///< [OUT] \~chinese 产品ID号 \~english Device ID Number + unsigned int nDeviceNumber; ///< [OUT] \~chinese 设备索引号 \~english Device Number + unsigned char chDeviceGUID + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 设备GUID号 \~english Device GUID Number + unsigned char chVendorName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 供应商名字 \~english Vendor Name + unsigned char chModelName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 型号名字 \~english Model Name + unsigned char chFamilyName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 家族名字 \~english Family Name + unsigned char chDeviceVersion + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 设备版本 \~english Device Version + unsigned char chManufacturerName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 制造商名字 \~english Manufacturer Name + unsigned char chSerialNumber + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 序列号 \~english Serial Number + unsigned char chUserDefinedName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 用户自定义名字 \~english User Defined Name + unsigned int nbcdUSB; ///< [OUT] \~chinese 支持的USB协议 \~english Support USB Protocol + unsigned int nDeviceAddress; ///< [OUT] \~chinese 设备地址 \~english Device Address + + unsigned int nReserved[2]; ///< \~chinese 预留 \~english Reserved + +} MV_USB3_DEVICE_INFO; + +/// \~chinese CameraLink设备信息 \~english CameraLink device info +typedef struct _MV_CamL_DEV_INFO_ +{ + unsigned char + chPortID[INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 端口号 \~english Port ID + unsigned char chModelName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 型号名字 \~english Model Name + unsigned char chFamilyName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 名称 \~english Family Name + unsigned char chDeviceVersion + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 设备版本 \~english Device Version + unsigned char chManufacturerName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 制造商名字 \~english Manufacturer Name + unsigned char chSerialNumber + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 序列号 \~english Serial Number + + unsigned int nReserved[38]; ///< \~chinese 预留 \~english Reserved + +} MV_CamL_DEV_INFO; + +///< \~chinese 设备传输层协议类型 \~english Device Transport Layer Protocol Type +#define MV_UNKNOW_DEVICE \ + 0x00000000 ///< \~chinese 未知设备类型,保留意义 \~english Unknown Device Type, Reserved +#define MV_GIGE_DEVICE 0x00000001 ///< \~chinese GigE设备 \~english GigE Device +#define MV_1394_DEVICE \ + 0x00000002 ///< \~chinese 1394-a/b 设备 \~english 1394-a/b Device +#define MV_USB_DEVICE 0x00000004 ///< \~chinese USB 设备 \~english USB Device +#define MV_CAMERALINK_DEVICE \ + 0x00000008 ///< \~chinese CameraLink设备 \~english CameraLink Device +#define MV_VIR_GIGE_DEVICE \ + 0x00000010 ///< \~chinese 虚拟GigE设备 \~english Virtual GigE Device +#define MV_VIR_USB_DEVICE \ + 0x00000020 ///< \~chinese 虚拟USB设备 \~english Virtual USB Device +#define MV_GENTL_GIGE_DEVICE \ + 0x00000040 ///< \~chinese 自研网卡下GigE设备 \~english GenTL GigE Device + +/// \~chinese 设备信息 \~english Device info +typedef struct _MV_CC_DEVICE_INFO_ +{ + unsigned short nMajorVer; ///< [OUT] \~chinese 主要版本 \~english Major Version + unsigned short nMinorVer; ///< [OUT] \~chinese 次要版本 \~english Minor Version + unsigned int nMacAddrHigh; ///< [OUT] \~chinese 高MAC地址 \~english High MAC Address + unsigned int nMacAddrLow; ///< [OUT] \~chinese 低MAC地址 \~english Low MAC Address + unsigned int + nTLayerType; ///< [OUT] \~chinese 设备传输层协议类型 \~english Device Transport Layer Protocol Type + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + + union { + MV_GIGE_DEVICE_INFO + stGigEInfo; ///< [OUT] \~chinese GigE设备信息 \~english GigE Device Info + MV_USB3_DEVICE_INFO + stUsb3VInfo; ///< [OUT] \~chinese USB设备信息 \~english USB Device Info + MV_CamL_DEV_INFO + stCamLInfo; ///< [OUT] \~chinese CameraLink设备信息 \~english CameraLink Device Info + // more ... + } SpecialInfo; + +} MV_CC_DEVICE_INFO; + +///< \~chinese 最多支持的传输层实例个数 \~english The maximum number of supported transport layer instances +#define MV_MAX_TLS_NUM 8 +///< \~chinese 最大支持的设备个数 \~english The maximum number of supported devices +#define MV_MAX_DEVICE_NUM 256 + +/// \~chinese 设备信息列表 \~english Device Information List +typedef struct _MV_CC_DEVICE_INFO_LIST_ +{ + unsigned int + nDeviceNum; ///< [OUT] \~chinese 在线设备数量 \~english Online Device Number + MV_CC_DEVICE_INFO * pDeviceInfo + [MV_MAX_DEVICE_NUM]; ///< [OUT] \~chinese 支持最多256个设备 \~english Support up to 256 devices + +} MV_CC_DEVICE_INFO_LIST; + +/// \~chinese 通过GenTL枚举到的接口信息 \~english Interface Information with GenTL +typedef struct _MV_GENTL_IF_INFO_ +{ + unsigned char chInterfaceID + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese GenTL接口ID \~english Interface ID + unsigned char chTLType + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 传输层类型 \~english GenTL Type + unsigned char chDisplayName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese Interface显示名称 \~english Display Name + unsigned int + nCtiIndex; ///< [OUT] \~chinese GenTL的cti文件索引 \~english The Index of Cti Files + + unsigned int nReserved[8]; ///< \~chinese 预留 \~english Reserved + +} MV_GENTL_IF_INFO; + +///< \~chinese 最大支持的GenTL接口数量 \~english The maximum number of GenTL interface supported +#define MV_MAX_GENTL_IF_NUM 256 + +/// \~chinese 通过GenTL枚举到的接口信息列表 \~english Inferface Information List with GenTL +typedef struct _MV_GENTL_IF_INFO_LIST_ +{ + unsigned int + nInterfaceNum; ///< [OUT] \~chinese 在线接口数量 \~english Online Inferface Number + MV_GENTL_IF_INFO * pIFInfo + [MV_MAX_GENTL_IF_NUM]; ///< [OUT] \~chinese 支持最多256个接口 \~english Support up to 256 inferfaces + +} MV_GENTL_IF_INFO_LIST; + +/// \~chinese 通过GenTL枚举到的设备信息 \~english Device Information with GenTL +typedef struct _MV_GENTL_DEV_INFO_ +{ + unsigned char chInterfaceID + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese GenTL接口ID \~english Interface ID + unsigned char chDeviceID + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 设备ID \~english Device ID + unsigned char chVendorName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 供应商名字 \~english Vendor Name + unsigned char chModelName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 型号名字 \~english Model Name + unsigned char chTLType + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 传输层类型 \~english GenTL Type + unsigned char chDisplayName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 设备显示名称 \~english Display Name + unsigned char chUserDefinedName + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 用户自定义名字 \~english User Defined Name + unsigned char chSerialNumber + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 序列号 \~english Serial Number + unsigned char chDeviceVersion + [INFO_MAX_BUFFER_SIZE]; ///< [OUT] \~chinese 设备版本号 \~english Device Version + unsigned int + nCtiIndex; ///< [OUT] \~chinese GenTL的cti文件索引 \~english The Index of Cti Files + + unsigned int nReserved[8]; ///< \~chinese 预留 \~english Reserved + +} MV_GENTL_DEV_INFO; + +///< \~chinese 最大支持的GenTL设备数量 \~english The maximum number of GenTL devices supported +#define MV_MAX_GENTL_DEV_NUM 256 + +/// \~chinese 通过GenTL枚举到的设备信息列表 \~english Device Information List with GenTL +typedef struct _MV_GENTL_DEV_INFO_LIST_ +{ + unsigned int + nDeviceNum; ///< [OUT] \~chinese 在线设备数量 \~english Online Device Number + MV_GENTL_DEV_INFO * pDeviceInfo + [MV_MAX_GENTL_DEV_NUM]; ///< [OUT] \~chinese 支持最多256个设备 \~english Support up to 256 devices + +} MV_GENTL_DEV_INFO_LIST; + +/// \~chinese 设备的访问模式 \~english Device Access Mode +#define MV_ACCESS_Exclusive \ + 1 /// \~chinese 独占权限,其他APP只允许读CCP寄存器 \~english Exclusive authority, other APP is only allowed to read the CCP register +#define MV_ACCESS_ExclusiveWithSwitch \ + 2 /// \~chinese 可以从5模式下抢占权限,然后以独占权限打开 \~english You can seize the authority from the 5 mode, and then open with exclusive authority +#define MV_ACCESS_Control \ + 3 /// \~chinese 控制权限,其他APP允许读所有寄存器 \~english Control authority, allows other APP reading all registers +#define MV_ACCESS_ControlWithSwitch \ + 4 /// \~chinese 可以从5的模式下抢占权限,然后以控制权限打开 \~english You can seize the authority from the 5 mode, and then open with control authority +#define MV_ACCESS_ControlSwitchEnable \ + 5 /// \~chinese 以可被抢占的控制权限打开 \~english Open with seized control authority +#define MV_ACCESS_ControlSwitchEnableWithKey \ + 6 /// \~chinese 可以从5的模式下抢占权限,然后以可被抢占的控制权限打开 \~english You can seize the authority from the 5 mode, and then open with seized control authority +#define MV_ACCESS_Monitor \ + 7 /// \~chinese 读模式打开设备,适用于控制权限下 \~english Open with read mode and is available under control authority + +/// \~chinese Chunk内容 \~english The content of ChunkData +typedef struct _MV_CHUNK_DATA_CONTENT_ +{ + unsigned char * pChunkData; ///< [OUT] \~chinese Chunk数据 \~english Chunk Data + unsigned int nChunkID; ///< [OUT] \~chinese Chunk ID \~english Chunk ID + unsigned int nChunkLen; ///< [OUT] \~chinese Chunk的长度 \~english Chunk Length + + unsigned int nReserved[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CHUNK_DATA_CONTENT; + +/// \~chinese 输出帧的信息 \~english Output Frame Information +typedef struct _MV_FRAME_OUT_INFO_EX_ +{ + unsigned short + nWidth; ///< [OUT] \~chinese 图像宽(最大65535,超出请用nExtendWidth) \~english Image Width (over 65535, use nExtendWidth) + unsigned short + nHeight; ///< [OUT] \~chinese 图像高(最大65535,超出请用nExtendHeight) \~english Image Height(over 65535, use nExtendHeight) + enum MvGvspPixelType + enPixelType; ///< [OUT] \~chinese 像素格式 \~english Pixel Type + + unsigned int nFrameNum; ///< [OUT] \~chinese 帧号 \~english Frame Number + unsigned int + nDevTimeStampHigh; ///< [OUT] \~chinese 时间戳高32位 \~english Timestamp high 32 bits + unsigned int + nDevTimeStampLow; ///< [OUT] \~chinese 时间戳低32位 \~english Timestamp low 32 bits + unsigned int + nReserved0; ///< [OUT] \~chinese 保留,8字节对齐 \~english Reserved, 8-byte aligned + int64_t + nHostTimeStamp; ///< [OUT] \~chinese 主机生成的时间戳 \~english Host-generated timestamp + + unsigned int nFrameLen; ///< [OUT] \~chinese 帧的长度 \~english The Length of Frame + + /// \~chinese 设备水印时标 \~english Device frame-specific time scale + unsigned int nSecondCount; ///< [OUT] \~chinese 秒数 \~english The Seconds + unsigned int + nCycleCount; ///< [OUT] \~chinese 周期数 \~english The Count of Cycle + unsigned int + nCycleOffset; ///< [OUT] \~chinese 周期偏移量 \~english The Offset of Cycle + + float fGain; ///< [OUT] \~chinese 增益 \~english Gain + float fExposureTime; ///< [OUT] \~chinese 曝光时间 \~english Exposure Time + unsigned int + nAverageBrightness; ///< [OUT] \~chinese 平均亮度 \~english Average brightness + + /// \~chinese 白平衡相关 \~english White balance + unsigned int nRed; ///< [OUT] \~chinese 红色 \~english Red + unsigned int nGreen; ///< [OUT] \~chinese 绿色 \~english Green + unsigned int nBlue; ///< [OUT] \~chinese 蓝色 \~english Blue + + unsigned int nFrameCounter; ///< [OUT] \~chinese 总帧数 \~english Frame Counter + unsigned int + nTriggerIndex; ///< [OUT] \~chinese 触发计数 \~english Trigger Counting + + unsigned int nInput; ///< [OUT] \~chinese 输入 \~english Input + unsigned int nOutput; ///< [OUT] \~chinese 输出 \~english Output + + /// \~chinese ROI区域 \~english ROI Region + unsigned short nOffsetX; ///< [OUT] \~chinese 水平偏移量 \~english OffsetX + unsigned short nOffsetY; ///< [OUT] \~chinese 垂直偏移量 \~english OffsetY + unsigned short + nChunkWidth; ///< [OUT] \~chinese Chunk宽 \~english The Width of Chunk + unsigned short + nChunkHeight; ///< [OUT] \~chinese Chunk高 \~english The Height of Chunk + + unsigned int + nLostPacket; ///< [OUT] \~chinese 本帧丢包数 \~english Lost Packet Number In This Frame + + unsigned int + nUnparsedChunkNum; ///< [OUT] \~chinese 未解析的Chunkdata个数 \~english Unparsed Chunk Number + union { + MV_CHUNK_DATA_CONTENT * + pUnparsedChunkContent; ///< [OUT] \~chinese 未解析的Chunk \~english Unparsed Chunk Content + int64_t nAligning; ///< [OUT] \~chinese 校准 \~english Aligning + } UnparsedChunkList; + + unsigned int nExtendWidth; ///< [OUT] \~chinese 图像宽(扩展变量) \~english Image Width + unsigned int nExtendHeight; ///< [OUT] \~chinese 图像高(扩展变量) \~english Image Height + + unsigned int nReserved[34]; ///< \~chinese 预留 \~english Reserved + +} MV_FRAME_OUT_INFO_EX; + +/// \~chinese 图像结构体,输出图像地址及图像信息 \~english Image Struct, output the pointer of Image and the information of the specific image +typedef struct _MV_FRAME_OUT_ +{ + unsigned char * pBufAddr; ///< [OUT] \~chinese 图像指针地址 \~english pointer of image + MV_FRAME_OUT_INFO_EX + stFrameInfo; ///< [OUT] \~chinese 图像信息 \~english information of the specific image + + unsigned int nRes[16]; ///< \~chinese 预留 \~english Reserved + +} MV_FRAME_OUT; + +/// \~chinese 取流策略 \~english The strategy of Grabbing +typedef enum _MV_GRAB_STRATEGY_ +{ + MV_GrabStrategy_OneByOne = + 0, ///< \~chinese 从旧到新一帧一帧的获取图像 \~english Grab One By One + MV_GrabStrategy_LatestImagesOnly = + 1, ///< \~chinese 获取列表中最新的一帧图像 \~english Grab The Latest Image + MV_GrabStrategy_LatestImages = + 2, ///< \~chinese 获取列表中最新的图像 \~english Grab The Latest Images + MV_GrabStrategy_UpcomingImage = + 3, ///< \~chinese 等待下一帧图像 \~english Grab The Upcoming Image + +} MV_GRAB_STRATEGY; + +/// \~chinese 网络传输的相关信息 \~english Network transmission information +typedef struct _MV_NETTRANS_INFO_ +{ + int64_t + nReceiveDataSize; ///< [OUT] \~chinese 已接收数据大小[Start和Stop之间] \~english Received Data Size + int + nThrowFrameCount; ///< [OUT] \~chinese 丢帧数量 \~english Throw frame number + unsigned int + nNetRecvFrameCount; ///< [OUT] \~chinese 已接收的帧数 \~english Received Frame Count + int64_t + nRequestResendPacketCount; ///< [OUT] \~chinese 请求重发包数 \~english Request Resend Packet Count + int64_t + nResendPacketCount; ///< [OUT] \~chinese 重发包数 \~english Resend Packet Count + +} MV_NETTRANS_INFO; + +/// \~chinese 信息类型 \~english Information Type +#define MV_MATCH_TYPE_NET_DETECT \ + 0x00000001 ///< \~chinese 网络流量和丢包信息 \~english Network traffic and packet loss information +#define MV_MATCH_TYPE_USB_DETECT \ + 0x00000002 ///< \~chinese host接收到来自U3V设备的字节总数 \~english The total number of bytes host received from U3V device + +/// \~chinese 全匹配的一种信息结构体 \~english A fully matched information structure +typedef struct _MV_ALL_MATCH_INFO_ +{ + unsigned int + nType; ///< [IN] \~chinese 需要输出的信息类型,e.g. MV_MATCH_TYPE_NET_DETECT \~english Information type need to output ,e.g. MV_MATCH_TYPE_NET_DETECT + void * + pInfo; ///< [OUT] \~chinese 输出的信息缓存,由调用者分配 \~english Output information cache, which is allocated by the caller + unsigned int + nInfoSize; ///< [IN] \~chinese 信息缓存的大小 \~english Information cache size + +} MV_ALL_MATCH_INFO; + +/// \~chinese 网络流量和丢包信息反馈结构体,对应类型为 MV_MATCH_TYPE_NET_DETECT \~english Network traffic and packet loss feedback structure, the corresponding type is MV_MATCH_TYPE_NET_DETECT +typedef struct _MV_MATCH_INFO_NET_DETECT_ +{ + int64_t + nReceiveDataSize; ///< [OUT] \~chinese 已接收数据大小[Start和Stop之间] \~english Received data size + int64_t + nLostPacketCount; ///< [OUT] \~chinese 丢失的包数量 \~english Number of packets lost + unsigned int + nLostFrameCount; ///< [OUT] \~chinese 丢帧数量 \~english Number of frames lost + unsigned int + nNetRecvFrameCount; ///< [OUT] \~chinese 保留 \~english Received Frame Count + int64_t + nRequestResendPacketCount; ///< [OUT] \~chinese 请求重发包数 \~english Request Resend Packet Count + int64_t + nResendPacketCount; ///< [OUT] \~chinese 重发包数 \~english Resend Packet Count + +} MV_MATCH_INFO_NET_DETECT; + +/// \~chinese host收到从u3v设备端的总字节数,对应类型为 MV_MATCH_TYPE_USB_DETECT \~english The total number of bytes host received from the u3v device side, the corresponding type is MV_MATCH_TYPE_USB_DETECT +typedef struct _MV_MATCH_INFO_USB_DETECT_ +{ + int64_t + nReceiveDataSize; ///< [OUT] \~chinese 已接收数据大小 [Open和Close之间] \~english Received data size + unsigned int + nReceivedFrameCount; ///< [OUT] \~chinese 已收到的帧数 \~english Number of frames received + unsigned int + nErrorFrameCount; ///< [OUT] \~chinese 错误帧数 \~english Number of error frames + + unsigned int + nReserved[2]; ///< \~chinese 保留 \~english Reserved + +} MV_MATCH_INFO_USB_DETECT; + +/// \~chinese 显示帧信息 \~english Display frame information +typedef struct _MV_DISPLAY_FRAME_INFO_ +{ + void * hWnd; ///< [IN] \~chinese 窗口句柄 \~english HWND + unsigned char * pData; ///< [IN] \~chinese 显示的数据 \~english Data Buffer + unsigned int nDataLen; ///< [IN] \~chinese 数据长度 \~english Data Size + unsigned short nWidth; ///< [IN] \~chinese 图像宽 \~english Width + unsigned short nHeight; ///< [IN] \~chinese 图像高 \~english Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned int nRes[4]; ///< \~chinese 保留 \~english Reserved + +} MV_DISPLAY_FRAME_INFO; + +typedef struct _MV_DISPLAY_FRAME_INFO_EX_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽 \~english Width + unsigned int nHeight; ///< [IN] \~chinese 图像高 \~english Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel type + + unsigned char * + pImageBuf; ///< [IN] \~chinese 输入图像缓存 \~english Input image buffer + unsigned int + nImageBufLen; ///< [IN] \~chinese 输入图像长度 \~english Input image length + + unsigned int nRes[4]; ///< \~chinese 保留 \~english Reserved + +} MV_DISPLAY_FRAME_INFO_EX; + +/// \~chinese 保存的3D数据格式 \~english The saved format for 3D data +enum MV_SAVE_POINT_CLOUD_FILE_TYPE +{ + MV_PointCloudFile_Undefined = + 0, ///< \~chinese 未定义的点云格式 \~english Undefined point cloud format + MV_PointCloudFile_PLY = + 1, ///< \~chinese PLY点云格式 \~english The point cloud format named PLY + MV_PointCloudFile_CSV = + 2, ///< \~chinese CSV点云格式 \~english The point cloud format named CSV + MV_PointCloudFile_OBJ = + 3, ///< \~chinese OBJ点云格式 \~english The point cloud format named OBJ + +}; + +/// \~chinese 保存3D数据到缓存 \~english Save 3D data to buffer +typedef struct _MV_SAVE_POINT_CLOUD_PARAM_ +{ + unsigned int + nLinePntNum; ///< [IN] \~chinese 行点数,即图像宽 \~english The number of points in each row,which is the width of the image + unsigned int + nLineNum; ///< [IN] \~chinese 行数,即图像高 \~english The number of rows,which is the height of the image + + enum MvGvspPixelType + enSrcPixelType; ///< [IN] \~chinese 输入数据的像素格式 \~english The pixel format of the input data + unsigned char * pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + unsigned char * + pDstBuf; ///< [OUT] \~chinese 输出像素数据缓存 \~english Output pixel data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小(nLinePntNum * nLineNum * (16*3 + 4) + 2048) \~english Output buffer size provided(nLinePntNum * nLineNum * (16*3 + 4) + 2048) + unsigned int + nDstBufLen; ///< [OUT] \~chinese 输出像素数据缓存长度 \~english Output pixel data buffer size + MV_SAVE_POINT_CLOUD_FILE_TYPE + enPointCloudFileType; ///< [IN] \~chinese 提供输出的点云文件类型 \~english Output point data file type provided + + unsigned int nReserved[8]; ///< \~chinese 保留字段 \~english Reserved + +} MV_SAVE_POINT_CLOUD_PARAM; + +/// \~chinese 保存图片格式 \~english Save image type +enum MV_SAVE_IAMGE_TYPE +{ + MV_Image_Undefined = + 0, ///< \~chinese 未定义的图像格式 \~english Undefined Image Type + MV_Image_Bmp = 1, ///< \~chinese BMP图像格式 \~english BMP Image Type + MV_Image_Jpeg = 2, ///< \~chinese JPEG图像格式 \~english Jpeg Image Type + MV_Image_Png = 3, ///< \~chinese PNG图像格式 \~english Png Image Type + MV_Image_Tif = 4, ///< \~chinese TIFF图像格式 \~english TIFF Image Type + +}; + +/// \~chinese 图片保存参数 \~english Save Image Parameters +typedef struct _MV_SAVE_IMAGE_PARAM_EX3_ +{ + unsigned char * pData; ///< [IN] \~chinese 输入数据缓存 \~english Input Data Buffer + unsigned int nDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input Data length + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 输入数据的像素格式 \~english Input Data Pixel Format + unsigned int nWidth; ///< [IN] \~chinese 图像宽 \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高 \~english Image Height + + unsigned char * + pImageBuffer; ///< [OUT] \~chinese 输出图片缓存 \~english Output Image Buffer + unsigned int nImageLen; ///< [OUT] \~chinese 输出图片长度 \~english Output Image length + unsigned int + nBufferSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Output buffer size provided + enum MV_SAVE_IAMGE_TYPE + enImageType; ///< [IN] \~chinese 输出图片格式 \~english Output Image Format + unsigned int + nJpgQuality; ///< [IN] \~chinese JPG编码质量(50-99],其它格式无效 \~english Encoding quality(50-99],Other formats are invalid + + unsigned int + iMethodValue; ///< [IN] \~chinese 插值方法 0-快速 1-均衡 2-最优(其它值默认为最优) \~english Bayer interpolation method 0-Fast 1-Equilibrium 2-Optimal + + unsigned int nReserved[3]; ///< \~chinese 预留 \~english Reserved + +} MV_SAVE_IMAGE_PARAM_EX3; + +// 保存图片到文件参数 +typedef struct _MV_SAVE_IMAGE_TO_FILE_PARAM_EX_ +{ + unsigned int nWidth; // [IN] 图像宽 + unsigned int nHeight; // [IN] 图像高 + MvGvspPixelType enPixelType; // [IN] 输入数据的像素格式 + unsigned char * pData; // [IN] 输入数据缓存 + unsigned int nDataLen; // [IN] 输入数据大小 + + MV_SAVE_IAMGE_TYPE enImageType; // [IN] 输入图片格式 + char * pcImagePath; // [IN] 输入文件路径 + + unsigned int nQuality; // [IN] JPG编码质量(50-99],PNG编码质量[0-9] + int iMethodValue; // [IN] 插值方法 0-快速 1-均衡 2-最优(其它值默认为最优) + unsigned int nReserved[8]; + +} MV_SAVE_IMAGE_TO_FILE_PARAM_EX; + +/// \~chinese 旋转角度 \~english Rotation angle +typedef enum _MV_IMG_ROTATION_ANGLE_ +{ + MV_IMAGE_ROTATE_90 = 1, + MV_IMAGE_ROTATE_180 = 2, + MV_IMAGE_ROTATE_270 = 3, + +} MV_IMG_ROTATION_ANGLE; + +/// \~chinese 图像旋转结构体 \~english Rotate image structure +typedef struct _MV_CC_ROTATE_IMAGE_PARAM_T_ +{ + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + unsigned int nWidth; ///< [IN][OUT] \~chinese 图像宽 \~english Width + unsigned int nHeight; ///< [IN][OUT] \~chinese 图像高 \~english Height + + unsigned char * pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + + MV_IMG_ROTATION_ANGLE + enRotationAngle; ///< [IN] \~chinese 旋转角度 \~english Rotation angle + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_ROTATE_IMAGE_PARAM; + +/// \~chinese 翻转类型 \~english Flip type +typedef enum _MV_IMG_FLIP_TYPE_ +{ + MV_FLIP_VERTICAL = 1, + MV_FLIP_HORIZONTAL = 2, + +} MV_IMG_FLIP_TYPE; + +/// \~chinese 图像翻转结构体 \~english Flip image structure +typedef struct _MV_CC_FLIP_IMAGE_PARAM_T_ +{ + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + unsigned int nWidth; ///< [IN] \~chinese 图像宽 \~english Width + unsigned int nHeight; ///< [IN] \~chinese 图像高 \~english Height + + unsigned char * pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + + MV_IMG_FLIP_TYPE enFlipType; ///< [IN] \~chinese 翻转类型 \~english Flip type + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_FLIP_IMAGE_PARAM; + +/// \~chinese 像素转换结构体 \~english Pixel convert structure +typedef struct _MV_CC_PIXEL_CONVERT_PARAM_EX_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽 \~english Width + unsigned int nHeight; ///< [IN] \~chinese 图像高 \~english Height + + enum MvGvspPixelType + enSrcPixelType; ///< [IN] \~chinese 源像素格式 \~english Source pixel format + unsigned char * pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + enum MvGvspPixelType + enDstPixelType; ///< [IN] \~chinese 目标像素格式 \~english Destination pixel format + unsigned char * + pDstBuffer; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int nDstLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + unsigned int + nDstBufferSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + + unsigned int nRes[4]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_PIXEL_CONVERT_PARAM_EX; + +/// \~chinese Gamma类型 \~english Gamma type +typedef enum _MV_CC_GAMMA_TYPE_ +{ + MV_CC_GAMMA_TYPE_NONE = 0, ///< \~chinese 不启用 \~english Disable + MV_CC_GAMMA_TYPE_VALUE = 1, ///< \~chinese Gamma值 \~english Gamma value + MV_CC_GAMMA_TYPE_USER_CURVE = + 2, ///< \~chinese Gamma曲线 \~english Gamma curve + ///< \~chinese 8位,长度:256*sizeof(unsigned char) \~english 8bit,length:256*sizeof(unsigned char) + ///< \~chinese 10位,长度:1024*sizeof(unsigned short) \~english 10bit,length:1024*sizeof(unsigned short) + ///< \~chinese 12位,长度:4096*sizeof(unsigned short) \~english 12bit,length:4096*sizeof(unsigned short) + ///< \~chinese 16位,长度:65536*sizeof(unsigned short) \~english 16bit,length:65536*sizeof(unsigned short) + MV_CC_GAMMA_TYPE_LRGB2SRGB = + 3, ///< \~chinese linear RGB to sRGB \~english linear RGB to sRGB + MV_CC_GAMMA_TYPE_SRGB2LRGB = + 4, ///< \~chinese sRGB to linear RGB(仅色彩插值时支持,色彩校正时无效) \~english sRGB to linear RGB + +} MV_CC_GAMMA_TYPE; + +// Gamma信息 +/// \~chinese Gamma信息结构体 \~english Gamma info structure +typedef struct _MV_CC_GAMMA_PARAM_T_ +{ + MV_CC_GAMMA_TYPE enGammaType; ///< [IN] \~chinese Gamma类型 \~english Gamma type + float fGammaValue; ///< [IN] \~chinese Gamma值:0.1 ~ 4.0 \~english Gamma value:0.1 ~ 4.0 + unsigned char * + pGammaCurveBuf; ///< [IN] \~chinese Gamma曲线缓存 \~english Gamma curve buffer + unsigned int + nGammaCurveBufLen; ///< [IN] \~chinese Gamma曲线长度 \~english Gamma curve buffer size + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_GAMMA_PARAM; + +/// \~chinese CCM参数 \~english CCM param +typedef struct _MV_CC_CCM_PARAM_T_ +{ + bool bCCMEnable; ///< [IN] \~chinese 是否启用CCM \~english CCM enable + int nCCMat + [9]; ///< [IN] \~chinese CCM矩阵(-8192~8192) \~english Color correction matrix(-8192~8192) + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_CCM_PARAM; + +/// \~chinese CCM参数 \~english CCM param +typedef struct _MV_CC_CCM_PARAM_EX_T_ +{ + bool bCCMEnable; ///< [IN] \~chinese 是否启用CCM \~english CCM enable + int nCCMat + [9]; ///< [IN] \~chinese CCM矩阵(-65536~65536) \~english Color correction matrix(-65536~65536) + unsigned int + nCCMScale; ///< [IN] \~chinese 量化系数(2的整数幂,最大65536) \~english Quantitative scale(Integer power of 2, <= 65536) + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_CCM_PARAM_EX; + +/// \~chinese 对比度调节结构体 \~english Contrast structure +typedef struct _MV_CC_CONTRAST_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度(最小8) \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度(最小8) \~english Image Height + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据大小 \~english Input data length + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + + unsigned int + nContrastFactor; ///< [IN] \~chinese 对比度值,[1,10000] \~english Contrast factor,[1,10000] + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_CONTRAST_PARAM; + +/// \~chinese 水印信息 \~english Frame-specific information +typedef struct _MV_CC_FRAME_SPEC_INFO_ +{ + /// \~chinese 设备水印时标 \~english Device frame-specific time scale + unsigned int nSecondCount; ///< [OUT] \~chinese 秒数 \~english The Seconds + unsigned int + nCycleCount; ///< [OUT] \~chinese 周期数 \~english The Count of Cycle + unsigned int + nCycleOffset; ///< [OUT] \~chinese 周期偏移量 \~english The Offset of Cycle + + float fGain; ///< [OUT] \~chinese 增益 \~english Gain + float fExposureTime; ///< [OUT] \~chinese 曝光时间 \~english Exposure Time + unsigned int + nAverageBrightness; ///< [OUT] \~chinese 平均亮度 \~english Average brightness + + /// \~chinese 白平衡相关 \~english White balance + unsigned int nRed; ///< [OUT] \~chinese 红色 \~english Red + unsigned int nGreen; ///< [OUT] \~chinese 绿色 \~english Green + unsigned int nBlue; ///< [OUT] \~chinese 蓝色 \~english Blue + + unsigned int nFrameCounter; ///< [OUT] \~chinese 总帧数 \~english Frame Counter + unsigned int + nTriggerIndex; ///< [OUT] \~chinese 触发计数 \~english Trigger Counting + + unsigned int nInput; ///< [OUT] \~chinese 输入 \~english Input + unsigned int nOutput; ///< [OUT] \~chinese 输出 \~english Output + + /// \~chinese ROI区域 \~english ROI Region + unsigned short nOffsetX; ///< [OUT] \~chinese 水平偏移量 \~english OffsetX + unsigned short nOffsetY; ///< [OUT] \~chinese 垂直偏移量 \~english OffsetY + unsigned short + nFrameWidth; ///< [OUT] \~chinese 水印宽 \~english The Width of Chunk + unsigned short + nFrameHeight; ///< [OUT] \~chinese 水印高 \~english The Height of Chunk + + unsigned int nReserved[16]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_FRAME_SPEC_INFO; + +/// \~chinese 无损解码参数 \~english High Bandwidth decode structure +typedef struct _MV_CC_HB_DECODE_PARAM_T_ +{ + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcLen; ///< [IN] \~chinese 输入数据大小 \~english Input data size + + unsigned int nWidth; ///< [OUT] \~chinese 图像宽 \~english Width + unsigned int nHeight; ///< [OUT] \~chinese 图像高 \~english Height + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据大小 \~english Output data size + enum MvGvspPixelType + enDstPixelType; ///< [OUT] \~chinese 输出的像素格式 \~english Output pixel format + + MV_CC_FRAME_SPEC_INFO + stFrameSpecInfo; ///< [OUT] \~chinese 水印信息 \~english Frame Spec Info + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_HB_DECODE_PARAM; + +/// \~chinese 录像格式定义 \~english Record Format Type +typedef enum _MV_RECORD_FORMAT_TYPE_ +{ + MV_FormatType_Undefined = + 0, ///< \~chinese 未定义的录像格式 \~english Undefined Recode Format Type + MV_FormatType_AVI = + 1, ///< \~chinese AVI录像格式 \~english AVI Recode Format Type + +} MV_RECORD_FORMAT_TYPE; + +/// \~chinese 录像参数 \~english Record Parameters +typedef struct _MV_CC_RECORD_PARAM_T_ +{ + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 输入数据的像素格式 \~english Pixel Type + + unsigned short nWidth; ///< [IN] \~chinese 图像宽(2的倍数) \~english Width + unsigned short nHeight; ///< [IN] \~chinese 图像高(2的倍数) \~english Height + + float fFrameRate; ///< [IN] \~chinese 帧率fps(大于1/16) \~english The Rate of Frame + unsigned int nBitRate; ///< [IN] \~chinese 码率kbps(128-16*1024) \~english The Rate of Bitrate + + MV_RECORD_FORMAT_TYPE + enRecordFmtType; ///< [IN] \~chinese 录像格式 \~english Recode Format Type + + char * + strFilePath; ///< [IN] \~chinese 录像文件存放路径(如果路径中存在中文,需转成utf-8) \~english File Path + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_RECORD_PARAM; + +/// \~chinese 传入的图像数据 \~english Input Data +typedef struct _MV_CC_INPUT_FRAME_INFO_T_ +{ + unsigned char * pData; ///< [IN] \~chinese 图像数据指针 \~english Record Data + unsigned int + nDataLen; ///< [IN] \~chinese 图像大小 \~english The Length of Record Data + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_INPUT_FRAME_INFO; + +/// \~chinese 采集模式 \~english Acquisition mode +typedef enum _MV_CAM_ACQUISITION_MODE_ +{ + MV_ACQ_MODE_SINGLE = 0, ///< \~chinese 单帧模式 \~english Single Mode + MV_ACQ_MODE_MUTLI = 1, ///< \~chinese 多帧模式 \~english Multi Mode + MV_ACQ_MODE_CONTINUOUS = 2, ///< \~chinese 持续采集模式 \~english Continuous Mode + +} MV_CAM_ACQUISITION_MODE; + +/// \~chinese 增益模式 \~english Gain Mode +typedef enum _MV_CAM_GAIN_MODE_ +{ + MV_GAIN_MODE_OFF = 0, ///< \~chinese 关闭 \~english Single Mode + MV_GAIN_MODE_ONCE = 1, ///< \~chinese 一次 \~english Multi Mode + MV_GAIN_MODE_CONTINUOUS = + 2, ///< \~chinese 连续 \~english Continuous Mode + +} MV_CAM_GAIN_MODE; + +/// \~chinese 曝光模式 \~english Exposure Mode +typedef enum _MV_CAM_EXPOSURE_MODE_ +{ + MV_EXPOSURE_MODE_TIMED = 0, ///< \~chinese 时间 \~english Timed + MV_EXPOSURE_MODE_TRIGGER_WIDTH = + 1, ///< \~chinese 触发脉冲宽度 \~english TriggerWidth +} MV_CAM_EXPOSURE_MODE; + +/// \~chinese 自动曝光模式 \~english Auto Exposure Mode +typedef enum _MV_CAM_EXPOSURE_AUTO_MODE_ +{ + MV_EXPOSURE_AUTO_MODE_OFF = 0, ///< \~chinese 关闭 \~english Off + MV_EXPOSURE_AUTO_MODE_ONCE = 1, ///< \~chinese 一次 \~english Once + MV_EXPOSURE_AUTO_MODE_CONTINUOUS = + 2, ///< \~chinese 连续 \~english Continuous + +} MV_CAM_EXPOSURE_AUTO_MODE; + +/// \~chinese 触发模式 \~english Trigger Mode +typedef enum _MV_CAM_TRIGGER_MODE_ +{ + MV_TRIGGER_MODE_OFF = 0, ///< \~chinese 关闭 \~english Off + MV_TRIGGER_MODE_ON = 1, ///< \~chinese 打开 \~english ON + +} MV_CAM_TRIGGER_MODE; + +/// \~chinese Gamma选择器 \~english Gamma Selector +typedef enum _MV_CAM_GAMMA_SELECTOR_ +{ + MV_GAMMA_SELECTOR_USER = + 1, ///< \~chinese 用户 \~english Gamma Selector User + MV_GAMMA_SELECTOR_SRGB = + 2, ///< \~chinese sRGB \~english Gamma Selector sRGB + +} MV_CAM_GAMMA_SELECTOR; + +/// \~chinese 白平衡 \~english White Balance +typedef enum _MV_CAM_BALANCEWHITE_AUTO_ +{ + MV_BALANCEWHITE_AUTO_OFF = 0, ///< \~chinese 关闭 \~english Off + MV_BALANCEWHITE_AUTO_ONCE = 2, ///< \~chinese 一次 \~english Once + MV_BALANCEWHITE_AUTO_CONTINUOUS = + 1, ///< \~chinese 连续 \~english Continuous + +} MV_CAM_BALANCEWHITE_AUTO; + +/// \~chinese 触发源 \~english Trigger Source +typedef enum _MV_CAM_TRIGGER_SOURCE_ +{ + MV_TRIGGER_SOURCE_LINE0 = 0, ///< \~chinese Line0 \~english Line0 + MV_TRIGGER_SOURCE_LINE1 = 1, ///< \~chinese Line1 \~english Line1 + MV_TRIGGER_SOURCE_LINE2 = 2, ///< \~chinese Line2 \~english Line2 + MV_TRIGGER_SOURCE_LINE3 = 3, ///< \~chinese Line3 \~english Line3 + MV_TRIGGER_SOURCE_COUNTER0 = 4, ///< \~chinese Conuter0 \~english Conuter0 + + MV_TRIGGER_SOURCE_SOFTWARE = 7, ///< \~chinese 软触发 \~english Software + MV_TRIGGER_SOURCE_FrequencyConverter = + 8, ///< \~chinese 变频器 \~english Frequency Converter + +} MV_CAM_TRIGGER_SOURCE; + +/// \~chinese GigEVision IP配置 \~english GigEVision IP Configuration +#define MV_IP_CFG_STATIC 0x05000000 ///< \~chinese 静态 \~english Static +#define MV_IP_CFG_DHCP 0x06000000 ///< \~chinese DHCP \~english DHCP +#define MV_IP_CFG_LLA 0x04000000 ///< \~chinese LLA \~english LLA + +/// \~chinese GigEVision网络传输模式 \~english GigEVision Net Transfer Mode +#define MV_NET_TRANS_DRIVER 0x00000001 ///< \~chinese 驱动 \~english Driver +#define MV_NET_TRANS_SOCKET 0x00000002 ///< \~chinese Socket \~english Socket + +/// \~chinese CameraLink波特率 \~english CameraLink Baud Rates (CLUINT32) +#define MV_CAML_BAUDRATE_9600 0x00000001 ///< \~chinese 9600 \~english 9600 +#define MV_CAML_BAUDRATE_19200 \ + 0x00000002 ///< \~chinese 19200 \~english 19200 +#define MV_CAML_BAUDRATE_38400 \ + 0x00000004 ///< \~chinese 38400 \~english 38400 +#define MV_CAML_BAUDRATE_57600 \ + 0x00000008 ///< \~chinese 57600 \~english 57600 +#define MV_CAML_BAUDRATE_115200 \ + 0x00000010 ///< \~chinese 115200 \~english 115200 +#define MV_CAML_BAUDRATE_230400 \ + 0x00000020 ///< \~chinese 230400 \~english 230400 +#define MV_CAML_BAUDRATE_460800 \ + 0x00000040 ///< \~chinese 460800 \~english 460800 +#define MV_CAML_BAUDRATE_921600 \ + 0x00000080 ///< \~chinese 921600 \~english 921600 +#define MV_CAML_BAUDRATE_AUTOMAX \ + 0x40000000 ///< \~chinese 最大值 \~english Auto Max + +/// \~chinese 异常消息类型 \~english Exception message type +#define MV_EXCEPTION_DEV_DISCONNECT \ + 0x00008001 ///< \~chinese 设备断开连接 \~english The device is disconnected +#define MV_EXCEPTION_VERSION_CHECK \ + 0x00008002 ///< \~chinese SDK与驱动版本不匹配 \~english SDK does not match the driver version + +/// \~chinese U3V流异常类型 +typedef enum _MV_CC_STREAM_EXCEPTION_TYPE_ +{ + MV_CC_STREAM_EXCEPTION_ABNORMAL_IMAGE = 0x4001, ///< \~chinese 异常的图像,该帧被丢弃 + MV_CC_STREAM_EXCEPTION_LIST_OVERFLOW = 0x4002, ///< \~chinese 缓存列表溢出,清除最旧的一帧 + MV_CC_STREAM_EXCEPTION_LIST_EMPTY = 0x4003, ///< \~chinese 缓存列表为空,该帧被丢弃 + MV_CC_STREAM_EXCEPTION_RECONNECTION = 0x4004, ///< \~chinese 断流恢复 + MV_CC_STREAM_EXCEPTION_DISCONNECTED = 0x4005, ///< \~chinese 断流,恢复失败,取流被中止 + MV_CC_STREAM_EXCEPTION_DEVICE = 0x4006, ///< \~chinese 设备异常,取流被中止 + +} MV_CC_STREAM_EXCEPTION_TYPE; + +///< \~chinese 设备Event事件名称最大长度 \~english Max length of event name +#define MAX_EVENT_NAME_SIZE 128 + +/// \~chinese Event事件回调信息\ \~english Event callback infomation +typedef struct _MV_EVENT_OUT_INFO_ +{ + char EventName + [MAX_EVENT_NAME_SIZE]; ///< [OUT] \~chinese Event名称 \~english Event name + + unsigned short nEventID; ///< [OUT] \~chinese Event号 \~english Event ID + unsigned short + nStreamChannel; ///< [OUT] \~chinese 流通道序号 \~english Circulation number + + unsigned int nBlockIdHigh; ///< [OUT] \~chinese 帧号高位 \~english BlockId high + unsigned int nBlockIdLow; ///< [OUT] \~chinese 帧号低位 \~english BlockId low + + unsigned int + nTimestampHigh; ///< [OUT] \~chinese 时间戳高位 \~english Timestramp high + unsigned int nTimestampLow; ///< [OUT] \~chinese 时间戳低位 \~english Timestramp low + + void * pEventData; ///< [OUT] \~chinese Event数据 \~english Event data + unsigned int nEventDataSize; ///< [OUT] \~chinese Event数据长度 \~english Event data len + + unsigned int nReserved[16]; ///< \~chinese 预留 \~english Reserved + +} MV_EVENT_OUT_INFO; + +/// \~chinese 文件存取 \~english File Access +typedef struct _MV_CC_FILE_ACCESS_T +{ + const char * pUserFileName; ///< [IN] \~chinese 用户文件名 \~english User file name + const char * pDevFileName; ///< [IN] \~chinese 设备文件名 \~english Device file name + + unsigned int nReserved[32]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_FILE_ACCESS; + +/// \~chinese 文件存取 \~english File Access +typedef struct _MV_CC_FILE_ACCESS_E +{ + char * + pUserFileBuf; ///< [IN] \~chinese 用户数据缓存 \~english User data Buffer + unsigned int + pFileBufSize; ///< [IN] \~chinese 用户数据缓存大小 \~english data buffer size + unsigned int + pFileBufLen; ///< [OUT][IN] \~chinese 文件数据缓存总长度(读取时输出文件数据总长度,写入时输入文件数据总长度) \~english data buffer len + const char * + pDevFileName; ///< [IN] \~chinese 设备文件名 \~english Device file name + + unsigned int nReserved + [32]; ///< \~chinese 预留 \~english Reserved +} MV_CC_FILE_ACCESS_EX; + +/// \~chinese 文件存取进度 \~english File Access Progress +typedef struct _MV_CC_FILE_ACCESS_PROGRESS_T +{ + int64_t nCompleted; ///< [OUT] \~chinese 已完成的长度 \~english Completed Length + int64_t nTotal; ///< [OUT] \~chinese 总长度 \~english Total Length + + unsigned int nReserved[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_FILE_ACCESS_PROGRESS; + +/// \~chinese Gige的传输类型 \~english The transmission type of Gige +typedef enum _MV_GIGE_TRANSMISSION_TYPE_ +{ + MV_GIGE_TRANSTYPE_UNICAST = + 0x0, ///< \~chinese 表示单播(默认) \~english Unicast mode + MV_GIGE_TRANSTYPE_MULTICAST = + 0x1, ///< \~chinese 表示组播(组播IP范围[224.*.*.*-239.*.*.*]) \~english Multicast mode + MV_GIGE_TRANSTYPE_LIMITEDBROADCAST = + 0x2, ///< \~chinese 表示局域网内广播,暂不支持 \~english Limited broadcast mode,not support + MV_GIGE_TRANSTYPE_SUBNETBROADCAST = + 0x3, ///< \~chinese 表示子网内广播,暂不支持 \~english Subnet broadcast mode,not support + MV_GIGE_TRANSTYPE_CAMERADEFINED = + 0x4, ///< \~chinese 表示从设备获取,暂不支持 \~english Transtype from camera,not support + MV_GIGE_TRANSTYPE_UNICAST_DEFINED_PORT = + 0x5, ///< \~chinese 表示用户自定义应用端接收图像数据Port号 \~english User Defined Receive Data Port + MV_GIGE_TRANSTYPE_UNICAST_WITHOUT_RECV = + 0x00010000, ///< \~chinese 表示设置了单播,但本实例不接收图像数据 \~english Unicast without receive data + MV_GIGE_TRANSTYPE_MULTICAST_WITHOUT_RECV = + 0x00010001, ///< \~chinese 表示组播模式,但本实例不接收图像数据 \~english Multicast without receive data + +} MV_GIGE_TRANSMISSION_TYPE; + +/// \~chinese 网络传输模式 \~english Transmission type +typedef struct _MV_TRANSMISSION_TYPE_T +{ + MV_GIGE_TRANSMISSION_TYPE + enTransmissionType; ///< [IN] \~chinese 传输模式 \~english Transmission type + unsigned int nDestIp; ///< [IN] \~chinese 目标IP,组播模式下有意义 \~english Destination IP + unsigned short + nDestPort; ///< [IN] \~chinese 目标Port,组播模式下有意义 \~english Destination port + + unsigned int nReserved[32]; ///< \~chinese 预留 \~english Reserved + +} MV_TRANSMISSION_TYPE; + +/// \~chinese 动作命令信息 \~english Action Command +typedef struct _MV_ACTION_CMD_INFO_T +{ + unsigned int + nDeviceKey; ///< [IN] \~chinese 设备密钥 \~english Device Key; + unsigned int + nGroupKey; ///< [IN] \~chinese 组键 \~english Group Key + unsigned int + nGroupMask; ///< [IN] \~chinese 组掩码 \~english Group Mask + + unsigned int + bActionTimeEnable; ///< [IN] \~chinese 只有设置成1时Action Time才有效,非1时无效 \~english Action Time Enable + int64_t + nActionTime; ///< [IN] \~chinese 预定的时间,和主频有关 \~english Action Time + + const char * + pBroadcastAddress; ///< [IN] \~chinese 广播包地址 \~english Broadcast Address + unsigned int + nTimeOut; ///< [IN] \~chinese 等待ACK的超时时间,如果为0表示不需要ACK \~english TimeOut + + unsigned int + bSpecialNetEnable; ///< [IN] \~chinese 只有设置成1时指定的网卡IP才有效,非1时无效 \~english Special IP Enable + unsigned int + nSpecialNetIP; ///< [IN] \~chinese 指定的网卡IP \~english Special Net IP address + + unsigned int nReserved + [14]; ///< \~chinese 预留 \~english Reserved + +} MV_ACTION_CMD_INFO; + +/// \~chinese 动作命令返回信息 \~english Action Command Result +typedef struct _MV_ACTION_CMD_RESULT_T +{ + unsigned char strDeviceAddress + [12 + 3 + 1]; ///< [OUT] \~chinese 设备IP \~english IP address of the device + + int + nStatus; ///< [OUT] \~chinese 状态码 \~english status code returned by the device + //1.0x0000:success. + //2.0x8001:Command is not supported by the device. + //3.0x8013:The device is not synchronized to a master clock to be used as time reference. + //4.0x8015:A device queue or packet data has overflowed. + //5.0x8016:The requested scheduled action command was requested at a time that is already past. + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MV_ACTION_CMD_RESULT; + +/// \~chinese 动作命令返回信息列表 \~english Action Command Result List +typedef struct _MV_ACTION_CMD_RESULT_LIST_T +{ + unsigned int + nNumResults; ///< [OUT] \~chinese 返回值个数 \~english Number of returned values + MV_ACTION_CMD_RESULT * + pResults; ///< [OUT] \~chinese 动作命令结果 \~english Reslut of action command + +} MV_ACTION_CMD_RESULT_LIST; + +/// \~chinese 每个节点对应的接口类型 \~english Interface type corresponds to each node +enum MV_XML_InterfaceType +{ + IFT_IValue, ///< \~chinese Value \~english IValue interface + IFT_IBase, ///< \~chinese Base \~english IBase interface + IFT_IInteger, ///< \~chinese Integer \~english IInteger interface + IFT_IBoolean, ///< \~chinese Boolean \~english IBoolean interface + IFT_ICommand, ///< \~chinese Command \~english ICommand interface + IFT_IFloat, ///< \~chinese Float \~english IFloat interface + IFT_IString, ///< \~chinese String \~english IString interface + IFT_IRegister, ///< \~chinese Register \~english IRegister interface + IFT_ICategory, ///< \~chinese Category \~english ICategory interface + IFT_IEnumeration, ///< \~chinese Enumeration \~english IEnumeration interface + IFT_IEnumEntry, ///< \~chinese EnumEntry \~english IEnumEntry interface + IFT_IPort, ///< \~chinese Port \~english IPort interface +}; + +/// \~chinese 节点的访问模式 \~english Node Access Mode +enum MV_XML_AccessMode +{ + AM_NI, ///< \~chinese 不可实现 \~english Not implemented + AM_NA, ///< \~chinese 不可用 \~english Not available + AM_WO, ///< \~chinese 只写 \~english Write Only + AM_RO, ///< \~chinese 只读 \~english Read Only + AM_RW, ///< \~chinese 读写 \~english Read and Write + AM_Undefined, ///< \~chinese 未定义 \~english Object is not yet initialized + AM_CycleDetect, ///< \~chinese 内部用于AccessMode循环检测 \~english used internally for AccessMode cycle detection +}; + +/// \~chinese 最大XML符号数 \~english Max XML Symbolic Number +#define MV_MAX_XML_SYMBOLIC_NUM 64 +/// \~chinese 枚举类型值 \~english Enumeration Value +typedef struct _MVCC_ENUMVALUE_T +{ + unsigned int nCurValue; ///< [OUT] \~chinese 当前值 \~english Current Value + unsigned int + nSupportedNum; ///< [OUT] \~chinese 数据的有效数据个数 \~english Number of valid data + unsigned int nSupportValue + [MV_MAX_XML_SYMBOLIC_NUM]; ///< [OUT] \~chinese 支持的枚举值 \~english Support Value + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_ENUMVALUE; + +/// \~chinese 最大枚举条目对应的符号长度 \~english Max Enum Entry Symbolic Number +#define MV_MAX_SYMBOLIC_LEN 64 +/// \~chinese 枚举类型条目 \~english Enumeration Entry +typedef struct _MVCC_ENUMENTRY_T +{ + unsigned int nValue; ///< [IN] \~chinese 指定值 \~english Value + char + chSymbolic[MV_MAX_SYMBOLIC_LEN]; ///< [OUT] \~chinese 指定值对应的符号 \~english Symbolic + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_ENUMENTRY; + +/// \~chinese Int类型值 \~english Int Value +typedef struct _MVCC_INTVALUE_T +{ + unsigned int nCurValue; ///< [OUT] \~chinese 当前值 \~english Current Value + unsigned int nMax; ///< [OUT] \~chinese 最大值 \~english Max + unsigned int nMin; ///< [OUT] \~chinese 最小值 \~english Min + unsigned int nInc; ///< [OUT] \~chinese \~english Inc + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_INTVALUE; + +/// \~chinese Int类型值Ex \~english Int Value Ex +typedef struct _MVCC_INTVALUE_EX_T +{ + int64_t nCurValue; ///< [OUT] \~chinese 当前值 \~english Current Value + int64_t nMax; ///< [OUT] \~chinese 最大值 \~english Max + int64_t nMin; ///< [OUT] \~chinese 最小值 \~english Min + int64_t nInc; ///< [OUT] \~chinese Inc \~english Inc + + unsigned int nReserved[16]; ///< \~chinese 预留 \~english Reserved + +} MVCC_INTVALUE_EX; + +/// \~chinese Float类型值 \~english Float Value +typedef struct _MVCC_FLOATVALUE_T +{ + float fCurValue; ///< [OUT] \~chinese 当前值 \~english Current Value + float fMax; ///< [OUT] \~chinese 最大值 \~english Max + float fMin; ///< [OUT] \~chinese 最小值 \~english Min + + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_FLOATVALUE; + +/// \~chinese String类型值 \~english String Value +typedef struct _MVCC_STRINGVALUE_T +{ + char chCurValue[256]; ///< [OUT] \~chinese 当前值 \~english Current Value + + int64_t nMaxLength; ///< [OUT] \~chinese 最大长度 \~english MaxLength + unsigned int nReserved[2]; ///< \~chinese 预留 \~english Reserved + +} MVCC_STRINGVALUE; + +/// \~chinese 辅助线颜色 \~english Color of Auxiliary Line +typedef struct _MVCC_COLORF +{ + float + fR; ///< [IN] \~chinese 红色,根据像素颜色的相对深度,范围为[0.0 , 1.0],代表着[0, 255]的颜色深度 \~english Red,Range[0.0, 1.0] + float + fG; ///< [IN] \~chinese 绿色,根据像素颜色的相对深度,范围为[0.0 , 1.0],代表着[0, 255]的颜色深度 \~english Green,Range[0.0, 1.0] + float + fB; ///< [IN] \~chinese 蓝色,根据像素颜色的相对深度,范围为[0.0 , 1.0],代表着[0, 255]的颜色深度 \~english Blue,Range[0.0, 1.0] + float + fAlpha; ///< [IN] \~chinese 透明度,根据像素颜色的相对透明度,范围为[0.0 , 1.0] (此参数功能暂不支持) \~english Alpha,Range[0.0, 1.0](Not Support) + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_COLORF; + +/// \~chinese 自定义点 \~english Point defined +typedef struct _MVCC_POINTF +{ + float + fX; ///< [IN] \~chinese 该点距离图像左边缘距离,根据图像的相对位置,范围为[0.0 , 1.0] \~english Distance From Left,Range[0.0, 1.0] + float + fY; ///< [IN] \~chinese 该点距离图像上边缘距离,根据图像的相对位置,范围为[0.0 , 1.0] \~english Distance From Top,Range[0.0, 1.0] + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_POINTF; + +/// \~chinese 矩形框区域信息 \~english Rect Area Info +typedef struct _MVCC_RECT_INFO +{ + float + fTop; ///< [IN] \~chinese 矩形上边缘距离图像上边缘的距离,根据图像的相对位置,范围为[0.0 , 1.0] \~english Distance From Top,Range[0, 1.0] + float + fBottom; ///< [IN] \~chinese 矩形下边缘距离图像上边缘的距离,根据图像的相对位置,范围为[0.0 , 1.0] \~english Distance From Top,Range[0, 1.0] + float + fLeft; ///< [IN] \~chinese 矩形左边缘距离图像左边缘的距离,根据图像的相对位置,范围为[0.0 , 1.0] \~english Distance From Left,Range[0, 1.0] + float + fRight; ///< [IN] \~chinese 矩形右边缘距离图像左边缘的距离,根据图像的相对位置,范围为[0.0 , 1.0] \~english Distance From Left,Range[0, 1.0] + + MVCC_COLORF + stColor; ///< [IN] \~chinese 辅助线颜色 \~english Color of Auxiliary Line + unsigned int + nLineWidth; ///< [IN] \~chinese 辅助线宽度,宽度只能是1或2 \~english Width of Auxiliary Line, width is 1 or 2 + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_RECT_INFO; + +/// \~chinese 圆形框区域信息 \~english Circle Area Info +typedef struct _MVCC_CIRCLE_INFO +{ + MVCC_POINTF + stCenterPoint; ///< [IN] \~chinese 圆心信息 \~english Circle Point Info + + float + fR1; ///< [IN] \~chinese 宽向半径,根据图像的相对位置[0, 1.0],半径与圆心的位置有关,需保证画出的圆在显示框范围之内,否则报错 \~english Windth Radius, Range[0, 1.0] + float + fR2; ///< [IN] \~chinese 高向半径,根据图像的相对位置[0, 1.0],半径与圆心的位置有关,需保证画出的圆在显示框范围之内,否则报错 \~english Height Radius, Range[0, 1.0] + + MVCC_COLORF + stColor; ///< [IN] \~chinese 辅助线颜色信息 \~english Color of Auxiliary Line + unsigned int + nLineWidth; ///< [IN] \~chinese 辅助线宽度,宽度只能是1或2 \~english Width of Auxiliary Line, width is 1 or 2 + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_CIRCLE_INFO; + +/// \~chinese 线条辅助线信息 \~english Linear Auxiliary Line Info +typedef struct _MVCC_LINES_INFO +{ + MVCC_POINTF + stStartPoint; ///< [IN] \~chinese 线条辅助线的起始点坐标 \~english The Start Point of Auxiliary Line + MVCC_POINTF + stEndPoint; ///< [IN] \~chinese 线条辅助线的终点坐标 \~english The End Point of Auxiliary Line + MVCC_COLORF + stColor; ///< [IN] \~chinese 辅助线颜色信息 \~english Color of Auxiliary Line + unsigned int + nLineWidth; ///< [IN] \~chinese 辅助线宽度,宽度只能是1或2 \~english Width of Auxiliary Line, width is 1 or 2 + unsigned int nReserved[4]; ///< \~chinese 预留 \~english Reserved + +} MVCC_LINES_INFO; + +///< \~chinese 分时曝光时最多将源图像拆分的个数 \~english The maximum number of source image to be split in time-division exposure +#define MV_MAX_SPLIT_NUM 8 + +/// \~chinese 图像重构的方式 \~english Image reconstruction method +typedef enum _MV_IMAGE_RECONSTRUCTION_METHOD_ +{ + MV_SPLIT_BY_LINE = + 1, ///< \~chinese 源图像按行拆分成多张图像 \~english Source image split into multiple images by line + +} MV_IMAGE_RECONSTRUCTION_METHOD; + +/// \~chinese 图像重构后的图像列表 \~english List of images after image reconstruction +typedef struct _MV_OUTPUT_IMAGE_INFO_ +{ + unsigned int + nWidth; ///< [OUT] \~chinese 源图像宽 \~english Source Image Width + unsigned int + nHeight; ///< [OUT] \~chinese 源图像高 \~english Source Image Height + enum MvGvspPixelType + enPixelType; ///< [OUT] \~chinese 像素格式 \~english Pixel format + + unsigned char * + pBuf; ///< [IN][OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int + nBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + unsigned int + nBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved +} MV_OUTPUT_IMAGE_INFO; + +/// \~chinese 重构图像参数信息 \~english Restructure image parameters +typedef struct _MV_RECONSTRUCT_IMAGE_PARAM_ +{ + unsigned int nWidth; ///< [IN] \~chinese 源图像宽 \~english Source Image Width + unsigned int nHeight; ///< [IN] \~chinese 源图像高 \~english Source Image Height + enum MvGvspPixelType enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned char * pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + unsigned int nExposureNum; ///< [IN] \~chinese 曝光个数(1-8] \~english Exposure number + MV_IMAGE_RECONSTRUCTION_METHOD + enReconstructMethod; ///< [IN] \~chinese 图像重构方式 \~english Image restructuring method + + MV_OUTPUT_IMAGE_INFO stDstBufList + [MV_MAX_SPLIT_NUM]; ///< [OUT] \~chinese 输出数据缓存信息 \~english Output data info + + unsigned int nRes[4]; +} MV_RECONSTRUCT_IMAGE_PARAM; + +#endif /* _MV_CAMERA_PARAMS_H_ */ diff --git a/io/hikrobot/include/MvCameraControl.h b/io/hikrobot/include/MvCameraControl.h new file mode 100644 index 0000000..cc2751f --- /dev/null +++ b/io/hikrobot/include/MvCameraControl.h @@ -0,0 +1,2595 @@ + +#ifndef _MV_CAMERA_CTRL_H_ +#define _MV_CAMERA_CTRL_H_ + +#include "CameraParams.h" +#include "MvErrorDefine.h" +#include "MvObsoleteInterfaces.h" + +#ifndef MV_CAMCTRL_API + +#if (defined(_WIN32) || defined(WIN64)) +#if defined(MV_CAMCTRL_EXPORTS) +#define MV_CAMCTRL_API __declspec(dllexport) +#else +#define MV_CAMCTRL_API __declspec(dllimport) +#endif +#else +#ifndef __stdcall +#define __stdcall +#endif + +#ifndef MV_CAMCTRL_API +#define MV_CAMCTRL_API +#endif +#endif + +#endif + +#ifdef MV_CAMCTRL_API + +#if (defined(_WIN32) || defined(WIN64)) +#if defined(MV_CAMCTRL_EXPORTS) +#define MV_CAMCTRL_API __declspec(dllexport) +#else +#define MV_CAMCTRL_API __declspec(dllimport) +#endif +#else +#ifndef __stdcall +#define __stdcall +#endif + +#if defined(MV_CAMCTRL_EXPORTS) +#define MV_CAMCTRL_API __attribute__((visibility("default"))) +#else +#define MV_CAMCTRL_API +#endif +#endif + +#endif + +#ifndef IN +#define IN +#endif + +#ifndef OUT +#define OUT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************** ch: 摘要 | en: Instructions**********************************************/ + +/** +* @~chinese +* 该头文件主要包含9部分: +* 1.相机的控制和取流接口 +* 2.相机属性万能配置接口&读写寄存器接口 +* 3.相机升级 +* 4.注册异常回调和事件接口 +* 5.仅GigE设备支持的接口 +* 6.仅CameraLink 设备支持的接口 +* 7.仅U3V设备支持的接口 +* 8.GenTL相关接口 +* 9.图像保存、格式转换等相关接口 + +* @~english +* The document mainly consists of nine parts: +* 1.Camera control and streaming· +* 2.Camera attribute nodes set and obtained universal interface +* 3.Camera upgrade and read-write register memory interface +* 4.Enrol abnormal callbacks and event interface +* 5.Only support GigE device interface +* 6.Only support camlink device interface +* 7.Only support U3V device interface +* 8.Only support GenTL device interface +* 9.Related image save and format convert interface +**/ + +/**************************Part1 ch: 相机的控制和取流接口 | en: Camera control and streaming******************************************/ +/* + * @~chinese + * @brief 获取SDK版本号 + * @return 返回4字节版本号 + |主 |次 |修正 | 测试| + 8bits 8bits 8bits 8bits + * @remarks 比如返回值为0x01000001,即SDK版本号为V1.0.0.1。 + + * @~english + * @brief Get SDK Version + * @return Always return 4 Bytes of version number + |Main |Sub |Rev | Test| + 8bits 8bits 8bits 8bits + * @remarks For example, if the return value is 0x01000001, the SDK version is V1.0.0.1. + ************************************************************************/ +MV_CAMCTRL_API unsigned int __stdcall MV_CC_GetSDKVersion(); + +/********************************************************************/ /** + * @~chinese + * @brief 获取支持的传输层 + * @return 支持的传输层编号 + + * @~english + * @brief Get supported Transport Layer + * @return Supported Transport Layer number + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_EnumerateTls(); + +/********************************************************************/ /** + * @~chinese + * @brief 枚举设备 + * @param nTLayerType [IN] 枚举传输层, 参数定义参见CameraParams.h定义, 如: #define MV_GIGE_DEVICE 0x00000001 GigE设备 + * @param pstDevList [IN][OUT] 设备列表 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设备列表的内存是在SDK内部分配的,多线程调用该接口时会进行设备列表内存的释放和申请,建议尽量避免多线程枚举操作。 + * @remarks 参数枚举传输层,适配传入MV_GIGE_DEVICE、MV_1394_DEVICE、MV_USB_DEVICE、MV_CAMERALINK_DEVICE;MV_GIGE_DEVICE该参数 + 传出所有GiGE相关的设备信息(包含虚拟GiGE和GenTL下的GiGE设备),MV_USB_DEVICE该参数传出所有USB设备,包含虚拟USB设备。 + + * @~english + * @brief Enumerate Device + * @param nTLayerType [IN] Enumerate TLs, Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_GIGE_DEVICE 0x00000001 + * @param pstDevList [IN][OUT] Device List + * @return Success, return MV_OK. Failure, return error code + * @remarks The memory of the device list is allocated within the SDK. When the interface is invoked by multiple threads, the memory of the device list will be released and applied + It is recommended to avoid multithreaded enumeration operations as much as possible. + * @remarks Transmission layer of enumeration, param only include MV_GIGE_DEVICE、MV_1394_DEVICE、MV_USB_DEVICE、MV_CAMERALINK_DEVICE; + MV_GIGE_DEVICE can output virtual and GenTL GiGE devices, MV_USB_DEVICE can output all USB devices, include virtual usb devices. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_EnumDevices( + IN unsigned int nTLayerType, IN OUT MV_CC_DEVICE_INFO_LIST * pstDevList); + +/********************************************************************/ /** + * @~chinese + * @brief 根据厂商名字枚举设备 + * @param nTLayerType [IN] 枚举传输层, 参数定义参见CameraParams.h定义, 如: #define MV_GIGE_DEVICE 0x00000001 GigE设备 + * @param pstDevList [IN][OUT] 设备列表 + * @param strManufacturerName [IN] 厂商名字 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 参数枚举传输层,适配传入MV_GIGE_DEVICE、MV_1394_DEVICE、MV_USB_DEVICE、MV_CAMERALINK_DEVICE;MV_GIGE_DEVICE该参数 + 传出所有GiGE相关的设备信息(包含虚拟GiGE和GenTL下的GiGE设备),MV_USB_DEVICE该参数传出所有USB设备,包含虚拟USB设备。 + * @remarks 设备列表的内存是在SDK内部分配的,多线程调用该接口时会进行设备列表内存的释放和申请,建议尽量避免多线程枚举操作。 + + * @~english + * @brief Enumerate device according to manufacture name + * @param nTLayerType [IN] Transmission layer of enumeration, , Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_GIGE_DEVICE 0x00000001 + * @param pstDevList [IN][OUT] Device list + * @param strManufacturerName [IN] Manufacture Name + * @return Success, return MV_OK. Failure, return error code + * @remarks Transmission layer of enumeration, param only include MV_GIGE_DEVICE、MV_1394_DEVICE、MV_USB_DEVICE、MV_CAMERALINK_DEVICE; + MV_GIGE_DEVICE can output virtual and GenTL GiGE devices, MV_USB_DEVICE can output all USB devices, include virtual usb devices. + * @remarks The memory of the device list is allocated within the SDK. When the interface is invoked by multiple threads, the memory of the device list will be released and applied. + It is recommended to avoid multithreaded enumeration operations as much as possible. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_EnumDevicesEx( + IN unsigned int nTLayerType, IN OUT MV_CC_DEVICE_INFO_LIST * pstDevList, + IN const char * strManufacturerName); + +/********************************************************************/ /** + * @~chinese + * @brief 枚举设备扩展(可指定排序方式枚举、根据厂商名字过滤) + * @param nTLayerType [IN] 枚举传输层(区分每一种传输层类型,不耦合), 参数定义参见CameraParams.h定义, 如: #define MV_GIGE_DEVICE 0x00000001 GigE设备 + * @param pstDevList [IN][OUT] 设备列表 + * @param strManufacturerName [IN] 厂商名字(可传NULL,即不过滤) + * @param enSortMethod [IN] 排序方式 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设备列表的内存是在SDK内部分配的,多线程调用该接口时会进行设备列表内存的释放和申请,建议尽量避免多线程枚举操作 + strManufacturerName可传入NULL,若传入NULL则返回排好序的所有设备列表,若不为NULL则只返回排好序的指定厂商设备列表。 + + * @~english + * @brief Enumerate device according to the specified ordering + * @param nTLayerType [IN] Transmission layer of enumeration(All layer protocol type can input), Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_GIGE_DEVICE 0x00000001 + * @param pstDevList [IN][OUT] Device list + * @param strManufacturerName [IN] Manufacture Name + * @param enSortMethod [IN] Sorting Method + * @return Success, return MV_OK. Failure, return error code + * @remarks The memory of the device list is allocated within the SDK. When the interface is invoked by multiple threads, the memory of the device list will be released and applied. + It is recommended to avoid multithreaded enumeration operations as much as possible. + strManufacturerName can be passed in NULL,if NULL is passed in, it will return the sorted list of all devices. + If it is not NULL,it will only return the sorted list of the specified manufacturer's devices. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_EnumDevicesEx2( + IN unsigned int nTLayerType, IN OUT MV_CC_DEVICE_INFO_LIST * pstDevList, + IN const char * strManufacturerName, IN MV_SORT_METHOD enSortMethod); + +/********************************************************************/ /** + * @~chinese + * @brief 设备是否可达 + * @param pstDevInfo [IN] 设备信息结构体 + * @param nAccessMode [IN] 访问权限,参数定义参见CameraParams.h定义, 如:#define MV_ACCESS_Exclusive 1 + * @remarks 读取设备CCP寄存器的值,判断当前状态是否具有某种访问权限 + 如果设备不支持MV_ACCESS_ExclusiveWithSwitch、MV_ACCESS_ControlWithSwitch、MV_ACCESS_ControlSwitchEnableWithKey这三种模式,接口返回false。 + 目前设备不支持这3种抢占模式,国际上主流的厂商的设备也都暂不支持这3种模式。 + 该接口支持 虚拟相机,GIGE相机,U3V相机,自研网卡设备。 + 该接口不支持CameraLink设备,不支持导入.cti方式采集卡设备。 + + * @~english + * @brief Is the device accessible + * @param pstDevInfo [IN] Device Information Structure + * @param nAccessMode [IN] Access Right, Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_ACCESS_Exclusive 1 + * @return Access, return true. Not access, return false + * @remarks Read device CCP register value and determine current access permission. + Return false if the device does not support the modes MV_ACCESS_ExclusiveWithSwitch, MV_ACCESS_ControlWithSwitch, MV_ACCESS_ControlSwitchEnableWithKey. + Currently the device does not support the 3 preemption modes, neither do the devices from other mainstream manufacturers. + This API is not supported by CameraLink device. + **************************************************************************/ +MV_CAMCTRL_API bool __stdcall MV_CC_IsDeviceAccessible( + IN MV_CC_DEVICE_INFO * pstDevInfo, IN unsigned int nAccessMode); + +/********************************************************************/ /** + * @~chinese + * @brief 设置SDK日志路径 + * @param strSDKLogPath [IN] SDK日志路径 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设置路径之后,可以指定路径存放日志, V2.4.1版本新增日志服务,开启服务之后该接口无效,默认日志服务为开启状态。 + + * @~english + * @brief Set SDK log path + * @param strSDKLogPath [IN] SDK log path + * @return Access, return true. Not access, return false + * @remarks For version V2.4.1, added log service, this API is invalid when the service is enabled.And The logging service is enabled by default + This API is used to set the log file storing path. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetSDKLogPath(IN const char * strSDKLogPath); + +/********************************************************************/ /** + * @~chinese + * @brief 创建设备句柄 + * @param handle [IN][OUT] 设备句柄 + * @param pstDevInfo [IN] 设备信息结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 根据输入的设备信息,创建库内部必须的资源和初始化内部模块 + 通过该接口创建句柄,调用SDK接口,会默认生成SDK日志文件,如果不需要生成日志文件,可以通过MV_CC_CreateHandleWithoutLog创建句柄 + + * @~english + * @brief Create Device Handle + * @param handle [IN][OUT] Device handle + * @param pstDevInfo [IN] Device Information Structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Create required resources within library and initialize internal module according to input device information. + Create handle and call SDK interface through this interface, and SDK log file will be created by default. + Creating handle through MV_CC_CreateHandleWithoutLog will not generate log files. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_CreateHandle( + OUT void ** handle, IN const MV_CC_DEVICE_INFO * pstDevInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 创建设备句柄,不生成日志 + * @param handle [IN][OUT] 设备句柄 + * @param pstDevInfo [IN] 设备信息结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 根据输入的设备信息,创建库内部必须的资源和初始化内部模块 + 通过该接口创建句柄,调用SDK接口,不会默认生成SDK日志文件,如果需要生成日志文件可以通过MV_CC_CreateHandle创建句柄,日志文件自动生成 + + * @~english + * @brief Create Device Handle without log + * @param handle [IN][OUT] Device handle + * @param pstDevInfo [IN] Device Information Structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Create required resources within library and initialize internal module according to input device information. + Create handle and call SDK interface through this interface, and SDK log file will not be created. To create logs, + create handle through MV_CC_CreateHandle, and log files will be automatically generated. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_CreateHandleWithoutLog( + OUT void ** handle, IN const MV_CC_DEVICE_INFO * pstDevInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 销毁设备句柄 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Destroy Device Handle + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_DestroyHandle(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 打开设备 + * @param handle [IN] 设备句柄 + * @param nAccessMode [IN] 访问权限, 参数定义参见CameraParams.h定义, 如:#define MV_ACCESS_Exclusive 1 + * @param nSwitchoverKey [IN] 切换访问权限时的密钥 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 根据设置的设备参数,找到对应的设备,连接设备, 调用接口时可不传入nAccessMode和nSwitchoverKey,此时默认设备访问模式为独占权限。 + 目前设备暂不支持MV_ACCESS_ExclusiveWithSwitch、MV_ACCESS_ControlWithSwitch、MV_ACCESS_ControlSwitchEnable、MV_ACCESS_ControlSwitchEnableWithKey这四种抢占模式 + 对于U3V设备,nAccessMode、nSwitchoverKey这两个参数无效 + + * @~english + * @brief Open Device + * @param handle [IN] Device handle + * @param nAccessMode [IN] Access Right, Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_ACCESS_Exclusive 1 + * @param nSwitchoverKey [IN] Switch key of access right + * @return Success, return MV_OK. Failure, return error code + * @remarks Find specific device and connect according to set device parameters.When calling the interface, the input of nAccessMode and nSwitchoverKey is optional, + and the device access mode is exclusive. Currently the device does not support the following preemption modes: + MV_ACCESS_ExclusiveWithSwitch, MV_ACCESS_ControlWithSwitch, MV_ACCESS_ControlSwitchEnableWithKey; For USB3Vision device, nAccessMode, nSwitchoverKey are invalid. + ************************************************************************/ +#ifndef __cplusplus +MV_CAMCTRL_API int __stdcall MV_CC_OpenDevice( + IN void * handle, IN unsigned int nAccessMode, IN unsigned short nSwitchoverKey); +#else +MV_CAMCTRL_API int __stdcall MV_CC_OpenDevice( + IN void * handle, IN unsigned int nAccessMode = MV_ACCESS_Exclusive, + IN unsigned short nSwitchoverKey = 0); +#endif + +/********************************************************************/ /** + * @~chinese + * @brief 关闭设备 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过MV_CC_OpenDevice连接设备后,可以通过该接口断开设备连接,释放资源 + + * @~english + * @brief Close Device + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + * @remarks After connecting to device through MV_CC_OpenDevice, use this interface to disconnect and release resources. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_CloseDevice(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 判断设备是否处于连接状态 + * @param handle [IN] 设备句柄 + * @return 设备处于连接状态,返回true;没连接或失去连接,返回false + + * @~english + * @brief Is The Device Connected + * @param handle [IN] Device handle + * @return Connected, return true. Not Connected or DIsconnected, return false + ***********************************************************************/ +MV_CAMCTRL_API bool __stdcall MV_CC_IsDeviceConnected(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 注册图像数据回调 + * @param handle [IN] 设备句柄 + * @param cbOutput [IN] 回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过该接口可以设置图像数据回调函数,在MV_CC_CreateHandle之后即可调用, 图像数据采集有两种方式,两种方式不能复用: + 方式一:调用MV_CC_RegisterImageCallBackEx设置图像数据回调函数,然后调用MV_CC_StartGrabbing开始采集,采集的图像数据在设置的回调函数中返回 + 方式二:调用MV_CC_StartGrabbing开始采集,然后在应用层循环调用MV_CC_GetOneFrameTimeout获取指定像素格式的帧数据, + 获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Register the image callback function + * @param handle [IN] Device handle + * @param cbOutput [IN] Callback function pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + * @remarks After MV_CC_CreateHandle, call this interface to set image data callback function.There are two available image data acquisition modes, and cannot be used together: + Mode 1: Call MV_CC_RegisterImageCallBack to set image data callback function, and then callMV_CC_StartGrabbing to start acquiring. The acquired image data will return in the set callback function. + Mode 2: Call MV_CC_StartGrabbing to start acquiring, and then call MV_CC_GetOneFrameTimeout repeatedly in application layer to get frame data of specified pixel format. When getting frame data, + the frequency of calling this interface should be controlled by upper layer application according to frame rate. + This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterImageCallBackEx( + IN void * handle, + IN void(__stdcall * cbOutput)( + unsigned char * pData, MV_FRAME_OUT_INFO_EX * pFrameInfo, void * pUser), + IN void * pUser); + +/********************************************************************/ /** + * @~chinese + * @brief 注册图像数据回调,RGB + * @param handle [IN] 设备句柄 + * @param cbOutput [IN] 回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过该接口可以设置图像数据回调函数,在MV_CC_CreateHandle之后即可调用。图像数据采集有两种方式,两种方式不能复用: + 方式一:调用MV_CC_RegisterImageCallBackForRGB设置RGB24格式图像数据回调函数,然后调用MV_CC_StartGrabbing开始采集,采集的图像数据在设置的回调函数中返回。 + 方式二:调用MV_CC_StartGrabbing开始采集,然后在应用层循环调用MV_CC_GetImageForRGB获取RGB24格式的帧数据, + 获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief register image data callback, RGB + * @param handle [IN] Device handle + * @param cbOutput [IN] Callback function pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + * @remarks Before calling this API to set image data callback function, you should call this API MV_CC_CreateHandle.There are two image acquisition modes, the two modes cannot be reused: + Mode 1: Call MV_CC_RegisterImageCallBackForRGB to set RGB24 format image data callback function, and then call MV_CC_StartGrabbing to start acquisition, the collected image data will be returned in the configured callback function. + Mode 2: Call MV_CC_StartGrabbing to start acquisition, and the call MV_CC_GetImageForRGB repeatedly in application layer to get frame data with RGB24 format. + When getting frame data, the upper application program should control the frequency of calling this API according to frame rate. + This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterImageCallBackForRGB( + IN void * handle, + IN void(__stdcall * cbOutput)( + unsigned char * pData, MV_FRAME_OUT_INFO_EX * pFrameInfo, void * pUser), + IN void * pUser); + +/********************************************************************/ /** + * @~chinese + * @brief 注册图像数据回调,BGR + * @param handle [IN] 设备句柄 + * @param cbOutput [IN] 回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过该接口可以设置图像数据回调函数,在MV_CC_CreateHandle之后即可调用。图像数据采集有两种方式,两种方式不能复用: + 方式一:调用MV_CC_RegisterImageCallBackForBGR设置BGR24图像数据回调函数,然后调用MV_CC_StartGrabbing开始采集,采集的图像数据在设置的回调函数中返回。 + 方式二:调用MV_CC_StartGrabbing开始采集,然后在应用层循环调用MV_CC_GetImageForBGR获取BGR24格式的帧数据, + 获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief register image data callback, BGR + * @param handle [IN] Device handle + * @param cbOutput [IN] Callback function pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + * @remarks Before calling this API to set image data callback function, you should call this API MV_CC_CreateHandle.There are two image acquisition modes, the two modes cannot be reused: + Mode 1: Call MV_CC_RegisterImageCallBackForBGR to set RGB24 format image data callback function, and then call MV_CC_StartGrabbing to start acquisition, the collected image data will be returned in the configured callback function. + Mode 2: Call MV_CC_StartGrabbing to start acquisition, and the call MV_CC_GetImageForBGR repeatedly in application layer to get frame data with BGR24 format. + When getting frame data,the upper application program should control the frequency of calling this API according to frame rate. + This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterImageCallBackForBGR( + IN void * handle, + IN void(__stdcall * cbOutput)( + unsigned char * pData, MV_FRAME_OUT_INFO_EX * pFrameInfo, void * pUser), + IN void * pUser); + +/********************************************************************/ /** + * @~chinese + * @brief 开始取流 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口不支持CameraLink设备。 + + * @~english + * @brief Start Grabbing + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_StartGrabbing(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 停止取流 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口不支持CameraLink设备。 + + * @~english + * @brief Stop Grabbing + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_StopGrabbing(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 获取一帧RGB数据,此函数为查询式获取,每次调用查询内部 + 缓存有无数据,有数据则获取数据,无数据返回错误码 + * @param handle [IN] 设备句柄 + * @param pData [IN][OUT] 图像数据接收指针 + * @param nDataSize [IN] 接收缓存大小 + * @param pstFrameInfo [IN][OUT] 图像信息结构体 + * @param nMsec [IN] 等待超时时间 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 每次调用该接口,将查询内部缓存是否有数据,如果有数据则转换成RGB24格式返回,如果没有数据则返回错误码。 + 因为图像转换成RGB24格式有耗时,所以当数据帧率过高时该接口可能会导致丢帧。调用该接口获取图像数据帧之前需要先调用MV_CC_StartGrabbing启动图像采集。 + 该接口为主动式获取帧数据,上层应用程序需要根据帧率,控制好调用该接口的频率。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Get one frame of RGB data, this function is using query to get data + query whether the internal cache has data, get data if there has, return error code if no data + * @param handle [IN] Device handle + * @param pData [IN][OUT] Image data receiving buffer + * @param nDataSize [IN] Buffer size + * @param pstFrameInfo [IN][OUT] Image information structure + * @param nMsec [IN] Waiting timeout + * @return Success, return MV_OK. Failure, return error code + * @remarks Each time the API is called, the internal cache is checked for data. If there is data, it will be transformed as RGB24 format for return, if there is no data, return error code. + As time-consuming exists when transform the image to RGB24 format,this API may cause frame loss when the data frame rate is too high. + Before calling this API to get image data frame, call MV_CC_StartGrabbing to start image acquisition. + This API can get frame data actively, the upper layer program should control the frequency of calling this API according to the frame rate. + This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetImageForRGB( + IN void * handle, IN OUT unsigned char * pData, IN unsigned int nDataSize, + IN OUT MV_FRAME_OUT_INFO_EX * pstFrameInfo, IN int nMsec); + +/********************************************************************/ /** + * @~chinese + * @brief 获取一帧BGR数据,此函数为查询式获取,每次调用查询内部 + 缓存有无数据,有数据则获取数据,无数据返回错误码 + * @param handle [IN] 设备句柄 + * @param pData [IN][OUT] 图像数据接收指针 + * @param nDataSize [IN] 接收缓存大小 + * @param pstFrameInfo [IN][OUT] 图像信息结构体 + * @param nMsec [IN] 等待超时时间 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 每次调用该接口,将查询内部缓存是否有数据,如果有数据则转换成BGR24格式返回,如果没有数据则返回错误码。 + 因为图像转换成BGR24格式有耗时,所以当数据帧率过高时该接口可能会导致丢帧.调用该接口获取图像数据帧之前需要先调用MV_CC_StartGrabbing启动图像采集。 + 该接口为主动式获取帧数据,上层应用程序需要根据帧率,控制好调用该接口的频率。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Get one frame of BGR data, this function is using query to get data + query whether the internal cache has data, get data if there has, return error code if no data + * @param handle [IN] Device handle + * @param pData [IN][OUT] Image data receiving buffer + * @param nDataSize [IN] Buffer size + * @param pstFrameInfo [IN][OUT] Image information structure + * @param nMsec [IN] Waiting timeout + * @return Success, return MV_OK. Failure, return error code + * @remarks Before calling this API to set image data callback function, you should call this API MV_CC_CreateHandle. + There are two image acquisition modes, the two modes cannot be reused: + Mode 1: Call MV_CC_RegisterImageCallBackForBGR to set RGB24 format image data callback function, and then call MV_CC_StartGrabbing to start acquisition, the collected image data will be returned in the configured callback function. + Mode 2: Call MV_CC_StartGrabbing to start acquisition, and the call MV_CC_GetImageForBGR repeatedly in application layer to get frame data with BGR24 format. + When getting frame data, the upper application program should control the frequency of calling this API according to frame rate. + This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetImageForBGR( + IN void * handle, IN OUT unsigned char * pData, IN unsigned int nDataSize, + IN OUT MV_FRAME_OUT_INFO_EX * pstFrameInfo, IN int nMsec); + +/********************************************************************/ /** + * @~chinese + * @brief 使用内部缓存获取一帧图片(与MV_CC_Display不能同时使用) + * @param handle [IN] 设备句柄 + * @param pstFrame [IN][OUT] 图像数据和图像信息 + * @param nMsec [IN] 等待超时时间,输入INFINITE时表示无限等待,直到收到一帧数据或者停止取流 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 调用该接口获取图像数据帧之前需要先调用MV_CC_StartGrabbing启动图像采集。该接口为主动式获取帧数据,上层应用程序需要根据帧率,控制好调用该接口的频率, + 该接口支持设置超时时间,SDK内部等待直到有数据时返回,可以增加取流平稳性,适合用于对平稳性要求较高的场合。 + 该接口与MV_CC_FreeImageBuffer配套使用,当处理完取到的数据后,需要用MV_CC_FreeImageBuffer接口将pFrame内的数据指针权限进行释放。 + 该接口与MV_CC_GetOneFrameTimeout相比,有着更高的效率。且其取流缓存的分配是由sdk内部自动分配的,而MV_CC_GetOneFrameTimeout接口是需要客户自行分配。 + 该接口在调用MV_CC_Display后无法取流。 + 该接口对于U3V、GIGE设备均可支持。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Get a frame of an image using an internal cache + * @param handle [IN] Device handle + * @param pstFrame [IN][OUT] Image data and image information + * @param nMsec [IN] Waiting timeout + * @return Success, return MV_OK. Failure, return error code + * @remarks Before calling this API to get image data frame, you should call MV_CC_StartGrabbing to start image acquisition. + This API can get frame data actively, the upper layer program should control the frequency of calling this API according to the frame rate. This API support setting timeout, and SDK will wait to return until data appears. This function will increase the streaming stability, which can be used in the situation with high stability requirement. + This API and MV_CC_FreeImageBuffer should be called in pairs, after processing the acquired data, you should call MV_CC_FreeImageBuffer to release the data pointer permission of pFrame. + This interface is more efficient than MV_CC_GetOneFrameTimeout. The allocation of the stream cache is automatically allocated within the SDK.The MV_CC_GetOneFrameTimeout interface needs to be allocated by customers themselves. + This API cannot be called to stream after calling MV_CC_Display. + This API is not supported by CameraLink device. + This API is supported by both USB3 vision camera and GigE camera. + *****************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetImageBuffer( + IN void * handle, IN OUT MV_FRAME_OUT * pstFrame, IN unsigned int nMsec); + +/********************************************************************/ /** + * @~chinese + * @brief 释放图像缓存(此接口用于释放不再使用的图像缓存,与MV_CC_GetImageBuffer配套使用) + * @param handle [IN] 设备句柄 + * @param pstFrame [IN] 图像数据和图像数据 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口与MV_CC_GetImageBuffer配套使用,使用MV_CC_GetImageBuffer接口取到的图像数据pFrame,需要用MV_CC_FreeImageBuffer接口进行权限释放 + 该接口对于取流效率高于GetOneFrameTimeout接口,且GetImageBuffer在不进行Free的情况下,最大支持输出的节点数与SetImageNode接口所设置的节点数相等,默认节点数是1 + 该接口对于U3V、GIGE设备均可支持 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Free image buffer(this interface can free image buffer, used with MV_CC_GetImageBuffer) + * @param handle [IN] Device handle + * @param pstFrame [IN] Image data and image information + * @return Success, return MV_OK. Failure, return error code + * @remarks This API and MV_CC_GetImageBuffer should be called in pairs, before calling MV_CC_GetImageBuffer to get image data pFrame, you should call MV_CC_FreeImageBuffer to release the permission. + Compared with API MV_CC_GetOneFrameTimeout + this API has higher efficiency of image acquisition. The max. number of nodes can be outputted is same as the "nNum" of API MV_CC_SetImageNodeNum, default value is 1. + This API is not supported by CameraLink device. + This API is supported by both USB3 vision camera and GigE camera. + **********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FreeImageBuffer(IN void * handle, IN MV_FRAME_OUT * pstFrame); + +/********************************************************************/ /** + * @~chinese + * @brief 采用超时机制获取一帧图片,SDK内部等待直到有数据时返回 + * @param handle [IN] 设备句柄 + * @param pData [IN][OUT] 图像数据接收指针 + * @param nDataSize [IN] 接收缓存大小 + * @param pstFrameInfo [IN][OUT] 图像信息结构体 + * @param nMsec [IN] 等待超时时间 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 调用该接口获取图像数据帧之前需要先调用MV_CC_StartGrabbing启动图像采集 + 该接口为主动式获取帧数据,上层应用程序需要根据帧率,控制好调用该接口的频率 + 该接口支持设置超时时间,SDK内部等待直到有数据时返回,可以增加取流平稳性,适合用于对平稳性要求较高的场合 + 该接口对于U3V、GIGE设备均可支持 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Timeout mechanism is used to get image, and the SDK waits inside until the data is returned + * @param handle [IN] Device handle + * @param pData [IN][OUT] Image data receiving buffer + * @param nDataSize [IN] Buffer size + * @param pstFrameInfo [IN][OUT] Image information structure + * @param nMsec [IN] Waiting timeout + * @return Success, return MV_OK. Failure, return error code + * @remarks Before calling this API to get image data frame, call MV_CC_StartGrabbing to start image acquisition. + This API can get frame data actively, the upper layer program should control the frequency of calling this API according to the frame rate. + This API supports setting timeout, SDK will wait to return until data appears. This function will increase the streaming stability, which can be used in the situation with high stability requirement. + Both the USB3Vision and GIGE camera can support this API. + This API is not supported by CameraLink device. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetOneFrameTimeout( + IN void * handle, IN OUT unsigned char * pData, IN unsigned int nDataSize, + IN OUT MV_FRAME_OUT_INFO_EX * pstFrameInfo, IN unsigned int nMsec); + +/********************************************************************/ /** + * @~chinese + * @brief 清除取流数据缓存 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口允许用户在不停止取流的时候,就能清除缓存中不需要的图像 + 该接口在连续模式切触发模式后,可以清除历史数据。 + + * @~english + * @brief if Image buffers has retrieved the data,Clear them + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface allows user to clear the unnecessary images from the buffer memory without stopping acquisition. + This interface allows user to clear previous data after switching from continuous mode to trigger mode. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ClearImageBuffer(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 获取当前图像缓存区的有效图像个数 + * @param handle [IN] 设备句柄 + * @param pnValidImageNum [IN][OUT] 当前图像缓存区中有效图像个数的指针 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks + + * @~english + * @brief Get the number of valid images in the current image buffer + * @param handle [IN] Device handle + * @param pnValidImageNum [IN][OUT] The number of valid images in the current image buffer + * @return Success, return MV_OK. Failure, return error code + * @remarks + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetValidImageNum( + IN void * handle, IN OUT unsigned int * pnValidImageNum); + +/********************************************************************/ /** + * @~chinese + * @brief 显示一帧图像 + * @param handle [IN] 设备句柄 + * @param pstDisplayInfo [IN] 图像信息 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 与设备类型无关 + + * @~english + * @brief Display one frame image + * @param handle [IN] Device handle + * @param pstDisplayInfo [IN] Frame Info + * @return Success, return MV_OK. Failure, return error code + * @remarks Not related to device type + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_DisplayOneFrame( + IN void * handle, IN MV_DISPLAY_FRAME_INFO * pstDisplayInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 显示一帧图像 + * @param handle [IN] 设备句柄 + * @param hWnd [IN] 窗口句柄 + * @param pstDisplayInfo [IN] 图像信息 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口支持PixelType_Gvsp_RGB8_Packed,PixelType_Gvsp_BGR8_Packed,PixelType_Gvsp_Mono8三种像素格式的渲染宽高大小至int类型 + * 其余像素格式渲染仅支持宽高至short + + * @~english + * @brief Display one frame image + * @param handle [IN] Device handle + * @param hWnd [IN] HWND + * @param pstDisplayInfo [IN] Frame Info + * @return Success, return MV_OK. Failure, return error code + * @remarks This API support rendering in three pixel formats:PixelType_Gvsp_RGB8_Packed,PixelType_Gvsp_BGR8_Packed and PixelType_Gvsp_Mono8,width and height to int. + * The rest of the pixel format rendering only supports width and height to short. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_DisplayOneFrameEx( + IN void * handle, IN void * hWnd, IN MV_DISPLAY_FRAME_INFO_EX * pstDisplayInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 设置SDK内部图像缓存节点个数,大于等于1,在抓图前调用 + * @param handle [IN] 设备句柄 + * @param nNum [IN] 缓存节点个数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 调用该接口可以设置SDK内部图像缓存节点个数,在调用MV_CC_StartGrabbing开始抓图前调用。 + 在SDK中默认是1个节点。 + 对于CameraLink设备:该接口不支持串口方式连接,但支持GenTL方式连接。 + + * @~english + * @brief Set the number of the internal image cache nodes in SDK, Greater than or equal to 1, to be called before the capture + * @param handle [IN] Device handle + * @param nNum [IN] Image Node Number + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this interface to set the number of SDK internal image buffer nodes. The interface should be called before calling MV_CC_StartGrabbing for capturing. + For CameraLink devices: This interface does not support serial connection, but supports GenTL connection. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetImageNodeNum(IN void * handle, IN unsigned int nNum); + +/********************************************************************/ /** + * @~chinese + * @brief 设置取流策略(该接口只支持windows平台) + * @param handle [IN] 设备句柄 + * @param enGrabStrategy [IN] 策略枚举值 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口定义了四种取流策略,用户可以根据实际需求进行选择。具体描述如下: + OneByOne:从旧到新一帧一帧的从输出缓存列表中获取图像,打开设备后默认为该策略 + LatestImagesOnly:仅从输出缓存列表中获取最新的一帧图像,同时清空输出缓存列表 + LatestImages:从输出缓存列表中获取最新的OutputQueueSize帧图像,其中OutputQueueSize范围为1-ImageNodeNum,可用MV_CC_SetOutputQueueSize接口设置,ImageNodeNum默认为1, + 可用MV_CC_SetImageNodeNum接口设置 OutputQueueSize设置成1等同于LatestImagesOnly策略,OutputQueueSize设置成ImageNodeNum等同于OneByOne策略 + UpcomingImage:在调用取流接口时忽略输出缓存列表中所有图像,并等待设备即将生成的一帧图像。该策略只支持GigE设备,不支持U3V设备 + + * @~english + * @brief Set Grab Strategy(This interface only supports windows platform) + * @param handle [IN] Device handle + * @param enGrabStrategy [IN] The value of Grab Strategy + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is set by four image acquisition approaches, the user may choose one as needed. Specific details are as followed: + OneByOne:Obtain image from output cache list frame by frame in order, this function is default strategy when device is on. + LatestImagesOnly:Obtain the latest image from output cache list only, meanwhile clear output cache list. + LatestImages:Obtain the latest OutputQueueSize image from output cache list, the range of OutputQueueSize is 1-ImageNodeNum, + the user may set the value of MV_CC_SetOutputQueueSizeinterface,the default value of ImageNodeNum is 1, + If the user usesMV_CC_SetImageNodeNuminterface to set up OutputQueueSize,when the value of OutputQueueSize is set to be 1, + the function will be same as LatestImagesOnly; if the value of OutputQueueSize is set to be ImageNodeNum, the function will be same as OneByOne. + UpcomingImage:Ignore all images in output cache list when calling image acuiqisiotn interface, wait the next upcoming image generated. Support for GigE camera only, not support for U3V camera. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetGrabStrategy( + IN void * handle, IN MV_GRAB_STRATEGY enGrabStrategy); + +/********************************************************************/ /** + * @~chinese + * @brief 设置输出缓存个数(该接口只支持windows平台,只有在MV_GrabStrategy_LatestImages策略下才有效,范围:1-ImageNodeNum) + * @param handle [IN] 设备句柄 + * @param nOutputQueueSize [IN] 输出缓存个数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口需与LatestImages取流策略配套调用,用于设置LatestImages策略下最多允许缓存图像的个数。可以在取流过程中动态调节输出缓存个数 + + * @~english + * @brief Set The Size of Output Queue(This interface only supports windows platform, Only work under the strategy of MV_GrabStrategy_LatestImages,rang:1-ImageNodeNum) + * @param handle [IN] Device handle + * @param nOutputQueueSize [IN] The Size of Output Queue + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface must be used with LatestImages Grab strategy, it is used for setting the maximum allowance queue size of the image under the LatestImages strategy. + The user may change the output queue size while grabbing images. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetOutputQueueSize( + IN void * handle, IN unsigned int nOutputQueueSize); + +/********************************************************************/ /** + * @~chinese + * @brief 获取设备信息,取流之前调用 + * @param handle [IN] 设备句柄 + * @param pstDevInfo [IN][OUT] 返回给调用者有关设备信息结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 支持用户在打开设备后获取设备信息,不支持GenTL设备 + 若该设备是GigE设备,则调用该接口存在阻塞风险,因此不建议在取流过程中调用该接口。 + + * @~english + * @brief Get device information + * @param handle [IN] Device handle + * @param pstDevInfo [IN][OUT] Structure pointer of device information + * @return Success, return MV_OK. Failure, return error code + * @remarks The API support users to access device information after opening the device,don't support GenTL Devices + If the device is a GigE camera, there is a blocking risk in calling the interface, so it is not recommended to call the interface during the fetching process. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetDeviceInfo( + IN void * handle, IN OUT MV_CC_DEVICE_INFO * pstDevInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 获取各种类型的信息 + * @param handle [IN] 设备句柄 + * @param pstInfo [IN][OUT] 返回给调用者有关设备各种类型的信息结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 接口里面输入需要获取的信息类型(指定MV_ALL_MATCH_INFO结构体中的nType类型),获取对应的信息(在MV_ALL_MATCH_INFO结构体中pInfo里返回) + 该接口的调用前置条件取决于所获取的信息类型,获取GigE设备的MV_MATCH_TYPE_NET_DETECT信息需在开启抓图之后调用,获取U3V设备的MV_MATCH_TYPE_USB_DETECT信息需在打开设备之后调用 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Get various type of information + * @param handle [IN] Device handle + * @param pstInfo [IN][OUT] Structure pointer of various type of information + * @return Success, return MV_OK. Failure, return error code + * @remarks Input required information type (specify nType in structure MV_ALL_MATCH_INFO) in the interface and get corresponding information (return in pInfo of structure MV_ALL_MATCH_INFO) + The calling precondition of this interface is determined by obtained information type. Call after enabling capture to get MV_MATCH_TYPE_NET_DETECT information of GigE device, + and call after starting device to get MV_MATCH_TYPE_USB_DETECT information of USB3Vision device. + This API is not supported by CameraLink device. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAllMatchInfo( + IN void * handle, IN OUT MV_ALL_MATCH_INFO * pstInfo); + +/*******************Part2 ch: 相机属性万能配置接口 | en: Camera attribute nodes universal interface*******************/ + +/********************************************************************/ /** + * @~chinese + * @brief 获取Integer属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值,如获取宽度信息则为"Width" + * @param pstIntValue [IN][OUT] 返回给调用者有关设备属性结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以获取int类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IInteger”的节点值都可以通过该接口获取,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Get Integer value + * @param handle [IN] Device handle + * @param strKey [IN] Key value, for example, using "Width" to get width + * @param pstIntValue [IN][OUT] Structure pointer of camera features + * @return Success, return MV_OK. Failure, return error code + * @remarks You can call this API to get the value of camera node with integer type after connecting the device. For strKey value, refer to MvCameraNode. + All the node values of "IInteger" in the list can be obtained via this API. strKey corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetIntValueEx( + IN void * handle, IN const char * strKey, IN OUT MVCC_INTVALUE_EX * pstIntValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Integer型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值,如获取宽度信息则为"Width" + * @param nValue [IN] 想要设置的设备的属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置int类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IInteger”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Set Integer value + * @param handle [IN] Device handle + * @param strKey [IN] Key value, for example, using "Width" to set width + * @param nValue [IN] Feature value to set + * @return Success, return MV_OK. Failure, return error code + * @remarks You can call this API to get the value of camera node with integer type after connecting the device. For strKey value, refer to MvCameraNode. + All the node values of "IInteger" in the list can be obtained via this API. strKey corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetIntValueEx( + IN void * handle, IN const char * strKey, IN int64_t nValue); + +/********************************************************************/ /** + * @~chinese + * @brief 获取Enum属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值,如获取像素格式信息则为"PixelFormat" + * @param pstEnumValue [IN][OUT] 返回给调用者有关设备属性结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以获取Enum类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IEnumeration”的节点值都可以通过该接口获取,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Get Enum value + * @param handle [IN] Device handle + * @param strKey [IN] Key value, for example, using "PixelFormat" to get pixel format + * @param pstEnumValue [IN][OUT] Structure pointer of camera features + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to get specified Enum nodes. For value of strKey, see MvCameraNode, + The node values of IEnumeration can be obtained through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetEnumValue( + IN void * handle, IN const char * strKey, IN OUT MVCC_ENUMVALUE * pstEnumValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Enum型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值,如获取像素格式信息则为"PixelFormat" + * @param nValue [IN] 想要设置的设备的属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置Enum类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IEnumeration”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Set Enum value + * @param handle [IN] Device handle + * @param strKey [IN] Key value, for example, using "PixelFormat" to set pixel format + * @param nValue [IN] Feature value to set + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to get specified Enum nodes. For value of strKey, see MvCameraNode, + The node values of IEnumeration can be obtained through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetEnumValue( + IN void * handle, IN const char * strKey, IN unsigned int nValue); + +/********************************************************************/ /** + * @~chinese + * @brief 获取Enum型节点指定值的符号 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值,如获取像素格式信息则为"PixelFormat" + * @param pstEnumEntry [IN][OUT] 想要获取的设备的属性符号 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以获取Enum类型的指定节点的值所对应的符号。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IEnumeration”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Get the symbolic of the specified value of the Enum type node + * @param handle [IN] Device handle + * @param strKey [IN] Key value, for example, using "PixelFormat" to set pixel format + * @param pstEnumEntry [IN][OUT] Symbolic to get + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this interface after connecting the device to obtain the symbol corresponding to the value of the specified node of Enum type. + The strKey value can refer to the XML node parameter type list. The node value with the data type "IEnumeration" in the table can be set through this interface. + The strKey parameter value corresponds to the "Name" column in the list. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetEnumEntrySymbolic( + IN void * handle, IN const char * strKey, IN OUT MVCC_ENUMENTRY * pstEnumEntry); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Enum型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值,如获取像素格式信息则为"PixelFormat" + * @param strValue [IN] 想要设置的设备的属性字符串 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置Enum类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IEnumeration”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Set Enum value + * @param handle [IN] Device handle + * @param strKey [IN] Key value, for example, using "PixelFormat" to set pixel format + * @param strValue [IN] Feature String to set + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this API after connecting the device. All the values of nodes with IEnumeration type can be set via this API. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetEnumValueByString( + IN void * handle, IN const char * strKey, IN const char * strValue); + +/********************************************************************/ /** + * @~chinese + * @brief 获取Float属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @param pstFloatValue [IN][OUT] 返回给调用者有关设备属性结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以获取float类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IFloat”的节点值都可以通过该接口获取,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Get Float value + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @param pstFloatValue [IN][OUT] Structure pointer of camera features + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to get specified float node. For detailed strKey value see: MvCameraNode. + The node values of IFloat can be obtained through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetFloatValue( + IN void * handle, IN const char * strKey, IN OUT MVCC_FLOATVALUE * pstFloatValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置float型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @param fValue [IN] 想要设置的设备的属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置float类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IFloat”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Set float value + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @param fValue [IN] Feature value to set + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to set specified float node. For detailed strKey value see: MvCameraNode. + The node values of IFloat can be set through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetFloatValue( + IN void * handle, IN const char * strKey, IN float fValue); + +/********************************************************************/ /** + * @~chinese + * @brief 获取Boolean属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @param pbValue [IN][OUT] 返回给调用者有关设备属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以获取bool类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IBoolean”的节点值都可以通过该接口获取,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Get Boolean value + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @param pbValue [IN][OUT] Structure pointer of camera features + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to get specified bool nodes. For value of strKey, see MvCameraNode. + The node values of IBoolean can be obtained through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBoolValue( + IN void * handle, IN const char * strKey, IN OUT bool * pbValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Boolean型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @param bValue [IN] 想要设置的设备的属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置bool类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IBoolean”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Set Boolean value + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @param bValue [IN] Feature value to set + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to set specified bool nodes. For value of strKey, see MvCameraNode. + The node values of IBoolean can be set through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBoolValue( + IN void * handle, IN const char * strKey, IN bool bValue); + +/********************************************************************/ /** + * @~chinese + * @brief 获取String属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @param pstStringValue [IN][OUT] 返回给调用者有关设备属性结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以获取string类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IString”的节点值都可以通过该接口获取,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Get String value + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @param pstStringValue [IN][OUT] Structure pointer of camera features + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to get specified string nodes. For value of strKey, see MvCameraNode. + The node values of IString can be obtained through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetStringValue( + IN void * handle, IN const char * strKey, IN OUT MVCC_STRINGVALUE * pstStringValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置String型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @param strValue [IN] 想要设置的设备的属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置string类型的指定节点的值。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“IString”的节点值都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Set String value + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @param strValue [IN] Feature value to set + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to set specified string nodes. For value of strKey, see MvCameraNode. + The node values of IString can be set through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetStringValue( + IN void * handle, IN const char * strKey, IN const char * strValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Command型属性值 + * @param handle [IN] 设备句柄 + * @param strKey [IN] 属性键值 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 连接设备之后调用该接口可以设置指定的Command类型节点。 + strKey取值可以参考XML节点参数类型列表,表格里面数据类型为“ICommand”的节点都可以通过该接口设置,strKey参数取值对应列表里面的“名称”一列。 + + * @~english + * @brief Send Command + * @param handle [IN] Device handle + * @param strKey [IN] Key value + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to set specified Command nodes. For value of strKey, see MvCameraNode. + The node values of ICommand can be set through this interface, strKey value corresponds to the Name column. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetCommandValue(IN void * handle, IN const char * strKey); + +/********************************************************************/ /** + * @~chinese + * @brief 读内存 + * @param handle [IN] 设备句柄 + * @param pBuffer [IN][OUT] 作为返回值使用,保存读到的内存值(GEV设备内存值是按照大端模式存储的,其它协议设备按照小端存储) + * @param nAddress [IN] 待读取的内存地址,该地址可以从设备的Camera.xml文件中获取,形如xxx_RegAddr的xml节点值 + * @param nLength [IN] 待读取的内存长度 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 访问设备,读取某段寄存器的数据。 + + * @~english + * @brief Read Memory + * @param handle [IN] Device Handle + * @param pBuffer [IN][OUT] Used as a return value, save the read-in memory value ( Memory value is stored in accordance with the big end model) + * @param nAddress [IN] Memory address to be read, which can be obtained from the Camera.xml file of the device, the form xml node value of xxx_RegAddr + * @param nLength [IN] Length of the memory to be read + * @return Success, return MV_OK. Failure, return error code + * @remarks Access device, read the data from certain register. +*************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ReadMemory( + IN void * handle, IN OUT void * pBuffer, IN int64_t nAddress, IN int64_t nLength); + +/********************************************************************/ /** + * @~chinese + * @brief 写内存 + * @param handle [IN] 设备句柄 + * @param pBuffer [IN] 待写入的内存值(注意GEV设备内存值要按照大端模式存储,其它协议设备按照小端存储) + * @param nAddress [IN] 待写入的内存地址,该地址可以从设备的Camera.xml文件中获取,形如xxx_RegAddr的xml节点值 + * @param nLength [IN] 待写入的内存长度 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 访问设备,把一段数据写入某段寄存器。 + + * @~english + * @brief Write Memory + * @param handle [IN] Device Handle + * @param pBuffer [IN] Memory value to be written ( Note the memory value to be stored in accordance with the big end model) + * @param nAddress [IN] Memory address to be written, which can be obtained from the Camera.xml file of the device, the form xml node value of xxx_RegAddr + * @param nLength [IN] Length of the memory to be written + * @return Success, return MV_OK. Failure, return error code + * @remarks Access device, write a piece of data into a certain segment of register. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_WriteMemory( + IN void * handle, IN const void * pBuffer, IN int64_t nAddress, IN int64_t nLength); + +/********************************************************************/ /** + * @~chinese + * @brief 清除GenICam节点缓存 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Invalidate GenICam Nodes + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_InvalidateNodes(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 获取设备属性树XML + * @param handle [IN] 设备句柄 + * @param pData [IN][OUT] XML数据接收缓存 + * @param nDataSize [IN] 接收缓存大小 + * @param pnDataLen [IN][OUT] 实际数据大小 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 当pData为NULL或nDataSize比实际的xml文件小时,不拷贝数据,由pnDataLen返回xml文件大小 + 当pData为有效缓存地址,且缓存足够大时,拷贝完整数据保存在该缓存里面,并由pnDataLen返回xml文件实际大小。 + + * @~english + * @brief Get camera feature tree XML + * @param handle [IN] Device handle + * @param pData [IN][OUT] XML data receiving buffer + * @param nDataSize [IN] Buffer size + * @param pnDataLen [IN][OUT] Actual data length + * @return Success, return MV_OK. Failure, return error code + * @remarks When pData is NULL or nDataSize than the actual XML file hours, do not copy the data, returned by pnDataLen XML file size. + When pData is a valid cache address and the cache is large enough, copy the full data into the cache, and pnDataLen returns the actual size of the XML file. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_GetGenICamXML( + IN void * handle, IN OUT unsigned char * pData, IN unsigned int nDataSize, + IN OUT unsigned int * pnDataLen); + +/********************************************************************/ /** + * @~chinese + * @brief 获得当前节点的访问模式 + * @param handle [IN] 设备句柄 + * @param strName [IN] 节点名称 + * @param penAccessMode [IN][OUT] 节点的访问模式 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Get Access mode of cur node + * @param handle [IN] Device handle + * @param strName [IN] Name of node + * @param penAccessMode [IN][OUT] Access mode of the node + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_GetNodeAccessMode( + IN void * handle, IN const char * strName, IN OUT MV_XML_AccessMode * penAccessMode); + +/********************************************************************/ /** + * @~chinese + * @brief 获得当前节点的类型 + * @param handle [IN] 设备句柄 + * @param strName [IN] 节点名称 + * @param penInterfaceType [IN][OUT] 节点的类型 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口可以在调用万能接口之前,提前知道节点类型,方便用户选择合适的万能接口进行节点值的设置和获取。 + + * @~english + * @brief Get Interface Type of cur node + * @param handle [IN] Device handle + * @param strName [IN] Name of node + * @param penInterfaceType [IN][OUT] Interface Type of the node + * @return Success, return MV_OK. Failure, return error code + * @remarks The interface can know the node type in advance before calling the universal interface, so as to facilitate users to select the appropriate universal interface for setting and obtaining the node value. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_GetNodeInterfaceType( + IN void * handle, IN const char * strName, IN OUT MV_XML_InterfaceType * penInterfaceType); + +/********************************************************************/ /** + * @~chinese + * @brief 保存设备属性 + * @param handle [IN] 设备句柄 + * @param strFileName [IN] 属性文件名 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Save camera feature + * @param handle [IN] Device handle + * @param strFileName [IN] File name + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FeatureSave(IN void * handle, IN const char * strFileName); + +/********************************************************************/ /** + * @~chinese + * @brief 导入设备属性 + * @param handle [IN] 设备句柄 + * @param strFileName [IN] 属性文件名 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Load camera feature + * @param handle [IN] Device handle + * @param strFileName [IN] File name + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FeatureLoad(IN void * handle, IN const char * strFileName); + +/********************************************************************/ /** + * @~chinese + * @brief 从设备读取文件 + * @param handle [IN] 设备句柄 + * @param pstFileAccess [IN] 文件存取结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Read the file from the camera + * @param handle [IN] Device handle + * @param pstFileAccess [IN] File access structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FileAccessRead( + IN void * handle, IN MV_CC_FILE_ACCESS * pstFileAccess); + +/********************************************************************/ /** + * @~chinese + * @brief 从设备读取文件,文件是Data数据 + * @param handle [IN] 设备句柄 + * @param pstFileAccess [IN] 文件存取结构体 + * @return 成功,返回MV_OK;错误,返回错误码 避免文件操作权限问题读失败 + + * @~english + * @brief Read the file data from the camera + * @param handle [IN] Device handle + * @param pstFileAccess [IN] File access structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FileAccessReadEx( + IN void * handle, IN OUT MV_CC_FILE_ACCESS_EX * pstFileAccessEx); + +/********************************************************************/ /** + * @~chinese + * @brief 将文件写入设备 + * @param handle [IN] 设备句柄 + * @param pstFileAccess [IN] 文件存取结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Write the file to camera + * @param handle [IN] Device handle + * @param pstFileAccess [IN] File access structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FileAccessWrite( + IN void * handle, IN MV_CC_FILE_ACCESS * pstFileAccess); + +/********************************************************************/ /** + * @~chinese + * @brief 将缓存(buffer)写入设备 + * @param handle [IN] 设备句柄 + * @param pstFileAccessEx [IN][OUT] 文件存取结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口直接使用缓存数据,进行读写操作,避免直接操作文件出现无权限的问题,是MV_CC_FileAccessWrite的扩展接口 + + * @~english + * @brief Write the data(buffer) to camera + * @param handle [IN] Device handle + * @param pstFileAccessEx [IN][OUT] File access structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface uses cached data for read and write,solve the problem of no permissions in direct operation files, it's an extended interface of MV_CC_FileAccessWrite. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FileAccessWriteEx( + IN void * handle, IN OUT MV_CC_FILE_ACCESS_EX * pstFileAccessEx); + +/********************************************************************/ /** + * @~chinese + * @brief 获取文件存取的进度 + * @param handle [IN] 设备句柄 + * @param pstFileAccessProgress [IN][OUT] 进度内容 + * @return 成功,返回MV_OK;错误,返回错误码 (当前文件存取的状态) + + * @~english + * @brief Get File Access Progress + * @param handle [IN] Device handle + * @param pstFileAccessProgress [IN][OUT] File access Progress + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetFileAccessProgress( + IN void * handle, IN OUT MV_CC_FILE_ACCESS_PROGRESS * pstFileAccessProgress); + +/*******************Part3 ch: 相机升级 | en: Camera upgrade *******************/ + +/********************************************************************/ /** + * @~chinese + * @brief 设备本地升级 + * @param handle [IN] 设备句柄 + * @param strFilePathName [IN] 文件名 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 通过该接口可以将升级固件文件发送给设备进行升级。 + 该接口需要等待升级固件文件成功传给设备端之后再返回,响应时间可能较长。 + + * @~english + * @brief Device Local Upgrade + * @param handle [IN] Device handle + * @param strFilePathName [IN] File name + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this API to send the upgrade firmware to the device for upgrade. + This API will wait for return until the upgrade firmware is sent to the device, this response may take a long time. + For CameraLink device, it keeps sending upgrade firmware continuously. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_LocalUpgrade(IN void * handle, IN const void * strFilePathName); + +/********************************************************************/ /** + * @~chinese + * @brief 获取升级进度 + * @param handle [IN] 设备句柄 + * @param pnProcess [IN][OUT] 进度接收地址 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @~english + * @brief Get Upgrade Progress + * @param handle [IN] Device handle + * @param pnProcess [IN][OUT] Progress receiving address + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetUpgradeProcess( + IN void * handle, IN OUT unsigned int * pnProcess); + +/*******************Part4 ch: 注册异常回调和事件接口 | en: Enrol abnormal callbacks and event interface*******************/ + +/********************************************************************/ /** + * @~chinese + * @brief 注册异常消息回调,在打开设备之后调用 + * @param handle [IN] 设备句柄 + * @param cbException [IN] 异常回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口需要在MV_CC_OpenDevice打开设备之后调用。 + 设备异常断开连接后可以在回调里面获取到异常消息,GigE设备掉线之后需要先调用MV_CC_CloseDevice接口关闭设备,再调用MV_CC_OpenDevice接口重新打开设备。 + + * @~english + * @brief Register Exception Message CallBack, call after open device + * @param handle [IN] Device handle + * @param cbException [IN] Exception Message CallBack Function Pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this interface after the device is opened by MV_CC_OpenDevice. + When device is exceptionally disconnected, the exception message can be obtained from callback function. For Disconnected GigE device, + first call MV_CC_CloseDevice to shut device, and then call MV_CC_OpenDevice to reopen the device. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterExceptionCallBack( + IN void * handle, IN void(__stdcall * cbException)(unsigned int nMsgType, void * pUser), + IN void * pUser); + +/********************************************************************/ /** + * @~chinese + * @brief 注册全部事件回调,在打开设备之后调用 + * @param handle [IN] 设备句柄 + * @param cbEvent [IN] 事件回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 通过该接口设置事件回调,可以在回调函数里面获取采集、曝光等事件信息 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Register event callback, which is called after the device is opened + * @param handle [IN] Device handle + * @param cbEvent [IN] Event CallBack Function Pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this API to set the event callback function to get the event information, e.g., acquisition, exposure, and so on + This API is not supported by CameraLink device. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterAllEventCallBack( + IN void * handle, IN void(__stdcall * cbEvent)(MV_EVENT_OUT_INFO * pEventInfo, void * pUser), + IN void * pUser); + +/********************************************************************/ /** + * @~chinese + * @brief 注册单个事件回调,在打开设备之后调用 + * @param handle [IN] 设备句柄 + * @param strEventName [IN] 事件名称 + * @param cbEvent [IN] 事件回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 通过该接口设置事件回调,可以在回调函数里面获取采集、曝光等事件信息。 + 该接口不支持CameraLink设备。 + + * @~english + * @brief Register single event callback, which is called after the device is opened + * @param handle [IN] Device handle + * @param strEventName [IN] Event name + * @param cbEvent [IN] Event CallBack Function Pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this API to set the event callback function to get the event information, e.g., acquisition, exposure, and so on. + This API is not supported by CameraLink device . +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterEventCallBackEx( + IN void * handle, IN const char * strEventName, + IN void(__stdcall * cbEvent)(MV_EVENT_OUT_INFO * pEventInfo, void * pUser), IN void * pUser); + +/*******************Part5 ch: 仅GigE设备支持的接口 | en: Only support GigE interface*******************/ + +/********************************************************************/ /** + * @~chinese + * @brief 设置枚举超时时间,仅支持GigE协议,范围 1 - UINT_MAX,(包括1,不包括UINT_MAX) + * @param nMilTimeout [IN] 超时时间,默认100ms + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 在调用MV_CC_EnumDevices等枚举接口前使用该接口,可设置枚举GIGE设备的网卡最大超时时间(默认100ms),可以减少最大超时时间,来加快枚举GIGE设备的速度 + * @remarks 该接口仅支持输入无符号整数 + + * @~english + * @brief Set enumerate device timeout,only support GigE,range 1 - UINT_MAX,(the minimum value is greater than or equal to 1,maximum value less than UINT_MAX) + * @param nMilTimeout [IN] time out,default 100ms + * @return Success, return MV_OK. Failure, return error code + * @remarks Before calling enum device interfaces,call MV_GIGE_SetEnumDevTimeout to set max timeout,can reduce the maximum timeout to speed up the enumeration of GigE devices. + * @remarks This API only support input of unsigned int. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetEnumDevTimeout(IN unsigned int nMilTimeout); + +/********************************************************************/ /** + * @~chinese + * @brief 强制IP + * @param handle [IN] 设备句柄 + * @param nIP [IN] 设置的IP + * @param nSubNetMask [IN] 子网掩码 + * @param nDefaultGateWay [IN] 默认网关 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 强制设置设备网络参数(包括IP、子网掩码、默认网关),强制设置之后将需要重新创建设备句柄,仅GigEVision设备支持 + 如果设备为DHCP的状态,调用该接口强制设置设备网络参数之后设备将会重启。 + + * @~english + * @brief Force IP + * @param handle [IN] Device handle + * @param nIP [IN] IP to set + * @param nSubNetMask [IN] Subnet mask + * @param nDefaultGateWay [IN] Default gateway + * @return Success, return MV_OK. Failure, return error code + * @remarks Force setting camera network parameter (including IP address, subnet mask, default gateway). After forced setting, device handle should be created again. + This function is only supported by GigEVision camera. + If device is in DHCP status, after calling this API to force setting camera network parameter, the device will restart. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_ForceIpEx( + IN void * handle, IN unsigned int nIP, IN unsigned int nSubNetMask, + IN unsigned int nDefaultGateWay); + +/********************************************************************/ /** + * @~chinese + * @brief 配置IP方式 + * @param handle [IN] 设备句柄 + * @param nType [IN] IP类型,见MV_IP_CFG_x + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 发送命令设置设备的IP方式,如DHCP、LLA等,仅GigEVision设备支持。 + + * @~english + * @brief IP configuration method + * @param handle [IN] Device handle + * @param nType [IN] IP type, refer to MV_IP_CFG_x + * @return Success, return MV_OK. Failure, return error code + * @remarks Send command to set camera IP mode, such as DHCP and LLA, only supported by GigEVision. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetIpConfig(IN void * handle, IN unsigned int nType); + +/********************************************************************/ /** + * @~chinese + * @brief 设置仅使用某种模式,type: MV_NET_TRANS_x,不设置时,默认优先使用driver + * @param handle [IN] 设备句柄 + * @param nType [IN] 网络传输模式,见MV_NET_TRANS_x + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 通过该接口可以设置SDK内部优先使用的网络模式,默认优先使用驱动模式,仅GigEVision设备支持。 + + * @~english + * @brief Set to use only one mode,type: MV_NET_TRANS_x. When do not set, priority is to use driver by default + * @param handle [IN] Device handle + * @param nType [IN] Net transmission mode, refer to MV_NET_TRANS_x + * @return Success, return MV_OK. Failure, return error code + * @remarksSet SDK internal priority network mode through this interface, drive mode by default, only supported by GigEVision camera. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetNetTransMode(IN void * handle, IN unsigned int nType); + +/********************************************************************/ /** + * @~chinese + * @brief 获取网络传输信息 + * @param handle [IN] 设备句柄 + * @param pstInfo [IN][OUT] 信息结构体 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 通过该接口可以获取网络传输相关信息,包括已接收数据大小、丢帧数量等,在MV_CC_StartGrabbing开启采集之后调用。仅GigEVision设备支持。 + + * @~english + * @brief Get net transmission information + * @param handle [IN] Device handle + * @param pstInfo [IN][OUT] Information Structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Get network transmission information through this API, including received data size, number of lost frames. + Call this API after starting image acquiring through MV_CC_StartGrabbing. This API is supported only by GigEVision Camera. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetNetTransInfo( + IN void * handle, IN OUT MV_NETTRANS_INFO * pstInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 设置枚举命令的回复包类型 + * @param nMode [IN] 回复包类型(默认广播),0-单播,1-广播 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口只对GigE相机有效。 + + * @~english + * @brief Setting the ACK mode of devices Discovery. + * @param nMode [IN] ACK mode(Default-Broadcast),0-Unicast,1-Broadcast. + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is ONLY effective on GigE cameras. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetDiscoveryMode(IN unsigned int nMode); + +/********************************************************************/ /** + * @~chinese + * @brief 设置GVSP取流超时时间 + * @param handle [IN] 设备句柄 + * @param nMillisec [IN] 超时时间,默认300ms,范围:>10ms + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 连接设备之后,取流动作发生前,调用该接口可以设置GVSP取流超时时间。GVSP取流超时设置过短可能造成图像异常,设置过长可能造成取流时间变长。 + + * @~english + * @brief Set GVSP streaming timeout + * @param handle [IN] Device handle + * @param nMillisec [IN] Timeout, default 300ms, range: >10ms + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, and just before start streaming, + * call this interface to set GVSP streaming timeout value. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetGvspTimeout(IN void * handle, IN unsigned int nMillisec); + +/********************************************************************/ /** + * @~chinese + * @brief 获取GVSP取流超时时间 + * @param handle [IN] 设备句柄 + * @param pnMillisec [IN][OUT] 超时时间指针,以毫秒位单位 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口用于获取当前的GVSP取流超时时间 + + * @~english + * @brief Get GVSP streaming timeout + * @param handle [IN] Device handle + * @param pnMillisec [IN][OUT] Timeout, ms as unit + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to get the current GVSP streaming timeout. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetGvspTimeout( + IN void * handle, IN OUT unsigned int * pnMillisec); + +/********************************************************************/ /** + * @~chinese + * @brief 设置GVCP命令超时时间 + * @param handle [IN] 设备句柄 + * @param nMillisec [IN] 超时时间,默认500ms,范围:0-10000ms + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 连接设备之后调用该接口可以设置GVCP命令超时时间。 + + * @~english + * @brief Set GVCP cammand timeout + * @param handle [IN] Device handle + * @param nMillisec [IN] Timeout, default 500ms, range: 0-10000ms + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to set GVCP command timeout time. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetGvcpTimeout(IN void * handle, IN unsigned int nMillisec); + +/********************************************************************/ /** + * @~chinese + * @brief 获取GVCP命令超时时间 + * @param handle [IN] 设备句柄 + * @param pnMillisec [IN][OUT] 超时时间指针,以毫秒位单位 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口用于获取当前的GVCP超时时间。 + + * @~english + * @brief Get GVCP cammand timeout + * @param handle [IN] Device handle + * @param pnMillisec [IN][OUT] Timeout, ms as unit + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to get the current GVCP timeout. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetGvcpTimeout( + IN void * handle, IN OUT unsigned int * pnMillisec); + +/********************************************************************/ /** + * @~chinese + * @brief 设置重传GVCP命令次数 + * @param handle [IN] 设备句柄 + * @param nRetryGvcpTimes [IN] 重传次数,范围:0-100 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口用于在GVCP包传输异常时,增加重传的次数,在一定程度上可以避免设备掉线,范围为0-100。 + + * @~english + * @brief Set the number of retry GVCP cammand + * @param handle [IN] Device handle + * @param nRetryGvcpTimes [IN] The number of retries,rang:0-100 + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to increase The Times of retransmission when GVCP packet transmission is abnormal,and to some extent, + it can avoid dropping the camera, with a range of 0-100. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetRetryGvcpTimes( + IN void * handle, IN unsigned int nRetryGvcpTimes); + +/********************************************************************/ /** + * @~chinese + * @brief 获取重传GVCP命令次数 + * @param handle [IN] 设备句柄 + * @param pnRetryGvcpTimes [IN][OUT] 重传次数指针 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口用于获取当前的GVCP重传次数,默认3次。 + + * @~english + * @brief Get the number of retry GVCP cammand + * @param handle [IN] Device handle + * @param pnRetryGvcpTimes [IN][OUT] The number of retries + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to get the current number of GVCP retransmissions, which defaults to 3. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetRetryGvcpTimes( + IN void * handle, IN OUT unsigned int * pnRetryGvcpTimes); + +/********************************************************************/ /** + * @~chinese + * @brief 获取最佳的packet size,该接口目前只支持GigE设备 + * @param handle [IN] 设备句柄 + * @return 最佳packetsize + * @remarks 获取最佳的packet size,对应GigEVision设备是SCPS。 + 该接口需要在MV_CC_OpenDevice之后、MV_CC_StartGrabbing之前调用。 + 该接口不支持CameraLink设备、U3V设备。 + 该接口不支持GenTL设备(协议不支持),如果是GenTL方式添加的网口相机,建议根据网络实际情况配置GevSCPSPacketSize,或者配置1500。 + + * @~english + * @brief Get the optimal Packet Size, Only support GigE Camera + * @param handle [IN] Device handle + * @return Optimal packetsize + * @remarks To get optimized packet size, for GigEVision device is SCPS, for USB3Vision device is the size of packet read from drive each time, + and it is the size of a packet transported on the network. The interface should be called after MV_CC_OpenDevice and before MV_CC_StartGrabbing. + This API is not supported by CameraLink device and U3V device. + This interface does not support GenTL devices (protocol not supported). If a network camera is added in GenTL mode, it is recommended to configure GevSCPSPacketSize according to the actual network situation,or 1500. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetOptimalPacketSize(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 设置是否打开重发包支持,及重发包设置 + * @param handle [IN] 设备句柄 + * @param bEnable [IN] 是否支持重发包 + * @param nMaxResendPercent [IN] 最大重发比 + * @param nResendTimeout [IN] 重发超时时间 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 连接设备之后调用该接口可以设置重发包属性,仅GigEVision设备支持。 + + * @~english + * @brief Set whethe to enable resend, and set resend + * @param handle [IN] Device handle + * @param bEnable [IN] enable resend + * @param nMaxResendPercent [IN] Max resend persent + * @param nResendTimeout [IN] Resend timeout + * @return Success, return MV_OK. Failure, return error code + * @remarks After the device is connected, call this interface to set resend packet properties, only supported by GigEVision camera. + ************************************************************************/ +#ifndef __cplusplus +MV_CAMCTRL_API int __stdcall MV_GIGE_SetResend( + IN void * handle, IN unsigned int bEnable, IN unsigned int nMaxResendPercent, + IN unsigned int nResendTimeout); +#else +MV_CAMCTRL_API int __stdcall MV_GIGE_SetResend( + IN void * handle, IN unsigned int bEnable, IN unsigned int nMaxResendPercent = 10, + IN unsigned int nResendTimeout = 50); +#endif + +/********************************************************************/ /** + * @~chinese + * @brief 设置重传命令最大尝试次数 + * @param handle [IN] 设备句柄 + * @param nRetryTimes [IN] 重传命令最大尝试次数,默认20 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口必须在调用MV_GIGE_SetResend开启重传包功能之后调用,否则失败且返回MV_E_CALLORDER + + * @~english + * @brief set the max resend retry times + * @param handle [IN] Device handle + * @param nRetryTimes [IN] The max times to retry resending lost packets,default 20 + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface MUST be called after enabling resending lost packets by calling MV_GIGE_SetResend, + * otherwise would fail and return MV_E_CALLORDER. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetResendMaxRetryTimes( + IN void * handle, IN unsigned int nRetryTimes); + +/********************************************************************/ /** + * @~chinese + * @brief 获取重传命令最大尝试次数 + * @param handle [IN] 设备句柄 + * @param pnRetryTimes [IN][OUT] 重传命令最大尝试次数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口必须在调用MV_GIGE_SetResend开启重传包功能之后调用,否则失败且返回MV_E_CALLORDER + + * @~english + * @brief get the max resend retry times + * @param handle [IN] Device handle + * @param pnRetryTimes [IN][OUT] The max times to retry resending lost packets + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface MUST be called after enabling resending lost packets by calling MV_GIGE_SetResend, + * otherwise would fail and return MV_E_CALLORDER. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetResendMaxRetryTimes( + IN void * handle, IN OUT unsigned int * pnRetryTimes); + +/********************************************************************/ /** + * @~chinese + * @brief 设置同一重传包多次请求之间的时间间隔 + * @param handle [IN] 设备句柄 + * @param nMillisec [IN] 同一重传包多次请求之间的时间间隔,默认10ms + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口必须在调用MV_GIGE_SetResend开启重传包功能之后调用,否则失败且返回MV_E_CALLORDER + + * @~english + * @brief set time interval between same resend requests + * @param handle [IN] Device handle + * @param nMillisec [IN] The time interval between same resend requests,default 10ms + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface MUST be called after enabling resending lost packets by calling MV_GIGE_SetResend, + * otherwise would fail and return MV_E_CALLORDER. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetResendTimeInterval( + IN void * handle, IN unsigned int nMillisec); + +/********************************************************************/ /** + * @~chinese + * @brief 获取同一重传包多次请求之间的时间间隔 + * @param handle [IN] 设备句柄 + * @param pnMillisec [IN][OUT] 同一重传包多次请求之间的时间间隔 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口必须在调用MV_GIGE_SetResend开启重传包功能之后调用,否则失败且返回MV_E_CALLORDER + + * @~english + * @brief get time interval between same resend requests + * @param handle [IN] Device handle + * @param pnMillisec [IN][OUT] The time interval between same resend requests + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface MUST be called after enabling resending lost packets by calling MV_GIGE_SetResend, + * otherwise would fail and return MV_E_CALLORDER. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetResendTimeInterval( + IN void * handle, IN OUT unsigned int * pnMillisec); + +/********************************************************************/ /** + * @~chinese + * @brief 设置传输模式,可以为单播模式、组播模式等 + * @param handle [IN] 设备句柄 + * @param stTransmissionType [IN] 传输模式结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过该接口可以设置传输模式为单播、组播等模式,仅GigEVision设备支持。 + + * @~english + * @brief Set transmission type,Unicast or Multicast + * @param handle [IN] Device handle + * @param stTransmissionType [IN] Struct of transmission type + * @return Success, return MV_OK. Failure, return error code + * @remarks Call this API to set the transmission mode as single cast mode and multicast mode. And this API is only valid for GigEVision camera. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetTransmissionType( + IN void * handle, IN MV_TRANSMISSION_TYPE * pstTransmissionType); + +/********************************************************************/ /** + * @~chinese + * @brief 发出动作命令 + * @param pstActionCmdInfo [IN] 动作命令信息 + * @param pstActionCmdResults [IN][OUT] 动作命令返回信息列表 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 仅GigEVision设备支持。 + + * @~english + * @brief Issue Action Command + * @param pstActionCmdInfo [IN] Action Command + * @param pstActionCmdResults [IN][OUT] Action Command Result List + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is supported only by GigEVision camera. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_IssueActionCommand( + IN MV_ACTION_CMD_INFO * pstActionCmdInfo, IN OUT MV_ACTION_CMD_RESULT_LIST * pstActionCmdResults); + +/********************************************************************/ /** + * @~chinese + * @brief 获取组播状态 + * @param pstDevInfo [IN] 设备信息结构体 + * @param pbStatus [IN][OUT] 组播状态,true:组播状态,false:非组播 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口用于判断设备当前是否处于组播状态,解决客户端枚举时需要打开设备判断组播的问题。 + + * @~english + * @brief Get Multicast Status + * @param pstDevInfo [IN] Device Information Structure + * @param pbStatus [IN][OUT] Status of Multicast + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to determine whether the camera is currently in multicast state, + and to solve the problem that the client needs to turn on the camera to determine multicast when enumerating. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetMulticastStatus( + IN MV_CC_DEVICE_INFO * pstDevInfo, IN OUT bool * pbStatus); + +/*******************Part6 ch: 仅CameraLink 设备支持的接口 | en: Only support camlink device interface*******************/ + +/***********************************************************************************************************/ /** + * @~chinese + * @brief 设置设备波特率 + * @param handle [IN] 设备句柄 + * @param nBaudrate [IN] 设置的波特率值,数值参考CameraParams.h中宏定义,如#define MV_CAML_BAUDRATE_9600 0x00000001 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口支持在设备未连接时调用。 + 因硬件/系统/外部干扰等因素,配置高波特率可能导致通信异常,建议配置波特率最大小于115200 + + * @~english + * @brief Set device baudrate using one of the CL_BAUDRATE_XXXX value + * @param handle [IN] Device handle + * @param nBaudrate [IN] baud rate to set. Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_CAML_BAUDRATE_9600 0x00000001 + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is supported only by CameraLink device. + This API support calls when devices are not connected. + Due to hardware/system/external interference and other factors, configuring a high baud rate may cause abnormal communication. + It is recommended to configure a baud rate of less than 115200 +************************************************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_SetDeviceBaudrate(IN void * handle, IN unsigned int nBaudrate); + +/********************************************************************/ /** + * @~chinese + * @brief 获取设备波特率 + * @param handle [IN] 设备句柄 + * @param pnCurrentBaudrate [IN][OUT] 波特率信息指针,数值参考CameraParams.h中宏定义,如#define MV_CAML_BAUDRATE_9600 0x00000001 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口支持在设备未连接时调用。 + + * @~english + * @brief Returns the current device baudrate, using one of the CL_BAUDRATE_XXXX value + * @param handle [IN] Device handle + * @param pnCurrentBaudrate [IN][OUT] Return pointer of baud rate to user. Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_CAML_BAUDRATE_9600 0x00000001 + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is supported only by CameraLink device. + This API support calls when devices are not connected. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_GetDeviceBaudrate( + IN void * handle, IN OUT unsigned int * pnCurrentBaudrate); + +/********************************************************************/ /** + * @~chinese + * @brief 获取设备与主机间连接支持的波特率 + * @param handle [IN] 设备句柄 + * @param pnBaudrateAblity [IN][OUT] 支持的波特率信息的指针。所支持波特率的或运算结果,单个数值参考CameraParams.h中宏定义,如MV_CAML_BAUDRATE_9600 0x00000001 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口支持在设备未连接时调用。 + + * @~english + * @brief Returns supported baudrates of the combined device and host interface + * @param handle [IN] Device handle + * @param pnBaudrateAblity [IN][OUT] Return pointer of the supported baudrates to user. 'OR' operation results of the supported baudrates. Refer to the 'CameraParams.h' for single value definitions, for example, MV_CAML_BAUDRATE_9600 0x00000001 + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is supported only by CameraLink device. + This API support calls when devices are not connected. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_GetSupportBaudrates( + IN void * handle, IN OUT unsigned int * pnBaudrateAblity); + +/********************************************************************/ /** + * @~chinese + * @brief 设置串口操作等待时长 + * @param handle [IN] 设备句柄 + * @param nMillisec [IN] 串口操作的等待时长, 单位为ms + * @return 成功,返回MV_OK,失败,返回错误码 + + * @~english + * @brief Sets the timeout for operations on the serial port + * @param handle [IN] Device handle + * @param nMillisec [IN] Timeout in [ms] for operations on the serial port. + * @return Success, return MV_OK. Failure, return error code +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_SetGenCPTimeOut(IN void * handle, IN unsigned int nMillisec); + +/*******************Part7 ch: 仅U3V设备支持的接口 | en: Only support U3V device interface*******************/ + +/********************************************************************/ /** + * @~chinese + * @brief 设置U3V的传输包大小 + * @param handle [IN] 设备句柄 + * @param nTransferSize [IN] 传输的包大小, Byte,默认为1M,rang:>=0x400,建议最大值:[windows] rang <= 0x400000;[Linux] rang <= 0x200000 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 增加传输包大小可以适当降低取流时的CPU占用率。但不同的PC和不同USB扩展卡存在不同的兼容性,如果该参数设置过大可能会出现取不到图像的风险。 + + * @~english + * @brief Set transfer size of U3V device + * @param handle [IN] Device handle + * @param nTransferSize [IN] Transfer size,Byte,default:1M,rang:>=0x400,Recommended maximum: [windows] rang <= 0x400000; [Linux] rang <= 0x200000 + * @return Success, return MV_OK. Failure, return error code + * @remarks Increasing the transmission packet size can reduce the CPU utilization at the time of fetching. However, different PCS and different USB extension CARDS have different compatibility, and if this parameter is set too large, there may be the risk of not getting the image. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_SetTransferSize( + IN void * handle, IN unsigned int nTransferSize); + +/********************************************************************/ /** + * @~chinese + * @brief 获取U3V的传输包大小 + * @param handle [IN] 设备句柄 + * @param pnTransferSize [IN][OUT] 传输的包大小指针, Byte + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口用于获取当前的U3V传输包大小,默认1M。 + + * @~english + * @brief Get transfer size of U3V device + * @param handle [IN] Device handle + * @param pnTransferSize [IN][OUT] Transfer size,Byte + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to get the current U3V transfer packet size, default 1M. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_GetTransferSize( + IN void * handle, IN OUT unsigned int * pnTransferSize); + +/********************************************************************/ /** + * @~chinese + * @brief 设置U3V的传输通道个数 + * @param handle [IN] 设备句柄 + * @param nTransferWays [IN] 传输通道个数,范围:1-10 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 用户可以根据PC的性能、设备出图帧率、图像大小和内存使用率等因素对该参数进行调节。但不同的PC和不同的USB扩展卡存在不同的兼容性。 + + * @~english + * @brief Set transfer ways of U3V device + * @param handle [IN] Device handle + * @param nTransferWays [IN] Transfer ways,rang:1-10 + * @return Success, return MV_OK. Failure, return error code + * @remarks Users can adjust this parameter according to PC performance, camera image frame rate, image size, memory utilization and other factors. But different PCS and different USB expansion CARDS have different compatibility. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_SetTransferWays( + IN void * handle, IN unsigned int nTransferWays); + +/********************************************************************/ /** + * @~chinese + * @brief 获取U3V的传输通道个数 + * @param handle [IN] 设备句柄 + * @param pnTransferWays [IN][OUT] 传输通道个数指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口用于获取当前的U3V异步取流节点个数,2000W设备的MONO8默认为3个,YUV为默认2个,RGB为默认1个,其它情况默认8个节点。 + + * @~english + * @brief Get transfer ways of U3V device + * @param handle [IN] Device handle + * @param pnTransferWays [IN][OUT] Transfer ways + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to get the current number of U3V asynchronous feed nodes. For 2000W camera, MONO8 defaults to 3, YUV defaults to 2, RGB defaults to 1, and other cases default to 8 nodes. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_GetTransferWays( + IN void * handle, IN OUT unsigned int * pnTransferWays); + +/********************************************************************/ /** + * @~chinese + * @brief 注册流异常消息回调,在打开设备之后调用(只支持U3V相机,不支持GenTL设备) + * @param handle [IN] 设备句柄 + * @param cbException [IN] 异常回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @~english + * @brief Register exception stream callBack, call after open device (only support U3V Camera, don't support GenTL Device) + * @param handle [IN] Device handle + * @param cbException [IN] Exception callback function pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_RegisterStreamExceptionCallBack( + IN void * handle, + IN void(__stdcall * cbException)(MV_CC_STREAM_EXCEPTION_TYPE enExceptionType, void * pUser), + IN void * pUser); + +/********************************************************************/ /** + * @~chinese + * @brief 设置U3V的事件缓存节点个数 + * @param handle [IN] 设备句柄 + * @param nEventNodeNum [IN] 事件缓存节点个数,范围:1-64 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口用于设置当前的U3V事件缓存节点个数,默认情况下为5个。 + + * @~english + * @brief Set the number of U3V device event cache nodes + * @param handle [IN] Device handle + * @param nEventNodeNum [IN] Event Node Number + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to set the current number of U3V event nodes. default to 5 nodes. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_SetEventNodeNum( + IN void * handle, IN unsigned int nEventNodeNum); + +/********************************************************************/ /** + * @~chinese + * @brief 设置U3V的同步读写超时时间,范围为1000 ~ UINT_MAX(包含1000,不包含UINT_MAX) + * @param handle [IN] 设备句柄 + * @param nMills [IN] 设置同步读写超时时间,默认时间为1000ms + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 增加设置同步读取时间接口,兼容部分相机配置参数很慢,超过1000ms的情况 + + * @~english + * @brief Set U3V Synchronisation timeout,range is 1000 ~ UINT_MAX(minimum value greater than 1000,maximum value less than UINT_MAX) + * @param handle [IN] Device handle + * @param nMills [IN] set synchronisation timeout(ms),default 1000ms + * @return Success, return MV_OK. Failure, return error code + * @remarks Increasing the SetSyncTimeOut can compatible with some camera configuretion parameters very slow,more than 1000ms +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_SetSyncTimeOut(IN void * handle, IN unsigned int nMills); + +/********************************************************************/ /** + * @~chinese + * @brief 获取U3V相机同步读写超时时间 + * @param handle [IN] 设备句柄 + * @param pnMills [IN][OUT] 获取的超时时间(ms) + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks 该接口用于获取当前的U3V同步读写超时时间大小,默认1000ms。 + + * @~english + * @brief Get U3V Camera Synchronisation timeout + * @param handle [IN] Device handle + * @param pnMills [IN][OUT] Get Synchronisation time(ms) + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface is used to get the current U3V timeout, default 1000ms. +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_USB_GetSyncTimeOut(IN void * handle, IN OUT unsigned int * pnMills); + +/*******************Part8 ch: GenTL相关接口 | en: GenTL related interface*******************/ + +/******************************************************************************/ /** + * @~chinese + * @brief 通过GenTL枚举Interfaces + * @param pstIFList [IN][OUT] Interfaces列表 + * @param strGenTLPath [IN] GenTL的cti文件路径 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks Interfaces列表的内存是在SDK内部分配的,多线程调用该接口时会进行设备列表内存的释放和申请 + 建议尽量避免多线程枚举操作。 + + * @~english + * @brief Enumerate Interfaces with GenTL + * @param pstIFList [IN][OUT] Interfaces List + * @param strGenTLPath [IN] GenTL cti file path + * @return Success, return MV_OK. Failure, return error code + * @remarks The memory of the Interfaces list is allocated within the SDK. When the interface is invoked by multiple threads, the memory of the device list will be released and applied.\n + It is recommended to avoid multithreaded enumeration operations as much as possible. + *******************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_EnumInterfacesByGenTL( + IN OUT MV_GENTL_IF_INFO_LIST * pstIFList, IN const char * strGenTLPath); + +/********************************************************************/ /** + * @~chinese + * @brief 卸载cti库 + * @param pGenTLPath [IN] 枚举卡时加载的cti文件路径 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 卸载前需要保证通过该cti枚举出的相机已全部关闭,否则报错前置条件错误。 + + * @~english + * @brief Unload cti library + * @param pGenTLPath [IN] GenTL cti file path + * @return Success, return MV_OK. Failure, return error code + * @remarks Make sure that all devices enumerated by this cti are already closed. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_UnloadGenTLLibrary(IN const char * pGenTLPath); + +/*****************************************************************************************************/ /** + * @~chinese + * @brief 通过GenTL Interface枚举设备 + * @param pstIFInfo [IN] Interface信息 + * @param pstDevList [IN][OUT] 设备列表 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设备列表的内存是在SDK内部分配的,多线程调用该接口时会进行设备列表内存的释放和申请 + 建议尽量避免多线程枚举操作。 + + * @~english + * @brief Enumerate Devices with GenTL interface + * @param pstIFInfo [IN] Interface information + * @param pstDevList [IN][OUT] Device List + * @return Success, return MV_OK. Failure, return error code + * @remarks The memory of the list is allocated within the SDK. When the interface is invoked by multiple threads, the memory of the device list will be released and applied.\n + It is recommended to avoid multithreaded enumeration operations as much as possible. + *****************************************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_EnumDevicesByGenTL( + IN MV_GENTL_IF_INFO * pstIFInfo, IN OUT MV_GENTL_DEV_INFO_LIST * pstDevList); + +/********************************************************************/ /** + * @~chinese + * @brief 通过GenTL设备信息创建设备句柄 + * @param handle [IN][OUT] 设备句柄 + * @param pstDevInfo [IN] 设备信息结构体指针 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 根据输入的设备信息,创建库内部必须的资源和初始化内部模块。 + + * @~english + * @brief Create Device Handle with GenTL Device Info + * @param handle [IN][OUT] Device handle + * @param pstDevInfo [IN] Device Information + * @return Success, return MV_OK. Failure, return error code + * @remarks Create required resources within library and initialize internal module according to input device information. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_CreateHandleByGenTL( + IN OUT void ** handle, IN const MV_GENTL_DEV_INFO * pstDevInfo); + +/*******************Part9 ch: 图像保存、格式转换等相关接口 | en: Related image save and format convert interface*******************/ + +/********************************************************************/ /** + * @~chinese + * @brief 保存图片,支持Bmp和Jpeg. + * @param handle [IN] 设备句柄 + * @param pstSaveParam [IN][OUT] 保存图片参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过该接口可以将从设备采集到的原始图像数据转换成JPEG或者BMP等格式并存放在指定内存中,然后用户可以将转换之后的数据直接保存成图片文件。 + 该接口调用无接口顺序要求,有图像源数据就可以进行转换,可以先调用MV_CC_GetOneFrameTimeout或者MV_CC_RegisterImageCallBackEx设置回调函数,获取一帧图像数据,然后再通过该接口转换格式。 + 该接口支持图像 宽、高、总长最大至 UINT_MAX, 其中MV_CC_SaveImageEx2支持 宽、高、总长最大至 USHRT_MAX + JPEG格式最大支持宽高为65500 + + * @~english + * @brief Save image, support Bmp and Jpeg. + * @param handle [IN] Device handle + * @param pstSaveParam [IN][OUT] Save image parameters structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Once there is image data, you can call this API to convert the data. + You can also call MV_CC_GetOneFrameTimeout or MV_CC_RegisterImageCallBackEx or MV_CC_GetImageBuffer to get one image frame and set the callback function, and then call this API to convert the format. + Comparing with the API MV_CC_SaveImageEx2, this API support the parameter nWidth/nHeight/nDataLen to UINT_MAX. + JPEG format supports a maximum width and height of 65500 + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SaveImageEx3( + IN void * handle, IN OUT MV_SAVE_IMAGE_PARAM_EX3 * pstSaveParam); + +/********************************************************************/ /** + * @~chinese + * @brief 保存图像到文件 + * @param handle [IN] 设备句柄 + * @param pstSaveFileParam [IN][OUT] 保存图片文件参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口支持BMP/JPEG/PNG/TIFF。 + 该接口支持图像 宽、高、总长最大至 UINT_MAX, 其中MV_CC_SaveImageToFile支持 宽、高、总长最大至 USHRT_MAX + 该接口是MV_CC_SaveImageToFile接口的扩展接口 + JPEG格式最大支持宽高为65500 + * @~english + * @brief Save the image file. + * @param handle [IN] Device handle + * @param pstSaveFileParam [IN][OUT] Save the image file parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API support BMP/JPEG/PNG/TIFF. + Comparing with the API MV_CC_SaveImageToFile, this API support the parameter nWidth/nHeight/nDataLen to UINT_MAX. + JPEG format supports a maximum width and height of 65500 + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SaveImageToFileEx( + IN void * handle, IN OUT MV_SAVE_IMAGE_TO_FILE_PARAM_EX * pstSaveFileParam); + +/********************************************************************/ /** + * @~chinese + * @brief 保存3D点云数据,支持PLY、CSV和OBJ三种格式 + * @param handle [IN] 设备句柄 + * @param pstPointDataParam [IN][OUT] 保存点云数据参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 3D数据格式保存成3D文件格式,支持PLY/CSV/OBJ, + 目前支持PixelType_Gvsp_Coord3D_ABC32、PixelType_Gvsp_Coord3D_ABC32f、PixelType_Gvsp_Coord3D_AB32、PixelType_Gvsp_Coord3D_AB32f、PixelType_Gvsp_Coord3D_AC32、PixelType_Gvsp_Coord3D_AC32f, + 暂不支持其他3D格式。 + + * @~english + * @brief Save 3D point data, support PLY、CSV and OBJ + * @param handle [IN] Device handle + * @param pstPointDataParam [IN][OUT] Save 3D point data parameters structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Save the 3D data format to 3D file format,support PLY、CSV and OBJ, + only support PixelType_Gvsp_Coord3D_ABC32、PixelType_Gvsp_Coord3D_ABC32f、PixelType_Gvsp_Coord3D_AB32、PixelType_Gvsp_Coord3D_AB32f、PixelType_Gvsp_Coord3D_AC32、PixelType_Gvsp_Coord3D_AC32f + Other 3D format is not supported now. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SavePointCloudData( + IN void * handle, IN OUT MV_SAVE_POINT_CLOUD_PARAM * pstPointDataParam); + +/********************************************************************/ /** + * @~chinese + * @brief 图像旋转 + * @param handle [IN] 设备句柄 + * @param pstRotateParam [IN][OUT] 图像旋转参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口只支持MONO8/RGB24/BGR24格式数据的90/180/270度旋转。 + + * @~english + * @brief Rotate Image + * @param handle [IN] Device handle + * @param pstRotateParam [IN][OUT] Rotate image parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API only support 90/180/270 rotation of data in the MONO8/RGB24/BGR24 format. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RotateImage( + IN void * handle, IN OUT MV_CC_ROTATE_IMAGE_PARAM * pstRotateParam); + +/********************************************************************/ /** + * @~chinese + * @brief 图像翻转 + * @param handle [IN] 设备句柄 + * @param pstFlipParam [IN][OUT] 图像翻转参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口只支持MONO8/RGB24/BGR24格式数据的垂直和水平翻转。 + + * @~english + * @brief Flip Image + * @param handle [IN] Device handle + * @param pstFlipParam [IN][OUT] Flip image parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API only support vertical and horizontal reverse of data in the MONO8/RGB24/BGR24 format. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_FlipImage( + IN void * handle, IN OUT MV_CC_FLIP_IMAGE_PARAM * pstFlipParam); + +/********************************************************************/ /** + * @~chinese + * @brief 像素格式转换 + * @param handle [IN] 设备句柄 + * @param pstCvtParam [IN][OUT] 像素格式转换参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过将接口可以将从设备采集到的原始图像数据转换成用户所需的像素格式并存放在指定内存中。 + 该接口调用无接口顺序要求,有图像源数据就可以进行转换,可以先调用MV_CC_GetOneFrameTimeout或者MV_CC_RegisterImageCallBackEx设置回调函数, + 获取一帧图像数据,然后再通过该接口转换格式。如果设备当前采集图像是JPEG压缩的格式,则不支持调用该接口进行转换。 + 该接口支持图像 宽、高、总长最大至 UINT_MAX, 其中MV_CC_ConvertPixelType支持 宽、高、总长最大至 USHRT_MAX + + * @~english + * @brief Pixel format conversion + * @param handle [IN] Device handle + * @param pstCvtParam [IN][OUT] Convert Pixel Type parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is used to transform the collected original data to pixel format and save to specified memory. + There is no order requirement to call this API, the transformation will execute when there is image data. + First call MV_CC_GetOneFrameTimeout or MV_CC_RegisterImageCallBackEx to set callback function, and get a frame of image data, + then call this API to transform the format. + Comparing with the API MV_CC_ConvertPixelType, this API support the parameter nWidth/nHeight/nSrcDataLen to UINT_MAX. + + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ConvertPixelTypeEx( + IN void * handle, IN OUT MV_CC_PIXEL_CONVERT_PARAM_EX * pstCvtParam); + +/********************************************************************/ /** + * @~chinese + * @brief 设置插值算法类型 + * @param handle [IN] 设备句柄 + * @param nBayerCvtQuality [IN] Bayer的插值方法 0-快速 1-均衡 2-最优(默认为最优) + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设置内部图像转换接口的Bayer插值算法类型参数,MV_CC_ConvertPixelType、MV_CC_GetImageForRGB/BGR接口内部使用的插值算法是该接口所设定的。 + + * @~english + * @brief Interpolation algorithm type setting + * @param handle [IN] Device handle + * @param nBayerCvtQuality [IN] Bayer interpolation method 0-Fast 1-Equilibrium 2-Optimal + * @return Success, return MV_OK. Failure, return error code + * @remarks Set the bell interpolation quality parameters of the internal image conversion interface, + and the interpolation algorithm used in the MV_CC_ConvertPixelType and MV_CC_GetImageForRGB/BGR interfaces is set by this interface. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerCvtQuality( + IN void * handle, IN unsigned int nBayerCvtQuality); + +/********************************************************************/ /** + * @~chinese + * @brief 插值算法平滑使能设置 + * @param handle [IN] 设备句柄 + * @param bFilterEnable [IN] 平滑使能(默认关闭) + * @return 成功,返回#MV_OK;错误,返回错误码 + * @remarks 设置内部图像转换接口的贝尔插值平滑使能参数,MV_CC_ConvertPixelType、MV_CC_SaveImageEx3接口内部使用的插值算法是该接口所设定的。 + + * @~english + * @brief Filter type of the bell interpolation quality algorithm setting + * @param handle [IN] Device handle + * @param bFilterEnable [IN] Filter type enable + * @return Success, return MV_OK. Failure, return error code + * @remarks Set the bell interpolation filter type parameters of the internal image conversion interface, + and the interpolation algorithm used in the MV CC ConvertPixelType and MV_CC_SaveImageEx3 interfaces is set by this interface. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerFilterEnable(IN void * handle, IN bool bFilterEnable); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Bayer格式的Gamma值 + * @param handle [IN] 设备句柄 + * @param fBayerGammaValue [IN] Gamma值:0.1 ~ 4.0 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设置该值后,在调用MV_CC_ConvertPixelType、MV_CC_SaveImageEx3、MV_CC_SaveImageToFile接口将Bayer8/10/12/16格式转成RGB24/48, RGBA32/64,BGR24/48,BGRA32/64时起效。 + + * @~english + * @brief Set Gamma value + * @param handle [IN] Device handle + * @param fBayerGammaValue [IN] Gamma value[0.1,4.0] + * @return Success, return MV_OK. Failure, return error code + * @remarks After setting the value, it works when calling MV_CC_ConvertPixelType\MV_CC_SaveImageEx3\MV_CC_SaveImageToFile API convert Bayer8/10/12/16 to RGB24/48, RGBA32/64,BGR24/48,BGRA32/64. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerGammaValue(IN void * handle, IN float fBayerGammaValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Mono8/Bayer8/10/12/16格式的Gamma值 + * @param handle [IN] 设备句柄 + * @param MvGvspPixelType enSrcPixelType [IN] 像素格式,支持PixelType_Gvsp_Mono8,Bayer8/10/12/16 + * @param fGammaValue [IN] Gamma值:0.1 ~ 4.0 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设置Mono8的gamma值后,在调用MV_CC_ConvertPixelType接口将Mono8转成Mono8时gamma值起效。 + * @remarks 设置Bayer8/10/12/16的gamma值后,在调用MV_CC_ConvertPixelType、MV_CC_SaveImageToFile、MV_CC_SaveImageEx3接口将Bayer8/10/12/16转RGB24/48, RGBA32/64,BGR24/48,BGRA32/64时gamma值起效。 + * @remarks 该接口兼容MV_CC_SetBayerGammaValue接口,新增支持Mono8像素格式 + + * @~english + * @brief Set Gamma value + * @param handle [IN] Device handle + * @param MvGvspPixelType enSrcPixelType [IN] PixelType,support PixelType_Gvsp_Mono8,Bayer8/10/12/16 + * @param fGammaValue [IN] Gamma value:0.1~ 4.0 + * @remarks After setting the gamma of Mono8 ,the gamma value takes effect when calling MV_CC_ConvertPixelType converts Mono8 to Mono8. + * @remarks After setting the gamma of Bayer8/10/12/16, the gamma value takes effect when calling MV_CC_ConvertPixelType\MV_CC_SaveImageToFile\MV_CC_SaveImageEx3 converts Bayer8/10/12/16 to RGB24/48,RGBA32/64,BGR24/48,BGRA32/64. + * @remarks This API compatible with MV_CC_SetBayerGammaValue, adds Mono8 PixelType. + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetGammaValue( + IN void * handle, IN enum MvGvspPixelType enSrcPixelType, IN float fGammaValue); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Bayer格式的Gamma信息 + * @param handle [IN] 设备句柄 + * @param pstGammaParam [IN] Gamma信息 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 设置该信息后,在调用MV_CC_ConvertPixelType、MV_CC_SaveImageEx3、MV_CC_SaveImageToFile接口将Bayer8/10/12/16格式转成RGB24/48, RGBA32/64,BGR24/48,BGRA32/64时Gamma值起效。 + + * @~english + * @brief Set Gamma param + * @param handle [IN] Device handle + * @param pstGammaParam [IN] Gamma param + * @return Success, return MV_OK. Failure, return error code + * @remarks After setting the param, it work in the calling MV_CC_ConvertPixelType\MV_CC_SaveImageEx3\MV_CC_SaveImageToFile API convert Bayer8/10/12/16 to RGB24/48, RGBA32/64,BGR24/48,BGRA32/64. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerGammaParam( + IN void * handle, IN MV_CC_GAMMA_PARAM * pstGammaParam); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Bayer格式的CCM使能和矩阵,量化系数默认1024 + * @param handle [IN] 设备句柄 + * @param pstCCMParam [IN] CCM参数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 开启CCM并设置CCM矩阵后,在调用MV_CC_ConvertPixelType、MV_CC_SaveImageEx3接口将Bayer8/10/12/16格式转成RGB24/48, RGBA32/64,BGR24/48,BGRA32/64时起效。 + + * @~english + * @brief Set CCM param,Scale default 1024 + * @param handle [IN] Device handle + * @param pstCCMParam [IN] CCM parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks After enable the color correction and set the color correction matrix, It work in the calling MV_CC_ConvertPixelType\MV_CC_SaveImageEx3 API convert Bayer8/10/12/16 to RGB24/48, RGBA32/64,BGR24/48,BGRA32/64. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerCCMParam( + IN void * handle, IN MV_CC_CCM_PARAM * pstCCMParam); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Bayer格式的CCM使能和矩阵 + * @param handle [IN] 设备句柄 + * @param pstCCMParam [IN] CCM参数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 开启CCM并设置CCM矩阵后,在调用MV_CC_ConvertPixelType、MV_CC_SaveImageEx3接口将Bayer8/10/12/16格式转成RGB24/48, RGBA32/64,BGR24/48,BGRA32/64时起效。 + + * @~english + * @brief Set CCM param + * @param handle [IN] Device handle + * @param pstCCMParam [IN] CCM parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks After enable the color correction and set the color correction matrix, It work in the calling MV_CC_ConvertPixelType\MV_CC_SaveImageEx3 API convert Bayer8/10/12/16 to RGB24/48, RGBA32/64,BGR24/48,BGRA32/64. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerCCMParamEx( + IN void * handle, IN MV_CC_CCM_PARAM_EX * pstCCMParam); + +/********************************************************************/ /** + * @~chinese + * @brief 图像对比度调节 + * @param handle [IN] 设备句柄 + * @param pstContrastParam [IN][OUT] 对比度调节参数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks + + * @~english + * @brief Adjust image contrast + * @param handle [IN] Device handle + * @param pstContrastParam [IN][OUT] Contrast parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ImageContrast( + IN void * handle, IN OUT MV_CC_CONTRAST_PARAM * pstContrastParam); + +/********************************************************************/ /** + * @~chinese + * @brief 无损解码 + * @param handle [IN] 设备句柄 + * @param pstDecodeParam [IN][OUT] 无损解码参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 将从相机中取到的无损压缩码流解码成裸数据,同时支持解析当前相机实时图像的水印信息(如果输入的无损码流不是当前相机或者不是实时取流的,则水印解析可能异常); + 若解码失败,请检查以下情况:(1)需要CPU支持 SSE AVX指令集(2)若当前帧异常(丢包等),可能导致解码异常(3)相机出图异常, 即使不丢包也会异常 + + * @~english + * @brief High Bandwidth Decode + * @param handle [IN] Device handle + * @param pstDecodeParam [IN][OUT] High Bandwidth Decode parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Decode the lossless compressed data from the camera into raw data,At the same time, it supports parsing the watermark information of the real-time image of the current camera (if the input lossless code stream is not the current camera or is not real-time streaming, the watermark parsing may be abnormal); + If decoding fails, please check the following: (1) The CPU is required to support the SSE AVX instruction set. (2) If the current frame is abnormal (packet loss, etc.), it may cause decoding exceptions. (3) The camera plot is abnormal, even if there is no packet loss, it may cause exceptions + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_HB_Decode( + IN void * handle, IN OUT MV_CC_HB_DECODE_PARAM * pstDecodeParam); + +/********************************************************************/ /** + * @~chinese + * @brief 在图像上绘制矩形框辅助线 + * @param handle [IN] 设备句柄 + * @param pRectInfo [IN] 矩形辅助线的信息 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口仅支持windows平台 + + * @~english + * @brief Draw Rect Auxiliary Line + * @param handle [IN] Device handle + * @param pRectInfo [IN] Rect Auxiliary Line Info + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface only supports windows platform. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_DrawRect(IN void * handle, IN MVCC_RECT_INFO * pRectInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 在图像上绘制圆形辅助线 + * @param handle [IN] 设备句柄 + * @param pCircleInfo [IN] 圆形辅助线的信息 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口仅支持windows平台 + + * @~english + * @brief Draw Circle Auxiliary Line + * @param handle [IN] Device Handle + * @param pCircleInfo [IN] Circle Auxiliary Line Info + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface only supports windows platform. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_DrawCircle(IN void * handle, IN MVCC_CIRCLE_INFO * pCircleInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 在图像上绘制线条 + * @param handle [IN] 设备句柄 + * @param pLinesInfo [IN] 线条辅助线信息 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口仅支持windows平台 + + * @~english + * @brief Draw Line Auxiliary Line + * @param handle [IN] Device Handle + * @param pLinesInfo [IN] Linear Auxiliary Line Info + * @return Success, return MV_OK. Failure, return error code + * @remarks This interface only supports windows platform. + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_DrawLines(IN void * handle, IN MVCC_LINES_INFO * pLinesInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 开始录像 + * @param handle [IN] 设备句柄 + * @param pstRecordParam [IN] 录像参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + 该接口最大支持Width*Height为8000*8000大小,否则会导致调用MV_CC_InputOneFrame接口错误。 + + * @~english + * @brief Start Record + * @param handle [IN] Device handle + * @param pstRecordParam [IN] Record param structure + * @return Success, return MV_OK. Failure, return error code + The maximum supported width * height of this interface is 8000 * 8000, otherwise it will result in calling MV_ CC_ InputOneFrame interface error. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_StartRecord( + IN void * handle, IN MV_CC_RECORD_PARAM * pstRecordParam); + +/********************************************************************/ /** + * @~chinese + * @brief 输入录像数据 + * @param handle [IN] 设备句柄 + * @param pstInputFrameInfo [IN] 录像数据结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Input RAW data to Record + * @param handle [IN] Device handle + * @param pstInputFrameInfo [IN] Record data structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_InputOneFrame( + IN void * handle, IN MV_CC_INPUT_FRAME_INFO * pstInputFrameInfo); + +/********************************************************************/ /** + * @~chinese + * @brief 停止录像 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Stop Record + * @param handle [IN] Device handle + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_StopRecord(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 打开获取或设置相机参数的GUI界面 + * @param handle [IN] 设备句柄 + * @return 成功,返回MV_OK,失败,返回错误码。 + * @remarks 通过MV_CC_OpenDevice连接设备后,可以通过该接口获取或设置设备参数。 + * @remarks 限制:在同一线程中多相机同时调用该接口,只能打开当前一个GUI界面,需要关闭当前相机GUI界面后,才可打开另一个相机的GUI界面(后续版本优化) + 该接口仅支持windows平台 + + * @~english + * @brief Open the GUI interface for getting or setting camera parameters + * @param handle [IN] Device handle + * @return Success, return MV_OK, Failure, return error code. + * @remarks After connecting to device through MV_CC_OpenDevice, use this interface to get or set device params. + * @remarks Limit: calling this interface multiple times in the same thread can only open one GUI interface. + You need to wait until the previous GUI interface is closed before opening the next GUI interface.(Subsequent version optimization) + This interface only supports windows platform. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_OpenParamsGUI(IN void * handle); + +/********************************************************************/ /** + * @~chinese + * @brief 重构图像(用于分时曝光功能) + * @param handle [IN] 设备句柄 + * @param pstReconstructParam [IN][OUT] 重构图像参数 + * @return 成功,返回MV_OK,失败,返回错误码。 + * @remarks 图像分割支持任意像素格式,图像分割应与线阵相机的“MultiLightControl”节点搭配使用,该节点可设置多个不同的曝光值,如MultiLightControl=2, + 相机会将两个不同曝光值所对应的两张图像交叠合并为一张图像(实际高度为两张图像的高度)发送给上层应用程序, + 调用该接口并传入分时曝光值nExposureNum为2,可将相机发送的一张图像分割为2张图像,这两张图像分别对应一个曝光值。 + 若使用普通相机或未打开线阵相机的“MultiLightControl”节点,则图像分割无意义,只是将图像按行分割为2,3,4张图像, + 每张图像的高度变为原图像的1/2,1/3,1/4(由nExposureNum决定)。 + + * @~english + * @brief Reconstruct Image(For time-division exposure function) + * @param handle [IN] Device handle + * @param pstReconstructParam [IN][OUT] Reconstruct image parameters + * @return Success, return MV_OK, Failure, return error code. + * @remarks Image segmentation supports any pixel format. Image segmentation should be used with the "MultiLightControl" node of the linear array camera. This node can set multiple different exposure values, such as MultiLightControl=2, + The camera will overlap and merge two images corresponding to two different exposure values into one image (the actual height is the height of the two images) and send it to the upper application. + Call the interface and pass in nExposureNum is two. One image sent by the camera can be divided into two images, each of which corresponds to an exposure value. + If an ordinary camera is used or the "MultiLightControl" node of the linear array camera is not turned on, the image segmentation is meaningless, but the image is divided into 2, 3, and 4 images by line. + The height of each image becomes 1/2, 1/3, 1/4 of the original image (determined by nExposureNum). + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ReconstructImage( + IN void * handle, IN OUT MV_RECONSTRUCT_IMAGE_PARAM * pstReconstructParam); + +#ifdef __cplusplus +} +#endif + +#endif //_MV_CAMERA_CTRL_H_ diff --git a/io/hikrobot/include/MvErrorDefine.h b/io/hikrobot/include/MvErrorDefine.h new file mode 100644 index 0000000..9e8f81d --- /dev/null +++ b/io/hikrobot/include/MvErrorDefine.h @@ -0,0 +1,143 @@ + +#ifndef _MV_ERROR_DEFINE_H_ +#define _MV_ERROR_DEFINE_H_ + +#include "MvISPErrorDefine.h" + +/********************************************************************/ +/// \~chinese +/// \name 正确码定义 +/// @{ +/// \~english +/// \name Definition of correct code +/// @{ +#define MV_OK 0x00000000 ///< \~chinese 成功,无错误 \~english Successed, no error +/// @} + +/********************************************************************/ +/// \~chinese +/// \name 通用错误码定义:范围0x80000000-0x800000FF +/// @{ +/// \~english +/// \name Definition of General error code +/// @{ +#define MV_E_HANDLE \ + 0x80000000 ///< \~chinese 错误或无效的句柄 \~english Error or invalid handle +#define MV_E_SUPPORT \ + 0x80000001 ///< \~chinese 不支持的功能 \~english Not supported function +#define MV_E_BUFOVER 0x80000002 ///< \~chinese 缓存已满 \~english Buffer overflow +#define MV_E_CALLORDER \ + 0x80000003 ///< \~chinese 函数调用顺序错误 \~english Function calling order error +#define MV_E_PARAMETER \ + 0x80000004 ///< \~chinese 错误的参数 \~english Incorrect parameter +#define MV_E_RESOURCE \ + 0x80000006 ///< \~chinese 资源申请失败 \~english Applying resource failed +#define MV_E_NODATA 0x80000007 ///< \~chinese 无数据 \~english No data +#define MV_E_PRECONDITION \ + 0x80000008 ///< \~chinese 前置条件有误,或运行环境已发生变化 \~english Precondition error, or running environment changed +#define MV_E_VERSION \ + 0x80000009 ///< \~chinese 版本不匹配 \~english Version mismatches +#define MV_E_NOENOUGH_BUF \ + 0x8000000A ///< \~chinese 传入的内存空间不足 \~english Insufficient memory +#define MV_E_ABNORMAL_IMAGE \ + 0x8000000B ///< \~chinese 异常图像,可能是丢包导致图像不完整 \~english Abnormal image, maybe incomplete image because of lost packet +#define MV_E_LOAD_LIBRARY \ + 0x8000000C ///< \~chinese 动态导入DLL失败 \~english Load library failed +#define MV_E_NOOUTBUF \ + 0x8000000D ///< \~chinese 没有可输出的缓存 \~english No Avaliable Buffer +#define MV_E_ENCRYPT 0x8000000E ///< \~chinese 加密错误 \~english Encryption error +#define MV_E_OPENFILE 0x8000000F ///< \~chinese 打开文件出现错误 \~english open file error +#define MV_E_UNKNOW 0x800000FF ///< \~chinese 未知的错误 \~english Unknown error +/// @} + +/********************************************************************/ +/// \~chinese +/// \name GenICam系列错误:范围0x80000100-0x800001FF +/// @{ +/// \~english +/// \name GenICam Series Error Codes: Range from 0x80000100 to 0x800001FF +/// @{ +#define MV_E_GC_GENERIC 0x80000100 ///< \~chinese 通用错误 \~english General error +#define MV_E_GC_ARGUMENT \ + 0x80000101 ///< \~chinese 参数非法 \~english Illegal parameters +#define MV_E_GC_RANGE \ + 0x80000102 ///< \~chinese 值超出范围 \~english The value is out of range +#define MV_E_GC_PROPERTY 0x80000103 ///< \~chinese 属性 \~english Property +#define MV_E_GC_RUNTIME \ + 0x80000104 ///< \~chinese 运行环境有问题 \~english Running environment error +#define MV_E_GC_LOGICAL 0x80000105 ///< \~chinese 逻辑错误 \~english Logical error +#define MV_E_GC_ACCESS \ + 0x80000106 ///< \~chinese 节点访问条件有误 \~english Node accessing condition error +#define MV_E_GC_TIMEOUT 0x80000107 ///< \~chinese 超时 \~english Timeout +#define MV_E_GC_DYNAMICCAST \ + 0x80000108 ///< \~chinese 转换异常 \~english Transformation exception +#define MV_E_GC_UNKNOW \ + 0x800001FF ///< \~chinese GenICam未知错误 \~english GenICam unknown error +/// @} + +/********************************************************************/ +/// \~chinese +/// \name GigE_STATUS对应的错误码:范围0x80000200-0x800002FF +/// @{ +/// \~english +/// \name GigE_STATUS Error Codes: Range from 0x80000200 to 0x800002FF +/// @{ +#define MV_E_NOT_IMPLEMENTED \ + 0x80000200 ///< \~chinese 命令不被设备支持 \~english The command is not supported by device +#define MV_E_INVALID_ADDRESS \ + 0x80000201 ///< \~chinese 访问的目标地址不存在 \~english The target address being accessed does not exist +#define MV_E_WRITE_PROTECT \ + 0x80000202 ///< \~chinese 目标地址不可写 \~english The target address is not writable +#define MV_E_ACCESS_DENIED \ + 0x80000203 ///< \~chinese 设备无访问权限 \~english No permission +#define MV_E_BUSY \ + 0x80000204 ///< \~chinese 设备忙,或网络断开 \~english Device is busy, or network disconnected +#define MV_E_PACKET \ + 0x80000205 ///< \~chinese 网络包数据错误 \~english Network data packet error +#define MV_E_NETER 0x80000206 ///< \~chinese 网络相关错误 \~english Network error +#define MV_E_IP_CONFLICT \ + 0x80000221 ///< \~chinese 设备IP冲突 \~english Device IP conflict +/// @} + +/********************************************************************/ +/// \~chinese +/// \name USB_STATUS对应的错误码:范围0x80000300-0x800003FF +/// @{ +/// \~english +/// \name USB_STATUS Error Codes: Range from 0x80000300 to 0x800003FF +/// @{ +#define MV_E_USB_READ \ + 0x80000300 ///< \~chinese 读usb出错 \~english Reading USB error +#define MV_E_USB_WRITE \ + 0x80000301 ///< \~chinese 写usb出错 \~english Writing USB error +#define MV_E_USB_DEVICE \ + 0x80000302 ///< \~chinese 设备异常 \~english Device exception +#define MV_E_USB_GENICAM 0x80000303 ///< \~chinese GenICam相关错误 \~english GenICam error +#define MV_E_USB_BANDWIDTH \ + 0x80000304 ///< \~chinese 带宽不足 \~english Insufficient bandwidth +#define MV_E_USB_DRIVER \ + 0x80000305 ///< \~chinese 驱动不匹配或者未装驱动 \~english Driver mismatch or unmounted drive +#define MV_E_USB_UNKNOW \ + 0x800003FF ///< \~chinese USB未知的错误 \~english USB unknown error +/// @} + +/********************************************************************/ +/// \~chinese +/// \name 升级时对应的错误码:范围0x80000400-0x800004FF +/// @{ +/// \~english +/// \name Upgrade Error Codes: Range from 0x80000400 to 0x800004FF +/// @{ +#define MV_E_UPG_FILE_MISMATCH \ + 0x80000400 ///< \~chinese 升级固件不匹配 \~english Firmware mismatches +#define MV_E_UPG_LANGUSGE_MISMATCH \ + 0x80000401 ///< \~chinese 升级固件语言不匹配 \~english Firmware language mismatches +#define MV_E_UPG_CONFLICT \ + 0x80000402 ///< \~chinese 升级冲突(设备已经在升级了再次请求升级即返回此错误) \~english Upgrading conflicted (repeated upgrading requests during device upgrade) +#define MV_E_UPG_INNER_ERR \ + 0x80000403 ///< \~chinese 升级时设备内部出现错误 \~english Camera internal error during upgrade +#define MV_E_UPG_UNKNOW \ + 0x800004FF ///< \~chinese 升级时未知错误 \~english Unknown error during upgrade +/// @} + +#endif //_MV_ERROR_DEFINE_H_ diff --git a/io/hikrobot/include/MvISPErrorDefine.h b/io/hikrobot/include/MvISPErrorDefine.h new file mode 100644 index 0000000..6da4ba0 --- /dev/null +++ b/io/hikrobot/include/MvISPErrorDefine.h @@ -0,0 +1,93 @@ + +#ifndef _MV_ISP_ERROR_DEFINE_H_ +#define _MV_ISP_ERROR_DEFINE_H_ + +/************************************************************************ +* 来自ISP算法库的错误码 +************************************************************************/ +// 通用类型 +#define MV_ALG_OK 0x00000000 //处理正确 +#define MV_ALG_ERR 0x10000000 //不确定类型错误 + +// 能力检查 +#define MV_ALG_E_ABILITY_ARG 0x10000001 //能力集中存在无效参数 + +// 内存检查 +#define MV_ALG_E_MEM_NULL 0x10000002 //内存地址为空 +#define MV_ALG_E_MEM_ALIGN 0x10000003 //内存对齐不满足要求 +#define MV_ALG_E_MEM_LACK 0x10000004 //内存空间大小不够 +#define MV_ALG_E_MEM_SIZE_ALIGN 0x10000005 //内存空间大小不满足对齐要求 +#define MV_ALG_E_MEM_ADDR_ALIGN 0x10000006 //内存地址不满足对齐要求 + +// 图像检查 +#define MV_ALG_E_IMG_FORMAT 0x10000007 //图像格式不正确或者不支持 +#define MV_ALG_E_IMG_SIZE 0x10000008 //图像宽高不正确或者超出范围 +#define MV_ALG_E_IMG_STEP 0x10000009 //图像宽高与step参数不匹配 +#define MV_ALG_E_IMG_DATA_NULL 0x1000000A //图像数据存储地址为空 + +// 输入输出参数检查 +#define MV_ALG_E_CFG_TYPE 0x1000000B //设置或者获取参数类型不正确 +#define MV_ALG_E_CFG_SIZE 0x1000000C //设置或者获取参数的输入、输出结构体大小不正确 +#define MV_ALG_E_PRC_TYPE 0x1000000D //处理类型不正确 +#define MV_ALG_E_PRC_SIZE 0x1000000E //处理时输入、输出参数大小不正确 +#define MV_ALG_E_FUNC_TYPE 0x1000000F //子处理类型不正确 +#define MV_ALG_E_FUNC_SIZE 0x10000010 //子处理时输入、输出参数大小不正确 + +// 运行参数检查 +#define MV_ALG_E_PARAM_INDEX 0x10000011 //index参数不正确 +#define MV_ALG_E_PARAM_VALUE 0x10000012 //value参数不正确或者超出范围 +#define MV_ALG_E_PARAM_NUM 0x10000013 //param_num参数不正确 + +// 接口调用检查 +#define MV_ALG_E_NULL_PTR 0x10000014 //函数参数指针为空 +#define MV_ALG_E_OVER_MAX_MEM 0x10000015 //超过限定的最大内存 +#define MV_ALG_E_CALL_BACK 0x10000016 //回调函数出错 + +// 算法库加密相关检查 +#define MV_ALG_E_ENCRYPT 0x10000017 //加密错误 +#define MV_ALG_E_EXPIRE 0x10000018 //算法库使用期限错误 + +// 内部模块返回的基本错误类型 +#define MV_ALG_E_BAD_ARG 0x10000019 //参数范围不正确 +#define MV_ALG_E_DATA_SIZE 0x1000001A //数据大小不正确 +#define MV_ALG_E_STEP 0x1000001B //数据step不正确 + +// cpu指令集支持错误码 +#define MV_ALG_E_CPUID 0x1000001C //cpu不支持优化代码中的指令集 + +#define MV_ALG_WARNING 0x1000001D //警告 + +#define MV_ALG_E_TIME_OUT 0x1000001E //算法库超时 +#define MV_ALG_E_LIB_VERSION 0x1000001F //算法版本号出错 +#define MV_ALG_E_MODEL_VERSION 0x10000020 //模型版本号出错 +#define MV_ALG_E_GPU_MEM_ALLOC 0x10000021 //GPU内存分配错误 +#define MV_ALG_E_FILE_NON_EXIST 0x10000022 //文件不存在 +#define MV_ALG_E_NONE_STRING 0x10000023 //字符串为空 +#define MV_ALG_E_IMAGE_CODEC 0x10000024 //图像解码器错误 +#define MV_ALG_E_FILE_OPEN 0x10000025 //打开文件错误 +#define MV_ALG_E_FILE_READ 0x10000026 //文件读取错误 +#define MV_ALG_E_FILE_WRITE 0x10000027 //文件写错误 +#define MV_ALG_E_FILE_READ_SIZE 0x10000028 //文件读取大小错误 +#define MV_ALG_E_FILE_TYPE 0x10000029 //文件类型错误 +#define MV_ALG_E_MODEL_TYPE 0x1000002A //模型类型错误 +#define MV_ALG_E_MALLOC_MEM 0x1000002B //分配内存错误 +#define MV_ALG_E_BIND_CORE_FAILED 0x1000002C //线程绑核失败 + +// 降噪特有错误码 +#define MV_ALG_E_DENOISE_NE_IMG_FORMAT 0x10402001 //噪声特性图像格式错误 +#define MV_ALG_E_DENOISE_NE_FEATURE_TYPE 0x10402002 //噪声特性类型错误 +#define MV_ALG_E_DENOISE_NE_PROFILE_NUM 0x10402003 //噪声特性个数错误 +#define MV_ALG_E_DENOISE_NE_GAIN_NUM 0x10402004 //噪声特性增益个数错误 +#define MV_ALG_E_DENOISE_NE_GAIN_VAL 0x10402005 //噪声曲线增益值输入错误 +#define MV_ALG_E_DENOISE_NE_BIN_NUM 0x10402006 //噪声曲线柱数错误 +#define MV_ALG_E_DENOISE_NE_INIT_GAIN 0x10402007 //噪声估计初始化增益设置错误 +#define MV_ALG_E_DENOISE_NE_NOT_INIT 0x10402008 //噪声估计未初始化 +#define MV_ALG_E_DENOISE_COLOR_MODE 0x10402009 //颜色空间模式错误 +#define MV_ALG_E_DENOISE_ROI_NUM 0x1040200a //图像ROI个数错误 +#define MV_ALG_E_DENOISE_ROI_ORI_PT 0x1040200b //图像ROI原点错误 +#define MV_ALG_E_DENOISE_ROI_SIZE 0x1040200c //图像ROI大小错误 +#define MV_ALG_E_DENOISE_GAIN_NOT_EXIST 0x1040200d //输入的相机增益不存在(增益个数已达上限) +#define MV_ALG_E_DENOISE_GAIN_BEYOND_RANGE 0x1040200e //输入的相机增益不在范围内 +#define MV_ALG_E_DENOISE_NP_BUF_SIZE 0x1040200f //输入的噪声特性内存大小错误 + +#endif //_MV_ISP_ERROR_DEFINE_H_ diff --git a/io/hikrobot/include/MvObsoleteInterfaces.h b/io/hikrobot/include/MvObsoleteInterfaces.h new file mode 100644 index 0000000..02c4cd2 --- /dev/null +++ b/io/hikrobot/include/MvObsoleteInterfaces.h @@ -0,0 +1,1960 @@ + +#ifndef _MV_OBSOLETE_INTERFACES_H_ +#define _MV_OBSOLETE_INTERFACES_H_ + +#include "CameraParams.h" +#include "MvErrorDefine.h" +#include "ObsoleteCamParams.h" + +/** +* @brief 动态库导入导出定义 +* @brief Import and export definition of the dynamic library +*/ +#ifndef MV_CAMCTRL_API + +#if (defined(_WIN32) || defined(WIN64)) +#if defined(MV_CAMCTRL_EXPORTS) +#define MV_CAMCTRL_API __declspec(dllexport) +#else +#define MV_CAMCTRL_API __declspec(dllimport) +#endif +#else +#ifndef __stdcall +#define __stdcall +#endif + +#ifndef MV_CAMCTRL_API +#define MV_CAMCTRL_API +#endif +#endif + +#endif + +#ifndef IN +#define IN +#endif + +#ifndef OUT +#define OUT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* 不建议使用的接口 */ +/* Interfaces not recommended */ +/************************************************************************/ +/************************************************************************ + * @fn MV_CC_GetImageInfo + * @brief 获取图像基本信息 + * @param handle [IN] 设备句柄 + * @param pstInfo [IN][OUT] 返回给调用者有关相机图像基本信息结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @fn MV_CC_GetImageInfo + * @brief Get basic information of image + * @param handle [IN] Device handle + * @param pstInfo [IN][OUT] Structure pointer of image basic information + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetImageInfo( + IN void * handle, IN OUT MV_IMAGE_BASIC_INFO * pstInfo); + +/************************************************************************ + * @fn MV_CC_GetTlProxy + * @brief 获取GenICam代理 + * @param handle [IN] 句柄地址 + * @return GenICam代理类指针 ,正常返回值非NULL;异常返回NULL + + * @fn MV_CC_GetTlProxy + * @brief Get GenICam proxy + * @param handle [IN] Handle address + * @return GenICam proxy pointer, normal, return non-NULL; exception, return NULL + ************************************************************************/ +MV_CAMCTRL_API void * __stdcall MV_CC_GetTlProxy(IN void * handle); + +/*********************************************************************** + * @fn MV_XML_GetRootNode + * @brief 获取根节点 + * @param handle [IN] 句柄 + * @param pstNode [OUT] 根节点信息结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_XML_GetRootNode + * @brief Get root node + * @param handle [IN] Handle + * @param pstNode [OUT] Root node information structure + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_GetRootNode( + IN void * handle, IN OUT MV_XML_NODE_FEATURE * pstNode); + +/*********************************************************************** + * @fn MV_XML_GetChildren + * @brief 从xml中获取指定节点的所有子节点,根节点为Root + * @param handle [IN] 句柄 + * @param pstNode [IN] 根节点信息结构体 + * @param pstNodesList [OUT] 节点列表结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_XML_GetChildren + * @brief Get all children node of specific node from xml, root node is Root + * @param handle [IN] Handle + * @param pstNode [IN] Root node information structure + * @param pstNodesList [OUT] Node information structure + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_GetChildren( + IN void * handle, IN MV_XML_NODE_FEATURE * pstNode, IN OUT MV_XML_NODES_LIST * pstNodesList); + +/*********************************************************************** + * @fn MV_XML_GetNodeFeature + * @brief 获得当前节点的属性 + * @param handle [IN] 句柄 + * @param pstNode [IN] 根节点信息结构体 + * @param pstFeature [OUT] 当前节点属性结构体, + pstFeature 具体结构体内容参考 MV_XML_FEATURE_x + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_XML_GetNodeFeature + * @brief Get current node feature + * @param handle [IN] Handle + * @param pstNode [IN] Root node information structure + * @param pstFeature [OUT] Current node feature structure + Details of pstFeature refer to MV_XML_FEATURE_x + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_GetNodeFeature( + IN void * handle, IN MV_XML_NODE_FEATURE * pstNode, IN OUT void * pstFeature); + +/*********************************************************************** + * @fn MV_XML_UpdateNodeFeature + * @brief 更新节点 + * @param handle [IN] 句柄 + * @param enType [IN] 节点类型 + * @param pstFeature [OUT] 当前节点属性结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_XML_UpdateNodeFeature + * @brief Update node + * @param handle [IN] Handle + * @param enType [IN] Node type + * @param pstFeature [OUT] Current node feature structure + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_UpdateNodeFeature( + IN void * handle, IN enum MV_XML_InterfaceType enType, IN void * pstFeature); + +// 有节点需要更新时的回调函数 +// 当调用MV_XML_UpdateNodeFeature接口更新节点属性时,注册的回调函数cbUpdate会在pstNodesList中返回与之相关联的节点 +/*********************************************************************** + * @fn MV_XML_RegisterUpdateCallBack + * @brief 注册更新回调 + * @param handle [IN] 句柄 + * @param cbUpdate [IN] 回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_XML_RegisterUpdateCallBack + * @brief Register update callback + * @param handle [IN] Handle + * @param cbUpdate [IN] Callback function pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_XML_RegisterUpdateCallBack( + IN void * handle, + IN void(__stdcall * cbUpdate)( + enum MV_XML_InterfaceType enType, void * pstFeature, MV_XML_NODES_LIST * pstNodesList, + void * pUser), + IN void * pUser); + +/************************************************************************/ +/* 弃用的接口(存在更优化的接口可替换) */ +/* Abandoned interface */ +/************************************************************************/ +/*********************************************************************** + * @fn MV_CC_GetOneFrame + * @brief 获取一帧图像,此函数为查询式获取,每次调用查询内部缓存有 + 无数据,有数据则范围数据,无数据返回错误码 + (该接口已弃用,建议改用 MV_CC_GetOneFrameTimeOut接口) + * @param handle [IN] 句柄 + * @param pData [OUT] 图像数据接收指针 + * @param nDataSize [IN] 接收缓存大小 + * @param pFrameInfo [OUT] 图像信息结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_CC_GetOneFrame + * @brief Get one frame data, this function is using query to get data, + query whether the internal cache has data, return data if there has, return error code if no data + (This interface is abandoned, it is recommended to use the MV_CC_GetOneFrameTimeOut) + * @param handle [IN] Handle + * @param pData [OUT] Recevied image data pointer + * @param nDataSize [IN] Recevied buffer size + * @param pFrameInfo [OUT] Image information structure + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetOneFrame( + IN void * handle, IN OUT unsigned char * pData, IN unsigned int nDataSize, + IN OUT MV_FRAME_OUT_INFO * pFrameInfo); + +/*********************************************************************** + * @fn MV_CC_GetOneFrameEx + * @brief 获取一帧trunck数据,此函数为查询式获取,每次调用查询内部 + 缓存有无数据,有数据则范围数据,无数据返回错误码 + (该接口已弃用,建议改用 MV_CC_GetOneFrameTimeOut接口) + * @param handle [IN] 句柄 + * @param pData [OUT] 图像数据接收指针 + * @param nDataSize [IN] 接收缓存大小 + * @param pFrameInfo [OUT] 图像信息结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_CC_GetOneFrameEx + * @brief Get one frame of trunck data, this function is using query to get data, + query whether the internal cache has data, return data if there has, return error code if no data + (This interface is abandoned, it is recommended to use the MV_CC_GetOneFrameTimeOut) + * @param handle [IN] Handle + * @param pData [OUT] Recevied image data pointer + * @param nDataSize [IN] Recevied buffer size + * @param pFrameInfo [OUT] Image information structure + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetOneFrameEx( + IN void * handle, IN OUT unsigned char * pData, IN unsigned int nDataSize, + IN OUT MV_FRAME_OUT_INFO_EX * pFrameInfo); + +/*********************************************************************** + * @fn MV_CC_RegisterImageCallBack + * @brief 注册图像数据回调(该接口已弃用,建议改用 MV_CC_RegisterImageCallBackEx接口) + * @param handle [IN] 句柄 + * @param cbOutput [IN] 回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_CC_RegisterImageCallBack + * @brief Register image data callback (This interface is abandoned, it is recommended to use the MV_CC_RegisterImageCallBackEx) + * @param handle [IN] Handle + * @param cbOutput [IN] Callback function pointer + * @param pUser [IN] User defined variable + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterImageCallBack( + void * handle, + void(__stdcall * cbOutput)(unsigned char * pData, MV_FRAME_OUT_INFO * pFrameInfo, void * pUser), + void * pUser); + +/************************************************************************ + * @fn MV_CC_SaveImage + * @brief 保存图片(该接口已弃用,建议改用 MV_CC_SaveImageEx2接口) + * @param pSaveParam [IN][OUT] 保存图片参数结构体 + pData; // [IN] 输入数据缓存 + nDataLen; // [IN] 输入数据大小 + enPixelType; // [IN] 输入数据的像素格式 + nWidth; // [IN] 图像宽 + nHeight; // [IN] 图像高 + pImageBuffer; // [OUT] 输出图片缓存 + nImageLen; // [OUT] 输出图片大小 + nBufferSize; // [IN] 提供的输出缓冲区大小 + enImageType; // [IN] 输出图片格式 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_CC_SaveImage + * @brief Save image (This interface is abandoned, it is recommended to use the MV_CC_SaveImageEx) + * @param pSaveParam [IN][OUT] Save image parameters structure + pData; // [IN] Input data buffer + nDataLen; // [IN] Input data size + enPixelType; // [IN] Input data pixel format + nWidth; // [IN] Width + nHeight; // [IN] Height + pImageBuffer; // [OUT] Output image buffer + nImageLen; // [OUT] Output image size + nBufferSize; // [IN] Provided output buffer size + enImageType; // [IN] Output image type + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SaveImage(IN OUT MV_SAVE_IMAGE_PARAM * pSaveParam); + +/************************************************************************ + * @fn MV_CC_SaveImageEx + * @brief 保存图片,支持Bmp和Jpeg.编码质量在50-99之前 (该接口已弃用,建议改用 MV_CC_SaveImageEx2接口) + * @param pSaveParam [IN][OUT] 保存图片参数结构体 + pData; // [IN] 输入数据缓存 + nDataLen; // [IN] 输入数据大小 + enPixelType; // [IN] 输入数据的像素格式 + nWidth; // [IN] 图像宽 + nHeight; // [IN] 图像高 + pImageBuffer; // [OUT] 输出图片缓存 + nImageLen; // [OUT] 输出图片大小 + nBufferSize; // [IN] 提供的输出缓冲区大小 + enImageType; // [IN] 输出图片格式 + nJpgQuality; // [IN] 编码质量, (50-99] + nReserved[4]; + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_CC_SaveImageEx + * @brief Save image, support Bmp and Jpeg. Encoding quality, (50-99] + * @param pSaveParam [IN][OUT] Save image parameters structure + pData; // [IN] Input data buffer + nDataLen; // [IN] Input data size + enPixelType; // [IN] Pixel format of input data + nWidth; // [IN] Image width + nHeight; // [IN] Image height + pImageBuffer; // [OUT] Output image buffer + nImageLen; // [OUT] Output image size + nBufferSize; // [IN] Output buffer size provided + enImageType; // [IN] Output image format + nJpgQuality; // [IN] Encoding quality, (50-99] + nReserved[4]; + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SaveImageEx(IN OUT MV_SAVE_IMAGE_PARAM_EX * pSaveParam); + +/********************************************************************/ /** + * @~chinese + * @brief Bayer噪声估计(该接口已弃用,建议改用ISP Tool方式进行标定) + * @param handle [IN] 设备句柄 + * @param pstNoiseEstimateParam [IN][OUT] Bayer噪声估计参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口只支持Bayer8/Bayer10/Bayer12格式,其它Bayer格式需先转成Bayer8/Bayer10/Bayer12格式。\n + 该接口只有在打开我司特定彩色相机后才可以正常使用,当相机被断开或者掉线后,继续使用该接口会报错。 + + * @~english + * @brief Noise estimate of Bayer format + * @param handle [IN] Device handle + * @param pstNoiseEstimateParam [IN][OUT] Noise estimate parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API only support Bayer8/Bayer10/Bayer12 format, other Bayer format must Convert to Bayer8/Bayer10/Bayer12 format.\n + This API is only available when the camera is turned on, and when the camera is disconnected or disconnected, continuing to use This API will return an error. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_BayerNoiseEstimate( + IN void * handle, IN OUT MV_CC_BAYER_NOISE_ESTIMATE_PARAM * pstNoiseEstimateParam); + +/********************************************************************/ /** + * @~chinese + * @brief Bayer空域降噪(该接口已弃用,建议改用ISP Tool方式进行降噪) + * @param handle [IN] 设备句柄 + * @param pstSpatialDenoiseParam [IN][OUT] Bayer空域降噪参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口只支持Bayer8/Bayer10/Bayer12格式,其它Bayer格式需先转成Bayer8/Bayer10/Bayer12格式。\n + 该接口只有在打开我司特定彩色相机后才可以正常使用,当相机被断开或者掉线后,继续使用该接口会报错。 + + * @~english + * @brief Spatial Denoise of Bayer format + * @param handle [IN] Device handle + * @param pstSpatialDenoiseParam [IN][OUT] Spatial Denoise parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API only support Bayer8/Bayer10/Bayer12 format, other Bayer format must Convert to Bayer8/Bayer10/Bayer12 format.\n + This API is only available when the camera is turned on, and when the camera is disconnected or disconnected, continuing to use This API will return an error. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_BayerSpatialDenoise( + IN void * handle, IN OUT MV_CC_BAYER_SPATIAL_DENOISE_PARAM * pstSpatialDenoiseParam); + +/********************************************************************/ /** + * @~chinese + * @brief 设置Bayer格式的CLUT使能和信息(该接口已弃用,建议改用ISP Tool方式进行设置) + * @param handle [IN] 设备句柄 + * @param pstCLUTParam [IN] CLUT参数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 开启CLUT并设置CLUT信息后,在调用MV_CC_ConvertPixelType、MV_CC_SaveImageEx2接口将Bayer8/10/12/16格式转成RGB24/48, RGBA32/64,BGR24/48,BGRA32/64时起效。 + + * @~english + * @brief Set CLUT param + * @param handle [IN] Device handle + * @param pstCLUTParam [IN] CLUT parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks After enable the CLUT and set CLUT, It work in the calling MV_CC_ConvertPixelType\MV_CC_SaveImageEx2 API convert Bayer8/10/12/16 to RGB24/48, RGBA32/64,BGR24/48,BGRA32/64. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBayerCLUTParam( + IN void * handle, IN MV_CC_CLUT_PARAM * pstCLUTParam); + +/********************************************************************/ /** + * @~chinese + * @brief 图像锐化(该接口已弃用,建议改用ISP Tool方式进行锐化) + * @param handle [IN] 设备句柄 + * @param pstSharpenParam [IN] 锐化参数 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Image sharpen + * @param handle [IN] Device handle + * @param pstSharpenParam [IN] Sharpen parameter structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ImageSharpen( + IN void * handle, IN OUT MV_CC_SHARPEN_PARAM * pstSharpenParam); + +/********************************************************************/ /** + * @~chinese + * @brief 色彩校正(包括CCM和CLUT)(该接口已弃用,建议改用ISP Tool方式进行校正) + * @param handle [IN] 设备句柄 + * @param pstColorCorrectParam [IN] 色彩校正参数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口支持单独CCM或者CLUT,也支持同时进行CCM和CLUT,用户可以通过CCM和CLUT信息中的使能开关进行选择。 + + * @~english + * @brief Color Correct(include CCM and CLUT) + * @param handle [IN] Device handle + * @param pstColorCorrectParam [IN] Color Correct parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API supports CCM or CLUT alone, as well as CCM and CLUT at the same time. The user can select by means of the enable switch in CCM and CLUT information. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ColorCorrect( + IN void * handle, IN OUT MV_CC_COLOR_CORRECT_PARAM * pstColorCorrectParam); + +/********************************************************************/ /** + * @~chinese + * @brief 噪声估计(该接口已弃用,建议改用ISP Tool方式进行标定) + * @param handle [IN] 设备句柄 + * @param pstNoiseEstimateParam [IN] 噪声估计参数 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 如果用户选择全图做噪声估计,nROINum可输入0,pstROIRect可置空。 + + * @~english + * @brief Noise Estimate + * @param handle [IN] Device handle + * @param pstNoiseEstimateParam [IN] Noise Estimate parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks If the user selects the full image, nROINum can be typed with 0 and pstROIRect empty. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_NoiseEstimate( + IN void * handle, IN OUT MV_CC_NOISE_ESTIMATE_PARAM * pstNoiseEstimateParam); + +/********************************************************************/ /** + * @~chinese + * @brief 空域降噪(该接口已弃用,建议改用ISP Tool方式进行降噪) + * @param handle [IN] 设备句柄 + * @param pstSpatialDenoiseParam [IN] 空域降噪参数 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief Spatial Denoise + * @param handle [IN] Device handle + * @param pstSpatialDenoiseParam [IN] Spatial Denoise parameter structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SpatialDenoise( + IN void * handle, IN OUT MV_CC_SPATIAL_DENOISE_PARAM * pstSpatialDenoiseParam); + +/********************************************************************/ /** + * @~chinese + * @brief LSC标定 + * @param handle [IN] 设备句柄 + * @param pstLSCCalibParam [IN] 标定参数 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief LSC Calib + * @param handle [IN] Device handle + * @param pstLSCCalibParam [IN] LSC Calib parameter structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_LSCCalib( + IN void * handle, IN OUT MV_CC_LSC_CALIB_PARAM * pstLSCCalibParam); + +/********************************************************************/ /** + * @~chinese + * @brief LSC校正 + * @param handle [IN] 设备句柄 + * @param pstLSCCorrectParam [IN] 校正参数 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @~english + * @brief LSC Correct + * @param handle [IN] Device handle + * @param pstLSCCorrectParam [IN] LSC Correct parameter structure + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_LSCCorrect( + IN void * handle, IN OUT MV_CC_LSC_CORRECT_PARAM * pstLSCCorrectParam); + +/************************************************************************ + * @fn MV_GIGE_ForceIp + * @brief 强制IP(该接口已弃用,建议改用 MV_GIGE_ForceIpEx接口) + * @param handle:设备句柄 + * @param nIP [IN] 设置的IP + * @return 见返回错误码 + + * @fn MV_GIGE_ForceIp + * @brief Force IP (This interface is abandoned, it is recommended to use the MV_GIGE_ForceIpEx) + * @param handle Handle + * @param nIP [IN] IP to set + * @return Refer to error code +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_ForceIp(IN void * handle, unsigned int nIP); + +/************************************************************************ + * @fn MV_CC_RegisterEventCallBack + * @brief 注册事件回调(该接口已弃用,建议改用 MV_CC_RegisterEventCallBackEx接口) + * @param handle:设备句柄 + * @param cbEvent [IN] 事件回调函数指针 + * @param pUser [IN] 用户自定义变量 + * @return 见返回错误码 + + * @fn MV_CC_RegisterEventCallBack + * @brief Register event callback (this interface has been deprecated and is recommended to be converted to the MV_CC_RegisterEventCallBackEx interface) + * @param handle:设备句柄 + * @param cbEvent [IN] event callback pointer + * @param pUser [IN] User defined value + * @return 见返回错误码 +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_RegisterEventCallBack( + void * handle, void(__stdcall * cbEvent)(unsigned int nExternalEventId, void * pUser), + void * pUser); + +/*********************************************************************** + * @fn MV_CC_Display + * @brief 显示图像,注册显示窗口,内部自动显示(与MV_CC_GetImageBuffer不能同时使用,建议改用MV_CC_DisplayOneFrame接口) + * @param handle [IN] 句柄 + * @param hWnd [IN] 显示窗口句柄 + * @return 成功,返回MV_OK;错误,返回错误码 + + * @fn MV_CC_Display + * @brief Display one frame image, register display window, automatic display internally + * @param handle [IN] Handle + * @param hWnd [IN] Display Window Handle + * @return Success, return MV_OK. Failure, return error code + ***********************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_Display(IN void * handle, void * hWnd); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetIntValue(IN void* handle, + IN const char* strKey, + OUT MVCC_INTVALUE *pIntValue); + * @brief 获取Integer属性值(建议改用MV_CC_GetIntValueEx接口) + * @param void* handle [IN] 相机句柄 + * @param char* strKey [IN] 属性键值,如获取宽度信息则为"Width" + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机属性结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetIntValue(IN void* handle, + IN const char* strKey, + OUT MVCC_INTVALUE *pIntValue); + * @brief Get Integer value + * @param void* handle [IN] Handle + * @param char* strKey [IN] Key value, for example, using "Width" to get width + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of camera features + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetIntValue( + IN void * handle, IN const char * strKey, OUT MVCC_INTVALUE * pIntValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetIntValue(IN void* handle, + IN const char* strKey, + IN unsigned int nValue); + * @brief 设置Integer型属性值(建议改用MV_CC_SetIntValueEx接口) + * @param void* handle [IN] 相机句柄 + * @param char* strKey [IN] 属性键值,如获取宽度信息则为"Width" + * const unsigned int nValue [IN] 想要设置的相机的属性值 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetIntValue(IN void* handle, + IN const char* strKey, + IN unsigned int nValue); + * @brief Set Integer value + * @param void* handle [IN] Handle + * @param char* strKey [IN] Key value, for example, using "Width" to set width + * const unsigned int nValue [IN] Feature value to set + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetIntValue( + IN void * handle, IN const char * strKey, IN unsigned int nValue); + +/************************************************************************/ +/* 相机参数获取和设置,此模块的所有接口已废弃,建议使用万能接口代替 */ +/* Get and set camara parameters, all interfaces of this module will be replaced by general interface*/ +/************************************************************************/ +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetWidth(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取图像宽度 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机宽度的信息结构体指针 + * 返回的pstValue结构体的意义 + * unsigned int nCurValue; // 代表相机当前的宽度值 + * unsigned int nMax; // 表示相机允许的最大可设置的宽度值 + * unsigned int nMin; // 表示相机允许的最小可设置的宽度值 + * unsigned int nInc; // 表示相机设置的宽度增量必须是nInc的倍数,否则无效 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + * 其他整型结构体参数的接口可参照此接口 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetWidth(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get image width + * @param void* handle [IN] Camera Handle + * MVCC_INTVALUE* pstValue [IN][OUT] Returns the information structure pointer about the camera's width for the caller + * The meaning of returns pstValue structure + * unsigned int nCurValue; // Represents the current width value of the camera + * unsigned int nMax; // Indicates the maximum settable width value allowed by the camera + * unsigned int nMin; // Indicates the minimum settable width value allowed by the camera + * unsigned int nInc; // Indicates that the width increment set by the camera must be a multiple of nInc, otherwise it is invalid + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + * Other Integer structure parameters interface can refer to this interface + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetWidth(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ +* @fn MV_CAMCTRL_API int __stdcall MV_CC_SetWidth(IN void* handle, IN const unsigned int nValue); +* @brief 设置图像宽度 +* @param void* handle [IN] 相机句柄 +* const unsigned int nValue [IN] 想要设置的相机宽度的值,注意此宽度值必须是MV_CC_GetWidth接口返回的pstValue中的nInc的倍数才能设置成功 +* @return 成功,返回MV_OK,并且相机宽度将会更改为相应值,失败,返回错误码 + +* @fn MV_CAMCTRL_API int __stdcall MV_CC_SetWidth(IN void* handle, IN const unsigned int nValue); + * @brief Set image width + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] To set the value of the camera width, note that the width value must be a multiple of nInc in the pstValue returned by the MV_CC_GetWidth interface + * @return Success, return MV_OK, and the camera width will change to the corresponding value. Failure, return error code +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetWidth(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetHeight(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取图像高度 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机高度的信息结构体指针 + * @return 成功,返回MV_OK,并将高度信息返回到结构体中,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetHeight(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get image height + * @param void* handle [IN] Camera handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera height to user + * @return Success, return MV_OK, and return height information to the structure. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetHeight(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetHeight(IN void* handle, IN const unsigned int nValue); + * @brief 设置图像高度 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的相机宽度的值,注意此宽度值必须是MV_CC_GetWidth接口返回的pstValue中的nInc的倍数才能设置成功 + * @return 成功,返回MV_OK,并且相机高度将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetHeight(IN void* handle, IN const unsigned int nValue); + * @brief Set image height + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] Camera height value to set, note that this value must be times of nInc of pstValue returned by MV_CC_GetWidth + * @return Success, return MV_OK, and the camera height will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetHeight(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAOIoffsetX(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取图像X偏移 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机X偏移的信息结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAOIoffsetX(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get image X offset + * @param void* handle [IN] Camera Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera X offset to user + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAOIoffsetX(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAOIoffsetX(IN void* handle, IN const unsigned int nValue); + * @brief 设置图像AOI偏移 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的相机AOI的值 + * @return 成功,返回MV_OK,并且相机AOI偏移将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAOIoffsetX(IN void* handle, IN const unsigned int nValue); + * @brief Set image X offset + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] Camera X offset value to set + * @return Success, return MV_OK, and the camera X offset will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetAOIoffsetX(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAOIoffsetY(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取图像Y偏移 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机Y偏移的信息结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAOIoffsetY(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get image Y offset + * @param void* handle [IN] Camera Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera Y offset to user + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAOIoffsetY(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAOIoffsetX(IN void* handle, IN const unsigned int nValue); + * @brief 设置图像AOI偏移 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的相机AOI的值 + * @return 成功,返回MV_OK,并且相机AOI偏移将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAOIoffsetY(IN void* handle, IN const unsigned int nValue); + * @brief Set image Y offset + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] Camera Y offset value to set + * @return Success, return MV_OK, and the camera Y offset will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetAOIoffsetY(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAutoExposureTimeLower(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取曝光下限 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机曝光值下限结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAutoExposureTimeLower(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get exposure lower limit + * @param void* handle [IN] Camera Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera exposure lower to user + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAutoExposureTimeLower( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAutoExposureTimeLower(IN void* handle, IN const unsigned int nValue); + * @brief 设置曝光值下限 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的曝光值下限 + * @return 成功,返回MV_OK,并且相机曝光下限将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAutoExposureTimeLower(IN void* handle, IN const unsigned int nValue); + * @brief Set exposure lower limit + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] Exposure lower to set + * @return Success, return MV_OK, and the camera exposure time lower limit value will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetAutoExposureTimeLower( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAutoExposureTimeUpper(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取曝光上限 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机曝光值上限结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAutoExposureTimeUpper(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get exposure upper limit + * @param void* handle [IN] Camera Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera exposure upper to user + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAutoExposureTimeUpper( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAutoExposureTimeUpper(IN void* handle, IN const unsigned int nValue); + * @brief 设置曝光值上限 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的曝光值上限 + * @return 成功,返回MV_OK,并且相机曝光上限将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAutoExposureTimeUpper(IN void* handle, IN const unsigned int nValue); + * @brief Set exposure upper limit + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] Exposure upper to set + * @return Success, return MV_OK, and the camera exposure time upper limit value will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetAutoExposureTimeUpper( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBrightness(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取亮度值 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机亮度结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBrightness(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get brightness + * @param void* handle [IN] Camera Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera brightness to user + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBrightness(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBrightness(IN void* handle, IN const unsigned int nValue); + * @brief 设置亮度值 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的亮度值 + * @return 成功,返回MV_OK,并且相机亮度将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBrightness(IN void* handle, IN const unsigned int nValue); + * @brief Set brightness + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] Brightness upper to set + * @return Success, return MV_OK, and the camera brightness value will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBrightness(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetFrameRate(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief 获取帧率 + * @param void* handle [IN] 相机句柄 + * MVCC_FLOATVALUE* pstValue [IN][OUT] 返回给调用者有关相机帧率的信息结构体指针 + * 返回的pstValue结构体的意义 + * float fCurValue; // 表示相机当前的帧率 + * float fMax; // 表示相机允许设置的最大帧率 + * float fMin; // 表示相机允许设置的最小帧率 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + * 其他浮点型结构体参数的接口可参照此接口 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetFrameRate(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief Get Frame Rate + * @param void* handle [IN] Camera Handle + * MVCC_FLOATVALUE* pstValue [IN][OUT] Return pointer of information structure related to camera frame rate to user + * The meaning of returns pstValue structure + * float fCurValue; // Indicates the current frame rate of the camera + * float fMax; // Indicates the maximum frame rate allowed by the camera + * float fMin; // Indicates the minimum frame rate allowed by the camera + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + * Other interface of Float structure parameters can refer to this interface + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetFrameRate( + IN void * handle, IN OUT MVCC_FLOATVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetFrameRate(IN void* handle, IN const float fValue); + * @brief 设置帧率 + * @param void* handle [IN] 相机句柄 + * const float fValue [IN] 想要设置的相机帧率 + * @return 成功,返回MV_OK,并且相机帧率将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetFrameRate(IN void* handle, IN const float fValue); + * @brief Set frame rate + * @param void* handle [IN] Camera Handle + * const float fValue [IN] Camera frame rate to set + * @return Success, return MV_OK, and camera frame rate will be changed to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetFrameRate(IN void * handle, IN const float fValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGain(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief 获取增益 + * @param void* handle [IN] 相机句柄 + * MVCC_FLOATVALUE* pstValue [IN][OUT] 返回给调用者有关相机增益的信息结构体指针 + * 返回的pstValue结构体的意义 + * float fCurValue; // 表示相机当前的帧率 + * float fMax; // 表示相机允许设置的最大帧率 + * float fMin; // 表示相机允许设置的最小帧率 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + * 其他浮点型结构体参数的接口可参照此接口 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGain(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief Get Gain + * @param void* handle [IN] Camera Handle + * MVCC_FLOATVALUE* pstValue [IN][OUT] Return pointer of information structure related to gain to user + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * float fCurValue; // Camera current gain + * float fMax; // The maximum gain camera allowed + * float fMin; // The minimum gain camera allowed + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + * Other interface of Float structure parameters can refer to this interface + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetGain(IN void * handle, IN OUT MVCC_FLOATVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGain(IN void* handle, IN const float fValue); + * @brief 设置帧率 + * @param void* handle [IN] 相机句柄 + * const float fValue [IN] 想要设置的相机帧率 + * @return 成功,返回MV_OK,并且相机帧率将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGain(IN void* handle, IN const float fValue); + * @brief Set Gain + * @param void* handle [IN] Camera Handle + * const float fValue [IN] Gain value to set + * @return Success, return MV_OK, and the camera gain value will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetGain(IN void * handle, IN const float fValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetExposureTime(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief 获取曝光时间 + * @param void* handle [IN] 相机句柄 + * MVCC_FLOATVALUE* pstValue [IN][OUT] 返回给调用者有关相机曝光时间的信息结构体指针 + * 返回的pstValue结构体的意义 + * float fCurValue; // 表示相机当前的帧率 + * float fMax; // 表示相机允许设置的最大帧率 + * float fMin; // 表示相机允许设置的最小帧率 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + * 其他浮点型结构体参数的接口可参照此接口 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetExposureTime(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief Get exposure time + * @param void* handle [IN] Camera Handle + * MVCC_FLOATVALUE* pstValue [IN][OUT] Return pointer of information structure related to exposure time to user + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * float fCurValue; // Camera current exposure time + * float fMax; // The maximum exposure time camera allowed + * float fMin; // The minimum exposure time camera allowed + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + * Other interface of Float structure parameters can refer to this interface + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetExposureTime( + IN void * handle, IN OUT MVCC_FLOATVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetExposureTime(IN void* handle, IN const float fValue); + * @brief 设置曝光时间 + * @param void* handle [IN] 相机句柄 + * const float fValue [IN] 想要设置的相机帧率 + * @return 成功,返回MV_OK,并且相机帧率将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetExposureTime(IN void* handle, IN const float fValue); + * @brief Set exposure time + * @param void* handle [IN] Camera Handle + * const float fValue [IN] Exposure time to set + * @return Success, return MV_OK, and the camera exposure time value will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetExposureTime(IN void * handle, IN const float fValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetPixelFormat(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取像素格式 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关像素格式的信息结构体指针 + * 返回的pstValue结构体的意义 + * unsigned int nCurValue; // 相机当前的像素格式,是枚举类型,比如说PixelType_Gvsp_Mono8, 这里获得的是其整型值,具体数值参照PixelType.h的MvGvspPixelType枚举类型 + * unsigned int nSupportedNum; // 相机支持的像素格式的个数 + * unsigned int nSupportValue[MV_MAX_XML_SYMBOLIC_NUM]; // 相机所有支持的像素格式对应的整型值列表,后面要设置像素格式时,参数必须是这个数组中的一种,否则无效 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 其他枚举类型参数接口可参照此接口,有关相应参数的枚举类型对应的整型值请查找PixelType.h 和 CameraParams.h中相应的定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetPixelFormat(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get Pixel Format + * @param void* handle [IN] Camera Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Returns the information structure pointer about pixel format for the caller + * The meaning of returns pstValue structure + * unsigned int nCurValue; // The current pixel format of the camera, is the enumeration type, such as PixelType_Gvsp_Mono8, here is the integer value, the specific value please refer to MvGvspPixelType enumeration type in PixelType.h + * unsigned int nSupportedNum; // Number of pixel formats supported by the camera + * unsigned int nSupportValue[MV_MAX_XML_SYMBOLIC_NUM]; // The integer values list correspond to all supported pixel formats of the camera, followed by when set the pixel format, the parameter must be one of this list, otherwise invalid + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Other interface of Enumeration structure parameters can refer to this interface, look for the corresponding definition in PixelType.h and CameraParams.h for the integer values of the enum type parameter + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetPixelFormat( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetPixelFormat(IN void* handle, IN const unsigned int nValue); + * @brief 设置像素格式 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的像素格式对应的整型值,调用此接口时可以直接填写枚举值,如MV_CC_SetPixelFormat(m_handle, PixelType_Gvsp_RGB8_Packed); + * @return 成功,返回MV_OK,并且相机像素格式将会更改为相应值,失败,返回错误码 + * + * 要设置的枚举类型必须是Get接口返回的nSupportValue[MV_MAX_XML_SYMBOLIC_NUM]中的一种,否则会失败 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetPixelFormat(IN void* handle, IN const unsigned int nValue); + * @brief Set Pixel Format + * @param void* handle [IN] Camera Handle + * const unsigned int nValue [IN] The corresponding integer value for pixel format to be set, when calling this interface can be directly filled in enumeration values, such as MV_CC_SetPixelFormat(m_handle, PixelType_Gvsp_RGB8_Packed); + * @return Success, return MV_OK, and the camera pixel format will change to the corresponding value. Failure, return error code + * + * Other interface of Enumeration structure parameters can refer to this interface, the enumeration type to be set must be one of the nSupportValue [MV_MAX_XML_SYMBOLIC_NUM] returned by the Get interface, otherwise it will fail + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetPixelFormat(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAcquisitionMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取采集模式 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关采集模式的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_ACQUISITION_MODE 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAcquisitionMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get acquisition mode + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of acquisition mode + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_ACQUISITION_MODE in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAcquisitionMode( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAcquisitionMode(IN void* handle, IN const unsigned int nValue); + * @brief 设置像素格式 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的采集模式对应的整型值 + * @return 成功,返回MV_OK,并且相机采集模式将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAcquisitionMode(IN void* handle, IN const unsigned int nValue); + * @brief Set acquisition mode + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to acquisition mode + * @return Success, return MV_OK, and the camera acquisition mode will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetAcquisitionMode( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGainMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取增益模式 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关增益模式的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_GAIN_MODE 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGainMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get gain mode + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of gain mode + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_GAIN_MODE in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetGainMode(IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGainMode(IN void* handle, IN const unsigned int nValue); + * @brief 设置增益模式 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的增益模式对应的整型值 + * @return 成功,返回MV_OK,并且相机增益模式将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGainMode(IN void* handle, IN const unsigned int nValue); + * @brief Set gain mode + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to gain mode + * @return Success, return MV_OK, and the camera gain mode will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetGainMode(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetExposureAutoMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取自动曝光模式 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关自动曝光模式的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_EXPOSURE_AUTO_MODE 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetExposureAutoMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get auto exposure mode + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of auto exposure mode + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_EXPOSURE_AUTO_MODE in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetExposureAutoMode( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetExposureAutoMode(IN void* handle, IN const unsigned int nValue); + * @brief 设置自动曝光模式 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的自动曝光模式对应的整型值 + * @return 成功,返回MV_OK,并且相机自动曝光模式将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetExposureAutoMode(IN void* handle, IN const unsigned int nValue); + * @brief Set auto exposure mode + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to auto exposure mode + * @return Success, return MV_OK, and the camera auto exposure mode will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetExposureAutoMode( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取触发模式 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关触发模式的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_TRIGGER_MODE 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerMode(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get trigger mode + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of trigger mode + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_TRIGGER_MODE in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerMode( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerMode(IN void* handle, IN const unsigned int nValue); + * @brief 设置触发模式 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的触发模式对应的整型值 + * @return 成功,返回MV_OK,并且相机触发模式将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerMode(IN void* handle, IN const unsigned int nValue); + * @brief Set trigger mode + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to trigger mode + * @return Success, return MV_OK, and the camera trigger mode will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerMode(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerDelay(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief 获取触发延时 + * @param void* handle [IN] 相机句柄 + * MVCC_FLOATVALUE* pstValue [IN][OUT] 返回给调用者有关相机触发延时的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + * 可参照接口MV_CC_GetFrameRate + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerDelay(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief Get tigger delay + * @param void* handle [IN] Handle + * MVCC_FLOATVALUE* pstValue [IN][OUT] Structure pointer of trigger delay + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + * Refer to MV_CC_GetFrameRate + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerDelay( + IN void * handle, IN OUT MVCC_FLOATVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerDelay(IN void* handle, IN const float fValue); + * @brief 设置触发延时 + * @param void* handle [IN] 相机句柄 + * const float fValue [IN] 想要设置的相机触发延时 + * @return 成功,返回MV_OK,并且相机触发延时将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerDelay(IN void* handle, IN const float fValue); + * @brief Set tigger delay + * @param void* handle [IN] Handle + * const float fValue [IN] Trigger delay to set + * @return Success, return MV_OK, and the camera trigger delay will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerDelay(IN void * handle, IN const float fValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerSource(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取触发源 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关触发源的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_TRIGGER_SOURCE 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerSource(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get trigger source + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of trigger source + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_TRIGGER_SOURCE in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetTriggerSource( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerSource(IN void* handle, IN const unsigned int nValue); + * @brief 设置触发源 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的触发源对应的整型值 + * @return 成功,返回MV_OK,并且相机触发源将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerSource(IN void* handle, IN const unsigned int nValue); + * @brief Set trigger source + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to trigger source + * @return Success, return MV_OK, and the camera trigger source will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetTriggerSource(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_TriggerSoftwareExecute(IN void* handle); + * @brief 软触发一次(接口仅在已选择的触发源为软件触发时有效) + * @param void* handle [IN] 相机句柄 + * @return 成功,返回MV_OK, 失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_TriggerSoftwareExecute(IN void* handle); + * @brief Execute software trigger once (this interface only valid when the trigger source is set to software) + * @param void* handle [IN] Handle + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_TriggerSoftwareExecute(IN void * handle); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGammaSelector(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取Gamma类型 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关Gamma类型的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_GAMMA_SELECTOR 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGammaSelector(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get Gamma mode + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of gamma mode + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_GAMMA_SELECTOR in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetGammaSelector( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGammaSelector(IN void* handle, IN const unsigned int nValue); + * @brief 设置Gamma类型 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的Gamma类型对应的整型值 + * @return 成功,返回MV_OK,并且相机Gamma类型将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGammaSelector(IN void* handle, IN const unsigned int nValue); + * @brief Set Gamma mode + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to gamma mode + * @return Success, return MV_OK, and the camera gamma mode will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetGammaSelector(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGamma(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief 获取Gamma值 + * @param void* handle [IN] 相机句柄 + * MVCC_FLOATVALUE* pstValue [IN][OUT] 返回给调用者有关相机Gamma值的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + * 可参照接口MV_CC_GetFrameRate + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetGamma(IN void* handle, IN OUT MVCC_FLOATVALUE* pstValue); + * @brief Get Gamma value + * @param void* handle [IN] Handle + * MVCC_FLOATVALUE* pstValue [IN][OUT] Structure pointer of gamma value + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + * Refer to MV_CC_GetFrameRate + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetGamma(IN void * handle, IN OUT MVCC_FLOATVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGamma(IN void* handle, IN const float fValue); + * @brief 设置Gamma值 + * @param void* handle [IN] 相机句柄 + * const float fValue [IN] 想要设置的相机Gamma值 + * @return 成功,返回MV_OK,并且相机Gamma值将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetGamma(IN void* handle, IN const float fValue); + * @brief Set Gamma value + * @param void* handle [IN] Handle + * const float fValue [IN] Gamma value to set + * @return Success, return MV_OK, and the camera gamma value will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetGamma(IN void * handle, IN const float fValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetSharpness(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取锐度 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机锐度结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetSharpness(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get sharpness + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of sharpness + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetSharpness(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetSharpness(IN void* handle, IN const unsigned int nValue); + * @brief 设置锐度 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的锐度 + * @return 成功,返回MV_OK,并且相机锐度将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetSharpness(IN void* handle, IN const unsigned int nValue); + * @brief Set sharpness + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Sharpness to set + * @return Success, return MV_OK, and the camera sharpness will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetSharpness(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetHue(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取灰度 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机灰度结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetHue(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get Hue + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of Hue + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetHue(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetHue(IN void* handle, IN const unsigned int nValue); + * @brief 设置灰度 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的灰度 + * @return 成功,返回MV_OK,并且相机灰度将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetHue(IN void* handle, IN const unsigned int nValue); + * @brief Set Hue + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Hue to set + * @return Success, return MV_OK, and the camera Hue will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetHue(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetSaturation(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取饱和度 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机饱和度结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetSaturation(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get Saturation + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of Saturation + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetSaturation(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetSaturation(IN void* handle, IN const unsigned int nValue); + * @brief 设置饱和度 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的饱和度 + * @return 成功,返回MV_OK,并且相机饱和度将会更改为相应值,失败,返回错误码 + +* @fn MV_CAMCTRL_API int __stdcall MV_CC_SetSaturation(IN void* handle, IN const unsigned int nValue); + * @brief Set Saturation + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Saturation to set + * @return Success, return MV_OK, and the camera Saturation will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetSaturation(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceWhiteAuto(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief 获取自动白平衡 + * @param void* handle [IN] 相机句柄 + * MVCC_ENUMVALUE* pstValue [IN][OUT] 返回给调用者的有关自动白平衡的信息结构体指针 + * @return 成功,返回MV_OK,并获得相应参数信息的结构体, 失败, 返回错误码 + * + 可参照接口MV_CC_GetPixelFormat,参考 CameraParam.h 中的 MV_CAM_BALANCEWHITE_AUTO 定义 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceWhiteAuto(IN void* handle, IN OUT MVCC_ENUMVALUE* pstValue); + * @brief Get Auto white balance + * @param void* handle [IN] Handle + * MVCC_ENUMVALUE* pstValue [IN][OUT] Structure pointer of auto white balance + * @return Success, return MV_OK, and get the structure of the corresponding parameters. Failure, return error code + * + Refer to MV_CC_GetPixelFormat and definition of MV_CAM_BALANCEWHITE_AUTO in CameraParam.h + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceWhiteAuto( + IN void * handle, IN OUT MVCC_ENUMVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceWhiteAuto(IN void* handle, IN const unsigned int nValue); + * @brief 设置自动白平衡 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 要设置的自动白平衡对应的整型值 + * @return 成功,返回MV_OK,并且相机自动白平衡将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceWhiteAuto(IN void* handle, IN const unsigned int nValue); + * @brief Set Auto white balance + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Integer value to set corresponding to auto white balance + * @return Success, return MV_OK, and the camera auto white balance will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceWhiteAuto( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioRed(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取白平衡 红 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机白平衡 红结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioRed(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get white balance red + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of white balance red + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioRed( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioRed(IN void* handle, IN const unsigned int nValue); + * @brief 设置白平衡 红 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的白平衡 红 + * @return 成功,返回MV_OK,并且相机白平衡 红将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioRed(IN void* handle, IN const unsigned int nValue); + * @brief Set white balance red + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] White balance red to set + * @return Success, return MV_OK, and the camera white balance red will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioRed( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioGreen(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取白平衡 绿 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机白平衡 绿结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioGreen(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get white balance green + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of white balance green + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioGreen( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioGreen(IN void* handle, IN const unsigned int nValue); + * @brief 设置白平衡 绿 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的白平衡 绿 + * @return 成功,返回MV_OK,并且相机白平衡 绿将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioGreen(IN void* handle, IN const unsigned int nValue); + * @brief Set white balance green + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] White balance green to set + * @return Success, return MV_OK, and the camera white balance green will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioGreen( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioBlue(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取白平衡 蓝 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机白平衡 蓝结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioBlue(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get white balance blue + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of white balance blue + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBalanceRatioBlue( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioBlue(IN void* handle, IN const unsigned int nValue); + * @brief 设置白平衡 蓝 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的白平衡 蓝 + * @return 成功,返回MV_OK,并且相机白平衡 蓝将会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioBlue(IN void* handle, IN const unsigned int nValue); + * @brief Set white balance blue + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] White balance blue to set + * @return Success, return MV_OK, and the camera white balance blue will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBalanceRatioBlue( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetFrameSpecInfoAbility(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取水印信息内包含的信息类型 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机水印信息内包含的信息类型结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetFrameSpecInfoAbility(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get information type included by frame stamp + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of information type included by frame stamp + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetFrameSpecInfoAbility( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetFrameSpecInfoAbility(IN void* handle, IN const unsigned int nValue); + * @brief 设置水印信息内包含的信息类型 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的水印信息内包含的信息类型 + * @return 成功,返回MV_OK,并且相机水印信息内包含的信息类型会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetFrameSpecInfoAbility(IN void* handle, IN const unsigned int nValue); + * @brief Set information type included by frame stamp + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Information type included by frame stamp to set + * @return Success, return MV_OK, and the camera information type included by frame stamp will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetFrameSpecInfoAbility( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetDeviceUserID(IN void* handle, IN OUT MVCC_STRINGVALUE* pstValue); + * @brief 获取设备自定义名字 + * @param void* handle [IN] 相机句柄 + * MVCC_STRINGVALUE* pstValue [IN OUT] 返回给调用者有关相机名字结构体指针 + * @return 成功,返回MV_OK,并且获取到相机的自定义名字,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetDeviceUserID(IN void* handle, IN OUT MVCC_STRINGVALUE* pstValue); + * @brief Get device user defined name + * @param void* handle [IN] Handle + * MVCC_STRINGVALUE* pstValue [IN OUT] Structure pointer of device name + * @return Success, return MV_OK, and get the camera user defined name. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetDeviceUserID( + IN void * handle, IN OUT MVCC_STRINGVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetDeviceUserID(IN void* handle, IN const char* chValue); + * @brief 设置设备自定义名字 + * @param void* handle [IN] 相机句柄 + * IN const char* chValue [IN] 设备名字 + * @return 成功,返回MV_OK,并且设置设备自定义名字,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetDeviceUserID(IN void* handle, IN const char* chValue); + * @brief Set device user defined name + * @param void* handle [IN] Handle + * IN const char* chValue [IN] Device name + * @return Success, return MV_OK, and set the camera user defined name. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetDeviceUserID(IN void * handle, IN const char * chValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBurstFrameCount(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取一次触发的帧数 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机一次触发的帧数结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetBurstFrameCount(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get frame number trigger by once + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of frame number trigger by once + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetBurstFrameCount( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBurstFrameCount(IN void* handle, IN const unsigned int nValue); + * @brief 设置一次触发的帧数 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的一次触发的帧数 + * @return 成功,返回MV_OK,并且相机一次触发的帧数会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetBurstFrameCount(IN void* handle, IN const unsigned int nValue); + * @brief Set frame number trigger by once + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Frame number trigger by once to set + * @return Success, return MV_OK, and the camera frame number trigger by once will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetBurstFrameCount( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAcquisitionLineRate(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取行频 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机行频结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetAcquisitionLineRate(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get line rate + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of line rate + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetAcquisitionLineRate( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAcquisitionLineRate(IN void* handle, IN const unsigned int nValue); + * @brief 设置行频 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的行频 + * @return 成功,返回MV_OK,并且相机行频会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetAcquisitionLineRate(IN void* handle, IN const unsigned int nValue); + * @brief Set line rate + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Line rate to set + * @return Success, return MV_OK, and the camera line rate will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetAcquisitionLineRate( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetHeartBeatTimeout(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取心跳信息 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机心跳信息结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_GetHeartBeatTimeout(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get heartbeat information + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of heartbeat information + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_GetHeartBeatTimeout( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetHeartBeatTimeout(IN void* handle, IN const unsigned int nValue); + * @brief 设置心跳信息 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的心跳信息 + * @return 成功,返回MV_OK,并且相机心跳信息会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_CC_SetHeartBeatTimeout(IN void* handle, IN const unsigned int nValue); + * @brief Set heartbeat information + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Heartbeat information to set + * @return Success, return MV_OK, and the camera heartbeat information will change to the corresponding value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SetHeartBeatTimeout( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCPSPacketSize(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取网络包大小 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机网络包大小结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCPSPacketSize(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get network packet size + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of network packet size + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCPSPacketSize( + IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCPSPacketSize(IN void* handle, IN const unsigned int nValue); + * @brief 设置网络包大小 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的网络包大小 + * @return 成功,返回MV_OK,并且相机网络包大小会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCPSPacketSize(IN void* handle, IN const unsigned int nValue); + * @brief Set network packet size + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Packet size to set + * @return Success, return MV_OK, and change packet size to setting value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCPSPacketSize( + IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCPD(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief 获取网络包发送间隔 + * @param void* handle [IN] 相机句柄 + * @param MVCC_INTVALUE* pstValue [IN][OUT] 返回给调用者有关相机网络包发送间隔结构体指针 + * @return 成功,返回MV_OK,失败,返回错误码 + * + * 可参照接口MV_CC_GetWidth + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCPD(IN void* handle, IN OUT MVCC_INTVALUE* pstValue); + * @brief Get network packet sending delay + * @param void* handle [IN] Handle + * @param MVCC_INTVALUE* pstValue [IN][OUT] Structure pointer of network packet sending delay + * @return Success, return MV_OK. Failure, return error code + * + * Refer to MV_CC_GetWidth + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCPD(IN void * handle, IN OUT MVCC_INTVALUE * pstValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCPD(IN void* handle, IN const unsigned int nValue); + * @brief 设置网络包发送间隔 + * @param void* handle [IN] 相机句柄 + * const unsigned int nValue [IN] 想要设置的网络包发送间隔 + * @return 成功,返回MV_OK,并且相机网络包发送间隔会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCPD(IN void* handle, IN const unsigned int nValue); + * @brief Set network packet sending delay + * @param void* handle [IN] Handle + * const unsigned int nValue [IN] Packet delay to set + * @return Success, return MV_OK, and change packet delay to setting value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCPD(IN void * handle, IN const unsigned int nValue); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCDA(IN void* handle, unsigned int* pnIP); + * @brief 获取接收端IP地址,0xa9fe0102 表示 169.254.1.2 + * @param void* handle [IN] 相机句柄 + * @param unsigned int* pnIP [IN][OUT] 返回给调用者接收端IP地址 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCDA(IN void* handle, unsigned int* pnIP); + * @brief Get receiver IP address, 0xa9fe0102 indicates 169.254.1.2 + * @param void* handle [IN] Handle + * @param unsigned int* pnIP [IN][OUT] Receiver IP address + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCDA(IN void * handle, unsigned int * pnIP); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCDA(IN void* handle, unsigned int nIP); + * @brief 设置接收端IP地址 + * @param void* handle [IN] 相机句柄 + * unsigned int nIP [IN] 想要设置的接收端IP地址 + * @return 成功,返回MV_OK,并且相机接收端IP地址会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCDA(IN void* handle, unsigned int nIP); + * @brief Set receiver IP address + * @param void* handle [IN] Handel + * unsigned int nIP [IN] Receiver IP address to set + * @return Success, return MV_OK, and change receiver IP address to setting value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCDA(IN void * handle, unsigned int nIP); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCSP(IN void* handle, unsigned int* pnPort); + * @brief 获取发送端的端口号 + * @param void* handle [IN] 相机句柄 + * @param unsigned int* pnPort [IN][OUT] 返回给调用者发送端的端口号 + * @return 成功,返回MV_OK,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCSP(IN void* handle, unsigned int* pnPort); + * @brief Get transmitter port number + * @param void* handle [IN] Handle + * @param unsigned int* pnPort [IN][OUT] Transmitter port number + * @return Success, return MV_OK. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_GetGevSCSP(IN void * handle, unsigned int * pnPort); + +/************************************************************************ + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCSP(IN void* handle, unsigned int nPort); + * @brief 设置发送端的端口号 + * @param void* handle [IN] 相机句柄 + * unsigned int nPort [IN] 想要设置的发送端的端口号 + * @return 成功,返回MV_OK,并且相机发送端的端口号会更改为相应值,失败,返回错误码 + + * @fn MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCSP(IN void* handle, unsigned int nPort); + * @brief Set transmitter port number + * @param void* handle [IN] Handle + * unsigned int nPort [IN] Transmitter port number to set + * @return Success, return MV_OK, and change transmitter port number to setting value. Failure, return error code + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_GIGE_SetGevSCSP(IN void * handle, unsigned int nPort); + +/********************************************************************/ /** + * @~chinese + * @brief 设置设备波特率 + * @param handle [IN] 设备句柄 + * @param nBaudrate [IN] 设置的波特率值,数值参考CameraParams.h中宏定义,如#define MV_CAML_BAUDRATE_9600 0x00000001 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks (该接口已弃用,建议改用 MV_CAML_SetDeviceBaudrate接口) + + * @~english + * @brief Set device baudrate using one of the CL_BAUDRATE_XXXX value + * @param handle [IN] Device handle + * @param nBaudrate [IN] baud rate to set. Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_CAML_BAUDRATE_9600 0x00000001 + * @return Success, return MV_OK. Failure, return error code + * @remarks (This interface is abandoned, it is recommended to use the MV_CAML_SetDeviceBaudrate) +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_SetDeviceBauderate(IN void * handle, unsigned int nBaudrate); + +/********************************************************************/ /** + * @~chinese + * @brief 获取设备波特率 + * @param handle [IN] 设备句柄 + * @param pnCurrentBaudrate [OUT] 波特率信息指针,数值参考CameraParams.h中宏定义,如#define MV_CAML_BAUDRATE_9600 0x00000001 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks (该接口已弃用,建议改用 MV_CAML_GetDeviceBaudrate接口) + + * @~english + * @brief Returns the current device baudrate, using one of the CL_BAUDRATE_XXXX value + * @param handle [IN] Device handle + * @param pnCurrentBaudrate [OUT] Return pointer of baud rate to user. Refer to the 'CameraParams.h' for parameter definitions, for example, #define MV_CAML_BAUDRATE_9600 0x00000001 + * @return Success, return MV_OK. Failure, return error code + * @remarks (This interface is abandoned, it is recommended to use the MV_CAML_GetDeviceBaudrate) +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_GetDeviceBauderate( + IN void * handle, unsigned int * pnCurrentBaudrate); + +/********************************************************************/ /** + * @~chinese + * @brief 获取设备与主机间连接支持的波特率 + * @param handle [IN] 设备句柄 + * @param pnBaudrateAblity [OUT] 支持的波特率信息的指针。所支持波特率的或运算结果,单个数值参考CameraParams.h中宏定义,如MV_CAML_BAUDRATE_9600 0x00000001 + * @return 成功,返回MV_OK,失败,返回错误码 + * @remarks (该接口已弃用,建议改用 MV_CAML_GetSupportBaudrates接口) + + * @~english + * @brief Returns supported baudrates of the combined device and host interface + * @param handle [IN] Device handle + * @param pnBaudrateAblity [OUT] Return pointer of the supported baudrates to user. 'OR' operation results of the supported baudrates. Refer to the 'CameraParams.h' for single value definitions, for example, MV_CAML_BAUDRATE_9600 0x00000001 + * @return Success, return MV_OK. Failure, return error code + * @remarks (This interface is abandoned, it is recommended to use the MV_CAML_GetSupportBaudrates) +************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CAML_GetSupportBauderates( + IN void * handle, unsigned int * pnBaudrateAblity); + +/********************************************************************/ /** + * @~chinese + * @brief 保存图片,支持Bmp和Jpeg. + * @param handle [IN] 设备句柄 + * @param pstSaveParam [IN][OUT] 保存图片参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过将接口可以将从设备采集到的原始图像数据转换成JPEG或者BMP等格式并存放在指定内存中,然后用户可以将转换之后的数据直接保存成图片文件。 + 该接口调用无接口顺序要求,有图像源数据就可以进行转换,可以先调用MV_CC_GetOneFrameTimeout或者MV_CC_RegisterImageCallBackEx设置回调函数,获取一帧图像数据,然后再通过该接口转换格式。 + MV_CC_SaveImageEx2比MV_CC_SaveImageEx增加参数handle,为了保证与其他接口的统一。 + + * @~english + * @brief Save image, support Bmp and Jpeg. + * @param handle [IN] Device handle + * @param pstSaveParam [IN][OUT] Save image parameters structure + * @return Success, return MV_OK. Failure, return error code + * @remarks Once there is image data, you can call this API to convert the data. + You can also call MV_CC_GetOneFrameTimeout or MV_CC_RegisterImageCallBackEx or MV_CC_GetImageBuffer to get one image frame and set the callback function, and then call this API to convert the format. + Comparing with the API MV_CC_SaveImageEx, this API added the parameter handle to ensure the unity with other API. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SaveImageEx2( + IN void * handle, MV_SAVE_IMAGE_PARAM_EX * pstSaveParam); + +/********************************************************************/ /** + * @~chinese + * @brief 保存图像到文件 + * @param handle [IN] 设备句柄 + * @param pstSaveFileParam [IN][OUT] 保存图片文件参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 该接口支持BMP/JPEG/PNG/TIFF。 + + * @~english + * @brief Save the image file. + * @param handle [IN] Device handle + * @param pstSaveFileParam [IN][OUT] Save the image file parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API support BMP/JPEG/PNG/TIFF. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_SaveImageToFile( + IN void * handle, MV_SAVE_IMG_TO_FILE_PARAM * pstSaveFileParam); + +/********************************************************************/ /** + * @~chinese + * @brief 像素格式转换 + * @param handle [IN] 设备句柄 + * @param pstCvtParam [IN][OUT] 像素格式转换参数结构体 + * @return 成功,返回MV_OK;错误,返回错误码 + * @remarks 通过将接口可以将从设备采集到的原始图像数据转换成用户所需的像素格式并存放在指定内存中。 + 该接口调用无接口顺序要求,有图像源数据就可以进行转换,可以先调用MV_CC_GetOneFrameTimeout或者MV_CC_RegisterImageCallBackEx设置回调函数, + 获取一帧图像数据,然后再通过该接口转换格式。如果设备当前采集图像是JPEG压缩的格式,则不支持调用该接口进行转换。 + + * @~english + * @brief Pixel format conversion + * @param handle [IN] Device handle + * @param pstCvtParam [IN][OUT] Convert Pixel Type parameter structure + * @return Success, return MV_OK. Failure, return error code + * @remarks This API is used to transform the collected original data to pixel format and save to specified memory. + There is no order requirement to call this API, the transformation will execute when there is image data. + First call MV_CC_GetOneFrameTimeout or MV_CC_RegisterImageCallBackEx to set callback function, and get a frame of image data, + then call this API to transform the format. + ************************************************************************/ +MV_CAMCTRL_API int __stdcall MV_CC_ConvertPixelType( + IN void * handle, IN OUT MV_CC_PIXEL_CONVERT_PARAM * pstCvtParam); + +#ifdef __cplusplus +} +#endif + +#endif //_MV_OBSOLETE_INTERFACES_H_ diff --git a/io/hikrobot/include/ObsoleteCamParams.h b/io/hikrobot/include/ObsoleteCamParams.h new file mode 100644 index 0000000..8572bfc --- /dev/null +++ b/io/hikrobot/include/ObsoleteCamParams.h @@ -0,0 +1,714 @@ + +#ifndef _MV_OBSOLETE_CAM_PARAMS_H_ +#define _MV_OBSOLETE_CAM_PARAMS_H_ + +#include "PixelType.h" + +/// \~chinese 输出帧的信息 \~english Output Frame Information +typedef struct _MV_FRAME_OUT_INFO_ +{ + unsigned short nWidth; ///< [OUT] \~chinese 图像宽 \~english Image Width + unsigned short nHeight; ///< [OUT] \~chinese 图像高 \~english Image Height + enum MvGvspPixelType enPixelType; ///< [OUT] \~chinese 像素格式 \~english Pixel Type + + unsigned int nFrameNum; ///< [OUT] \~chinese 帧号 \~english Frame Number + unsigned int + nDevTimeStampHigh; ///< [OUT] \~chinese 时间戳高32位 \~english Timestamp high 32 bits + unsigned int + nDevTimeStampLow; ///< [OUT] \~chinese 时间戳低32位 \~english Timestamp low 32 bits + unsigned int + nReserved0; ///< [OUT] \~chinese 保留,8字节对齐 \~english Reserved, 8-byte aligned + int64_t + nHostTimeStamp; ///< [OUT] \~chinese 主机生成的时间戳 \~english Host-generated timestamp + + unsigned int nFrameLen; + + unsigned int nLostPacket; // 本帧丢包数 + unsigned int nReserved[2]; +} MV_FRAME_OUT_INFO; + +/// \~chinese 保存图片参数 \~english Save image type +typedef struct _MV_SAVE_IMAGE_PARAM_T_ +{ + unsigned char * pData; ///< [IN] \~chinese 输入数据缓存 \~english Input Data Buffer + unsigned int nDataLen; ///< [IN] \~chinese 输入数据大小 \~english Input Data Size + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 输入像素格式 \~english Input Data Pixel Format + unsigned short nWidth; ///< [IN] \~chinese 图像宽 \~english Image Width + unsigned short nHeight; ///< [IN] \~chinese 图像高 \~english Image Height + + unsigned char * + pImageBuffer; ///< [OUT] \~chinese 输出图片缓存 \~english Output Image Buffer + unsigned int nImageLen; ///< [OUT] \~chinese 输出图片大小 \~english Output Image Size + unsigned int + nBufferSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Output buffer size provided + enum MV_SAVE_IAMGE_TYPE + enImageType; ///< [IN] \~chinese 输出图片格式 \~english Output Image Format + +} MV_SAVE_IMAGE_PARAM; + +typedef struct _MV_IMAGE_BASIC_INFO_ +{ + unsigned short nWidthValue; + unsigned short nWidthMin; + unsigned int nWidthMax; + unsigned int nWidthInc; + + unsigned int nHeightValue; + unsigned int nHeightMin; + unsigned int nHeightMax; + unsigned int nHeightInc; + + float fFrameRateValue; + float fFrameRateMin; + float fFrameRateMax; + + unsigned int enPixelType; ///< [OUT] \~chinese 当前的像素格式 \~english Current pixel format + unsigned int + nSupportedPixelFmtNum; ///< [OUT] \~chinese 支持的像素格式种类 \~english Support pixel format + unsigned int enPixelList[MV_MAX_XML_SYMBOLIC_NUM]; + unsigned int nReserved[8]; + +} MV_IMAGE_BASIC_INFO; + +/// \~chinese 噪声特性类型 \~english Noise feature type +typedef enum _MV_CC_BAYER_NOISE_FEATURE_TYPE +{ + MV_CC_BAYER_NOISE_FEATURE_TYPE_INVALID = + 0, ///< \~chinese 无效值 \~english Invalid + MV_CC_BAYER_NOISE_FEATURE_TYPE_PROFILE = + 1, ///< \~chinese 噪声曲线 \~english Noise curve + MV_CC_BAYER_NOISE_FEATURE_TYPE_LEVEL = + 2, ///< \~chinese 噪声水平 \~english Noise level + MV_CC_BAYER_NOISE_FEATURE_TYPE_DEFAULT = + 1, ///< \~chinese 默认值 \~english Default + +} MV_CC_BAYER_NOISE_FEATURE_TYPE; + +/// \~chinese Bayer格式降噪特性信息 \~english Denoise profile info +typedef struct _MV_CC_BAYER_NOISE_PROFILE_INFO_T_ +{ + unsigned int nVersion; ///< \~chinese 版本 \~english version + MV_CC_BAYER_NOISE_FEATURE_TYPE + enNoiseFeatureType; ///< \~chinese 噪声特性类型 \~english noise feature type + enum MvGvspPixelType + enPixelType; ///< \~chinese 图像格式 \~english image format + int nNoiseLevel; ///< \~chinese 平均噪声水平 \~english noise level + unsigned int + nCurvePointNum; ///< \~chinese 曲线点数 \~english curve point number + int * nNoiseCurve; ///< \~chinese 噪声曲线 \~english noise curve + int * nLumCurve; ///< \~chinese 亮度曲线 \~english luminance curve + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_BAYER_NOISE_PROFILE_INFO; + +/// \~chinese Bayer格式噪声估计参数 \~english Bayer noise estimate param +typedef struct _MV_CC_BAYER_NOISE_ESTIMATE_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽(大于等于8) \~english Width + unsigned int nHeight; ///< [IN] \~chinese 图像高(大于等于8) \~english Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned char * + pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int + nSrcDataLen; ///< [IN] \~chinese 输入数据大小 \~english Input data size + + unsigned int + nNoiseThreshold; ///< [IN] \~chinese 噪声阈值(0-4095) \~english Noise Threshold + + unsigned char * + pCurveBuf; ///< [IN] \~chinese 用于存储噪声曲线和亮度曲线(需要外部分配,缓存大小:4096 * sizeof(int) * 2) \~english Buffer used to store noise and brightness curves, size:4096 * sizeof(int) * 2) + MV_CC_BAYER_NOISE_PROFILE_INFO + stNoiseProfile; ///< [OUT] \~chinese 降噪特性信息 \~english Denoise profile + + unsigned int + nThreadNum; ///< [IN] \~chinese 线程数量,0表示算法库根据硬件自适应;1表示单线程(默认);大于1表示线程数目 \~english Thread number, 0 means that the library is adaptive to the hardware, 1 means single thread(Default value), Greater than 1 indicates the number of threads + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_BAYER_NOISE_ESTIMATE_PARAM; + +/// \~chinese Bayer格式空域降噪参数 \~english Bayer spatial Denoise param +typedef struct _MV_CC_BAYER_SPATIAL_DENOISE_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽(大于等于8) \~english Width + unsigned int nHeight; ///< [IN] \~chinese 图像高(大于等于8) \~english Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned char * + pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int + nSrcDataLen; ///< [IN] \~chinese 输入数据大小 \~english Input data size + + unsigned char * + pDstBuf; ///< [OUT] \~chinese 输出降噪后的数据 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int + nDstBufLen; ///< [OUT] \~chinese 输出降噪后的数据长度 \~english Output data length + + MV_CC_BAYER_NOISE_PROFILE_INFO + stNoiseProfile; ///< [IN] \~chinese 降噪特性信息(来源于噪声估计) \~english Denoise profile + unsigned int + nDenoiseStrength; ///< [IN] \~chinese 降噪强度(0-100) \~english nDenoise Strength + unsigned int + nSharpenStrength; ///< [IN] \~chinese 锐化强度(0-32) \~english Sharpen Strength + unsigned int + nNoiseCorrect; ///< [IN] \~chinese 噪声校正系数(0-1280) \~english Noise Correct + + unsigned int + nThreadNum; ///< [IN] \~chinese 线程数量,0表示算法库根据硬件自适应;1表示单线程(默认);大于1表示线程数目 \~english Thread number, 0 means that the library is adaptive to the hardware, 1 means single thread(Default value), Greater than 1 indicates the number of threads + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_BAYER_SPATIAL_DENOISE_PARAM; + +/// \~chinese CLUT参数 \~english CLUT param +typedef struct _MV_CC_CLUT_PARAM_T_ +{ + bool bCLUTEnable; ///< [IN] \~chinese 是否启用CLUT \~english CLUT enable + unsigned int + nCLUTScale; ///< [IN] \~chinese 量化系数(2的整数幂,最大65536) \~english Quantitative scale(Integer power of 2, <= 65536) + unsigned int + nCLUTSize; ///< [IN] \~chinese CLUT大小,[17,33](建议值17) \~english CLUT size[17,33](Recommended values of 17) + unsigned char * pCLUTBuf; ///< [IN] \~chinese 量化CLUT表 \~english CLUT buffer + unsigned int + nCLUTBufLen; ///< [IN] \~chinese 量化CLUT缓存大小(nCLUTSize*nCLUTSize*nCLUTSize*sizeof(int)*3) \~english CLUT buffer length(nCLUTSize*nCLUTSize*nCLUTSize*sizeof(int)*3) + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_CLUT_PARAM; + +/// \~chinese 锐化结构体 \~english Sharpen structure +typedef struct _MV_CC_SHARPEN_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度(最小8) \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度(最小8) \~english Image Height + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据大小 \~english Input data length + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + + unsigned int + nSharpenAmount; ///< [IN] \~chinese 锐度调节强度,[0,500] \~english Sharpen amount,[0,500] // [nSharpenAmount 作废, 使用 nSharpenPosAmount & nSharpenNegAmount 替代 ] + unsigned int + nSharpenRadius; ///< [IN] \~chinese 锐度调节半径(半径越大,耗时越长),[1,21] \~english Sharpen radius(The larger the radius, the longer it takes),[1,21] + unsigned int + nSharpenThreshold; ///< [IN] \~chinese 锐度调节阈值,[0,255] \~english Sharpen threshold,[0,255] + + unsigned int nSharpenPosAmount; // [IN] 锐度调节正向强度,范围:[0, 500] + unsigned int nSharpenNegAmount; // [IN] 锐度调节负向强度,范围:[0, 500] + + unsigned int nRes[6]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_SHARPEN_PARAM; + +/// \~chinese 色彩校正结构体 \~english Color correct structure +typedef struct _MV_CC_COLOR_CORRECT_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度 \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度 \~english Image Height + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据大小 \~english Input data length + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + + unsigned int + nImageBit; ///< [IN] \~chinese 有效图像位数(8,10,12,16) \~english Image bit(8 or 10 or 12 or 16) + MV_CC_GAMMA_PARAM stGammaParam; ///< [IN] \~chinese Gamma信息 \~english Gamma info + MV_CC_CCM_PARAM_EX stCCMParam; ///< [IN] \~chinese CCM信息 \~english CCM info + MV_CC_CLUT_PARAM stCLUTParam; ///< [IN] \~chinese CLUT信息 \~english CLUT info + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_COLOR_CORRECT_PARAM; + +/// \~chinese 矩形ROI结构体 \~english Rect ROI structure +typedef struct _MV_CC_RECT_I_ +{ + unsigned int nX; ///< \~chinese 矩形左上角X轴坐标 \~english X Position + unsigned int nY; ///< \~chinese 矩形左上角Y轴坐标 \~english Y Position + unsigned int nWidth; ///< \~chinese 矩形宽度 \~english Rect Width + unsigned int nHeight; ///< \~chinese 矩形高度 \~english Rect Height + +} MV_CC_RECT_I; + +/// \~chinese 噪声估计结构体 \~english Noise estimate structure +typedef struct _MV_CC_NOISE_ESTIMATE_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度(最小8) \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度(最小8) \~english Image Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据大小 \~english Input data length + + MV_CC_RECT_I * pstROIRect; ///< [IN] \~chinese 图像ROI \~english Image ROI + unsigned int nROINum; ///< [IN] \~chinese ROI个数 \~english ROI number + + ///< \~chinese Bayer域噪声估计参数,Mono8/RGB域无效 \~english Bayer Noise estimate param,Mono8/RGB formats are invalid + unsigned int + nNoiseThreshold; ///< [IN] \~chinese 噪声阈值[0,4095] \~english Noise threshold[0,4095] + ///< \~chinese 建议值:8bit,0xE0;10bit,0x380;12bit,0xE00 \~english Suggestive value:8bit,0xE0;10bit,0x380;12bit,0xE00 + + unsigned char * + pNoiseProfile; ///< [OUT] \~chinese 输出噪声特性 \~english Output Noise Profile + unsigned int + nNoiseProfileSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int + nNoiseProfileLen; ///< [OUT] \~chinese 输出噪声特性长度 \~english Output Noise Profile length + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_NOISE_ESTIMATE_PARAM; + +/// \~chinese 空域降噪结构体 \~english Spatial denoise structure +typedef struct _MV_CC_SPATIAL_DENOISE_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度(最小8) \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度(最小8) \~english Image Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据大小 \~english Input data length + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出降噪后的数据 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出降噪后的数据长度 \~english Output data length + + unsigned char * + pNoiseProfile; ///< [IN] \~chinese 输入噪声特性 \~english Input Noise Profile + unsigned int + nNoiseProfileLen; ///< [IN] \~chinese 输入噪声特性长度 \~english Input Noise Profile length + + ///< \~chinese Bayer域空域降噪参数,Mono8/RGB域无效 \~english Bayer Spatial denoise param,Mono8/RGB formats are invalid + unsigned int + nBayerDenoiseStrength; ///< [IN] \~chinese 降噪强度[0,100] \~english Denoise Strength[0,100] + unsigned int + nBayerSharpenStrength; ///< [IN] \~chinese 锐化强度[0,32] \~english Sharpen Strength[0,32] + unsigned int + nBayerNoiseCorrect; ///< [IN] \~chinese 噪声校正系数[0,1280] \~english Noise Correct[0,1280] + + ///< \~chinese Mono8/RGB域空域降噪参数,Bayer域无效 \~english Mono8/RGB Spatial denoise param,Bayer formats are invalid + unsigned int + nNoiseCorrectLum; ///< [IN] \~chinese 亮度校正系数[1,2000] \~english Noise Correct Lum[1,2000] + unsigned int + nNoiseCorrectChrom; ///< [IN] \~chinese 色调校正系数[1,2000] \~english Noise Correct Chrom[1,2000] + unsigned int + nStrengthLum; ///< [IN] \~chinese 亮度降噪强度[0,100] \~english Strength Lum[0,100] + unsigned int + nStrengthChrom; ///< [IN] \~chinese 色调降噪强度[0,100] \~english Strength Chrom[0,100] + unsigned int + nStrengthSharpen; ///< [IN] \~chinese 锐化强度[1,1000] \~english Strength Sharpen[1,1000] + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_SPATIAL_DENOISE_PARAM; + +/// \~chinese LSC标定结构体 \~english LSC calib structure +typedef struct _MV_CC_LSC_CALIB_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度[16,65535] \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度[16-65535] \~english Image Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + unsigned char * + pCalibBuf; ///< [OUT] \~chinese 输出标定表缓存 \~english Output calib buffer + unsigned int + nCalibBufSize; ///< [IN] \~chinese 提供的标定表缓冲大小(nWidth*nHeight*sizeof(unsigned short)) \~english Provided output buffer size + unsigned int + nCalibBufLen; ///< [OUT] \~chinese 输出标定表缓存长度 \~english Output calib buffer length + + unsigned int nSecNumW; ///< [IN] \~chinese 宽度分块数 \~english Width Sec num + unsigned int nSecNumH; ///< [IN] \~chinese 高度分块数 \~english Height Sec num + unsigned int nPadCoef; ///< [IN] \~chinese 边缘填充系数[1,5] \~english Pad Coef[1,5] + unsigned int + nCalibMethod; ///< [IN] \~chinese 标定方式(0-中心为基准;1-最亮区域为基准;2-目标亮度为基准) \~english Calib method + unsigned int + nTargetGray; ///< [IN] \~chinese 目标亮度(标定方式为2时有效) \~english Target Gray + ///< \~chinese 8位,范围:[0,255] \~english 8bit,range:[0,255] + ///< \~chinese 10位,范围:[0,1023] \~english 10bit,range:[0,1023] + ///< \~chinese 12位,范围:[0,4095] \~english 12bit,range:[0,4095] + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_LSC_CALIB_PARAM; + +/// \~chinese LSC校正结构体 \~english LSC correct structure +typedef struct _MV_CC_LSC_CORRECT_PARAM_T_ +{ + unsigned int nWidth; ///< [IN] \~chinese 图像宽度[16,65535] \~english Image Width + unsigned int nHeight; ///< [IN] \~chinese 图像高度[16,65535] \~english Image Height + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 像素格式 \~english Pixel format + unsigned char * pSrcBuf; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcBufLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + unsigned char * pDstBuf; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int + nDstBufSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + unsigned int nDstBufLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + + unsigned char * + pCalibBuf; ///< [IN] \~chinese 输入标定表缓存 \~english Input calib buffer + unsigned int + nCalibBufLen; ///< [IN] \~chinese 输入标定表缓存长度 \~english Input calib buffer length + + unsigned int nRes[8]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_LSC_CORRECT_PARAM; + +/// \~chinese 某个节点对应的子节点个数最大值 \~english The maximum number of child nodes corresponding to a node +#define MV_MAX_XML_NODE_NUM_C 128 + +/// \~chinese 节点名称字符串最大长度 \~english The maximum length of node name string +#define MV_MAX_XML_NODE_STRLEN_C 64 + +/// \~chinese 节点String值最大长度 \~english The maximum length of Node String +#define MV_MAX_XML_STRVALUE_STRLEN_C 64 + +/// \~chinese 节点描述字段最大长度 \~english The maximum length of the node description field +#define MV_MAX_XML_DISC_STRLEN_C 512 + +/// \~chinese 最多的单元数 \~english The maximum number of units +#define MV_MAX_XML_ENTRY_NUM 10 + +/// \~chinese 父节点个数上限 \~english The maximum number of parent nodes +#define MV_MAX_XML_PARENTS_NUM 8 + +/// \~chinese 每个已经实现单元的名称长度 \~english The length of the name of each unit that has been implemented +#define MV_MAX_XML_SYMBOLIC_STRLEN_C 64 + +enum MV_XML_Visibility +{ + V_Beginner = 0, ///< Always visible + V_Expert = 1, ///< Visible for experts or Gurus + V_Guru = 2, ///< Visible for Gurus + V_Invisible = 3, ///< Not Visible + V_Undefined = 99 ///< Object is not yet initialized +}; + +/// \~chinese 单个节点基本属性 | en:Single Node Basic Attributes +typedef struct _MV_XML_NODE_FEATURE_ +{ + enum MV_XML_InterfaceType enType; ///< \~chinese 节点类型 \~english Node Type + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Is visibility + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 节点描述,目前暂不支持 \~english Node Description, NOT SUPPORT NOW + char strDisplayName + [MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 显示名称 \~english Display Name + char strName[MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 节点名 \~english Node Name + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 提示 \~english Notice + + unsigned int nReserved[4]; +} MV_XML_NODE_FEATURE; + +/// \~chinese 节点列表 | en:Node List +typedef struct _MV_XML_NODES_LIST_ +{ + unsigned int nNodeNum; ///< \~chinese 节点个数 \~english Node Number + MV_XML_NODE_FEATURE stNodes[MV_MAX_XML_NODE_NUM_C]; +} MV_XML_NODES_LIST; + +typedef struct _MV_XML_FEATURE_Value_ +{ + enum MV_XML_InterfaceType enType; ///< \~chinese 节点类型 \~english Node Type + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 节点描述,目前暂不支持 \~english Node Description, NOT SUPPORT NOW + char strDisplayName + [MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 显示名称 \~english Display Name + char strName[MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 节点名 \~english Node Name + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 提示 \~english Notice + unsigned int nReserved[4]; +} MV_XML_FEATURE_Value; + +typedef struct _MV_XML_FEATURE_Base_ +{ + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode +} MV_XML_FEATURE_Base; + +typedef struct _MV_XML_FEATURE_Integer_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + int64_t nValue; ///< \~chinese 当前值 \~english Current Value + int64_t nMinValue; ///< \~chinese 最小值 \~english Min Value + int64_t nMaxValue; ///< \~chinese 最大值 \~english Max Value + int64_t nIncrement; ///< \~chinese 增量 \~english Increment + + unsigned int nReserved[4]; + +} MV_XML_FEATURE_Integer; + +typedef struct _MV_XML_FEATURE_Boolean_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + bool bValue; ///< \~chinese 当前值 \~english Current Value + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Boolean; + +typedef struct _MV_XML_FEATURE_Command_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Command; + +typedef struct _MV_XML_FEATURE_Float_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + double dfValue; ///< \~chinese 当前值 \~english Current Value + double dfMinValue; ///< \~chinese 最小值 \~english Min Value + double dfMaxValue; ///< \~chinese 最大值 \~english Max Value + double dfIncrement; ///< \~chinese 增量 \~english Increment + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Float; + +typedef struct _MV_XML_FEATURE_String_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + char + strValue[MV_MAX_XML_STRVALUE_STRLEN_C]; ///< \~chinese 当前值 \~english Current Value + + unsigned int nReserved[4]; +} MV_XML_FEATURE_String; + +typedef struct _MV_XML_FEATURE_Register_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + int64_t nAddrValue; ///< \~chinese 当前值 \~english Current Value + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Register; + +typedef struct _MV_XML_FEATURE_Category_ +{ + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 节点描述 目前暂不支持 \~english Node Description, NOT SUPPORT NOW + char strDisplayName + [MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 显示名称 \~english Display Name + char strName[MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 节点名 \~english Node Name + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 提示 \~english Notice + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Category; + +typedef struct _MV_XML_FEATURE_EnumEntry_ +{ + char strName[MV_MAX_XML_NODE_STRLEN_C]; + char strDisplayName[MV_MAX_XML_NODE_STRLEN_C]; + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 目前暂不支持 \~english NOT SUPPORT NOW + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; + int bIsImplemented; + int nParentsNum; + MV_XML_NODE_FEATURE stParentsList[MV_MAX_XML_PARENTS_NUM]; + + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + int64_t nValue; ///< \~chinese 当前值 \~english Current Value + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + int nReserved[8]; + +} MV_XML_FEATURE_EnumEntry; + +typedef struct _MV_XML_FEATURE_Enumeration_ +{ + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 节点描述 目前暂不支持 \~english Node Description, NOT SUPPORT NOW + char strDisplayName + [MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 显示名称 \~english Display Name + char strName[MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 节点名 \~english Node Name + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 提示 \~english Notice + + int nSymbolicNum; ///< \~chinese ymbolic数 \~english Symbolic Number + char strCurrentSymbolic + [MV_MAX_XML_SYMBOLIC_STRLEN_C]; ///< \~chinese 当前Symbolic索引 \~english Current Symbolic Index + char strSymbolic[MV_MAX_XML_SYMBOLIC_NUM][MV_MAX_XML_SYMBOLIC_STRLEN_C]; + enum MV_XML_AccessMode enAccessMode; ////< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + int64_t nValue; ///< \~chinese 当前值 \~english Current Value + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Enumeration; + +typedef struct _MV_XML_FEATURE_Port_ +{ + enum MV_XML_Visibility enVisivility; ///< \~chinese 是否可见 \~english Visible + char strDescription + [MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 节点描述,目前暂不支持 \~english Node Description, NOT SUPPORT NOW + char strDisplayName + [MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 显示名称 \~english Display Name + char strName[MV_MAX_XML_NODE_STRLEN_C]; ///< \~chinese 节点名 \~english Node Name + char strToolTip[MV_MAX_XML_DISC_STRLEN_C]; ///< \~chinese 提示 \~english Notice + + enum MV_XML_AccessMode enAccessMode; ///< \~chinese 访问模式 \~english Access Mode + int + bIsLocked; ///< \~chinese 是否锁定。0-否;1-是,目前暂不支持 \~english Locked. 0-NO; 1-YES, NOT SUPPORT NOW + + unsigned int nReserved[4]; +} MV_XML_FEATURE_Port; + +typedef struct _MV_XML_CAMERA_FEATURE_ +{ + enum MV_XML_InterfaceType enType; + union { + MV_XML_FEATURE_Integer stIntegerFeature; + MV_XML_FEATURE_Float stFloatFeature; + MV_XML_FEATURE_Enumeration stEnumerationFeature; + MV_XML_FEATURE_String stStringFeature; + } SpecialFeature; + +} MV_XML_CAMERA_FEATURE; + +/// \~chinese 图片保存参数 \~english Save Image Parameters +typedef struct _MV_SAVE_IMAGE_PARAM_T_EX_ +{ + unsigned char * pData; ///< [IN] \~chinese 输入数据缓存 \~english Input Data Buffer + unsigned int nDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input Data length + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese 输入数据的像素格式 \~english Input Data Pixel Format + unsigned short nWidth; ///< [IN] \~chinese 图像宽 \~english Image Width + unsigned short nHeight; ///< [IN] \~chinese 图像高 \~english Image Height + + unsigned char * + pImageBuffer; ///< [OUT] \~chinese 输出图片缓存 \~english Output Image Buffer + unsigned int nImageLen; ///< [OUT] \~chinese 输出图片长度 \~english Output Image length + unsigned int + nBufferSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Output buffer size provided + enum MV_SAVE_IAMGE_TYPE + enImageType; ///< [IN] \~chinese 输出图片格式 \~english Output Image Format + unsigned int + nJpgQuality; ///< [IN] \~chinese JPG编码质量(50-99],其它格式无效 \~english Encoding quality(50-99],Other formats are invalid + + unsigned int + iMethodValue; ///< [IN] \~chinese 插值方法 0-快速 1-均衡 2-最优(其它值默认为最优) \~english Bayer interpolation method 0-Fast 1-Equilibrium 2-Optimal + + unsigned int nReserved[3]; ///< \~chinese 预留 \~english Reserved + +} MV_SAVE_IMAGE_PARAM_EX; + +/// \~chinese 图片保存参数 \~english Save Image Parameters +typedef struct _MV_SAVE_IMG_TO_FILE_PARAM_ +{ + enum MvGvspPixelType + enPixelType; ///< [IN] \~chinese输入数据的像素格式 \~english The pixel format of the input data + unsigned char * pData; ///< [IN] \~chinese 输入数据缓存 \~english Input Data Buffer + unsigned int nDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input Data length + unsigned short nWidth; ///< [IN] \~chinese 图像宽 \~english Image Width + unsigned short nHeight; ///< [IN] \~chinese 图像高 \~english Image Height + enum MV_SAVE_IAMGE_TYPE + enImageType; ///< [IN] \~chinese 输入图片格式 \~english Input Image Format + unsigned int + nQuality; ///< [IN] \~chinese JPG编码质量(50-99],PNG编码质量[0-9],其它格式无效 \~english JPG Encoding quality(50-99],PNG Encoding quality[0-9],Other formats are invalid + char pImagePath[256]; ///< [IN] \~chinese 输入文件路径 \~english Input file path + + int + iMethodValue; ///< [IN] \~chinese 插值方法 0-快速 1-均衡 2-最优(其它值默认为最优) \~english Bayer interpolation method 0-Fast 1-Equilibrium 2-Optimal + + unsigned int nReserved[8]; ///< \~chinese 预留 \~english Reserved + +} MV_SAVE_IMG_TO_FILE_PARAM; + +// \~chinese 像素转换结构体 \~english Pixel convert structure +typedef struct _MV_CC_PIXEL_CONVERT_PARAM_ +{ + unsigned short nWidth; ///< [IN] \~chinese 图像宽 \~english Width + unsigned short nHeight; ///< [IN] \~chinese 图像高 \~english Height + + enum MvGvspPixelType + enSrcPixelType; ///< [IN] \~chinese 源像素格式 \~english Source pixel format + unsigned char * pSrcData; ///< [IN] \~chinese 输入数据缓存 \~english Input data buffer + unsigned int nSrcDataLen; ///< [IN] \~chinese 输入数据长度 \~english Input data length + + enum MvGvspPixelType + enDstPixelType; ///< [IN] \~chinese 目标像素格式 \~english Destination pixel format + unsigned char * + pDstBuffer; ///< [OUT] \~chinese 输出数据缓存 \~english Output data buffer + unsigned int nDstLen; ///< [OUT] \~chinese 输出数据长度 \~english Output data length + unsigned int + nDstBufferSize; ///< [IN] \~chinese 提供的输出缓冲区大小 \~english Provided output buffer size + + unsigned int nRes[4]; ///< \~chinese 预留 \~english Reserved + +} MV_CC_PIXEL_CONVERT_PARAM; + +#endif /* _MV_OBSOLETE_CAM_PARAMS_H_ */ diff --git a/io/hikrobot/include/PixelType.h b/io/hikrobot/include/PixelType.h new file mode 100644 index 0000000..9dccb05 --- /dev/null +++ b/io/hikrobot/include/PixelType.h @@ -0,0 +1,248 @@ + +#ifndef _MV_PIXEL_TYPE_H_ +#define _MV_PIXEL_TYPE_H_ + +/************************************************************************/ +/* GigE Vision (2.0.03) PIXEL FORMATS */ +/************************************************************************/ + +// Indicate if pixel is monochrome or RGB +#define MV_GVSP_PIX_MONO 0x01000000 +#define MV_GVSP_PIX_RGB 0x02000000 // deprecated in version 1.1 +#define MV_GVSP_PIX_COLOR 0x02000000 +#define MV_GVSP_PIX_CUSTOM 0x80000000 +#define MV_GVSP_PIX_COLOR_MASK 0xFF000000 + +// Indicate effective number of bits occupied by the pixel (including padding). +// This can be used to compute amount of memory required to store an image. +#define MV_PIXEL_BIT_COUNT(n) ((n) << 16) + +#define MV_GVSP_PIX_EFFECTIVE_PIXEL_SIZE_MASK 0x00FF0000 +#define MV_GVSP_PIX_EFFECTIVE_PIXEL_SIZE_SHIFT 16 + +// Pixel ID: lower 16-bit of the pixel formats +#define MV_GVSP_PIX_ID_MASK 0x0000FFFF +#define MV_GVSP_PIX_COUNT 0x46 // next Pixel ID available + +enum MvGvspPixelType +{ +// Undefined pixel type +#ifdef WIN32 + PixelType_Gvsp_Undefined = 0xFFFFFFFF, + +#else + PixelType_Gvsp_Undefined = -1, + +#endif + // Mono buffer format defines + PixelType_Gvsp_Mono1p = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(1) | 0x0037), + PixelType_Gvsp_Mono2p = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(2) | 0x0038), + PixelType_Gvsp_Mono4p = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(4) | 0x0039), + PixelType_Gvsp_Mono8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0001), + PixelType_Gvsp_Mono8_Signed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0002), + PixelType_Gvsp_Mono10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0003), + PixelType_Gvsp_Mono10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0004), + PixelType_Gvsp_Mono12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0005), + PixelType_Gvsp_Mono12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0006), + PixelType_Gvsp_Mono14 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0025), + PixelType_Gvsp_Mono16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0007), + + // Bayer buffer format defines + PixelType_Gvsp_BayerGR8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0008), + PixelType_Gvsp_BayerRG8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0009), + PixelType_Gvsp_BayerGB8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000A), + PixelType_Gvsp_BayerBG8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000B), + PixelType_Gvsp_BayerRBGG8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0046), + PixelType_Gvsp_BayerGR10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000C), + PixelType_Gvsp_BayerRG10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000D), + PixelType_Gvsp_BayerGB10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000E), + PixelType_Gvsp_BayerBG10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000F), + PixelType_Gvsp_BayerGR12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0010), + PixelType_Gvsp_BayerRG12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0011), + PixelType_Gvsp_BayerGB12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0012), + PixelType_Gvsp_BayerBG12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0013), + PixelType_Gvsp_BayerGR10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0026), + PixelType_Gvsp_BayerRG10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0027), + PixelType_Gvsp_BayerGB10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0028), + PixelType_Gvsp_BayerBG10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0029), + PixelType_Gvsp_BayerGR12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002A), + PixelType_Gvsp_BayerRG12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002B), + PixelType_Gvsp_BayerGB12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002C), + PixelType_Gvsp_BayerBG12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002D), + PixelType_Gvsp_BayerGR16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x002E), + PixelType_Gvsp_BayerRG16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x002F), + PixelType_Gvsp_BayerGB16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0030), + PixelType_Gvsp_BayerBG16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0031), + + // RGB Packed buffer format defines + PixelType_Gvsp_RGB8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0014), + PixelType_Gvsp_BGR8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0015), + PixelType_Gvsp_RGBA8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0016), + PixelType_Gvsp_BGRA8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0017), + PixelType_Gvsp_RGB10_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0018), + PixelType_Gvsp_BGR10_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0019), + PixelType_Gvsp_RGB12_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x001A), + PixelType_Gvsp_BGR12_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x001B), + PixelType_Gvsp_RGB16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0033), + PixelType_Gvsp_BGR16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x004B), + PixelType_Gvsp_RGBA16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0064), + PixelType_Gvsp_BGRA16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0051), + PixelType_Gvsp_RGB10V1_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x001C), + PixelType_Gvsp_RGB10V2_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x001D), + PixelType_Gvsp_RGB12V1_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(36) | 0X0034), + PixelType_Gvsp_RGB565_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0035), + PixelType_Gvsp_BGR565_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0X0036), + + // YUV Packed buffer format defines + PixelType_Gvsp_YUV411_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x001E), + PixelType_Gvsp_YUV422_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x001F), + PixelType_Gvsp_YUV422_YUYV_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0032), + PixelType_Gvsp_YUV444_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0020), + PixelType_Gvsp_YCBCR8_CBYCR = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x003A), + PixelType_Gvsp_YCBCR422_8 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x003B), + PixelType_Gvsp_YCBCR422_8_CBYCRY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0043), + PixelType_Gvsp_YCBCR411_8_CBYYCRYY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x003C), + PixelType_Gvsp_YCBCR601_8_CBYCR = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x003D), + PixelType_Gvsp_YCBCR601_422_8 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x003E), + PixelType_Gvsp_YCBCR601_422_8_CBYCRY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0044), + PixelType_Gvsp_YCBCR601_411_8_CBYYCRYY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x003F), + PixelType_Gvsp_YCBCR709_8_CBYCR = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0040), + PixelType_Gvsp_YCBCR709_422_8 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0041), + PixelType_Gvsp_YCBCR709_422_8_CBYCRY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0045), + PixelType_Gvsp_YCBCR709_411_8_CBYYCRYY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x0042), + + // YUV420 + PixelType_Gvsp_YUV420SP_NV12 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x8001), + PixelType_Gvsp_YUV420SP_NV21 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x8002), + + // RGB Planar buffer format defines + PixelType_Gvsp_RGB8_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0021), + PixelType_Gvsp_RGB10_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0022), + PixelType_Gvsp_RGB12_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0023), + PixelType_Gvsp_RGB16_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0024), + + // 自定义的图片格式 + PixelType_Gvsp_Jpeg = (MV_GVSP_PIX_CUSTOM | MV_PIXEL_BIT_COUNT(24) | 0x0001), + + PixelType_Gvsp_Coord3D_ABC32f = + (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(96) | 0x00C0), //0x026000C0 + PixelType_Gvsp_Coord3D_ABC32f_Planar = + (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(96) | 0x00C1), //0x026000C1 + + // 该值被废弃,请参考PixelType_Gvsp_Coord3D_AC32f_64; the value is discarded + PixelType_Gvsp_Coord3D_AC32f = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(40) | 0x00C2), + // 该值被废弃; the value is discarded (已放入Chunkdata) + PixelType_Gvsp_COORD3D_DEPTH_PLUS_MASK = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(28) | 0x0001), + + PixelType_Gvsp_Coord3D_ABC32 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(96) | 0x3001), //0x82603001 + PixelType_Gvsp_Coord3D_AB32f = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x3002), //0x82403002 + PixelType_Gvsp_Coord3D_AB32 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x3003), //0x82403003 + PixelType_Gvsp_Coord3D_AC32f_64 = + (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x00C2), //0x024000C2 + PixelType_Gvsp_Coord3D_AC32f_Planar = + (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x00C3), //0x024000C3 + PixelType_Gvsp_Coord3D_AC32 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x3004), //0x82403004 + PixelType_Gvsp_Coord3D_A32f = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x00BD), //0x012000BD + PixelType_Gvsp_Coord3D_A32 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x3005), //0x81203005 + PixelType_Gvsp_Coord3D_C32f = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x00BF), //0x012000BF + PixelType_Gvsp_Coord3D_C32 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x3006), //0x81203006 + PixelType_Gvsp_Coord3D_ABC16 = + (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x00B9), //0x023000B9 + PixelType_Gvsp_Coord3D_C16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x00B8), //0x011000B8 + + PixelType_Gvsp_Float32 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x0001), //0x81200001 + + //无损压缩像素格式定义 + PixelType_Gvsp_HB_Mono8 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0001), + PixelType_Gvsp_HB_Mono10 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0003), + PixelType_Gvsp_HB_Mono10_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0004), + PixelType_Gvsp_HB_Mono12 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0005), + PixelType_Gvsp_HB_Mono12_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0006), + PixelType_Gvsp_HB_Mono16 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0007), + PixelType_Gvsp_HB_BayerGR8 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0008), + PixelType_Gvsp_HB_BayerRG8 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0009), + PixelType_Gvsp_HB_BayerGB8 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000A), + PixelType_Gvsp_HB_BayerBG8 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000B), + PixelType_Gvsp_HB_BayerRBGG8 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0046), + PixelType_Gvsp_HB_BayerGR10 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000C), + PixelType_Gvsp_HB_BayerRG10 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000D), + PixelType_Gvsp_HB_BayerGB10 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000E), + PixelType_Gvsp_HB_BayerBG10 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000F), + PixelType_Gvsp_HB_BayerGR12 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0010), + PixelType_Gvsp_HB_BayerRG12 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0011), + PixelType_Gvsp_HB_BayerGB12 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0012), + PixelType_Gvsp_HB_BayerBG12 = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0013), + PixelType_Gvsp_HB_BayerGR10_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0026), + PixelType_Gvsp_HB_BayerRG10_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0027), + PixelType_Gvsp_HB_BayerGB10_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0028), + PixelType_Gvsp_HB_BayerBG10_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0029), + PixelType_Gvsp_HB_BayerGR12_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002A), + PixelType_Gvsp_HB_BayerRG12_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002B), + PixelType_Gvsp_HB_BayerGB12_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002C), + PixelType_Gvsp_HB_BayerBG12_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002D), + PixelType_Gvsp_HB_YUV422_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x001F), + PixelType_Gvsp_HB_YUV422_YUYV_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0032), + PixelType_Gvsp_HB_RGB8_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0014), + PixelType_Gvsp_HB_BGR8_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0015), + PixelType_Gvsp_HB_RGBA8_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0016), + PixelType_Gvsp_HB_BGRA8_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0017), + PixelType_Gvsp_HB_RGB16_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0033), + PixelType_Gvsp_HB_BGR16_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x004B), + PixelType_Gvsp_HB_RGBA16_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0064), + PixelType_Gvsp_HB_BGRA16_Packed = + (MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0051), + +}; + +#ifdef WIN32 +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +#endif /* _MV_PIXEL_TYPE_H_ */ diff --git a/io/hikrobot/lib/amd64/libMvCameraControl.so b/io/hikrobot/lib/amd64/libMvCameraControl.so new file mode 100644 index 0000000..347ccae Binary files /dev/null and b/io/hikrobot/lib/amd64/libMvCameraControl.so differ diff --git a/io/hikrobot/lib/arm64/libMvCameraControl.so b/io/hikrobot/lib/arm64/libMvCameraControl.so new file mode 100644 index 0000000..d5a1a1b Binary files /dev/null and b/io/hikrobot/lib/arm64/libMvCameraControl.so differ diff --git a/io/mindvision/include/CameraApi.h b/io/mindvision/include/CameraApi.h new file mode 100644 index 0000000..a4abdc2 --- /dev/null +++ b/io/mindvision/include/CameraApi.h @@ -0,0 +1,5149 @@ +#ifndef _MVCAMAPI_H_ +#define _MVCAMAPI_H_ + +#define MVSDK_API + +#ifdef __cplusplus + extern "C" { + #endif + + +#include "CameraDefine.h" +#include "CameraStatus.h" + + + +/******************************************************/ +// 函数名 : CameraSdkInit +// 功能描述 : 相机SDK初始化,在调用任何SDK其他接口前,必须 +// 先调用该接口进行初始化。该函数在整个进程运行 +// 期间只需要调用一次。 +// 参数 : iLanguageSel 用于选择SDK内部提示信息和界面的语种, +// 0:表示英文,1:表示中文。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSdkInit(int iLanguageSel); + +/******************************************************/ +// 函数名 : CameraSetDataDirectory +// 功能描述 : 设置相机数据文件的存储目录(.config .mvdat等) +// 需要在CameraInit打开相机前设置好 +// 默认目录为当前目录 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetDataDirectory(char const* dirname); + +/******************************************************/ +// 函数名 : CameraUSBDeviceInit +// 功能描述 : (已废弃,无需调用) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraUSBDeviceInit(); + +/******************************************************/ +// 函数名 : CameraUSBDeviceUninit +// 功能描述 : (已废弃,无需调用) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraUSBDeviceUninit(); + + +/******************************************************/ +// 函数名 : CameraEnumerateDevice +// 功能描述 : 枚举设备,并建立设备列表。在调用CameraInit +// 之前,必须调用该函数来获得设备的信息。 +// 参数 : pCameraList 设备列表数组指针。 +// piNums 设备的个数指针,调用时传入pCameraList +// 数组的元素个数,函数返回时,保存实际找到的设备个数。 +// 注意,piNums指向的值必须初始化,且不超过pCameraList数组元素个数, +// 否则有可能造成内存溢出。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraEnumerateDevice( + tSdkCameraDevInfo* pCameraList, + INT* piNums +); + +/******************************************************/ +// 函数名 : CameraIdleStateDevice +// 功能描述 : 当前系统有未使用的相机信息。 +// 参数 : pCameraList 设备列表数组指针。 +// piNums 设备的个数指针,调用时传入pCameraList +// 数组的元素个数,函数返回时,保存实际找到的设备个数。 +// 注意,piNums指向的值必须初始化,且不超过pCameraList数组元素个数, +// 否则有可能造成内存溢出。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraIdleStateDevice( + tSdkCameraDevInfo* pCameraList, + INT* piNums +); + + +/******************************************************/ +// 函数名 : CameraEnumerateDeviceEx +// 功能描述 : 枚举设备,并建立设备列表。在调用CameraInitEx +// 之前,必须调用该函数枚举设备。 +// 参数 : +// 返回值 : 返回设备个数,0表示无。 +/******************************************************/ +MVSDK_API INT CameraEnumerateDeviceEx( +); + + +/******************************************************/ +// 函数名 : CameraIsOpened +// 功能描述 : 检测设备是否已经被其他应用程序打开。在调用CameraInit +// 之前,可以使用该函数进行检测,如果已经被打开,调用 +// CameraInit会返回设备已经被打开的错误码。 +// 参数 : pCameraList 设备的枚举信息结构体指针,由CameraEnumerateDevice获得。 +// pOpened 设备的状态指针,返回设备是否被打开的状态,TRUE为打开,FALSE为空闲。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraIsOpened( + tSdkCameraDevInfo* pCameraList, + BOOL* pOpened +); + + +/******************************************************/ +// 函数名 : CameraInit +// 功能描述 : 相机初始化。初始化成功后,才能调用任何其他 +// 相机相关的操作接口。 +// 参数 : pCameraInfo 该相机的设备描述信息,由CameraEnumerateDevice +// 函数获得。 +// iParamLoadMode 相机初始化时使用的参数加载方式。-1表示使用上次退出时的参数加载方式。 +// emTeam 初始化时使用的参数组。-1表示加载上次退出时的参数组。 +// pCameraHandle 相机的句柄指针,初始化成功后,该指针 +// 返回该相机的有效句柄,在调用其他相机 +// 相关的操作接口时,都需要传入该句柄,主要 +// 用于多相机之间的区分。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraInit( + tSdkCameraDevInfo* pCameraInfo, + int emParamLoadMode, + int emTeam, + CameraHandle* pCameraHandle +); + +/******************************************************/ +// 函数名 : CameraInitEx +// 功能描述 : 相机初始化。初始化成功后,才能调用任何其他 +// 相机相关的操作接口。 +// 参数 : iDeviceIndex 相机的索引号,CameraEnumerateDeviceEx返回相机个数。 +// iParamLoadMode 相机初始化时使用的参数加载方式。-1表示使用上次退出时的参数加载方式。 +// emTeam 初始化时使用的参数组。-1表示加载上次退出时的参数组。 +// pCameraHandle 相机的句柄指针,初始化成功后,该指针 +// 返回该相机的有效句柄,在调用其他相机 +// 相关的操作接口时,都需要传入该句柄,主要 +// 用于多相机之间的区分。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraInitEx( + int iDeviceIndex, + int iParamLoadMode, + int emTeam, + CameraHandle* pCameraHandle +); + +/// @ingroup API_OPEN +/// \~chinese +/// \brief 相机初始化。初始化成功后,才能调用其他相机相关的操作接口。 +/// \param [in] CameraName 相机昵称。@link #tSdkCameraDevInfo.acFriendlyName @endlink +/// \param [out] pCameraHandle 相机的句柄指针,初始化成功后,该指针返回该相机的有效句柄。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief The camera is initialized. After successful initialization, other camera-related operation interfaces can be called. +/// \param [in] CameraName Camera friendly name.@link #tSdkCameraDevInfo.acFriendlyName @endlink +/// \param [out] pCameraHandle The handle pointer of the camera, after successful initialization, returns the camera's effective handle. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraInitEx2( + char* CameraName, + CameraHandle *pCameraHandle +); + +/******************************************************/ +// 函数名 : CameraSetCallbackFunction +// 功能描述 : 设置图像捕获的回调函数。当捕获到新的图像数据帧时, +// pCallBack所指向的回调函数就会被调用。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pCallBack 回调函数指针。 +// pContext 回调函数的附加参数,在回调函数被调用时 +// 该附加参数会被传入,可以为NULL。多用于 +// 多个相机时携带附加信息。 +// pCallbackOld 用于保存当前的回调函数。可以为NULL。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetCallbackFunction( + CameraHandle hCamera, + CAMERA_SNAP_PROC pCallBack, + PVOID pContext, + CAMERA_SNAP_PROC* pCallbackOld +); + +/******************************************************/ +// 函数名 : CameraUnInit +// 功能描述 : 相机反初始化。释放资源。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraUnInit( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraGetInformation +// 功能描述 : 获得相机的描述信息 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbuffer 指向相机描述信息指针的指针。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetInformation( + CameraHandle hCamera, + char** pbuffer +); + +/******************************************************/ +// 函数名 : CameraImageProcess +// 功能描述 : 将获得的相机原始输出图像数据进行处理,叠加饱和度、 +// 颜色增益和校正、降噪等处理效果,最后得到RGB888 +// 格式的图像数据。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbyIn 输入图像数据的缓冲区地址,不能为NULL。 +// pbyOut 处理后图像输出的缓冲区地址,不能为NULL。 +// pFrInfo 输入图像的帧头信息,处理完成后,帧头信息 +// 中的图像格式uiMediaType会随之改变。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraImageProcess( + CameraHandle hCamera, + BYTE* pbyIn, + BYTE* pbyOut, + tSdkFrameHead* pFrInfo +); + +/******************************************************/ +// 函数名 : CameraImageProcessEx +// 功能描述 : 将获得的相机原始输出图像数据进行处理,叠加饱和度 +// 颜色增益和校正、降噪等处理效果,最后得到RGB888 +// 格式的图像数据。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbyIn 输入图像数据的缓冲区地址,不能为NULL。 +// pbyOut 处理后图像输出的缓冲区地址,不能为NULL。 +// pFrInfo 输入图像的帧头信息,处理完成后,帧头信息 +// uOutFormat 处理完后图像的输出格式 +// uReserved 预留参数,必须设置为0 +// 中的图像格式uiMediaType会随之改变。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraImageProcessEx( + CameraHandle hCamera, + BYTE *pbyIn, + BYTE *pbyOut, + tSdkFrameHead *pFrInfo, + UINT uOutFormat, + UINT uReserved +); + +/******************************************************/ +// 函数名 : CameraDisplayInit +// 功能描述 : 初始化SDK内部的显示模块。在调用CameraDisplayRGB24 +// 前必须先调用该函数初始化。如果您在二次开发中, +// 使用自己的方式进行图像显示(不调用CameraDisplayRGB24), +// 则不需要调用本函数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// hWndDisplay 显示窗口的句柄,一般为窗口的m_hWnd成员。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraDisplayInit( + CameraHandle hCamera, + HWND hWndDisplay +); + +/******************************************************/ +// 函数名 : CameraDisplayRGB24 +// 功能描述 : 显示图像。必须调用过CameraDisplayInit进行 +// 初始化才能调用本函数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbyRGB24 图像的数据缓冲区,RGB888格式。 +// pFrInfo 图像的帧头信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraDisplayRGB24( + CameraHandle hCamera, + BYTE* pbyRGB24, + tSdkFrameHead* pFrInfo +); + +/******************************************************/ +// 函数名 : CameraSetDisplayMode +// 功能描述 : 设置显示的模式。必须调用过CameraDisplayInit +// 进行初始化才能调用本函数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMode 显示模式,DISPLAYMODE_SCALE或者 +// DISPLAYMODE_REAL,具体参见CameraDefine.h +// 中emSdkDisplayMode的定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetDisplayMode( + CameraHandle hCamera, + INT iMode +); + +/******************************************************/ +// 函数名 : CameraSetDisplayOffset +// 功能描述 : 设置显示的起始偏移值。仅当显示模式为DISPLAYMODE_REAL +// 时有效。例如显示控件的大小为320X240,而图像的 +// 的尺寸为640X480,那么当iOffsetX = 160,iOffsetY = 120时 +// 显示的区域就是图像的居中320X240的位置。必须调用过 +// CameraDisplayInit进行初始化才能调用本函数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iOffsetX 偏移的X坐标。 +// iOffsetY 偏移的Y坐标。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetDisplayOffset( + CameraHandle hCamera, + int iOffsetX, + int iOffsetY +); + +/******************************************************/ +// 函数名 : CameraSetDisplaySize +// 功能描述 : 设置显示控件的尺寸。必须调用过 +// CameraDisplayInit进行初始化才能调用本函数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iWidth 宽度 +// iHeight 高度 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetDisplaySize( + CameraHandle hCamera, + INT iWidth, + INT iHeight +); + +/******************************************************/ +// 函数名 : CameraGetImageBuffer +// 功能描述 : 获得一帧图像数据。为了提高效率,SDK在图像抓取时采用了零拷贝机制, +// CameraGetImageBuffer实际获得是内核中的一个缓冲区地址, +// 该函数成功调用后,必须调用CameraReleaseImageBuffer释放由 +// CameraGetImageBuffer得到的缓冲区,以便让内核继续使用 +// 该缓冲区。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pFrameInfo 图像的帧头信息指针。 +// pbyBuffer 指向图像的数据的缓冲区指针。由于 +// 采用了零拷贝机制来提高效率,因此 +// 这里使用了一个指向指针的指针。 +// UINT wTimes 抓取图像的超时时间。单位毫秒。在 +// wTimes时间内还未获得图像,则该函数 +// 会返回超时信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetImageBuffer( + CameraHandle hCamera, + tSdkFrameHead* pFrameInfo, + BYTE** pbyBuffer, + UINT wTimes +); + +/******************************************************/ +// 函数名 : CameraGetImageBufferEx +// 功能描述 : 获得一帧图像数据。该接口获得的图像是经过处理后的RGB格式。该函数调用后, +// 不需要调用 CameraReleaseImageBuffer 释放,也不要调用free之类的函数释放 +// 来释放该函数返回的图像数据缓冲区。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piWidth 整形指针,返回图像的宽度 +// piHeight 整形指针,返回图像的高度 +// UINT wTimes 抓取图像的超时时间。单位毫秒。在 +// wTimes时间内还未获得图像,则该函数 +// 会返回超时信息。 +// 返回值 : 成功时,返回RGB数据缓冲区的首地址; +// 否则返回0。 +/******************************************************/ +MVSDK_API unsigned char* CameraGetImageBufferEx( + CameraHandle hCamera, + INT* piWidth, + INT* piHeight, + UINT wTimes +); + + +/******************************************************/ +// 函数名 : CameraSnapToBuffer +// 功能描述 : 抓拍一张图像到缓冲区中。相机会进入抓拍模式,并且 +// 自动切换到抓拍模式的分辨率进行图像捕获。然后将 +// 捕获到的数据保存到缓冲区中。 +// 该函数成功调用后,必须调用CameraReleaseImageBuffer +// 释放由CameraSnapToBuffer得到的缓冲区。具体请参考 +// CameraGetImageBuffer函数的功能描述部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pFrameInfo 指针,返回图像的帧头信息。 +// pbyBuffer 指向指针的指针,用来返回图像缓冲区的地址。 +// uWaitTimeMs 超时时间,单位毫秒。在该时间内,如果仍然没有 +// 成功捕获的数据,则返回超时信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSnapToBuffer( + CameraHandle hCamera, + tSdkFrameHead* pFrameInfo, + BYTE** pbyBuffer, + UINT uWaitTimeMs +); + +/******************************************************/ +// 函数名 : CameraReleaseImageBuffer +// 功能描述 : 释放由CameraGetImageBuffer获得的缓冲区。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbyBuffer 由CameraGetImageBuffer获得的缓冲区地址。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraReleaseImageBuffer( + CameraHandle hCamera, + BYTE* pbyBuffer +); + +/******************************************************/ +// 函数名 : CameraPlay +// 功能描述 : 让SDK进入工作模式,开始接收来自相机发送的图像 +// 数据。如果当前相机是触发模式,则需要接收到 +// 触发帧以后才会更新图像。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraPlay( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraPause +// 功能描述 : 让SDK进入暂停模式,不接收来自相机的图像数据, +// 同时也会发送命令让相机暂停输出,释放传输带宽。 +// 暂停模式下,可以对相机的参数进行配置,并立即生效。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraPause( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraStop +// 功能描述 : 让SDK进入停止状态,一般是反初始化时调用该函数, +// 该函数被调用,不能再对相机的参数进行配置。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraStop( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraInitRecord +// 功能描述 : 初始化一次录像。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iFormat 录像的格式,当前只支持不压缩和MSCV两种方式。 +// 0:不压缩;1:MSCV方式压缩。 +// pcSavePath 录像文件保存的路径。 +// b2GLimit 如果为TRUE,则文件大于2G时自动分割。 +// dwQuality 录像的质量因子,越大,则质量越好。范围1到100. +// iFrameRate 录像的帧率。建议设定的比实际采集帧率大, +// 这样就不会漏帧。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraInitRecord( + CameraHandle hCamera, + int iFormat, + char* pcSavePath, + BOOL b2GLimit, + DWORD dwQuality, + int iFrameRate +); + +/******************************************************/ +// 函数名 : CameraStopRecord +// 功能描述 : 结束本次录像。当CameraInitRecord后,可以通过该函数 +// 来结束一次录像,并完成文件保存操作。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraStopRecord( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraPushFrame +// 功能描述 : 将一帧数据存入录像流中。必须调用CameraInitRecord +// 才能调用该函数。CameraStopRecord调用后,不能再调用 +// 该函数。由于我们的帧头信息中携带了图像采集的时间戳 +// 信息,因此录像可以精准的时间同步,而不受帧率不稳定 +// 的影响。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbyImageBuffer 图像的数据缓冲区,必须是RGB格式。 +// pFrInfo 图像的帧头信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraPushFrame( + CameraHandle hCamera, + BYTE* pbyImageBuffer, + tSdkFrameHead* pFrInfo +); + +/******************************************************/ +// 函数名 : CameraSaveImage +// 功能描述 : 将图像缓冲区的数据保存成图片文件。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// lpszFileName 图片保存文件完整路径。 +// pbyImageBuffer 图像的数据缓冲区。 +// pFrInfo 图像的帧头信息。 +// byFileType 图像保存的格式。取值范围参见CameraDefine.h +// 中emSdkFileType的类型定义。目前支持 +// BMP、JPG、PNG、RAW四种格式。其中RAW表示 +// 相机输出的原始数据,保存RAW格式文件要求 +// pbyImageBuffer和pFrInfo是由CameraGetImageBuffer +// 获得的数据,而且未经CameraImageProcess转换 +// 成BMP格式;反之,如果要保存成BMP、JPG或者 +// PNG格式,则pbyImageBuffer和pFrInfo是由 +// CameraImageProcess处理后的RGB格式数据。 +// 具体用法可以参考Advanced的例程。 +// byQuality 图像保存的质量因子,仅当保存为JPG格式 +// 时该参数有效,范围1到100。其余格式 +// 可以写成0。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSaveImage( + CameraHandle hCamera, + char* lpszFileName, + BYTE* pbyImageBuffer, + tSdkFrameHead* pFrInfo, + BYTE byFileType, + BYTE byQuality +); + +/******************************************************/ +// 函数名 : CameraGetImageResolution +// 功能描述 : 获得当前预览的分辨率。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// psCurVideoSize 结构体指针,用于返回当前的分辨率。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetImageResolution( + CameraHandle hCamera, + tSdkImageResolution* psCurVideoSize +); + +/******************************************************/ +// 函数名 : CameraSetImageResolution +// 功能描述 : 设置预览的分辨率。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pImageResolution 结构体指针,用于返回当前的分辨率。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetImageResolution( + CameraHandle hCamera, + tSdkImageResolution* pImageResolution +); + +/// @ingroup API_ROI +/// \~chinese +/// \brief 获得当前预览的分辨率。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iIndex 索引号,[0,N]表示预设的分辨率(N 为预设分辨率的最大个数,一般不超过20),OXFF 表示自定义分辨率(ROI) +/// \param [in] Mode 0: 普通模式 1:Sum 2:Average 3:Skip 4:Resample +/// \param [in] ModeSize 普通模式下忽略,第1位表示2X2 第二位表示3X3 ... +/// \param [in] x 水平偏移 +/// \param [in] y 垂直偏移 +/// \param [in] width 宽 +/// \param [in] height 高 +/// \param [in] ZoomWidth 最终输出时缩放宽度,0表示不缩放 +/// \param [in] ZoomHeight 最终输出时缩放高度,0表示不缩放 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the current preview resolution. +/// \param [in] hCamera Camera handle. +/// \param [in] iIndex Index number, [0,N] indicates the default resolution (N is the maximum number of preset resolutions, generally no more than 20), OXFF indicates custom resolution (ROI) +/// \param [in] Mode 0: Normal Mode 1:Sum 2:Average 3:Skip 4:Resample +/// \param [in] ModeSize ignored in normal mode, the first bit represents 2X2 the second bit represents 3X3 ... +/// \param [in] x horizontal offset +/// \param [in] y vertical offset +/// \param [in] width width +/// \param [in] height high +/// \param [in] ZoomWidth Scale width when final output, 0 means not zoom +/// \param [in] ZoomHeight Scales the height of the final output, 0 means no scaling +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetImageResolutionEx( + CameraHandle hCamera, + int iIndex, + int Mode, + UINT ModeSize, + int x, + int y, + int width, + int height, + int ZoomWidth, + int ZoomHeight + ); + +/******************************************************/ +// 函数名 : CameraGetMediaType +// 功能描述 : 获得相机当前输出原始数据的格式索引号。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piMediaType 指针,用于返回当前格式类型的索引号。 +// 由CameraGetCapability获得相机的属性, +// 在tSdkCameraCapbility结构体中的pMediaTypeDesc +// 成员中,以数组的形式保存了相机支持的格式, +// piMediaType所指向的索引号,就是该数组的索引号。 +// pMediaTypeDesc[*piMediaType].iMediaType则表示当前格式的 +// 编码。该编码请参见CameraDefine.h中[图像格式定义]部分。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetMediaType( + CameraHandle hCamera, + INT* piMediaType +); + +/******************************************************/ +// 函数名 : CameraSetMediaType +// 功能描述 : 设置相机的输出原始数据格式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMediaType 由CameraGetCapability获得相机的属性, +// 在tSdkCameraCapbility结构体中的pMediaTypeDesc +// 成员中,以数组的形式保存了相机支持的格式, +// iMediaType就是该数组的索引号。 +// pMediaTypeDesc[iMediaType].iMediaType则表示当前格式的 +// 编码。该编码请参见CameraDefine.h中[图像格式定义]部分。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetMediaType( + CameraHandle hCamera, + INT iMediaType +); + +/******************************************************/ +// 函数名 : CameraSetAeState +// 功能描述 : 设置相机曝光的模式。自动或者手动。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bAeState TRUE,使能自动曝光;FALSE,停止自动曝光。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeState( + CameraHandle hCamera, + BOOL bAeState +); + +/******************************************************/ +// 函数名 : CameraGetAeState +// 功能描述 : 获得相机当前的曝光模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pAeState 指针,用于返回自动曝光的使能状态。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAeState( + CameraHandle hCamera, + BOOL* pAeState +); + +/******************************************************/ +// 函数名 : CameraSetSharpness +// 功能描述 : 设置图像的处理的锐化参数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iSharpness 锐化参数。范围由CameraGetCapability +// 获得,一般是[0,100],0表示关闭锐化处理。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetSharpness( + CameraHandle hCamera, + int iSharpness +); + +/******************************************************/ +// 函数名 : CameraGetSharpness +// 功能描述 : 获取当前锐化设定值。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piSharpness 指针,返回当前设定的锐化的设定值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetSharpness( + CameraHandle hCamera, + int* piSharpness +); + +/******************************************************/ +// 函数名 : CameraSetLutMode +// 功能描述 : 设置相机的查表变换模式LUT模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// emLutMode LUTMODE_PARAM_GEN 表示由伽马和对比度参数动态生成LUT表。 +// LUTMODE_PRESET 表示使用预设的LUT表。 +// LUTMODE_USER_DEF 表示使用用户自定的LUT表。 +// LUTMODE_PARAM_GEN的定义参考CameraDefine.h中emSdkLutMode类型。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetLutMode( + CameraHandle hCamera, + int emLutMode +); + +/******************************************************/ +// 函数名 : CameraGetLutMode +// 功能描述 : 获得相机的查表变换模式LUT模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pemLutMode 指针,返回当前LUT模式。意义与CameraSetLutMode +// 中emLutMode参数相同。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetLutMode( + CameraHandle hCamera, + int* pemLutMode +); + +/******************************************************/ +// 函数名 : CameraSelectLutPreset +// 功能描述 : 选择预设LUT模式下的LUT表。必须先使用CameraSetLutMode +// 将LUT模式设置为预设模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iSel 表的索引号。表的个数由CameraGetCapability +// 获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSelectLutPreset( + CameraHandle hCamera, + int iSel +); + +/******************************************************/ +// 函数名 : CameraGetLutPresetSel +// 功能描述 : 获得预设LUT模式下的LUT表索引号。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piSel 指针,返回表的索引号。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetLutPresetSel( + CameraHandle hCamera, + int* piSel +); + +/******************************************************/ +// 函数名 : CameraSetCustomLut +// 功能描述 : 设置自定义的LUT表。必须先使用CameraSetLutMode +// 将LUT模式设置为自定义模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iChannel 指定要设定的LUT颜色通道,当为LUT_CHANNEL_ALL时, +// 三个通道的LUT将被同时替换。 +// 参考CameraDefine.h中emSdkLutChannel定义。 +// pLut 指针,指向LUT表的地址。LUT表为无符号短整形数组,数组大小为 +// 4096,分别代码颜色通道从0到4096(12bit颜色精度)对应的映射值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetCustomLut( + CameraHandle hCamera, + int iChannel, + USHORT* pLut +); + +/******************************************************/ +// 函数名 : CameraGetCustomLut +// 功能描述 : 获得当前使用的自定义LUT表。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iChannel 指定要获得的LUT颜色通道。当为LUT_CHANNEL_ALL时, +// 返回红色通道的LUT表。 +// 参考CameraDefine.h中emSdkLutChannel定义。 +// pLut 指针,指向LUT表的地址。LUT表为无符号短整形数组,数组大小为 +// 4096,分别代码颜色通道从0到4096(12bit颜色精度)对应的映射值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetCustomLut( + CameraHandle hCamera, + int iChannel, + USHORT* pLut +); + +/******************************************************/ +// 函数名 : CameraGetCurrentLut +// 功能描述 : 获得相机当前的LUT表,在任何LUT模式下都可以调用, +// 用来直观的观察LUT曲线的变化。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iChannel 指定要获得的LUT颜色通道。当为LUT_CHANNEL_ALL时, +// 返回红色通道的LUT表。 +// 参考CameraDefine.h中emSdkLutChannel定义。 +// pLut 指针,指向LUT表的地址。LUT表为无符号短整形数组,数组大小为 +// 4096,分别代码颜色通道从0到4096(12bit颜色精度)对应的映射值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetCurrentLut( + CameraHandle hCamera, + int iChannel, + USHORT* pLut +); + +/******************************************************/ +// 函数名 : CameraSetWbMode +// 功能描述 : 设置相机白平衡模式。分为手动和自动两种方式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bAuto TRUE,则表示使能自动模式。 +// FALSE,则表示使用手动模式,通过调用 +// CameraSetOnceWB来进行一次白平衡。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetWbMode( + CameraHandle hCamera, + BOOL bAuto +); + +/******************************************************/ +// 函数名 : CameraGetWbMode +// 功能描述 : 获得当前的白平衡模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbAuto 指针,返回TRUE表示自动模式,FALSE +// 为手动模式。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetWbMode( + CameraHandle hCamera, + BOOL* pbAuto +); + +/******************************************************/ +// 函数名 : CameraSetPresetClrTemp +// 功能描述 : 选择指定预设色温模式 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iSel 预设色温的模式索引号,从0开始 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetPresetClrTemp( + CameraHandle hCamera, + int iSel +); + +/******************************************************/ +// 函数名 : CameraGetPresetClrTemp +// 功能描述 : 获得当前选择的预设色温模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piSel 指针,返回选择的预设色温索引号 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetPresetClrTemp( + CameraHandle hCamera, + int* piSel +); + +/******************************************************/ +// 函数名 : CameraSetUserClrTempGain +// 功能描述 : 设置自定义色温模式下的数字增益 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iRgain 红色增益,范围0到400,表示0到4倍 +// iGgain 绿色增益,范围0到400,表示0到4倍 +// iBgain 蓝色增益,范围0到400,表示0到4倍 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetUserClrTempGain( + CameraHandle hCamera, + int iRgain, + int iGgain, + int iBgain +); + + +/******************************************************/ +// 函数名 : CameraGetUserClrTempGain +// 功能描述 : 获得自定义色温模式下的数字增益 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piRgain 指针,返回红色增益,范围0到400,表示0到4倍 +// piGgain 指针,返回绿色增益,范围0到400,表示0到4倍 +// piBgain 指针,返回蓝色增益,范围0到400,表示0到4倍 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetUserClrTempGain( + CameraHandle hCamera, + int* piRgain, + int* piGgain, + int* piBgain +); + +/******************************************************/ +// 函数名 : CameraSetUserClrTempMatrix +// 功能描述 : 设置自定义色温模式下的颜色矩阵 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pMatrix 指向一个float[3][3]数组的首地址 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetUserClrTempMatrix( + CameraHandle hCamera, + float* pMatrix +); + + +/******************************************************/ +// 函数名 : CameraGetUserClrTempMatrix +// 功能描述 : 获得自定义色温模式下的颜色矩阵 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pMatrix 指向一个float[3][3]数组的首地址 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetUserClrTempMatrix( + CameraHandle hCamera, + float* pMatrix +); + +/******************************************************/ +// 函数名 : CameraSetClrTempMode +// 功能描述 : 设置白平衡时使用的色温模式, +// 支持的模式有三种,分别是自动,预设和自定义。 +// 自动模式下,会自动选择合适的色温模式 +// 预设模式下,会使用用户指定的色温模式 +// 自定义模式下,使用用户自定义的色温数字增益和矩阵 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMode 模式,只能是emSdkClrTmpMode中定义的一种 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetClrTempMode( + CameraHandle hCamera, + int iMode +); + +/******************************************************/ +// 函数名 : CameraGetClrTempMode +// 功能描述 : 获得白平衡时使用的色温模式。参考CameraSetClrTempMode +// 中功能描述部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pimode 指针,返回模式选择,参考emSdkClrTmpMode类型定义 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetClrTempMode( + CameraHandle hCamera, + int* pimode +); + + + +/******************************************************/ +// 函数名 : CameraSetOnceWB +// 功能描述 : 在手动白平衡模式下,调用该函数会进行一次白平衡。 +// 生效的时间为接收到下一帧图像数据时。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOnceWB( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraSetOnceBB +// 功能描述 : 执行一次黑平衡操作。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOnceBB( + CameraHandle hCamera +); + + +/******************************************************/ +// 函数名 : CameraSetAeTarget +// 功能描述 : 设定自动曝光的亮度目标值。设定范围由CameraGetCapability +// 函数获得。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iAeTarget 亮度目标值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeTarget( + CameraHandle hCamera, + int iAeTarget +); + +/******************************************************/ +// 函数名 : CameraGetAeTarget +// 功能描述 : 获得自动曝光的亮度目标值。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// *piAeTarget 指针,返回目标值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAeTarget( + CameraHandle hCamera, + int* piAeTarget +); + + +/******************************************************/ +// 函数名 : CameraSetAeExposureRange +// 功能描述 : 设定自动曝光模式的曝光时间调节范围 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// fMinExposureTime 最小曝光时间(微秒) +// fMaxExposureTime 最大曝光时间(微秒) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeExposureRange( + CameraHandle hCamera, + double fMinExposureTime, + double fMaxExposureTime + ); + +/******************************************************/ +// 函数名 : CameraGetAeExposureRange +// 功能描述 : 获得自动曝光模式的曝光时间调节范围 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// fMinExposureTime 最小曝光时间(微秒) +// fMaxExposureTime 最大曝光时间(微秒) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAeExposureRange( + CameraHandle hCamera, + double* fMinExposureTime, + double* fMaxExposureTime + ); + +/******************************************************/ +// 函数名 : CameraSetAeAnalogGainRange +// 功能描述 : 设定自动曝光模式的增益调节范围 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMinAnalogGain 最小增益 +// iMaxAnalogGain 最大增益 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeAnalogGainRange( + CameraHandle hCamera, + int iMinAnalogGain, + int iMaxAnalogGain + ); + +/******************************************************/ +// 函数名 : CameraGetAeAnalogGainRange +// 功能描述 : 获得自动曝光模式的增益调节范围 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMinAnalogGain 最小增益 +// iMaxAnalogGain 最大增益 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAeAnalogGainRange( + CameraHandle hCamera, + int* iMinAnalogGain, + int* iMaxAnalogGain + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 设置自动曝光模式的调节阈值 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iThreshold 如果 abs(目标亮度-图像亮度) < iThreshold 则停止自动调节 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the adjustment threshold for auto exposure mode +/// \param [in] hCamera Camera handle. +/// \param [in] iThreshold Stops automatic adjustment if abs (target brightness - image brightness) < iThreshold +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetAeThreshold( + CameraHandle hCamera, + int iThreshold + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获取自动曝光模式的调节阈值 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] iThreshold 读取到的调节阈值 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get adjustment threshold for auto exposure mode +/// \param [in] hCamera Camera handle. +/// \param [out] iThreshold Read Threshold +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetAeThreshold( + CameraHandle hCamera, + int* iThreshold + ); + +/******************************************************/ +// 函数名 : CameraSetExposureTime +// 功能描述 : 设置曝光时间。单位为微秒。对于CMOS传感器,其曝光 +// 的单位是按照行来计算的,因此,曝光时间并不能在微秒 +// 级别连续可调。而是会按照整行来取舍。在调用 +// 本函数设定曝光时间后,建议再调用CameraGetExposureTime +// 来获得实际设定的值。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// fExposureTime 曝光时间,单位微秒。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetExposureTime( + CameraHandle hCamera, + double fExposureTime +); + +/******************************************************/ +// 函数名 : CameraGetExposureLineTime +// 功能描述 : 获得一行的曝光时间。对于CMOS传感器,其曝光 +// 的单位是按照行来计算的,因此,曝光时间并不能在微秒 +// 级别连续可调。而是会按照整行来取舍。这个函数的 +// 作用就是返回CMOS相机曝光一行对应的时间。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pfLineTime 指针,返回一行的曝光时间,单位为微秒。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExposureLineTime( + CameraHandle hCamera, + double* pfLineTime +); + +/******************************************************/ +// 函数名 : CameraGetExposureTime +// 功能描述 : 获得相机的曝光时间。请参见CameraSetExposureTime +// 的功能描述。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pfExposureTime 指针,返回当前的曝光时间,单位微秒。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExposureTime( + CameraHandle hCamera, + double* pfExposureTime +); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获得相机的曝光时间范围 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pfMin 指针,返回曝光时间的最小值,单位微秒。 +/// \param [out] pfMax 指针,返回曝光时间的最大值,单位微秒。 +/// \param [out] pfStep 指针,返回曝光时间的步进值,单位微秒。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get camera exposure time range +/// \param [in] hCamera Camera handle. +/// \param [out] pfMin Returns the minimum exposure time in microseconds. +/// \param [out] pfMax Returns the maximum exposure time in microseconds. +/// \param [out] pfStep Returns the exposure time in microseconds. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetExposureTimeRange( + CameraHandle hCamera, + double* pfMin, + double* pfMax, + double* pfStep + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 设置多重曝光时间。单位为微秒。(此功能仅线阵相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index 曝光索引。 +/// \param [in] fExposureTime 曝光时间,单位微秒。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 对于CMOS传感器,其曝光的单位是按照行来计算的,因此,曝光时间并不能在微秒级别连续可调。而是会按照整行来取舍。在调用本函数设定曝光时间后,建议再调用@link #CameraGetMultiExposureTime @endlink来获得实际设定的值。 +/// \~english +/// \brief Set the multiple exposure time. The unit is microseconds. (This feature is only supported by line camera) +/// \param [in] hCamera Camera handle. +/// \param [in] index Exposure index. +/// \param [in] fExposureTime Exposure time in microseconds. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note For CMOS sensors, the unit of exposure is calculated in rows, so the exposure time cannot be continuously adjusted in microseconds. Instead, the entire line will be chosen. After calling this function to set the exposure time, it is recommended to call @link #CameraGetMultiExposureTime @endlink to get the actual set value. +MVSDK_API CameraSdkStatus CameraSetMultiExposureTime( + CameraHandle hCamera, + int index, + double fExposureTime + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获取多重曝光时间。单位为微秒。(此功能仅线阵相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index 曝光索引。 +/// \param [out] fExposureTime 返回曝光时间,单位微秒。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the multiple exposure time. The unit is microseconds. (This feature is only supported by line camera) +/// \param [in] hCamera Camera handle. +/// \param [in] index Exposure index. +/// \param [out] fExposureTime Returns exposure time in microseconds. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetMultiExposureTime( + CameraHandle hCamera, + int index, + double* fExposureTime + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 设置多重曝光使能个数。(此功能仅线阵相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] count 使能个数。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the number of multiple exposure enable. (This feature is only supported by line camera) +/// \param [in] hCamera Camera handle. +/// \param [in] count The number of exposures enabled. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetMultiExposureCount( + CameraHandle hCamera, + int count + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获取多重曝光使能个数。(此功能仅线阵相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [out] count 使能个数。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the number of multiple exposure enable. (This feature is only supported by line camera) +/// \param [in] hCamera Camera handle. +/// \param [out] count The number of exposures enabled. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetMultiExposureCount( + CameraHandle hCamera, + int* count + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获取多重曝光的最大曝光个数。(此功能仅线阵相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [out] max_count 支持的最大曝光个数。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the maximum number of exposures for multiple exposures. (This feature is only supported by line camera) +/// \param [in] hCamera Camera handle. +/// \param [out] max_count The maximum number of exposures supported. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetMultiExposureMaxCount( + CameraHandle hCamera, + int* max_count + ); + +/******************************************************/ +// 函数名 : CameraSetAnalogGain +// 功能描述 : 设置相机的图像模拟增益值。该值乘以CameraGetCapability获得 +// 的相机属性结构体中sExposeDesc.fAnalogGainStep,就 +// 得到实际的图像信号放大倍数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iAnalogGain 设定的模拟增益值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAnalogGain( + CameraHandle hCamera, + INT iAnalogGain +); + +/******************************************************/ +// 函数名 : CameraGetAnalogGain +// 功能描述 : 获得图像信号的模拟增益值。参见CameraSetAnalogGain +// 详细说明。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piAnalogGain 指针,返回当前的模拟增益值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAnalogGain( + CameraHandle hCamera, + INT* piAnalogGain +); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 设置相机的模拟增益放大倍数。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] fGain 设定的模拟增益放大倍数。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the image gain magnification of the camera. +/// \param [in] hCamera Camera handle. +/// \param [in] fGain Gain magnification. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetAnalogGainX( + CameraHandle hCamera, + float fGain + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获得图像信号的模拟增益放大倍数。 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pfGain 指针,返回当前的模拟增益放大倍数。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \see CameraSetAnalogGainX +/// \~english +/// \brief Obtain the gain magnification of the image signal. +/// \param [in] hCamera Camera handle. +/// \param [out] pfGain pointer, returns the current gain magnification. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \see CameraSetAnalogGainX +MVSDK_API CameraSdkStatus CameraGetAnalogGainX( + CameraHandle hCamera, + float* pfGain + ); + +/// @ingroup API_EXPOSURE +/// \~chinese +/// \brief 获得相机的模拟增益放大倍数取值范围 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pfMin 指针,返回最小倍数。 +/// \param [out] pfMax 指针,返回最大倍数。 +/// \param [out] pfStep 指针,返回步进值。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the value range of the camera's gain magnification +/// \param [in] hCamera Camera handle. +/// \param [out] pfMin pointer, returns the minimum multiple. +/// \param [out] pfMax pointer, returns the maximum multiple. +/// \param [out] pfStep pointer, returns the step value. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetAnalogGainXRange( + CameraHandle hCamera, + float* pfMin, + float* pfMax, + float* pfStep + ); + +/******************************************************/ +// 函数名 : CameraSetGain +// 功能描述 : 设置图像的数字增益。设定范围由CameraGetCapability +// 获得的相机属性结构体中sRgbGainRange成员表述。 +// 实际的放大倍数是设定值/100。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iRGain 红色通道的增益值。 +// iGGain 绿色通道的增益值。 +// iBGain 蓝色通道的增益值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetGain( + CameraHandle hCamera, + int iRGain, + int iGGain, + int iBGain +); + + +/******************************************************/ +// 函数名 : CameraGetGain +// 功能描述 : 获得图像处理的数字增益。具体请参见CameraSetGain +// 的功能描述部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piRGain 指针,返回红色通道的数字增益值。 +// piGGain 指针,返回绿色通道的数字增益值。 +// piBGain 指针,返回蓝色通道的数字增益值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetGain( + CameraHandle hCamera, + int* piRGain, + int* piGGain, + int* piBGain +); + + +/******************************************************/ +// 函数名 : CameraSetGamma +// 功能描述 : 设定LUT动态生成模式下的Gamma值。设定的值会 +// 马上保存在SDK内部,但是只有当相机处于动态 +// 参数生成的LUT模式时,才会生效。请参考CameraSetLutMode +// 的函数说明部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iGamma 要设定的Gamma值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetGamma( + CameraHandle hCamera, + int iGamma +); + +/******************************************************/ +// 函数名 : CameraGetGamma +// 功能描述 : 获得LUT动态生成模式下的Gamma值。请参考CameraSetGamma +// 函数的功能描述。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piGamma 指针,返回当前的Gamma值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetGamma( + CameraHandle hCamera, + int* piGamma +); + +/******************************************************/ +// 函数名 : CameraSetContrast +// 功能描述 : 设定LUT动态生成模式下的对比度值。设定的值会 +// 马上保存在SDK内部,但是只有当相机处于动态 +// 参数生成的LUT模式时,才会生效。请参考CameraSetLutMode +// 的函数说明部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iContrast 设定的对比度值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetContrast( + CameraHandle hCamera, + int iContrast +); + +/******************************************************/ +// 函数名 : CameraGetContrast +// 功能描述 : 获得LUT动态生成模式下的对比度值。请参考 +// CameraSetContrast函数的功能描述。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piContrast 指针,返回当前的对比度值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetContrast( + CameraHandle hCamera, + int* piContrast +); + +/******************************************************/ +// 函数名 : CameraSetSaturation +// 功能描述 : 设定图像处理的饱和度。对黑白相机无效。 +// 设定范围由CameraGetCapability获得。100表示 +// 表示原始色度,不增强。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iSaturation 设定的饱和度值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetSaturation( + CameraHandle hCamera, + int iSaturation +); + +/******************************************************/ +// 函数名 : CameraGetSaturation +// 功能描述 : 获得图像处理的饱和度。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piSaturation 指针,返回当前图像处理的饱和度值。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetSaturation( + CameraHandle hCamera, + int* piSaturation +); + +/******************************************************/ +// 函数名 : CameraSetMonochrome +// 功能描述 : 设置彩色转为黑白功能的使能。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bEnable TRUE,表示将彩色图像转为黑白。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetMonochrome( + CameraHandle hCamera, + BOOL bEnable +); + +/******************************************************/ +// 函数名 : CameraGetMonochrome +// 功能描述 : 获得彩色转换黑白功能的使能状况。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbEnable 指针。返回TRUE表示开启了彩色图像 +// 转换为黑白图像的功能。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetMonochrome( + CameraHandle hCamera, + BOOL* pbEnable +); + +/******************************************************/ +// 函数名 : CameraSetInverse +// 功能描述 : 设置彩图像颜色翻转功能的使能。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bEnable TRUE,表示开启图像颜色翻转功能, +// 可以获得类似胶卷底片的效果。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetInverse( + CameraHandle hCamera, + BOOL bEnable +); + +/******************************************************/ +// 函数名 : CameraGetInverse +// 功能描述 : 获得图像颜色反转功能的使能状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbEnable 指针,返回该功能使能状态。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetInverse( + CameraHandle hCamera, + BOOL* pbEnable +); + +/******************************************************/ +// 函数名 : CameraSetAntiFlick +// 功能描述 : 设置自动曝光时抗频闪功能的使能状态。对于手动 +// 曝光模式下无效。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bEnable TRUE,开启抗频闪功能;FALSE,关闭该功能。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAntiFlick( + CameraHandle hCamera, + BOOL bEnable +); + +/******************************************************/ +// 函数名 : CameraGetAntiFlick +// 功能描述 : 获得自动曝光时抗频闪功能的使能状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbEnable 指针,返回该功能的使能状态。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAntiFlick( + CameraHandle hCamera, + BOOL* pbEnable +); + +/******************************************************/ +// 函数名 : CameraGetLightFrequency +// 功能描述 : 获得自动曝光时,消频闪的频率选择。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piFrequencySel 指针,返回选择的索引号。0:50HZ 1:60HZ +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetLightFrequency( + CameraHandle hCamera, + int* piFrequencySel +); + +/******************************************************/ +// 函数名 : CameraSetLightFrequency +// 功能描述 : 设置自动曝光时消频闪的频率。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iFrequencySel 0:50HZ , 1:60HZ +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetLightFrequency( + CameraHandle hCamera, + int iFrequencySel +); + +/******************************************************/ +// 函数名 : CameraSetFrameSpeed +// 功能描述 : 设定相机输出图像的帧率。相机可供选择的帧率模式由 +// CameraGetCapability获得的信息结构体中iFrameSpeedDesc +// 表示最大帧率选择模式个数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iFrameSpeed 选择的帧率模式索引号,范围从0到 +// CameraGetCapability获得的信息结构体中iFrameSpeedDesc - 1 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetFrameSpeed( + CameraHandle hCamera, + int iFrameSpeed +); + +/******************************************************/ +// 函数名 : CameraGetFrameSpeed +// 功能描述 : 获得相机输出图像的帧率选择索引号。具体用法参考 +// CameraSetFrameSpeed函数的功能描述部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piFrameSpeed 指针,返回选择的帧率模式索引号。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetFrameSpeed( + CameraHandle hCamera, + int* piFrameSpeed +); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设定相机的帧频(面阵)或行频(线阵)。(仅部分网口相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] RateHZ 帧频或行频(<=0表示最大频率)。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the frame frequency (area) or line frequency (line scan). (only supported by some gige camera) +/// \param [in] hCamera Camera handle. +/// \param [in] RateHZ frame rate or line rate (<=0 means maximum frequency). +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetFrameRate( + CameraHandle hCamera, + int RateHZ + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获取设定的相机帧频(面阵)或行频(线阵) +/// \param [in] hCamera 相机的句柄。 +/// \param [out] RateHZ 帧频或行频(<=0表示最大频率)。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the frame frequency (area) or line frequency (line scan). +/// \param [in] hCamera Camera handle. +/// \param [out] RateHZ frame rate or line rate (<=0 means maximum frequency). +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetFrameRate( + CameraHandle hCamera, + int* RateHZ + ); + +/******************************************************/ +// 函数名 : CameraSetParameterMode +// 功能描述 : 设定参数存取的目标对象。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMode 参数存取的对象。参考CameraDefine.h +// 中emSdkParameterMode的类型定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetParameterMode( + CameraHandle hCamera, + int iMode +); + +/******************************************************/ +// 函数名 : CameraGetParameterMode +// 功能描述 : +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// int* piTarget +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetParameterMode( + CameraHandle hCamera, + int* piTarget +); + +/******************************************************/ +// 函数名 : CameraSetParameterMask +// 功能描述 : 设置参数存取的掩码。参数加载和保存时会根据该 +// 掩码来决定各个模块参数的是否加载或者保存。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uMask 掩码。参考CameraDefine.h中PROP_SHEET_INDEX +// 类型定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetParameterMask( + CameraHandle hCamera, + UINT uMask +); + +/******************************************************/ +// 函数名 : CameraSaveParameter +// 功能描述 : 保存当前相机参数到指定的参数组中。相机提供了A,B,C,D +// A,B,C,D四组空间来进行参数的保存。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iTeam PARAMETER_TEAM_A 保存到A组中, +// PARAMETER_TEAM_B 保存到B组中, +// PARAMETER_TEAM_C 保存到C组中, +// PARAMETER_TEAM_D 保存到D组中 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSaveParameter( + CameraHandle hCamera, + int iTeam +); + + +/******************************************************/ +// 函数名 : CameraSaveParameterToFile +// 功能描述 : 保存当前相机参数到指定的文件中。该文件可以复制到 +// 别的电脑上供其他相机加载,也可以做参数备份用。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// sFileName 参数文件的完整路径。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSaveParameterToFile( + CameraHandle hCamera, + char* sFileName +); + + +/******************************************************/ +// 函数名 : CameraReadParameterFromFile +// 功能描述 : 从PC上指定的参数文件中加载参数。我公司相机参数 +// 保存在PC上为.config后缀的文件,位于安装下的 +// Camera\Configs文件夹中。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// *sFileName 参数文件的完整路径。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraReadParameterFromFile( + CameraHandle hCamera, + char* sFileName +); + +/******************************************************/ +// 函数名 : CameraLoadParameter +// 功能描述 : 加载指定组的参数到相机中。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iTeam PARAMETER_TEAM_A 加载A组参数, +// PARAMETER_TEAM_B 加载B组参数, +// PARAMETER_TEAM_C 加载C组参数, +// PARAMETER_TEAM_D 加载D组参数, +// PARAMETER_TEAM_DEFAULT 加载默认参数。 +// 类型定义参考CameraDefine.h中emSdkParameterTeam类型 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraLoadParameter( + CameraHandle hCamera, + int iTeam +); + +/******************************************************/ +// 函数名 : CameraGetCurrentParameterGroup +// 功能描述 : 获得当前选择的参数组。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piTeam 指针,返回当前选择的参数组。返回值 +// 参考CameraLoadParameter中iTeam参数。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetCurrentParameterGroup( + CameraHandle hCamera, + int* piTeam +); + +/******************************************************/ +// 函数名 : CameraSetTransPackLen +// 功能描述 : 设置相机传输图像数据的分包大小。 +// 目前的SDK版本中,该接口仅对GIGE接口相机有效, +// 用来控制网络传输的分包大小。对于支持巨帧的网卡, +// 我们建议选择8K的分包大小,可以有效的降低传输 +// 所占用的CPU处理时间。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iPackSel 分包长度选择的索引号。分包长度可由 +// 获得相机属性结构体中pPackLenDesc成员表述, +// iPackLenDesc成员则表示最大可选的分包模式个数。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetTransPackLen( + CameraHandle hCamera, + INT iPackSel +); + +/******************************************************/ +// 函数名 : CameraGetTransPackLen +// 功能描述 : 获得相机当前传输分包大小的选择索引号。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piPackSel 指针,返回当前选择的分包大小索引号。 +// 参见CameraSetTransPackLen中iPackSel的 +// 说明。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetTransPackLen( + CameraHandle hCamera, + INT* piPackSel +); + +/******************************************************/ +// 函数名 : CameraIsAeWinVisible +// 功能描述 : 获得自动曝光参考窗口的显示状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbIsVisible 指针,返回TRUE,则表示当前窗口会 +// 被叠加在图像内容上。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraIsAeWinVisible( + CameraHandle hCamera, + BOOL* pbIsVisible +); + +/******************************************************/ +// 函数名 : CameraSetAeWinVisible +// 功能描述 : 设置自动曝光参考窗口的显示状态。当设置窗口状态 +// 为显示,调用CameraImageOverlay后,能够将窗口位置 +// 以矩形的方式叠加在图像上。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bIsVisible TRUE,设置为显示;FALSE,不显示。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeWinVisible( + CameraHandle hCamera, + BOOL bIsVisible +); + +/******************************************************/ +// 函数名 : CameraGetAeWindow +// 功能描述 : 获得自动曝光参考窗口的位置。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piHOff 指针,返回窗口位置左上角横坐标值。 +// piVOff 指针,返回窗口位置左上角纵坐标值。 +// piWidth 指针,返回窗口的宽度。 +// piHeight 指针,返回窗口的高度。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAeWindow( + CameraHandle hCamera, + INT* piHOff, + INT* piVOff, + INT* piWidth, + INT* piHeight +); + +/******************************************************/ +// 函数名 : CameraSetAeWindow +// 功能描述 : 设置自动曝光的参考窗口。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iHOff 窗口左上角的横坐标 +// iVOff 窗口左上角的纵坐标 +// iWidth 窗口的宽度 +// iHeight 窗口的高度 +// 如果iHOff、iVOff、iWidth、iHeight全部为0,则 +// 窗口设置为每个分辨率下的居中1/2大小。可以随着 +// 分辨率的变化而跟随变化;如果iHOff、iVOff、iWidth、iHeight +// 所决定的窗口位置范围超出了当前分辨率范围内, +// 则自动使用居中1/2大小窗口。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeWindow( + CameraHandle hCamera, + int iHOff, + int iVOff, + int iWidth, + int iHeight +); + +/******************************************************/ +// 函数名 : CameraSetMirror +// 功能描述 : 设置图像镜像操作。镜像操作分为水平和垂直两个方向。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iDir 表示镜像的方向。0,表示水平方向;1,表示垂直方向。 +// bEnable TRUE,使能镜像;FALSE,禁止镜像 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetMirror( + CameraHandle hCamera, + int iDir, + BOOL bEnable +); + +/******************************************************/ +// 函数名 : CameraGetMirror +// 功能描述 : 获得图像的镜像状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iDir 表示要获得的镜像方向。 +// 0,表示水平方向;1,表示垂直方向。 +// pbEnable 指针,返回TRUE,则表示iDir所指的方向 +// 镜像被使能。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetMirror( + CameraHandle hCamera, + int iDir, + BOOL* pbEnable +); + +/// @ingroup API_MIRROR +/// \~chinese +/// \brief 设置硬件镜像。分为水平和垂直两个方向。(仅部分网口、U3相机支持此功能) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iDir 表示镜像的方向。0,表示水平方向;1,表示垂直方向。 +/// \param [in] bEnable TRUE,使能镜像;FALSE,禁止镜像 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set up the hardware mirror. Divided into two directions, horizontal and vertical. (Only some GigE and U3 cameras support this feature) +/// \param [in] hCamera Camera handle. +/// \param [in] iDir Indicates the direction of the mirror. 0 means horizontal direction; 1 means vertical direction. +/// \param [in] bEnable TRUE to enable mirroring; FALSE to disable mirroring +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetHardwareMirror( + CameraHandle hCamera, + int iDir, + BOOL bEnable + ); + +/// @ingroup API_MIRROR +/// \~chinese +/// \brief 获取设置的硬件镜像状态。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iDir 表示要获得的镜像方向。0,表示水平方向;1,表示垂直方向。 +/// \param [out] pbEnable 指针,返回TRUE,则表示iDir所指的方向镜像被使能。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the hardware mirrored state of the image. +/// \param [in] hCamera Camera handle. +/// \param [in] iDir Indicates the mirroring direction to be obtained. 0 means horizontal direction; 1 means vertical direction. +/// \param [out] pbEnable Returns TRUE, indicating that the direction mirror image of iDir is enabled. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetHardwareMirror( + CameraHandle hCamera, + int iDir, + BOOL* pbEnable + ); + +/// @ingroup API_MIRROR +/// \~chinese +/// \brief 设置图像旋转操作 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iRot 表示旋转的角度(逆时针方向)(0:不旋转 1:90度 2:180度 3:270度) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set image rotation operation +/// \param [in] hCamera Camera handle. +/// \param [in] iRot rotation angle (counterclockwise) (0: no rotation 1:90 degrees 2:180 degrees 3:270 degrees) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetRotate( + CameraHandle hCamera, + int iRot + ); + +/// @ingroup API_MIRROR +/// \~chinese +/// \brief 获得图像的旋转状态。 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] iRot 表示要获得的旋转方向。(逆时针方向)(0:不旋转 1:90度 2:180度 3:270度) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the rotation state of the image. +/// \param [in] hCamera Camera handle. +/// \param [out] iRot Indicates the direction of rotation to get. (Counterclockwise) (0: Do not rotate 1:90 degree 2: 180 degree 3: 270 degree) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetRotate( + CameraHandle hCamera, + int* iRot + ); + +/******************************************************/ +// 函数名 : CameraGetWbWindow +// 功能描述 : 获得白平衡参考窗口的位置。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// PiHOff 指针,返回参考窗口的左上角横坐标 。 +// PiVOff 指针,返回参考窗口的左上角纵坐标 。 +// PiWidth 指针,返回参考窗口的宽度。 +// PiHeight 指针,返回参考窗口的高度。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetWbWindow( + CameraHandle hCamera, + INT* PiHOff, + INT* PiVOff, + INT* PiWidth, + INT* PiHeight +); + +/******************************************************/ +// 函数名 : CameraSetWbWindow +// 功能描述 : 设置白平衡参考窗口的位置。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iHOff 参考窗口的左上角横坐标。 +// iVOff 参考窗口的左上角纵坐标。 +// iWidth 参考窗口的宽度。 +// iHeight 参考窗口的高度。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetWbWindow( + CameraHandle hCamera, + INT iHOff, + INT iVOff, + INT iWidth, + INT iHeight +); + +/******************************************************/ +// 函数名 : CameraIsWbWinVisible +// 功能描述 : 获得白平衡窗口的显示状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbShow 指针,返回TRUE,则表示窗口是可见的。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraIsWbWinVisible( + CameraHandle hCamera, + BOOL* pbShow +); + +/******************************************************/ +// 函数名 : CameraSetWbWinVisible +// 功能描述 : 设置白平衡窗口的显示状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bShow TRUE,则表示设置为可见。在调用 +// CameraImageOverlay后,图像内容上将以矩形 +// 的方式叠加白平衡参考窗口的位置。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetWbWinVisible( + CameraHandle hCamera, + BOOL bShow +); + +/******************************************************/ +// 函数名 : CameraImageOverlay +// 功能描述 : 将输入的图像数据上叠加十字线、白平衡参考窗口、 +// 自动曝光参考窗口等图形。只有设置为可见状态的 +// 十字线和参考窗口才能被叠加上。 +// 注意,该函数的输入图像必须是RGB格式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pRgbBuffer 图像数据缓冲区。 +// pFrInfo 图像的帧头信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraImageOverlay( + CameraHandle hCamera, + BYTE* pRgbBuffer, + tSdkFrameHead* pFrInfo +); + +/******************************************************/ +// 函数名 : CameraSetCrossLine +// 功能描述 : 设置指定十字线的参数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iLine 表示要设置第几条十字线的状态。范围为[0,8],共9条。 +// x 十字线中心位置的横坐标值。 +// y 十字线中心位置的纵坐标值。 +// uColor 十字线的颜色,格式为(R|(G<<8)|(B<<16)) +// bVisible 十字线的显示状态。TRUE,表示显示。 +// 只有设置为显示状态的十字线,在调用 +// CameraImageOverlay后才会被叠加到图像上。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetCrossLine( + CameraHandle hCamera, + int iLine, + INT x, + INT y, + UINT uColor, + BOOL bVisible +); + +/******************************************************/ +// 函数名 : CameraGetCrossLine +// 功能描述 : 获得指定十字线的状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iLine 表示要获取的第几条十字线的状态。范围为[0,8],共9条。 +// px 指针,返回该十字线中心位置的横坐标。 +// py 指针,返回该十字线中心位置的横坐标。 +// pcolor 指针,返回该十字线的颜色,格式为(R|(G<<8)|(B<<16))。 +// pbVisible 指针,返回TRUE,则表示该十字线可见。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetCrossLine( + CameraHandle hCamera, + INT iLine, + INT* px, + INT* py, + UINT* pcolor, + BOOL* pbVisible +); + +/******************************************************/ +// 函数名 : CameraGetCapability +// 功能描述 : 获得相机的特性描述结构体。该结构体中包含了相机 +// 可设置的各种参数的范围信息。决定了相关函数的参数 +// 返回,也可用于动态创建相机的配置界面。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pCameraInfo 指针,返回该相机特性描述的结构体。 +// tSdkCameraCapbility在CameraDefine.h中定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetCapability( + CameraHandle hCamera, + tSdkCameraCapbility* pCameraInfo +); + +/******************************************************/ +// 函数名 : CameraGetCapabilityEx +// 功能描述 : 获得相机的特性描述结构体。该结构体中包含了相机 +// 可设置的各种参数的范围信息。决定了相关函数的参数 +// 返回,也可用于动态创建相机的配置界面。 +// 参数 : sDeviceModel 相机的型号,由扫描列表中获取 +// pCameraInfo 指针,返回该相机特性描述的结构体。 +// tSdkCameraCapbility在CameraDefine.h中定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetCapabilityEx( + char* sDeviceModel, + tSdkCameraCapbility* pCameraInfo, + PVOID hCameraHandle +); + + +/******************************************************/ +// 函数名 : CameraWriteSN +// 功能描述 : 设置相机的序列号。我公司相机序列号分为3级。 +// 0级的是我公司自定义的相机序列号,出厂时已经 +// 设定好,1级和2级留给二次开发使用。每级序列 +// 号长度都是32个字节。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbySN 序列号的缓冲区。 +// iLevel 要设定的序列号级别,只能是1或者2。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraWriteSN( + CameraHandle hCamera, + BYTE* pbySN, + INT iLevel +); + +/******************************************************/ +// 函数名 : CameraReadSN +// 功能描述 : 读取相机指定级别的序列号。序列号的定义请参考 +// CameraWriteSN函数的功能描述部分。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pbySN 序列号的缓冲区。 +// iLevel 要读取的序列号级别。只能是1和2。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraReadSN( + CameraHandle hCamera, + BYTE* pbySN, + INT iLevel +); +/******************************************************/ +// 函数名 : CameraSetTriggerDelayTime +// 功能描述 : 设置硬件触发模式下的触发延时时间,单位微秒。 +// 当硬触发信号来临后,经过指定的延时,再开始采集 +// 图像。仅部分型号的相机支持该功能。具体请查看 +// 产品说明书。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uDelayTimeUs 硬触发延时。单位微秒。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetTriggerDelayTime( + CameraHandle hCamera, + UINT uDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetTriggerDelayTime +// 功能描述 : 获得当前设定的硬触发延时时间。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// puDelayTimeUs 指针,返回延时时间,单位微秒。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetTriggerDelayTime( + CameraHandle hCamera, + UINT* puDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraSetTriggerPeriodTime +// 功能描述 : 一次触发多帧时,设置2帧之间的间隔时间。 +// 仅部分型号的相机支持该功能。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// time 间隔时间(微秒) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetTriggerPeriodTime( + CameraHandle hCamera, + UINT time +); + +/******************************************************/ +// 函数名 : CameraGetTriggerPeriodTime +// 功能描述 : 获取当前设置的间隔时间。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// time 间隔时间(微秒) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetTriggerPeriodTime( + CameraHandle hCamera, + UINT* time +); + +/******************************************************/ +// 函数名 : CameraSetTriggerCount +// 功能描述 : 设置触发模式下的触发帧数。对软件触发和硬件触发 +// 模式都有效。默认为1帧,即一次触发信号采集一帧图像。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iCount 一次触发采集的帧数。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetTriggerCount( + CameraHandle hCamera, + INT iCount +); + +/******************************************************/ +// 函数名 : CameraGetTriggerCount +// 功能描述 : 获得一次触发的帧数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// INT* piCount +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetTriggerCount( + CameraHandle hCamera, + INT* piCount +); + +/******************************************************/ +// 函数名 : CameraSoftTrigger +// 功能描述 : 执行一次软触发。执行后,会触发由CameraSetTriggerCount +// 指定的帧数。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSoftTrigger( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraSetTriggerMode +// 功能描述 : 设置相机的触发模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iModeSel 模式选择索引号。可设定的模式由 +// CameraGetCapability函数获取。请参考 +// CameraDefine.h中tSdkCameraCapbility的定义。 +// 一般情况,0表示连续采集模式;1表示 +// 软件触发模式;2表示硬件触发模式。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetTriggerMode( + CameraHandle hCamera, + int iModeSel +); + +/******************************************************/ +// 函数名 : CameraGetTriggerMode +// 功能描述 : 获得相机的触发模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piModeSel 指针,返回当前选择的相机触发模式的索引号。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetTriggerMode( + CameraHandle hCamera, + INT* piModeSel +); + +/******************************************************/ +// 函数名 : CameraSetStrobeMode +// 功能描述 : 设置IO引脚端子上的STROBE信号。该信号可以做闪光灯控制,也可以做外部机械快门控制。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iMode 当为STROBE_SYNC_WITH_TRIG_AUTO 和触发信号同步,触发后,相机进行曝光时,自动生成STROBE信号。 +// 此时,有效极性可设置(CameraSetStrobePolarity)。 +// 当为STROBE_SYNC_WITH_TRIG_MANUAL时,和触发信号同步,触发后,STROBE延时指定的时间后(CameraSetStrobeDelayTime), +// 再持续指定时间的脉冲(CameraSetStrobePulseWidth), +// 有效极性可设置(CameraSetStrobePolarity)。 +// 当为STROBE_ALWAYS_HIGH时,STROBE信号恒为高,忽略其他设置 +// 当为STROBE_ALWAYS_LOW时,STROBE信号恒为低,忽略其他设置 +// +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetStrobeMode( + CameraHandle hCamera, + INT iMode +); + +/******************************************************/ +// 函数名 : CameraGetStrobeMode +// 功能描述 : 或者当前STROBE信号设置的模式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piMode 指针,返回STROBE_SYNC_WITH_TRIG_AUTO,STROBE_SYNC_WITH_TRIG_MANUAL、STROBE_ALWAYS_HIGH或者STROBE_ALWAYS_LOW。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetStrobeMode( + CameraHandle hCamera, + INT* piMode +); + +/******************************************************/ +// 函数名 : CameraSetStrobeDelayTime +// 功能描述 : 当STROBE信号处于STROBE_SYNC_WITH_TRIG时,通过该函数设置其相对触发信号延时时间。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uDelayTimeUs 相对触发信号的延时时间,单位为us。可以为0,但不能为负数。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetStrobeDelayTime( + CameraHandle hCamera, + UINT uDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetStrobeDelayTime +// 功能描述 : 当STROBE信号处于STROBE_SYNC_WITH_TRIG时,通过该函数获得其相对触发信号延时时间。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// upDelayTimeUs 指针,返回延时时间,单位us。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetStrobeDelayTime( + CameraHandle hCamera, + UINT* upDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraSetStrobePulseWidth +// 功能描述 : 当STROBE信号处于STROBE_SYNC_WITH_TRIG时,通过该函数设置其脉冲宽度。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uTimeUs 脉冲的宽度,单位为时间us。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetStrobePulseWidth( + CameraHandle hCamera, + UINT uTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetStrobePulseWidth +// 功能描述 : 当STROBE信号处于STROBE_SYNC_WITH_TRIG时,通过该函数获得其脉冲宽度。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// upTimeUs 指针,返回脉冲宽度。单位为时间us。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetStrobePulseWidth( + CameraHandle hCamera, + UINT* upTimeUs +); + +/******************************************************/ +// 函数名 : CameraSetStrobePolarity +// 功能描述 : 当STROBE信号处于STROBE_SYNC_WITH_TRIG时,通过该函数设置其有效电平的极性。默认为高有效,当触发信号到来时,STROBE信号被拉高。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iPolarity STROBE信号的极性,0为低电平有效,1为高电平有效。默认为高电平有效。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetStrobePolarity( + CameraHandle hCamera, + INT uPolarity +); + +/******************************************************/ +// 函数名 : CameraGetStrobePolarity +// 功能描述 : 获得相机当前STROBE信号的有效极性。默认为高电平有效。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// ipPolarity 指针,返回STROBE信号当前的有效极性。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetStrobePolarity( + CameraHandle hCamera, + INT* upPolarity +); + +/******************************************************/ +// 函数名 : CameraSetExtTrigSignalType +// 功能描述 : 设置相机外触发信号的种类。上边沿、下边沿、或者高、低电平方式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iType 外触发信号种类,返回值参考CameraDefine.h中 +// emExtTrigSignal类型定义。 + +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetExtTrigSignalType( + CameraHandle hCamera, + INT iType +); + +/******************************************************/ +// 函数名 : CameraGetExtTrigSignalType +// 功能描述 : 获得相机当前外触发信号的种类。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// ipType 指针,返回外触发信号种类,返回值参考CameraDefine.h中 +// emExtTrigSignal类型定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExtTrigSignalType( + CameraHandle hCamera, + INT* ipType +); + +/******************************************************/ +// 函数名 : CameraSetExtTrigShutterType +// 功能描述 : 设置外触发模式下,相机快门的方式,默认为标准快门方式。 +// 部分滚动快门的CMOS相机支持GRR方式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iType 外触发快门方式。参考CameraDefine.h中emExtTrigShutterMode类型。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetExtTrigShutterType( + CameraHandle hCamera, + INT iType +); + +/******************************************************/ +// 函数名 : CameraSetExtTrigShutterType +// 功能描述 : 获得外触发模式下,相机快门的方式,默认为标准快门方式。 +// 部分滚动快门的CMOS相机支持GRR方式。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// ipType 指针,返回当前设定的外触发快门方式。返回值参考 +// CameraDefine.h中emExtTrigShutterMode类型。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExtTrigShutterType( + CameraHandle hCamera, + INT* ipType +); + +/******************************************************/ +// 函数名 : CameraSetExtTrigDelayTime +// 功能描述 : 设置外触发信号延时时间,默认为0,单位为微秒。 +// 当设置的值uDelayTimeUs不为0时,相机接收到外触发信号后,将延时uDelayTimeUs个微秒后再进行图像捕获。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uDelayTimeUs 延时时间,单位为微秒,默认为0. +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetExtTrigDelayTime( + CameraHandle hCamera, + UINT uDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetExtTrigDelayTime +// 功能描述 : 获得设置的外触发信号延时时间,默认为0,单位为微秒。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// UINT* upDelayTimeUs +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExtTrigDelayTime( + CameraHandle hCamera, + UINT* upDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraSetExtTrigBufferedDelayTime +// 功能描述 : 设置外触发信号延时时间,默认为0,单位为微秒。 +// 当设置的值uDelayTimeUs不为0时,相机接收到外触发信号后,将延时uDelayTimeUs个微秒后再进行图像捕获。 +// 并且会把延时期间收到的触发信号缓存起来,被缓存的信号也将延时uDelayTimeUs个微秒后生效(最大缓存个数128)。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uDelayTimeUs 延时时间,单位为微秒,默认为0. +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetExtTrigBufferedDelayTime( + CameraHandle hCamera, + UINT uDelayTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetExtTrigBufferedDelayTime +// 功能描述 : 获得设置的外触发信号延时时间,默认为0,单位为微秒。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// puDelayTimeUs 延时时间,单位为微秒,默认为0. +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExtTrigBufferedDelayTime( + CameraHandle hCamera, + UINT* puDelayTimeUs +); + +/// @ingroup API_TRIGGER +/// \~chinese +/// \brief 设置外触发信号间隔时间,默认为0,单位为微秒。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] uTimeUs 间隔时间,单位为微秒,默认为0. +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the interval time of external trigger signal. The default is 0 and the unit is microsecond. +/// \param [in] hCamera Camera handle. +/// \param [in] uTimeUs Interval time in microseconds. Default is 0. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetExtTrigIntervalTime( + CameraHandle hCamera, + UINT uTimeUs + ); + +/// @ingroup API_TRIGGER +/// \~chinese +/// \brief 获得设置的外触发信号间隔时间,默认为0,单位为微秒。 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] upTimeUs 触发间隔 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the set external trigger signal interval time, the default is 0, the unit is microseconds. +/// \param [in] hCamera Camera handle. +/// \param [out] upTimeUs trigger interval +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetExtTrigIntervalTime( + CameraHandle hCamera, + UINT* upTimeUs + ); + +/******************************************************/ +// 函数名 : CameraSetExtTrigJitterTime +// 功能描述 : 设置相机外触发信号的消抖时间。默认为0,单位为微秒。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// UINT uTimeUs +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetExtTrigJitterTime( + CameraHandle hCamera, + UINT uTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetExtTrigJitterTime +// 功能描述 : 获得设置的相机外触发消抖时间,默认为0.单位为微妙 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// UINT* upTimeUs +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExtTrigJitterTime( + CameraHandle hCamera, + UINT* upTimeUs +); + +/******************************************************/ +// 函数名 : CameraGetExtTrigCapability +// 功能描述 : 获得相机外触发的属性掩码 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// puCapabilityMask 指针,返回该相机外触发特性掩码,掩码参考CameraDefine.h中 +// EXT_TRIG_MASK_ 开头的宏定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetExtTrigCapability( + CameraHandle hCamera, + UINT* puCapabilityMask +); + + +/******************************************************/ +// 函数名 : CameraGetResolutionForSnap +// 功能描述 : 获得抓拍模式下的分辨率选择索引号。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pImageResolution 指针,返回抓拍模式的分辨率。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetResolutionForSnap( + CameraHandle hCamera, + tSdkImageResolution* pImageResolution +); + +/******************************************************/ +// 函数名 : CameraSetResolutionForSnap +// 功能描述 : 设置抓拍模式下相机输出图像的分辨率。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pImageResolution 如果pImageResolution->iWidth +// 和 pImageResolution->iHeight都为0, +// 则表示设定为跟随当前预览分辨率。抓 +// 怕到的图像的分辨率会和当前设定的 +// 预览分辨率一样。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetResolutionForSnap( + CameraHandle hCamera, + tSdkImageResolution* pImageResolution +); + +/******************************************************/ +// 函数名 : CameraCustomizeResolution +// 功能描述 : 打开分辨率自定义面板,并通过可视化的方式 +// 来配置一个自定义分辨率。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pImageCustom 指针,返回自定义的分辨率。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraCustomizeResolution( + CameraHandle hCamera, + tSdkImageResolution* pImageCustom +); + +/******************************************************/ +// 函数名 : CameraCustomizeReferWin +// 功能描述 : 打开参考窗口自定义面板。并通过可视化的方式来 +// 获得一个自定义窗口的位置。一般是用自定义白平衡 +// 和自动曝光的参考窗口。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iWinType 要生成的参考窗口的用途。0,自动曝光参考窗口; +// 1,白平衡参考窗口。 +// hParent 调用该函数的窗口的句柄。可以为NULL。 +// piHOff 指针,返回自定义窗口的左上角横坐标。 +// piVOff 指针,返回自定义窗口的左上角纵坐标。 +// piWidth 指针,返回自定义窗口的宽度。 +// piHeight 指针,返回自定义窗口的高度。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraCustomizeReferWin( + CameraHandle hCamera, + INT iWinType, + HWND hParent, + INT* piHOff, + INT* piVOff, + INT* piWidth, + INT* piHeight +); + +/******************************************************/ +// 函数名 : CameraShowSettingPage +// 功能描述 : 设置相机属性配置窗口显示状态。必须先调用CameraCreateSettingPage +// 成功创建相机属性配置窗口后,才能调用本函数进行 +// 显示。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bShow TRUE,显示;FALSE,隐藏。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraShowSettingPage( + CameraHandle hCamera, + BOOL bShow +); + +/******************************************************/ +// 函数名 : CameraCreateSettingPage +// 功能描述 : 创建该相机的属性配置窗口。调用该函数,SDK内部会 +// 帮您创建好相机的配置窗口,省去了您重新开发相机 +// 配置界面的时间。强烈建议使用您使用该函数让 +// SDK为您创建好配置窗口。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// hParent 应用程序主窗口的句柄。可以为NULL。 +// pWinText 字符串指针,窗口显示的标题栏。 +// pCallbackFunc 窗口消息的回调函数,当相应的事件发生时, +// pCallbackFunc所指向的函数会被调用, +// 例如切换了参数之类的操作时,pCallbackFunc +// 被回调时,在入口参数处指明了消息类型。 +// 这样可以方便您自己开发的界面和我们生成的UI +// 之间进行同步。该参数可以为NULL。 +// pCallbackCtx 回调函数的附加参数。可以为NULL。pCallbackCtx +// 会在pCallbackFunc被回调时,做为参数之一传入。 +// 您可以使用该参数来做一些灵活的判断。 +// uReserved 预留。必须设置为0。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraCreateSettingPage( + CameraHandle hCamera, + HWND hParent, + char* pWinText, + CAMERA_PAGE_MSG_PROC pCallbackFunc, + PVOID pCallbackCtx, + UINT uReserved +); + +/******************************************************/ +// 函数名 : CameraCreateSettingPageEx +// 功能描述 : 创建该相机的属性配置窗口。调用该函数,SDK内部会 +// 帮您创建好相机的配置窗口,省去了您重新开发相机 +// 配置界面的时间。强烈建议使用您使用该函数让 +// SDK为您创建好配置窗口。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraCreateSettingPageEx( + CameraHandle hCamera +); + + +/******************************************************/ +// 函数名 : CameraSetActiveSettingSubPage +// 功能描述 : 设置相机配置窗口的激活页面。相机配置窗口有多个 +// 子页面构成,该函数可以设定当前哪一个子页面 +// 为激活状态,显示在最前端。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// index 子页面的索引号。参考CameraDefine.h中 +// PROP_SHEET_INDEX的定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetActiveSettingSubPage( + CameraHandle hCamera, + INT index +); + +/******************************************************/ +// 函数名 : CameraSpecialControl +// 功能描述 : 相机一些特殊配置所调用的接口,二次开发时一般不需要 +// 调用。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// dwCtrlCode 控制码。 +// dwParam 控制子码,不同的dwCtrlCode时,意义不同。 +// lpData 附加参数。不同的dwCtrlCode时,意义不同。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSpecialControl( + CameraHandle hCamera, + DWORD dwCtrlCode, + DWORD dwParam, + LPVOID lpData +); + +/******************************************************/ +// 函数名 : CameraGetFrameStatistic +// 功能描述 : 获得相机接收帧率的统计信息,包括错误帧和丢帧的情况。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// psFrameStatistic 指针,返回统计信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetFrameStatistic( + CameraHandle hCamera, + tSdkFrameStatistic* psFrameStatistic +); + +/******************************************************/ +// 函数名 : CameraGetStatisticResend +// 功能描述 : 获得相机统计信息之重传数量 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pResendCount 指针,返回重传数量。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetStatisticResend( + CameraHandle hCamera, + UINT* pResendCount +); + +/******************************************************/ +// 函数名 : CameraSetNoiseFilter +// 功能描述 : 设置图像降噪模块的使能状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// bEnable TRUE,使能;FALSE,禁止。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetNoiseFilter( + CameraHandle hCamera, + BOOL bEnable +); + +/******************************************************/ +// 函数名 : CameraGetNoiseFilterState +// 功能描述 : 获得图像降噪模块的使能状态。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// *pEnable 指针,返回状态。TRUE,为使能。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetNoiseFilterState( + CameraHandle hCamera, + BOOL* pEnable +); + +/******************************************************/ +// 函数名 : CameraRstTimeStamp +// 功能描述 : 复位图像采集的时间戳,从0开始。 +// 参数 : CameraHandle hCamera +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraRstTimeStamp( + CameraHandle hCamera +); + +/******************************************************/ +// 函数名 : CameraSaveUserData +// 功能描述 : 将用户自定义的数据保存到相机的非易性存储器中。 +// 每个型号的相机可能支持的用户数据区最大长度不一样。 +// 可以从设备的特性描述中获取该长度信息。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uStartAddr 起始地址,从0开始。 +// pbData 数据缓冲区指针 +// ilen 写入数据的长度,ilen + uStartAddr必须 +// 小于用户区最大长度 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSaveUserData( + CameraHandle hCamera, + UINT uStartAddr, + BYTE *pbData, + int ilen +); + +/******************************************************/ +// 函数名 : CameraLoadUserData +// 功能描述 : 从相机的非易性存储器中读取用户自定义的数据。 +// 每个型号的相机可能支持的用户数据区最大长度不一样。 +// 可以从设备的特性描述中获取该长度信息。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uStartAddr 起始地址,从0开始。 +// pbData 数据缓冲区指针,返回读到的数据。 +// ilen 读取数据的长度,ilen + uStartAddr必须 +// 小于用户区最大长度 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraLoadUserData( + CameraHandle hCamera, + UINT uStartAddr, + BYTE *pbData, + int ilen +); + +/******************************************************/ +// 函数名 : CameraGetFriendlyName +// 功能描述 : 读取用户自定义的设备昵称。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pName 指针,返回指向0结尾的字符串, +// 设备昵称不超过32个字节,因此该指针 +// 指向的缓冲区必须大于等于32个字节空间。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetFriendlyName( + CameraHandle hCamera, + char* pName +); + +/******************************************************/ +// 函数名 : CameraSetFriendlyName +// 功能描述 : 设置用户自定义的设备昵称。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pName 指针,指向0结尾的字符串, +// 设备昵称不超过32个字节,因此该指针 +// 指向字符串必须小于等于32个字节空间。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetFriendlyName( + CameraHandle hCamera, + char* pName +); + +/******************************************************/ +// 函数名 : CameraSdkGetVersionString +// 功能描述 : +// 参数 : pVersionString 指针,返回SDK版本字符串。 +// 该指针指向的缓冲区大小必须大于 +// 32个字节 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSdkGetVersionString( + char* pVersionString +); + +/******************************************************/ +// 函数名 : CameraCheckFwUpdate +// 功能描述 : 检测固件版本,是否需要升级。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pNeedUpdate 指针,返回固件检测状态,TRUE表示需要更新 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraCheckFwUpdate( + CameraHandle hCamera, + BOOL* pNeedUpdate +); + +/******************************************************/ +// 函数名 : CameraGetFirmwareVision +// 功能描述 : 获得固件版本的字符串 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pVersion 必须指向一个大于32字节的缓冲区, +// 返回固件的版本字符串。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetFirmwareVersion( + CameraHandle hCamera, + char* pVersion +); + +/******************************************************/ +// 函数名 : CameraGetEnumInfo +// 功能描述 : 获得指定设备的枚举信息 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pCameraInfo 指针,返回设备的枚举信息。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetEnumInfo( + CameraHandle hCamera, + tSdkCameraDevInfo* pCameraInfo +); + +/******************************************************/ +// 函数名 : CameraGetInerfaceVersion +// 功能描述 : 获得指定设备接口的版本 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pVersion 指向一个大于32字节的缓冲区,返回接口版本字符串。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetInerfaceVersion( + CameraHandle hCamera, + char* pVersion +); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 设置指定IO的电平状态,IO为输出型IO,相机预留可编程输出IO的个数由@link #tSdkCameraCapbility.iOutputIoCounts @endlink决定。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [in] uState 要设定的状态(GE、SUA: 0(高) 1(低)) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 已废弃,使用CameraSetIOStateEx,它对所有型号相机的输出状态值统一为1高 0低 +/// \~english +/// \brief Set the level state of the specified IO. IO is the output IO. The number of programmable output IOs for the camera is determined by @link #tSdkCameraCapbility.iOutputIoCounts @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [in] uState The state to set(GE、SUA: 0(high) 1(low)) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note Obsolete, use CameraSetIOStateEx, which has a unified output state value of 1 high and 0 low for all models of cameras +MVSDK_API CameraSdkStatus CameraSetIOState( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT uState +); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 设置指定IO的电平状态,IO为输出型IO,相机预留可编程输出IO的个数由@link #tSdkCameraCapbility.iOutputIoCounts @endlink决定。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [in] uState 要设定的状态(1为高,0为低) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the level state of the specified IO. IO is the output IO. The number of programmable output IOs for the camera is determined by @link #tSdkCameraCapbility.iOutputIoCounts @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [in] uState The state to set, 1 is high, 0 is low +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetIOStateEx( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT uState + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 读取指定IO的电平状态,IO为输出型IO,相机预留可编程输出IO的个数由@link #tSdkCameraCapbility.iOutputIoCounts @endlink决定。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [out] puState 返回IO状态(GE、SUA: 0(高) 1(低)) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 已废弃,使用CameraGetOutPutIOStateEx,它对所有型号相机的输出状态值统一为1高 0低 +/// \~english +/// \brief Read the level state of the specified IO. IO is the output IO. The number of programmable output IOs for the camera is determined by @link #tSdkCameraCapbility.iOutputIoCounts @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [out] puState return IO state(GE、SUA: 0(high) 1(low)) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note Obsolete, use CameraGetOutPutIOStateEx, which has a unified output state value of 1 high and 0 low for all models of cameras +MVSDK_API CameraSdkStatus CameraGetOutPutIOState( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT* puState + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 读取指定IO的电平状态,IO为输出型IO,相机预留可编程输出IO的个数由@link #tSdkCameraCapbility.iOutputIoCounts @endlink决定。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [out] puState 返回IO状态,1为高,0为低 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Read the level state of the specified IO. IO is the output IO. The number of programmable output IOs for the camera is determined by @link #tSdkCameraCapbility.iOutputIoCounts @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [out] puState return IO state, 1 is high, 0 is low +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetOutPutIOStateEx( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT* puState + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 读取指定IO的电平状态,IO为输入型IO,相机预留可编程输出IO的个数由@link #tSdkCameraCapbility.iInputIoCounts @endlink决定。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [out] puState 指针,返回IO状态(GE、SUA: 0(高) 1(低)) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 已废弃,使用CameraGetIOStateEx,它对所有型号相机的输入状态值统一为1高 0低 +/// \~english +/// \brief Read the level state of the specified IO, IO is input type IO, the number of programmable output IOs that the camera reserves is decided by @link #tSdkCameraCapbility.iInputIoCounts @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [out] puState returns IO state(GE、SUA: 0(high) 1(low)) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note Obsolete, use CameraGetIOStateEx, which has a unified input state value of 1 high and 0 low for all models of cameras +MVSDK_API CameraSdkStatus CameraGetIOState( + CameraHandle hCamera, + INT iInputIOIndex, + UINT* puState +); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 读取指定IO的电平状态,IO为输入型IO,相机预留可编程输出IO的个数由@link #tSdkCameraCapbility.iInputIoCounts @endlink决定。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [out] puState 指针,返回IO状态,1为高,0为低 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Read the level state of the specified IO, IO is input type IO, the number of programmable output IOs that the camera reserves is decided by @link #tSdkCameraCapbility.iInputIoCounts @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [out] puState returns IO state, 1 is high, 0 is low +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetIOStateEx( + CameraHandle hCamera, + INT iInputIOIndex, + UINT* puState + ); + +/******************************************************/ +// 函数名 : CameraSetInPutIOMode +// 功能描述 : 设置输入IO的模式,相机 +// 预留可编程输出IO的个数由tSdkCameraCapbility中 +// iInputIoCounts决定。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iInputIOIndex IO的索引号,从0开始。 +// iMode IO模式,参考CameraDefine.h中emCameraGPIOMode +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetInPutIOMode( + CameraHandle hCamera, + INT iInputIOIndex, + INT iMode + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输入IO的模式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [out] piMode IO模式,参考@link #emCameraGPIOMode @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the input IO mode +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [out] piMode IO mode, reference @link #emCameraGPIOMode @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetInPutIOMode( + CameraHandle hCamera, + INT iInputIOIndex, + INT* piMode + ); + +/******************************************************/ +// 函数名 : CameraSetOutPutIOMode +// 功能描述 : 设置输出IO的模式,相机 +// 预留可编程输出IO的个数由tSdkCameraCapbility中 +// iOutputIoCounts决定。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iOutputIOIndex IO的索引号,从0开始。 +// iMode IO模式,参考CameraDefine.h中emCameraGPIOMode +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOutPutIOMode( + CameraHandle hCamera, + INT iOutputIOIndex, + INT iMode + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输出IO的模式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [out] piMode IO模式,参考@link #emCameraGPIOMode @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the output IO mode +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [out] piMode IO mode, reference @link #emCameraGPIOMode @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetOutPutIOMode( + CameraHandle hCamera, + INT iOutputIOIndex, + INT* piMode + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输入IO的模式支持能力 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [out] piCapbility IO模式支持位掩码 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the mode support capability of the input IO +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [out] piCapbility IO mode support bit mask +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetInPutIOModeCapbility( + CameraHandle hCamera, + INT iInputIOIndex, + UINT* piCapbility + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输出IO的模式支持能力 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [out] piCapbility IO模式支持位掩码 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the mode support capability of the output IO +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [out] piCapbility IO mode support bit mask +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetOutPutIOModeCapbility( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT* piCapbility + ); + +/******************************************************/ +// 函数名 : CameraSetOutPutPWM +// 功能描述 : 设置PWM型输出的参数,相机 +// 预留可编程输出IO的个数由tSdkCameraCapbility中 +// iOutputIoCounts决定。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iOutputIOIndex IO的索引号,从0开始。 +// iCycle PWM的周期,单位(us) +// uDuty 占用比,取值1%~99% +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOutPutPWM( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT iCycle, + UINT uDuty + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 设置编码器有效方向 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] dir 有效方向(0:正反转都有效 1:顺时针(A相超前于B) 2:逆时针) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the effective direction of the rotary encoder +/// \param [in] hCamera Camera handle. +/// \param [in] dir Valid direction (0: Both positive and negative are valid 1: Clockwise (A phase leads B) 2: Counterclockwise) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetRotaryEncDir( + CameraHandle hCamera, + INT dir + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取编码器有效方向 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] dir 有效方向(0:正反转都有效 1:顺时针(A相超前于B) 2:逆时针) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the effective direction of the rotary encoder +/// \param [in] hCamera Camera handle. +/// \param [out] dir Valid direction (0: Both positive and negative are valid 1: Clockwise (A phase leads B) 2: Counterclockwise) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetRotaryEncDir( + CameraHandle hCamera, + INT* dir + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 设置编码器频率 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] mul 倍频 +/// \param [in] div 分频 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the frequency of the rotary encoder +/// \param [in] hCamera Camera handle. +/// \param [in] mul frequency multiplier +/// \param [in] div frequency division +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetRotaryEncFreq( + CameraHandle hCamera, + INT mul, + INT div + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取编码器频率 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] mul 倍频 +/// \param [out] div 分频 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the frequency of the rotary encoder +/// \param [in] hCamera Camera handle. +/// \param [out] mul frequency multiplier +/// \param [out] div frequency division +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetRotaryEncFreq( + CameraHandle hCamera, + INT* mul, + INT* div + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 设置输入IO的格式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [in] iFormat IO格式,参考@link #emCameraGPIOFormat @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the input IO format +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [in] iFormat IO format, reference @link #emCameraGPIOFormat @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetInPutIOFormat( + CameraHandle hCamera, + INT iInputIOIndex, + INT iFormat + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输入IO的格式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [out] piFormat IO格式,参考@link #emCameraGPIOFormat @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the input IO format +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [out] piFormat IO format, reference @link #emCameraGPIOFormat @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetInPutIOFormat( + CameraHandle hCamera, + INT iInputIOIndex, + INT* piFormat + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 设置输出IO的格式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [in] iFormat IO格式,参考@link #emCameraGPIOFormat @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the output IO format +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [in] iFormat IO format, reference @link #emCameraGPIOFormat @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetOutPutIOFormat( + CameraHandle hCamera, + INT iOutputIOIndex, + INT iFormat + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输出IO的格式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [out] piFormat IO格式,参考@link #emCameraGPIOFormat @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the output IO format +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [out] piFormat IO format, reference @link #emCameraGPIOFormat @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetOutPutIOFormat( + CameraHandle hCamera, + INT iOutputIOIndex, + INT* piFormat + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输入IO的格式支持能力 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iInputIOIndex IO的索引号,从0开始。 +/// \param [out] piCapbility IO格式支持位掩码 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the format support capability of the input IO +/// \param [in] hCamera Camera handle. +/// \param [in] iInputIOIndex IO index, starting from 0. +/// \param [out] piCapbility IO format support bit mask +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetInPutIOFormatCapbility( + CameraHandle hCamera, + INT iInputIOIndex, + UINT* piCapbility + ); + +/// @ingroup API_GPIO +/// \~chinese +/// \brief 获取输出IO的格式支持能力 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iOutputIOIndex IO的索引号,从0开始。 +/// \param [out] piCapbility IO格式支持位掩码 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the format support capability of the output IO +/// \param [in] hCamera Camera handle. +/// \param [in] iOutputIOIndex IO index, starting from 0. +/// \param [out] piCapbility IO format support bit mask +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetOutPutIOFormatCapbility( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT* piCapbility + ); + +/******************************************************/ +// 函数名 : CameraSetOutPutDelayTime +// 功能描述 : 设置输出延时,相机 +// 预留可编程输出IO的个数由tSdkCameraCapbility中 +// iOutputIoCounts决定。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iOutputIOIndex IO的索引号,从0开始。 +// uDelayTimeUs 延时,单位(us) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOutPutDelayTime( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT uDelayTimeUs + ); + +/******************************************************/ +// 函数名 : CameraSetOutPutPulseWidth +// 功能描述 : 设置输出脉宽,相机 +// 预留可编程输出IO的个数由tSdkCameraCapbility中 +// iOutputIoCounts决定。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iOutputIOIndex IO的索引号,从0开始。 +// uTimeUs 脉宽,单位(us) +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOutPutPulseWidth( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT uTimeUs + ); + +/******************************************************/ +// 函数名 : CameraSetOutPutPolarity +// 功能描述 : 设置输出极性,相机 +// 预留可编程输出IO的个数由tSdkCameraCapbility中 +// iOutputIoCounts决定。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iOutputIOIndex IO的索引号,从0开始。 +// uPolarity 0:正向 1:反向 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetOutPutPolarity( + CameraHandle hCamera, + INT iOutputIOIndex, + UINT uPolarity + ); + +/******************************************************/ +// 函数名 : CameraSetAeAlgorithm +// 功能描述 : 设置自动曝光时选择的算法,不同的算法适用于 +// 不同的场景。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iIspProcessor 选择执行该算法的对象,参考CameraDefine.h +// emSdkIspProcessor的定义 +// iAeAlgorithmSel 要选择的算法编号。从0开始,最大值由tSdkCameraCapbility +// 中iAeAlmSwDesc和iAeAlmHdDesc决定。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetAeAlgorithm( + CameraHandle hCamera, + INT iIspProcessor, + INT iAeAlgorithmSel +); + +/******************************************************/ +// 函数名 : CameraGetAeAlgorithm +// 功能描述 : 获得当前自动曝光所选择的算法 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iIspProcessor 选择执行该算法的对象,参考CameraDefine.h +// emSdkIspProcessor的定义 +// piAeAlgorithmSel 返回当前选择的算法编号。从0开始,最大值由tSdkCameraCapbility +// 中iAeAlmSwDesc和iAeAlmHdDesc决定。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetAeAlgorithm( + CameraHandle hCamera, + INT iIspProcessor, + INT* piAlgorithmSel +); + +/******************************************************/ +// 函数名 : CameraSetBayerDecAlgorithm +// 功能描述 : 设置Bayer数据转彩色的算法。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iIspProcessor 选择执行该算法的对象,参考CameraDefine.h +// emSdkIspProcessor的定义 +// iAlgorithmSel 要选择的算法编号。从0开始,最大值由tSdkCameraCapbility +// 中iBayerDecAlmSwDesc和iBayerDecAlmHdDesc决定。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetBayerDecAlgorithm( + CameraHandle hCamera, + INT iIspProcessor, + INT iAlgorithmSel +); + +/******************************************************/ +// 函数名 : CameraGetBayerDecAlgorithm +// 功能描述 : 获得Bayer数据转彩色所选择的算法。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iIspProcessor 选择执行该算法的对象,参考CameraDefine.h +// emSdkIspProcessor的定义 +// piAlgorithmSel 返回当前选择的算法编号。从0开始,最大值由tSdkCameraCapbility +// 中iBayerDecAlmSwDesc和iBayerDecAlmHdDesc决定。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetBayerDecAlgorithm( + CameraHandle hCamera, + INT iIspProcessor, + INT* piAlgorithmSel +); + +/******************************************************/ +// 函数名 : CameraSetIspProcessor +// 功能描述 : 设置图像处理单元的算法执行对象,由PC端或者相机端 +// 来执行算法,当由相机端执行时,会降低PC端的CPU占用率。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iIspProcessor 参考CameraDefine.h中 +// emSdkIspProcessor的定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetIspProcessor( + CameraHandle hCamera, + INT iIspProcessor +); + +/******************************************************/ +// 函数名 : CameraGetIspProcessor +// 功能描述 : 获得图像处理单元的算法执行对象。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piIspProcessor 返回选择的对象,返回值参考CameraDefine.h中 +// emSdkIspProcessor的定义。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetIspProcessor( + CameraHandle hCamera, + INT* piIspProcessor +); + +/******************************************************/ +// 函数名 : CameraSetBlackLevel +// 功能描述 : 设置图像的黑电平基准,默认值为0 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iBlackLevel 要设定的电平值。范围为0到255。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetBlackLevel( + CameraHandle hCamera, + INT iBlackLevel +); + +/******************************************************/ +// 函数名 : CameraGetBlackLevel +// 功能描述 : 获得图像的黑电平基准,默认值为0 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piBlackLevel 返回当前的黑电平值。范围为0到255。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetBlackLevel( + CameraHandle hCamera, + INT* piBlackLevel +); + +/******************************************************/ +// 函数名 : CameraSetWhiteLevel +// 功能描述 : 设置图像的白电平基准,默认值为255 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// iWhiteLevel 要设定的电平值。范围为0到255。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetWhiteLevel( + CameraHandle hCamera, + INT iWhiteLevel +); + +/******************************************************/ +// 函数名 : CameraGetWhiteLevel +// 功能描述 : 获得图像的白电平基准,默认值为255 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// piWhiteLevel 返回当前的白电平值。范围为0到255。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetWhiteLevel( + CameraHandle hCamera, + INT* piWhiteLevel +); + +/******************************************************/ +// 函数名 : CameraSetIspOutFormat +// 功能描述 : 设置CameraGetImageBuffer函数的图像处理的输出格式,支持 +// CAMERA_MEDIA_TYPE_MONO8和CAMERA_MEDIA_TYPE_RGB8 +// (在CameraDefine.h中定义)三种,分别对应8位灰度图像和24位彩色图像。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// uFormat 要设定格式。CAMERA_MEDIA_TYPE_MONO8或者CAMERA_MEDIA_TYPE_RGB8 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraSetIspOutFormat( + CameraHandle hCamera, + UINT uFormat +); + +/******************************************************/ +// 函数名 : CameraGetIspOutFormat +// 功能描述 : 获得图像处理的输出格式,目前只支持 +// CAMERA_MEDIA_TYPE_MONO8和CAMERA_MEDIA_TYPE_RGB8 +// (在CameraDefine.h中定义)两种,其他的参数会返回错误。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// puFormat 返回当前设定的格式。CAMERA_MEDIA_TYPE_MONO8或者CAMERA_MEDIA_TYPE_RGB8 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetIspOutFormat( + CameraHandle hCamera, + UINT* puFormat +); + +/******************************************************/ +// 函数名 : CameraGetErrorString +// 功能描述 : 获得错误码对应的描述字符串 +// 参数 : iStatusCode 错误码。(定义于CameraStatus.h中) +// 返回值 : 成功时,返回错误码对应的字符串首地址; +// 否则返回NULL。 +/******************************************************/ +MVSDK_API char* CameraGetErrorString( + CameraSdkStatus iStatusCode +); + + +/******************************************************/ +// 函数名 : CameraReConnect +// 功能描述 : 重新连接设备,用于USB设备意外掉线后重连 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraReConnect( + CameraHandle hCamera +); + + +/******************************************************/ +// 函数名 : CameraConnectTest +// 功能描述 : 测试相机的连接状态,用于检测相机是否掉线 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0),表示相机连接状态正常; +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraConnectTest( + CameraHandle hCamera +); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置相机的LED使能状态,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [in] enable 使能状态 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the camera's LED enable status, without the LED's model, this function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [in] enable enable state +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLedEnable( + CameraHandle hCamera, + int index, + BOOL enable + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获得相机的LED使能状态,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [out] enable 指针,返回LED使能状态 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the camera's LED enable status, without the LED's model, this function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [out] enable Return LED enable status +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetLedEnable( + CameraHandle hCamera, + int index, + BOOL* enable + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置相机的LED开关状态,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [in] onoff LED开关状态 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the camera's LED switch status, without the LED's model, this function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [in] onoff LED on/off status +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLedOnOff( + CameraHandle hCamera, + int index, + BOOL onoff + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获得相机的LED开关状态,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [out] onoff 返回LED开关状态 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the camera's LED switch status, without the LED model, this function returns an error code that does not support. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [out] onoff Returns LED switch status +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetLedOnOff( + CameraHandle hCamera, + int index, + BOOL* onoff + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置相机的LED持续时间,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [in] duration LED持续时间,单位毫秒 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the camera's LED duration, without the LED model, this function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [in] duration LED duration in milliseconds +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLedDuration( + CameraHandle hCamera, + int index, + UINT duration + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获得相机的LED持续时间,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [out] duration 返回LED持续时间,单位毫秒 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the camera's LED duration, without the LED model, this function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [out] duration Returns the LED duration in milliseconds +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetLedDuration( + CameraHandle hCamera, + int index, + UINT* duration + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置相机的LED亮度,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [in] uBrightness LED亮度值,范围0到255. 0表示关闭,255最亮。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the camera's LED brightness, without the LED model, this function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [in] uBrightness LED brightness value, range 0 to 255. 0 means off, 255 brightest. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLedBrightness( + CameraHandle hCamera, + int index, + UINT uBrightness +); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获得相机的LED亮度,不带LED的型号,此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index LED灯的索引号,从0开始。如果只有一个可控制亮度的LED,则该参数为0 。 +/// \param [out] uBrightness 指针,返回LED亮度值,范围0到255. 0表示关闭,255最亮。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the camera's LED brightness, without the LED model, this function returns an error code that does not support. +/// \param [in] hCamera Camera handle. +/// \param [in] index The index of the LED, starting from 0. If there is only one LED that can control the brightness, this parameter is 0. +/// \param [out] uBrightness Returns the LED brightness value in the range 0 to 255. 0 means off, 255 is the brightest. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetLedBrightness( + CameraHandle hCamera, + int index, + UINT* uBrightness +); + +// 申请一段对齐的内存空间。功能和malloc类似,但是返回的内存是以align指定的字节数对齐的 +MVSDK_API BYTE* CameraAlignMalloc(int size, int align); + +//释放由CameraAlignMalloc 函数分配的内存空间。 +MVSDK_API void CameraAlignFree(BYTE* membuffer); + +/******************************************************/ +// 函数名 : CameraCommonCall +// 功能描述 : 相机的一些特殊功能调用,二次开发时一般不需要调用。 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// pszCall 功能及参数 +// pszResult 调用结果,不同的pszCall时,意义不同。 +// uResultBufSize pszResult指向的缓冲区的字节大小 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraCommonCall( + CameraHandle hCamera, + char const* pszCall, + char* pszResult, + UINT uResultBufSize +); + +/******************************************************/ +// 函数名 : CameraGetEyeCount +// 功能描述 : 获取多目相机的目数 +// 参数 : hCamera 相机的句柄,由CameraInit函数获得。 +// 返回值 : 成功时,返回CAMERA_STATUS_SUCCESS (0); +// 否则返回非0值的错误码,请参考CameraStatus.h +// 中错误码的定义。 +/******************************************************/ +MVSDK_API CameraSdkStatus CameraGetEyeCount( + CameraHandle hCamera, + int *eyecount +); + +/// @ingroup API_MULTI_EYE +/// \~chinese +/// \brief 对多目相机帧内的某个单目图做ISP +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iEyeIndex 单目索引。 +/// \param [in] pbyIn 输入图像数据的缓冲区地址,不能为NULL。 +/// \param [in] pInFrInfo 输入图像数据的帧头,不能为NULL。 +/// \param [out] pbyOut 处理后图像输出的缓冲区地址,不能为NULL。 +/// \param [out] pOutFrInfo 处理后图像的帧头信息,不能为NULL。 +/// \param [in] uOutFormat 处理完后图像的输出格式。 +/// \param [in] uReserved 预留参数,必须设置为0。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Do ISP for a certain monocular in the multi-camera frame. +/// \param [in] hCamera Camera handle. +/// \param [in] iEyeIndex eye index. +/// \param [in] pbyIn Input the buffer address of the image data, which cannot be NULL. +/// \param [in] pInFrInfo Input the frame header of the image data, which cannot be NULL. +/// \param [out] pbyOut The buffer address of the image output after processing, cannot be NULL. +/// \param [out] pOutFrInfo The header information of the processed image cannot be NULL. +/// \param [in] uOutFormat The output format of the image after processing. +/// \param [in] uReserved Reservation parameters must be set to 0. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraMultiEyeImageProcess( + CameraHandle hCamera, + int iEyeIndex, + BYTE* pbyIn, + tSdkFrameHead* pInFrInfo, + BYTE* pbyOut, + tSdkFrameHead* pOutFrInfo, + UINT uOutFormat, + UINT uReserved + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置光源控制器的输出模式(智能相机系列且需要硬件支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index 控制器索引 +/// \param [in] mode 输出模式(0:跟随闪光灯 1:手动) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the output mode of the light controller (Smart camera series and hardware support required) +/// \param [in] hCamera Camera handle. +/// \param [in] index controller index +/// \param [in] mode output mode (0: follow strobe 1: manual) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLightingControllerMode( + CameraHandle hCamera, + int index, + int mode + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置光源控制器的输出状态(智能相机系列且需要硬件支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] index 控制器索引 +/// \param [in] state 输出状态(0:关闭 1:打开) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the output status of the light controller (Smart camera series and hardware support required) +/// \param [in] hCamera Camera handle. +/// \param [in] index controller index +/// \param [in] state output state (0: off 1: on) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLightingControllerState( + CameraHandle hCamera, + int index, + int state + ); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 翻转帧数据 +/// \param [inout] pFrameBuffer 帧数据 +/// \param [in] pFrameHead 帧头 +/// \param [in] Flags 1:上下 2:左右 3:上下、左右皆做一次翻转(相当于旋转180度) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Flip frame data +/// \param [inout] pFrameBuffer frame data +/// \param [in] pFrameHead Frame Header +/// \param [in] Flags 1: Up and down 2: Around 3: Up and down, left and right are all flipped (equivalent to 180 degrees rotation) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlipFrameBuffer( + BYTE *pFrameBuffer, + tSdkFrameHead* pFrameHead, + int Flags + ); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 转换帧数据格式 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pInFrameBuffer 输入帧数据 +/// \param [out] pOutFrameBuffer 输出帧数据 +/// \param [in] outWidth 输出宽度 +/// \param [in] outHeight 输出高度 +/// \param [in] outMediaType 输出格式 @see CameraSetIspOutFormat +/// \param [inout] pFrameHead 帧头信息(转换成功后,里面的信息会被修改为输出帧的信息) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Conversion frame data format +/// \param [in] hCamera Camera handle. +/// \param [in] pInFrameBuffer input frame data +/// \param [out] pOutFrameBuffer output frame data +/// \param [in] outWidth output width +/// \param [in] outHeight output height +/// \param [in] outMediaType output format @see CameraSetIspOutFormat +/// \param [inout] pFrameHead frame header information (after successful conversion, the information inside will be modified to output frame information) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraConvertFrameBufferFormat( + CameraHandle hCamera, + BYTE *pInFrameBuffer, + BYTE *pOutFrameBuffer, + int outWidth, + int outHeight, + UINT outMediaType, + tSdkFrameHead* pFrameHead + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获取当前帧的ID,需相机支持(网口全系列支持),此函数返回错误代码,表示不支持。 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] id 帧ID +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief The ID of the current frame needs to be supported by the camera (supported by the full range of network ports). This function returns an error code indicating that it is not supported. +/// \param [in] hCamera Camera handle. +/// \param [out] id Frame ID +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetFrameID( + CameraHandle hCamera, + UINT* id + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获取当前帧的时间戳(单位微秒) +/// \param [in] hCamera 相机的句柄。 +/// \param [out] TimeStampL 时间戳低32位 +/// \param [out] TimeStampH 时间戳高32位 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the timestamp of the current frame (in microseconds) +/// \param [in] hCamera Camera handle. +/// \param [out] TimeStampL timestamp low 32 bits +/// \param [out] TimeStampH Timestamp high 32 bits +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetFrameTimeStamp( + CameraHandle hCamera, + UINT* TimeStampL, + UINT* TimeStampH + ); + +/// @ingroup API_RECONNECT +/// \~chinese +/// \brief 设置相机连接状态改变的回调通知函数。当相机掉线、重连时,pCallBack所指向的回调函数就会被调用。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pCallBack 回调函数指针。 +/// \param [in] pContext 回调函数的附加参数,在回调函数被调用时该附加参数会被传入,可以为NULL。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Sets the callback notification function for camera connection state changes. When the camera is disconnected and reconnected, the callback function pointed to by pCallBack will be called. +/// \param [in] hCamera Camera handle. +/// \param [in] pCallBack callback function pointer. +/// \param [in] pContext Additional parameter of the callback function. This additional parameter will be passed in when the callback function is called. It can be NULL. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetConnectionStatusCallback( + CameraHandle hCamera, + CAMERA_CONNECTION_STATUS_CALLBACK pCallBack, + PVOID pContext + ); + +/// @ingroup API_ENUM +/// \~chinese +/// \brief 从指定IP枚举GIGE设备,并建立设备列表(适用于相机和电脑不在同一网段的情况) +/// \param [in] ppIpList 目标IP +/// \param [in] numIp 目标IP个数 +/// \param [out] pCameraList 设备列表数组指针 +/// \param [inout] piNums 设备的个数指针,调用时传入pCameraList数组的元素个数,函数返回时,保存实际找到的设备个数 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义 +/// \warning piNums指向的值必须初始化,且不超过pCameraList数组元素个数,否则有可能造成内存溢出 +/// \note 返回的相机信息列表,会根据acFriendlyName排序的。例如可以将两个相机分别改为“Camera1”和“Camera2”的名字后,名字为“Camera1”的相机会排前面,名为“Camera2”的相机排后面。 +/// \~english +/// \brief Enumerates GIGE devices from the specified IP and builds a device list (applicable when the camera and computer are not on the same network segment) +/// \param [in] ppIpList target IP +/// \param [in] numIp number of target IPs +/// \param [out] pCameraList Device list array pointer +/// \param [inout] piNums The number of pointers to the device, the number of elements passed to the pCameraList array at the time of the call. When the function returns, the number of devices actually found is saved. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \warning piNums The value pointed to must be initialized and does not exceed the number of pCameraList array elements, otherwise it may cause memory overflow +/// \note The list of returned camera information will be sorted according to acFriendlyName. For example, after changing the two cameras to the names of "Camera1" and "Camera2," the camera named "Camera1" will be in front, and the camera named "Camera2" will be behind the row. +MVSDK_API CameraSdkStatus CameraGigeEnumerateDevice( + char const** ppIpList, + int numIp, + tSdkCameraDevInfo* pCameraList, + INT* piNums +); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 配置网口相机的一些功能选项 +/// \param [in] optionName 选项("NumBuffers", "3") +/// \param [in] value 值 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Configure some options for the gige camera +/// \param [in] optionName option name("NumBuffers", "3") +/// \param [in] value setting value +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGigeSetOption( + char const* optionName, + char const* value + ); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 获取GIGE相机的IP地址 +/// \param [in] pCameraInfo 相机的设备描述信息,可由@link #CameraEnumerateDevice @endlink函数获得。 +/// \param [out] CamIp 相机IP(注意:必须保证传入的缓冲区大于等于16字节) +/// \param [out] CamMask 相机子网掩码(注意:必须保证传入的缓冲区大于等于16字节) +/// \param [out] CamGateWay 相机网关(注意:必须保证传入的缓冲区大于等于16字节) +/// \param [out] EtIp 网卡IP(注意:必须保证传入的缓冲区大于等于16字节) +/// \param [out] EtMask 网卡子网掩码(注意:必须保证传入的缓冲区大于等于16字节) +/// \param [out] EtGateWay 网卡网关(注意:必须保证传入的缓冲区大于等于16字节) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the GIGE camera's IP address +/// \param [in] pCameraInfo camera's device description information can be obtained by @link #CameraEnumerateDevice @endlink function. +/// \param [out] CamIp camera IP (Note: must ensure that the incoming buffer is greater than or equal to 16 bytes) +/// \param [out] CamMask camera subnet mask (Note: must ensure that the incoming buffer is greater than or equal to 16 bytes) +/// \param [out] CamGateWay camera gateway (Note: must ensure that the incoming buffer is greater than or equal to 16 bytes) +/// \param [out] EtIp network card IP (Note: must ensure that the incoming buffer is greater than or equal to 16 bytes) +/// \param [out] EtMask subnet mask (Note: must ensure that the incoming buffer is greater than or equal to 16 bytes) +/// \param [out] EtGateWay NIC Gateway (Note: must ensure that the incoming buffer is greater than or equal to 16 bytes) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGigeGetIp( + tSdkCameraDevInfo* pCameraInfo, + char* CamIp, + char* CamMask, + char* CamGateWay, + char* EtIp, + char* EtMask, + char* EtGateWay + ); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 设置GIGE相机的IP地址 +/// \param [in] pCameraInfo 相机的设备描述信息,可由@link #CameraEnumerateDevice @endlink函数获得。 +/// \param [in] Ip 相机IP(如:192.168.1.100) +/// \param [in] SubMask 相机子网掩码(如:255.255.255.0) +/// \param [in] GateWay 相机网关(如:192.168.1.1) +/// \param [in] bPersistent TRUE: 设置相机为固定IP,FALSE:设置相机自动分配IP(忽略参数Ip, SubMask, GateWay) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the GIGE camera's IP address +/// \param [in] pCameraInfo camera's device description information can be obtained by @link #CameraEnumerateDevice @endlink function. +/// \param [in] Ip camera IP (eg 192.168.1.100) +/// \param [in] SubMask camera subnet mask (eg 255.255.255.0) +/// \param [in] GateWay Camera Gateway (eg 192.168.1.1) +/// \param [in] bPersistent TRUE: Set camera to fixed IP, FALSE: Set camera to assign IP automatically (ignoring parameters Ip, SubMask, GateWay) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGigeSetIp( + tSdkCameraDevInfo* pCameraInfo, + char const* Ip, + char const* SubMask, + char const* GateWay, + BOOL bPersistent + ); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 获取GIGE相机的MAC地址 +/// \param [in] pCameraInfo 相机的设备描述信息,可由@link #CameraEnumerateDevice @endlink函数获得。 +/// \param [out] CamMac 相机MAC(注意:必须保证传入的缓冲区大于等于18字节) +/// \param [out] EtMac 网卡MAC(注意:必须保证传入的缓冲区大于等于18字节) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Select the LUT table in the preset LUT mode. +/// \param [in] pCameraInfo camera's device description information can be obtained by @link #CameraEnumerateDevice @endlink function. +/// \param [out] CamMac camera MAC (Note: must ensure that the incoming buffer is greater than or equal to 18 bytes) +/// \param [out] EtMac network card MAC (Note: must ensure that the incoming buffer is greater than or equal to 18 bytes) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGigeGetMac( + tSdkCameraDevInfo* pCameraInfo, + char* CamMac, + char* EtMac + ); + +/// @ingroup API_GRAB +/// \~chinese +/// \brief 清空相机内已缓存的所有帧 +/// \param [in] hCamera 相机的句柄。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Clear all cached frames in the camera +/// \param [in] hCamera Camera handle. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraClearBuffer( + CameraHandle hCamera + ); + +/// @ingroup API_GRAB +/// \~chinese +/// \brief 获得一帧图像数据。 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pFrameInfo 图像的帧头信息指针。 +/// \param [out] pbyBuffer 指向图像的数据的缓冲区指针。 +/// \param [in] wTimes 抓取图像的超时时间。 +/// \param [in] Priority 取图优先级 详见:@link #emCameraGetImagePriority @endlink +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 除了多一个优先级参数外与@link #CameraGetImageBuffer @endlink相同 +/// \~english +/// \brief Get a frame of image data. +/// \param [in] hCamera Camera handle. +/// \param [out] pFrameInfo Frame header information pointer +/// \param [out] pbyBuffer Pointer to the buffer of data for the image. +/// \param [in] wTimes The time-out time for capturing images. +/// \param [in] Priority map priority See: @link #emCameraGetImagePriority @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note Same as @link #CameraGetImageBuffer @endlink except one more priority parameter +MVSDK_API CameraSdkStatus CameraGetImageBufferPriority( + CameraHandle hCamera, + tSdkFrameHead* pFrameInfo, + BYTE** pbyBuffer, + UINT wTimes, + UINT Priority + ); + +/// @ingroup API_GRAB +/// \~chinese +/// \brief 获得一帧图像数据。该接口获得的图像是经过处理后的RGB格式。 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] piWidth 整形指针,返回图像的宽度 +/// \param [out] piHeight 整形指针,返回图像的高度 +/// \param [in] wTimes 抓取图像的超时时间。单位毫秒。 +/// \param [in] Priority 取图优先级 详见:@link #emCameraGetImagePriority @endlink +/// \return 成功时,返回RGB数据缓冲区的首地址;否则返回0。 +/// \note 除了多一个优先级参数外与@link #CameraGetImageBufferEx @endlink相同 +/// \~english +/// \brief Get a frame of image data. The image obtained by this interface is the processed RGB format. +/// \param [in] hCamera Camera handle. +/// \param [out] piWidth Returns the width of the image +/// \param [out] piHeight Returns the height of the image +/// \param [in] wTimes The time-out time for capturing images. The unit is milliseconds. +/// \param [in] Priority map priority See: @link #emCameraGetImagePriority @endlink +/// \return Returns the first address of the RGB data buffer when successful; otherwise returns 0. +/// \note Same as @link #CameraGetImageBufferEx @endlink except one more priority parameter +MVSDK_API unsigned char* CameraGetImageBufferPriorityEx( + CameraHandle hCamera, + INT* piWidth, + INT* piHeight, + UINT wTimes, + UINT Priority + ); + +/// @ingroup API_TRIGGER +/// \~chinese +/// \brief 执行软触发。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] uFlags 功能标志,详见@link #emCameraSoftTriggerExFlags @endlink中的定义 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \see CameraSoftTrigger +/// \~english +/// \brief Perform a soft trigger. +/// \param [in] hCamera Camera handle. +/// \param [in] uFlags function flags, as defined in @link #emCameraSoftTriggerExFlags @endlink +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \see CameraSoftTrigger +MVSDK_API CameraSdkStatus CameraSoftTriggerEx( + CameraHandle hCamera, + UINT uFlags + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 当相机处于触发模式(软触发或硬触发)时,相机发送一帧到PC,如相机未收到PC端的接收确认,相机可以把帧重发几次。用本函数设置相机重发次数。(仅网口相机支持) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] count 重发次数(<=0表示禁用重发功能) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief When the camera is in the trigger mode (soft trigger or hard trigger), the camera sends a frame to the PC. If the camera does not receive the reception confirmation from the PC, the camera can retransmit the frame several times. Use this function to set the number of camera resends. (only supported by Gige camera) +/// \param [in] hCamera Camera handle. +/// \param [in] count number of resends (<=0 means disable resends) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetFrameResendCount( + CameraHandle hCamera, + int count + ); + +/// @ingroup API_BASIC +/// \~chinese +/// \brief 配置系统选项(通常需要在CameraInit打开相机之前配置好) +/// \param [in] optionName 选项("NumBuffers", "3") +/// \param [in] value 值 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Configure system options (usually required before CameraInit turns on the camera) +/// \param [in] optionName option name("NumBuffers", "3") +/// \param [in] value setting value +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetSysOption( + char const* optionName, + char const* value + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 使能坏点修正 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] bEnable TRUE: 使能坏点修正 FALSE: 关闭坏点修正 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Enable dead pixel correction +/// \param [in] hCamera Camera handle. +/// \param [in] bEnable TRUE: Enable dead pixel correction FALSE: Turn off dead pixel correction +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetCorrectDeadPixel( + CameraHandle hCamera, + BOOL bEnable + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 获取坏点修正使能状态 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pbEnable 返回使能状态 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get dead pixel correction enabled +/// \param [in] hCamera Camera handle. +/// \param [out] pbEnable Returns enable state +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetCorrectDeadPixel( + CameraHandle hCamera, + BOOL* pbEnable + ); + +/// @ingroup API_UNDISTORT +/// \~chinese +/// \brief 设置校正参数 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] width 图片宽度 +/// \param [in] height 图片高度 +/// \param [in] cameraMatrix 内参(fx, fy, cx, cy) +/// \param [in] distCoeffs 畸变系数(k1,k2,p1,p2,k3) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set undistort parameters +/// \param [in] hCamera Camera handle. +/// \param [in] width image width +/// \param [in] height image height +/// \param [in] cameraMatrix internal matrix(fx, fy, cx, cy) +/// \param [in] distCoeffs distortion coefficient (k1, k2, p1, p2, k3) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetUndistortParams( + CameraHandle hCamera, + int width, + int height, + double cameraMatrix[4], + double distCoeffs[5] + ); + +/// @ingroup API_UNDISTORT +/// \~chinese +/// \brief 获取校正参数 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] width 图片宽度 +/// \param [out] height 图片高度 +/// \param [out] cameraMatrix 内参(fx, fy, cx, cy) +/// \param [out] distCoeffs 畸变系数(k1,k2,p1,p2,k3) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get undistort parameters +/// \param [in] hCamera Camera handle. +/// \param [out] width image width +/// \param [out] height image height +/// \param [out] cameraMatrix internal matrix(fx, fy, cx, cy) +/// \param [out] distCoeffs distortion coefficient (k1, k2, p1, p2, k3) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetUndistortParams( + CameraHandle hCamera, + int *width, + int *height, + double cameraMatrix[4], + double distCoeffs[5] + ); + +/// @ingroup API_UNDISTORT +/// \~chinese +/// \brief 使能镜头校正 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] bEnable 使能校正 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set undistort enable status +/// \param [in] hCamera Camera handle. +/// \param [in] bEnable enable status +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetUndistortEnable( + CameraHandle hCamera, + BOOL bEnable + ); + +/// @ingroup API_UNDISTORT +/// \~chinese +/// \brief 获取镜头校正使能状态 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] bEnable 使能校正 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get undistort enable status +/// \param [in] hCamera Camera handle. +/// \param [out] bEnable enable status +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetUndistortEnable( + CameraHandle hCamera, + BOOL* bEnable + ); + +/// @ingroup API_UNDISTORT +/// \~chinese +/// \brief 打开校正编辑面板 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] hParent 调用该函数的窗口的句柄。可以为NULL。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Open the undistort editing panel +/// \param [in] hCamera Camera handle. +/// \param [in] hParent The handle of the window that called the function. Can be NULL. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraCustomizeUndistort( + CameraHandle hCamera, + HWND hParent + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 使能平场校正 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] bEnable TRUE: 使能平场校正 FALSE: 关闭平场校正 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Enable flat field correction +/// \param [in] hCamera Camera handle. +/// \param [in] bEnable TRUE: Enable flat field correction FALSE: Turn off flat field correction +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlatFieldingCorrectSetEnable( + CameraHandle hCamera, + BOOL bEnable + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 获取平场校正使能状态 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pbEnable 返回使能状态 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get Plane Correction Enable Status +/// \param [in] hCamera Camera handle. +/// \param [out] pbEnable Returns enable state +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlatFieldingCorrectGetEnable( + CameraHandle hCamera, + BOOL* pbEnable + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 设置平场校正参数 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pDarkFieldingImage 暗场图片 +/// \param [in] pDarkFieldingFrInfo 暗场图片信息 +/// \param [in] pLightFieldingImage 明场图片 +/// \param [in] pLightFieldingFrInfo 明场图片信息 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set flat field correction parameters +/// \param [in] hCamera Camera handle. +/// \param [in] pDarkFieldingImage dark field image +/// \param [in] pDarkFieldingFrInfo dark field image information +/// \param [in] pLightFieldingImage Brightfield image +/// \param [in] pLightFieldingFrInfo Brightfield image information +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlatFieldingCorrectSetParameter( + CameraHandle hCamera, + BYTE const* pDarkFieldingImage, + tSdkFrameHead const* pDarkFieldingFrInfo, + BYTE const* pLightFieldingImage, + tSdkFrameHead const* pLightFieldingFrInfo + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 获取平场校正参数的状态 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pbValid 返回参数是否有效 +/// \param [out] pFilePath 返回参数文件的路径 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get status of flat field correction parameters +/// \param [in] hCamera Camera handle. +/// \param [out] pbValid Return whether the parameter is valid +/// \param [out] pFilePath Returns the path of the parameter file +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlatFieldingCorrectGetParameterState( + CameraHandle hCamera, + BOOL *pbValid, + char *pFilePath + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 保存平场校正参数到文件 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pszFileName 文件路径 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Save flat correction parameters to file +/// \param [in] hCamera Camera handle. +/// \param [in] pszFileName file path +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlatFieldingCorrectSaveParameterToFile( + CameraHandle hCamera, + char const* pszFileName + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 从文件中加载平场校正参数 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pszFileName 文件路径 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Load flat field correction parameters from file +/// \param [in] hCamera Camera handle. +/// \param [in] pszFileName file path +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraFlatFieldingCorrectLoadParameterFromFile( + CameraHandle hCamera, + char const* pszFileName + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 设置3D降噪参数 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] bEnable 启用或禁用 +/// \param [in] nCount 使用几张图片进行降噪(2-8张) +/// \param [in] Weights 降噪权重,如当使用3张图片进行降噪则这个参数可以传入3个浮点(0.3,0.3,0.4),最后一张图片的权重大于前2张。如果不需要使用权重,则把这个参数传入0,表示所有图片的权重相同(0.33,0.33,0.33) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set 3D noise reduction parameters +/// \param [in] hCamera Camera handle. +/// \param [in] bEnable enable or disable +/// \param [in] nCount Noise reduction using several pictures (2-8) +/// \param [in] Weights Noise reduction weight, such as when using 3 pictures for noise reduction, this parameter can be passed in 3 floating points (0.3, 0.3, 0.4). The weight of the last picture is larger than the first 2 pictures. . If you do not need to use weights, then pass this parameter to 0, indicating that all images have the same weight (0.33, 0.33, 0.33) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetDenoise3DParams( + CameraHandle hCamera, + BOOL bEnable, + int nCount, + float *Weights + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 获取当前的3D降噪参数 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] bEnable 启用或禁用 +/// \param [out] nCount 使用了几张图片进行降噪 +/// \param [out] bUseWeight 是否使用了降噪权重 +/// \param [out] Weights 降噪权重 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get current 3D noise reduction parameters +/// \param [in] hCamera Camera handle. +/// \param [out] bEnable enable or disable +/// \param [out] nCount uses several pictures for noise reduction +/// \param [out] bUseWeight whether to use noise reduction weights +/// \param [out] Weights Noise Reduction Weights +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetDenoise3DParams( + CameraHandle hCamera, + BOOL *bEnable, + int *nCount, + BOOL *bUseWeight, + float *Weights + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 对一组帧进行一次降噪处理 +/// \param [in] InFramesHead 输入帧头 +/// \param [in] InFramesData 输入帧数据 +/// \param [in] nCount 输入帧的数量 +/// \param [in] Weights 降噪权重 +/// \param [out] OutFrameHead 输出帧头 +/// \param [out] OutFrameData 输出帧数据 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Perform a noise reduction on a group of frames +/// \param [in] InFramesHead input frame header +/// \param [in] InFramesData input frame data +/// \param [in] nCount Number of input frames +/// \param [in] Weights Noise Reduction Weight +/// \param [out] OutFrameHead output frame header +/// \param [out] OutFrameData output frame data +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraManualDenoise3D( + tSdkFrameHead *InFramesHead, + BYTE **InFramesData, + int nCount, + float *Weights, + tSdkFrameHead *OutFrameHead, + BYTE *OutFrameData + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获取输出格式的特性支持。(比如:H264、H265支持设置码率) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iMediaType 输出格式索引 +/// \param [out] uCap 特性支持 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the feature support of the output format. (For example: H264, H265 support setting bit rate) +/// \param [in] hCamera Handle of the camera. +/// \param [in] iMediaType output format index +/// \param [out] uCap feature support +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetMediaCapability( + CameraHandle hCamera, + int iMediaType, + UINT *uCap + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置码率。(仅部分输出格式支持,比如:H264、H265) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iMediaType 输出格式索引 +/// \param [in] uRate 码率 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the bit rate. (Only some output formats are supported, such as H264, H265) +/// \param [in] hCamera Handle of the camera. +/// \param [in] iMediaType output format index +/// \param [in] uRate bit rate +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetMediaBitRate( + CameraHandle hCamera, + int iMediaType, + UINT uRate + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获取码率设置。(仅部分输出格式支持,比如:H264、H265) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iMediaType 输出格式索引 +/// \param [out] uRate 码率 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the bit rate. (Only some output formats are supported, such as H264, H265) +/// \param [in] hCamera Handle of the camera. +/// \param [in] iMediaType output format index +/// \param [out] uRate bit rate +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetMediaBitRate( + CameraHandle hCamera, + int iMediaType, + UINT *uRate + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置相机帧事件回调函数。当帧开始以及帧完成时,pCallBack所指向的回调函数就会被调用。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pCallBack 回调函数指针。 +/// \param [in] pContext 回调函数的附加参数,在回调函数被调用时该附加参数会被传入,可以为NULL。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 对于全局快门相机帧开始表示一帧曝光结束 +/// \~english +/// \brief Set the camera frame event callback function. When the frame starts and when the frame is completed, the callback function pointed to by pCallBack will be called. +/// \param [in] hCamera Camera handle. +/// \param [in] pCallBack callback function pointer. +/// \param [in] pContext Additional parameter of the callback function. This additional parameter will be passed in when the callback function is called. It can be NULL. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note For the start of the global shutter camera frame, it means the end of a frame exposure +MVSDK_API CameraSdkStatus CameraSetFrameEventCallback( + CameraHandle hCamera, + CAMERA_FRAME_EVENT_CALLBACK pCallBack, + PVOID pContext + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 设置降噪系数. +/// \param [in] hCamera 相机的句柄。 +/// \param [in] value [0,7] +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the noise reduction coefficient. +/// \param [in] hCamera Camera handle. +/// \param [in] value [0,7] +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetNoiseReductionValue( + CameraHandle hCamera, + int value + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 获取降噪系数. +/// \param [in] hCamera 相机的句柄。 +/// \param [out] value [0,7] +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the noise reduction coefficient. +/// \param [in] hCamera Camera handle. +/// \param [out] value [0,7] +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetNoiseReductionValue( + CameraHandle hCamera, + int* value + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 设置对数曲线值 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] value [0,255] +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set logarithmic curve value +/// \param [in] hCamera Camera handle. +/// \param [in] value [0,255] +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetLogarithmicCurveValue( + CameraHandle hCamera, + int value + ); + +/// @ingroup API_ENHANCE +/// \~chinese +/// \brief 获取对数曲线值 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] value [0,255] +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get logarithmic curve value +/// \param [in] hCamera Camera handle. +/// \param [out] value [0,255] +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetLogarithmicCurveValue( + CameraHandle hCamera, + int* value + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 设置拼接行数,从多帧提取指定的行数拼接成一帧 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] numLines 拼接行数(默认为0,0表示不做拼接处理) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Set the number of splicing lines, extract the specified number of lines from multiple frames and splice them into one frame (only support line scan series) +/// \param [in] hCamera Camera handle. +/// \param [in] numLines Number of splicing lines (default is 0, 0 means no splicing processing) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSetSpliceLines( + CameraHandle hCamera, + int numLines + ); + +/// @ingroup API_ADVANCE +/// \~chinese +/// \brief 获取拼接行数 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] numLines 拼接行数(默认为0,0表示不做拼接处理) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get the number of splicing lines +/// \param [in] hCamera Camera handle. +/// \param [in] numLines Number of splicing lines (default is 0, 0 means no splicing processing) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraGetSpliceLines( + CameraHandle hCamera, + int* numLines + ); + +/// @ingroup API_GRAB +/// \~chinese +/// \brief 从指定通道获取数据。(仅部分相机硬件支持此功能) +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pszChannelName 通道名。 +/// \param [out] pFrameInfo 图像的帧头信息指针。 +/// \param [out] pbyBuffer 返回图像数据的缓冲区指针。 +/// \param [in] wTimes 抓取图像的超时时间,单位毫秒。在wTimes时间内还未获得图像,则该函数会返回超时错误。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Get data from the specified channel. (Only some camera hardware supports this function) +/// \param [in] hCamera Handle of the camera. +/// \param [in] pszChannelName Channel name. +/// \param [out] pFrameInfo The header information pointer of the image. +/// \param [out] pbyBuffer Returns the buffer pointer of the image data. +/// \param [in] wTimes Timeout for grabbing an image in milliseconds. The function returns a timeout error if no image has been obtained within wTimes. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSnapChannelBuffer( + CameraHandle hCamera, + char const* pszChannelName, + tSdkFrameHead* pFrameInfo, + BYTE** pbyBuffer, + UINT wTimes + ); + +/// @ingroup API_GRAB +/// \~chinese +/// \brief 释放由@link #CameraSnapChannelBuffer @endlink获得的缓冲区。 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pszChannelName 通道名。 +/// \param [in] pbyBuffer 帧缓冲区地址。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Releases the buffer obtained by @link #CameraSnapChannelBuffer @endlink. +/// \param [in] hCamera Camera handle. +/// \param [in] pszChannelName Channel name. +/// \param [in] pbyBuffer Frame buffer address. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraReleaseChannelBuffer( + CameraHandle hCamera, + char const* pszChannelName, + BYTE* pbyBuffer + ); + +/// @ingroup API_UTIL +/// \~chinese +/// \brief 图片清晰度评估 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] iAlgorithSel 使用的评估算法,参考@link emEvaluateDefinitionAlgorith @endlink的定义 +/// \param [in] pbyIn 输入图像数据的缓冲区地址,不能为NULL。 +/// \param [in] pFrInfo 输入图像的帧头信息 +/// \param [out] DefinitionValue 返回的清晰度估值(越大越清晰) +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Image clarity assessment +/// \param [in] hCamera Camera handle. +/// \param [in] iAlgorithSel The currently used evaluation algorithm, see @link emEvaluateDefinitionAlgorith @endlink +/// \param [in] pbyIn The buffer address of the input image data. Cannot be NULL. +/// \param [in] pFrInfo input image frame header information +/// \param [out] DefinitionValue Returns the sharpness value (greater the clearer) +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraEvaluateImageDefinition( + CameraHandle hCamera, + INT iAlgorithSel, + BYTE* pbyIn, + tSdkFrameHead* pFrInfo, + double* DefinitionValue + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 打开坏点编辑面板 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] hParent 调用该函数的窗口的句柄。可以为NULL。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Open the dead pixels editing panel +/// \param [in] hCamera Camera handle. +/// \param [in] hParent The handle of the window that called the function. Can be NULL. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraCustomizeDeadPixels( + CameraHandle hCamera, + HWND hParent + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 读取相机坏点 +/// \param [in] hCamera 相机的句柄。 +/// \param [out] pRows 坏点y坐标 +/// \param [out] pCols 坏点x坐标 +/// \param [out] pNumPixel 输入时表示行列缓冲区的大小,返回时表示行列缓冲区中返回的坏点数量。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \note 当pRows或者pCols为NULL时函数会把相机当前的坏点个数通过pNumPixel返回 +/// \~english +/// \brief Reading camera dead pixels +/// \param [in] hCamera Camera handle. +/// \param [out] pRows dead pixels y coordinates +/// \param [out] pCols bad x coordinate +/// \param [out] pNumPixel Inputs the size of the row and column buffers. When returned, it indicates the number of bad pixels returned in the row and column buffers. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +/// \note When pRows or pCols is NULL, the function will return the camera's current number of dead pixels through pNumPixel. +MVSDK_API CameraSdkStatus CameraReadDeadPixels( + CameraHandle hCamera, + USHORT* pRows, + USHORT* pCols, + UINT* pNumPixel + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 添加相机坏点 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pRows 坏点y坐标 +/// \param [in] pCols 坏点x坐标 +/// \param [in] NumPixel 行列缓冲区中的坏点个数 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Add camera dead pixels +/// \param [in] hCamera Camera handle. +/// \param [in] pRows dead point y coordinates +/// \param [in] pCols bad x coordinate +/// \param [in] NumPixel Number of dead pixels in row buffer +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraAddDeadPixels( + CameraHandle hCamera, + USHORT* pRows, + USHORT* pCols, + UINT NumPixel + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 删除相机指定坏点 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] pRows 坏点y坐标 +/// \param [in] pCols 坏点x坐标 +/// \param [in] NumPixel 行列缓冲区中的坏点个数 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Delete camera specified dead pixels +/// \param [in] hCamera Camera handle. +/// \param [in] pRows dead point y coordinates +/// \param [in] pCols bad x coordinate +/// \param [in] NumPixel Number of dead pixels in row buffer +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraRemoveDeadPixels( + CameraHandle hCamera, + USHORT* pRows, + USHORT* pCols, + UINT NumPixel + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 删除相机的所有坏点 +/// \param [in] hCamera 相机的句柄。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Remove all camera's dead pixels +/// \param [in] hCamera Camera handle. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraRemoveAllDeadPixels( + CameraHandle hCamera + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 保存相机坏点到相机存储中 +/// \param [in] hCamera 相机的句柄。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Save camera dead pixels to camera memory +/// \param [in] hCamera Camera handle. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSaveDeadPixels( + CameraHandle hCamera + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 保存相机坏点到文件中 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] sFileName 文件的完整路径。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Save Camera Dead Points to File +/// \param [in] hCamera Camera handle. +/// \param [in] sFileName Full path to the file. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraSaveDeadPixelsToFile( + CameraHandle hCamera, + char const* sFileName + ); + +/// @ingroup API_DEAD_PIXEL +/// \~chinese +/// \brief 从文件加载相机坏点 +/// \param [in] hCamera 相机的句柄。 +/// \param [in] sFileName 文件的完整路径。 +/// \return 成功返回 CAMERA_STATUS_SUCCESS(0)。否则返回非0值的错误码, 请参考 CameraStatus.h 中错误码的定义。 +/// \~english +/// \brief Loading camera dead pixels from file +/// \param [in] hCamera Camera handle. +/// \param [in] sFileName Full path to the file. +/// \return Returns CAMERA_STATUS_SUCCESS(0) successfully. Otherwise, it returns a non-zero error code. Please refer to the definition of the error code in CameraStatus.h. +MVSDK_API CameraSdkStatus CameraLoadDeadPixelsFromFile( + CameraHandle hCamera, + char const* sFileName + ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/io/mindvision/include/CameraDefine.h b/io/mindvision/include/CameraDefine.h new file mode 100644 index 0000000..e94a7ac --- /dev/null +++ b/io/mindvision/include/CameraDefine.h @@ -0,0 +1,842 @@ +#pragma once +#ifndef _CAMERA_DEFINE_H_ +#define _CAMERA_DEFINE_H_ + +#include "CameraStatus.h" + +#define MAX_CROSS_LINE 9 + +//相机的句柄类型定义 +typedef int CameraHandle; +typedef int INT; +typedef int LONG; +typedef unsigned int UINT; +typedef unsigned long long UINT64; +typedef int BOOL; +typedef unsigned char BYTE; +typedef unsigned int DWORD; +typedef void* PVOID; +typedef void* HWND; +typedef char* LPCTSTR; +typedef unsigned short USHORT; +typedef short SHORT; + typedef unsigned char* LPBYTE; +typedef char CHAR; +typedef char TCHAR; +typedef unsigned short WORD; +typedef INT HANDLE; +typedef void VOID; +typedef unsigned int ULONG; +typedef void* LPVOID; +typedef unsigned char UCHAR; +typedef void* HMODULE; + +#define TRUE 1 +#define FALSE 0 +//图像查表变换的方式 +typedef enum +{ + LUTMODE_PARAM_GEN=0,//通过调节参数动态生成LUT表 + LUTMODE_PRESET, //使用预设的LUT表 + LUTMODE_USER_DEF //使用用户自定义的LUT表 +}emSdkLutMode; + +//相机的视频流控制 +typedef enum +{ + RUNMODE_PLAY=0, //正常预览,捕获到图像就显示。(如果相机处于触发模式,则会等待触发帧的到来) + RUNMODE_PAUSE, //暂停,会暂停相机的图像输出,同时也不会去捕获图像 + RUNMODE_STOP //停止相机工作。反初始化后,相机就处于停止模式 +}emSdkRunMode; + +//SDK内部显示接口的显示方式 +typedef enum +{ + DISPLAYMODE_SCALE=0, //缩放显示模式,缩放到显示控件的尺寸 + DISPLAYMODE_REAL //1:1显示模式,当图像尺寸大于显示控件的尺寸时,只显示局部 +}emSdkDisplayMode; + +//录像状态 +typedef enum +{ + RECORD_STOP = 0, //停止 + RECORD_START, //录像中 + RECORD_PAUSE //暂停 +}emSdkRecordMode; + +//图像的镜像操作 +typedef enum +{ + MIRROR_DIRECTION_HORIZONTAL = 0,//水平镜像 + MIRROR_DIRECTION_VERTICAL //垂直镜像 +}emSdkMirrorDirection; + +/// @ingroup MV_ENUM_TYPE +/// \~chinese 图像的旋转操作 +/// \~english Rotation of the image +typedef enum +{ + ROTATE_DIRECTION_0=0, ///< \~chinese 不旋转 \~english Do not rotate + ROTATE_DIRECTION_90=1, ///< \~chinese 逆时针90度 \~english Counterclockwise 90 degrees + ROTATE_DIRECTION_180=2, ///< \~chinese 逆时针180度 \~english Counterclockwise 180 degrees + ROTATE_DIRECTION_270=3, ///< \~chinese 逆时针270度 \~english Counterclockwise 270 degrees +}emSdkRotateDirection; + +//相机视频的帧率 +typedef enum +{ + FRAME_SPEED_LOW = 0, //低速模式 + FRAME_SPEED_NORMAL, //普通模式 + FRAME_SPEED_HIGH, //高速模式(需要较高的传输带宽,多设备共享传输带宽时会对帧率的稳定性有影响) + FRAME_SPEED_SUPER //超高速模式(需要较高的传输带宽,多设备共享传输带宽时会对帧率的稳定性有影响) +}emSdkFrameSpeed; + +//保存文件的格式类型 +typedef enum +{ + FILE_JPG = 1,//JPG + FILE_BMP = 2,//BMP + FILE_RAW = 4,//相机输出的bayer格式文件,对于不支持bayer格式输出相机,无法保存为该格式 + FILE_PNG = 8, //PNG + FILE_BMP_8BIT = 16, ///< \~chinese BMP 8bit \~english BMP 8bit + FILE_PNG_8BIT = 32, ///< \~chinese PNG 8bit \~english PNG 8bit + FILE_RAW_16BIT = 64, ///< \~chinese RAW 16bit \~english RAW 16bit +}emSdkFileType; + +/// @ingroup MV_ENUM_TYPE +/// \~chinese 相机中的图像传感器的工作模式 +/// \~english Image Sensor Operation Mode in Camera +typedef enum +{ + /// \~chinese 连续采集模式 + /// \~english Continuous acquisition mode + CONTINUATION=0, + + /// \~chinese 软件触发模式,由软件发送指令后,传感器开始采集指定帧数的图像,采集完成后,停止输出 + /// \~english Software trigger mode. After the software sends the instruction, the sensor starts to capture the image of the specified frame number. After the acquisition is completed, the output is stopped. + SOFT_TRIGGER=1, + + /// \~chinese 硬件触发模式,当接收到外部信号,传感器开始采集指定帧数的图像,采集完成后,停止输出 + /// \~english In the hardware trigger mode, when receiving an external signal, the sensor starts to capture the image of the specified frame number. After the acquisition is completed, the output is stopped. + EXTERNAL_TRIGGER=2, + + /// \~chinese 编码器触发模式(仅用于线阵相机) + /// \~english Encoder trigger mode (only for line scan cameras) + ROTARYENC_TRIGGER=3, + + /// \~chinese 编码器条件触发模式(仅用于线阵相机) + /// \~english Encoder condition trigger mode (only for line scan cameras) + ROTARYENC_COND_TRIGGER=4, +} emSdkSnapMode; + +//自动曝光时抗频闪的频闪 +typedef enum +{ + LIGHT_FREQUENCY_50HZ = 0,//50HZ,一般的灯光都是50HZ + LIGHT_FREQUENCY_60HZ //60HZ,主要是指显示器的 +}emSdkLightFrequency; + +//相机的配置参数,分为A,B,C,D 4组进行保存。 +typedef enum +{ + PARAMETER_TEAM_DEFAULT = 0xff, + PARAMETER_TEAM_A = 0, + PARAMETER_TEAM_B = 1, + PARAMETER_TEAM_C = 2, + PARAMETER_TEAM_D = 3 +}emSdkParameterTeam; + + +/*emSdkParameterMode 相机参数加载模式,参数加载分为从文件和从设备加载两种方式 + +PARAM_MODE_BY_MODEL:所有同型号的相机共用ABCD四组参数文件。修改 + 一台相机的参数文件,会影响到整个同型号的 + 相机参数加载。 + +PARAM_MODE_BY_NAME:所有设备名相同的相机,共用ABCD四组参数文件。 + 默认情况下,当电脑上只接了某型号一台相机时, + 设备名都是一样的,而您希望某一台相机能够加载 + 不同的参数文件,则可以通过修改其设备名的方式 + 来让其加载指定的参数文件。 + +PARAM_MODE_BY_SN:相机按照自己的唯一序列号来加载ABCD四组参数文件, + 序列号在出厂时已经固化在相机内,每台相机的序列号 + 都不相同,通过这种方式,每台相机的参数文件都是独立的。 + +您可以根据自己的使用环境,灵活使用以上几种方式加载参数。例如,以 +MV-U300为例,您希望多台该型号的相机在您的 电脑上都共用4组参数,那么就 +使用PARAM_MODE_BY_MODEL方式;如果您希望其中某一台或者某几台MV-U300能 +使用自己参数文件而其余的MV-U300又要使用相同的参数文件,那么使用 +PARAM_MODE_BY_NAME方式;如果您希望每台MV-U300都使用不同的参数文件,那么 +使用PARAM_MODE_BY_SN方式。 +参数文件存在安装目录的 \Camera\Configs 目录下,以config为后缀名的文件。 +*/ +typedef enum +{ + PARAM_MODE_BY_MODEL = 0, //根据相机型号名从文件中加载参数,例如MV-U300 + PARAM_MODE_BY_NAME, //根据设备昵称(tSdkCameraDevInfo.acFriendlyName)从文件中加载参数,例如MV-U300,该昵称可自定义 + PARAM_MODE_BY_SN, //根据设备的唯一序列号从文件中加载参数,序列号在出厂时已经写入设备,每台相机拥有不同的序列号。 + PARAM_MODE_IN_DEVICE //从设备的固态存储器中加载参数。不是所有的型号都支持从相机中读写参数组,由tSdkCameraCapbility.bParamInDevice决定 +}emSdkParameterMode; + + +//SDK生成的相机配置页面掩码值 +typedef enum +{ + PROP_SHEET_INDEX_EXPOSURE=0, ///< \~chinese 曝光设置 \~english Exposure Settings + PROP_SHEET_INDEX_ISP_COLOR=1, ///< \~chinese 颜色矩阵设置 \~english Color Matrix Settings + PROP_SHEET_INDEX_ISP_LUT=2, ///< \~chinese LUT设置 \~english LUT setting + PROP_SHEET_INDEX_ISP_SHAPE=3, ///< \~chinese 变换设置 \~english transform settings + PROP_SHEET_INDEX_VIDEO_FORMAT=4, ///< \~chinese 格式设置 \~english Formatting + PROP_SHEET_INDEX_RESOLUTION=5, ///< \~chinese 分辨率设置 \~english resolution setting + PROP_SHEET_INDEX_IO_CTRL=6, ///< \~chinese IO控制 \~english IO control + PROP_SHEET_INDEX_TRIGGER_SET=7, ///< \~chinese 触发模式 \~english trigger setting + PROP_SHEET_INDEX_OVERLAY=8, ///< \~chinese 十字线 \~english Crosshair + PROP_SHEET_INDEX_DEVICE_INFO=9, ///< \~chinese 设备信息 \~english Device Information + PROP_SHEET_INDEX_WDR=10, ///< \~chinese 宽动态 \~english Wide Dynamic + PROP_SHEET_INDEX_MULTI_EXPOSURE=11, ///< \~chinese 多重曝光 \~english Multi exposure + PROP_SHEET_INDEX_SPECIAL=12, ///< \~chinese 特殊设置 \~english Special settings + PROP_SHEET_INDEX_GIGE=13, ///< \~chinese GIGE设置 \~english GIGE settings + PROP_SHEET_INDEX_GF_SETTING_I = 14, ///< \~chinese GF系列红外相机设置I \~english GF Settings I + PROP_SHEET_INDEX_GF_SETTING_II = 15, ///< \~chinese GF系列红外相机设置II \~english GF Settings II + PROP_SHEET_INDEX_NEW_ISP_COLOR = 16, ///< \~chinese 白平衡设置 \~english WB Settings +}emSdkPropSheetMask; + +//SDK生成的相机配置页面的回调消息类型 +typedef enum +{ + SHEET_MSG_LOAD_PARAM_DEFAULT = 0, //参数被恢复成默认后,触发该消息 + SHEET_MSG_LOAD_PARAM_GROUP, //加载指定参数组,触发该消息 + SHEET_MSG_LOAD_PARAM_FROMFILE, //从指定文件加载参数后,触发该消息 + SHEET_MSG_SAVE_PARAM_GROUP //当前参数组被保存时,触发该消息 +}emSdkPropSheetMsg; + +//可视化选择参考窗口的类型 +typedef enum +{ + REF_WIN_AUTO_EXPOSURE = 0, + REF_WIN_WHITE_BALANCE, +}emSdkRefWinType; + +//可视化选择参考窗口的类型 +typedef enum +{ + RES_MODE_PREVIEW = 0, + RES_MODE_SNAPSHOT, +}emSdkResolutionMode; + +//白平衡时色温模式 +typedef enum +{ + CT_MODE_AUTO = 0, //自动识别色温 + CT_MODE_PRESET, //使用指定的预设色温 + CT_MODE_USER_DEF //自定义色温(增益和矩阵) +}emSdkClrTmpMode; + +//LUT的颜色通道 +typedef enum +{ + LUT_CHANNEL_ALL = 0,//R,B,G三通道同时调节 + LUT_CHANNEL_RED, //红色通道 + LUT_CHANNEL_GREEN, //绿色通道 + LUT_CHANNEL_BLUE, //蓝色通道 +}emSdkLutChannel; + +//ISP处理单元 +typedef enum +{ + ISP_PROCESSSOR_PC = 0,//使用PC的软件ISP模块 + ISP_PROCESSSOR_DEVICE //使用相机自带的硬件ISP模块 +}emSdkIspProcessor; + +//闪光灯信号控制方式 +typedef enum +{ + STROBE_SYNC_WITH_TRIG_AUTO = 0, //和触发信号同步,触发后,相机进行曝光时,自动生成STROBE信号。此时,有效极性可设置(CameraSetStrobePolarity)。 + STROBE_SYNC_WITH_TRIG_MANUAL, //和触发信号同步,触发后,STROBE延时指定的时间后(CameraSetStrobeDelayTime),再持续指定时间的脉冲(CameraSetStrobePulseWidth),有效极性可设置(CameraSetStrobePolarity)。 + STROBE_ALWAYS_HIGH, //始终为高,忽略STROBE信号的其他设置 + STROBE_ALWAYS_LOW //始终为低,忽略STROBE信号的其他设置 +}emStrobeControl; + +//硬件外触发的信号种类 +typedef enum +{ + EXT_TRIG_LEADING_EDGE = 0, //上升沿触发,默认为该方式 + EXT_TRIG_TRAILING_EDGE, //下降沿触发 + EXT_TRIG_HIGH_LEVEL, //高电平触发,电平宽度决定曝光时间,仅部分型号的相机支持电平触发方式。 + EXT_TRIG_LOW_LEVEL //低电平触发, +}emExtTrigSignal; + +//硬件外触发时的快门方式 +typedef enum +{ + EXT_TRIG_EXP_STANDARD = 0, //标准方式,默认为该方式。 + EXT_TRIG_EXP_GRR, //全局复位方式,部分滚动快门的CMOS型号的相机支持该方式,配合外部机械快门,可以达到全局快门的效果,适合拍高速运动的物体 +}emExtTrigShutterMode; + +/// @ingroup MV_ENUM_TYPE +/// \~chinese 清晰度评估算法 +/// \~english Sharpness assessment algorithm +typedef enum +{ + EVALUATE_DEFINITION_DEVIATION=0, ///< \~chinese 方差法 \~english Variance method + EVALUATE_DEFINITION_SMD=1, ///< \~chinese 相邻像素灰度方差法 \~english Adjacent Pixel Gray Difference Method + EVALUATE_DEFINITION_GRADIENT=2, ///< \~chinese 梯度统计 \~english Gradient statistics + EVALUATE_DEFINITION_SOBEL=3, ///< \~chinese Sobel \~english Sobel + EVALUATE_DEFINITION_ROBERT=4, ///< \~chinese Robert \~english Robert + EVALUATE_DEFINITION_LAPLACE=5, ///< \~chinese Laplace \~english Laplace + + EVALUATE_DEFINITION_ALG_MAX=6, ///< \~chinese 算法个数 \~english The number of algorithms +}emEvaluateDefinitionAlgorith; + +// GPIO模式 +typedef enum +{ + IOMODE_TRIG_INPUT=0, ///< \~chinese 触发输入 \~english Trigger input + IOMODE_STROBE_OUTPUT=1, ///< \~chinese 闪光灯输出 \~english Strobe output + IOMODE_GP_INPUT=2, ///< \~chinese 通用型输入 \~english Universal input + IOMODE_GP_OUTPUT=3, ///< \~chinese 通用型输出 \~english Universal output + IOMODE_PWM_OUTPUT=4, ///< \~chinese PWM型输出 \~english PWM output + IOMODE_ROTARYENC_INPUT=5, ///< \~chinese 编码器输入 \~english rotary input +}emCameraGPIOMode; + +/// @ingroup MV_ENUM_TYPE +/// \~chinese GPIO 格式 +/// \~english GPIO Format +typedef enum +{ + IOFORMAT_SINGLE=0, ///< \~chinese 单端 \~english single ended + IOFORMAT_RS422=1, ///< \~chinese 差分RS422 \~english Differential RS422 + IOFORMAT_RS422_TERM=2, ///< \~chinese 差分RS422带终端电阻 \~english Differential RS422 and Termination Enable + IOFORMAT_OCEP=3, ///< \~chinese 光耦 \~english opticalcoupler equipment +}emCameraGPIOFormat; + +/// @ingroup MV_ENUM_TYPE +/// \~chinese 取图优先级 +/// \~english Get Image priority +typedef enum +{ + CAMERA_GET_IMAGE_PRIORITY_OLDEST=0, ///< \~chinese 获取缓存中最旧的一帧 \~english Get the oldest frame in the cache + CAMERA_GET_IMAGE_PRIORITY_NEWEST=1, ///< \~chinese 获取缓存中最新的一帧(比此帧旧的将全部丢弃) \~english Get the latest frame in the cache (older than this frame will be discarded) + + /// \~chinese 丢弃缓存中的所有帧,并且如果此刻相机正在曝光或传输将会被立即打断,等待接收下一帧 + /// \note 某些型号的相机不支持此功能,对于不支持此功能的相机这个标志相当于@link #CAMERA_GET_IMAGE_PRIORITY_OLDEST @endlink + /// \~english All frames in the cache are discarded, and if the camera is now being exposed or transmitted it will be immediately interrupted, waiting to receive the next frame + /// \note Some models do not support this feature. For cameras that do not support this feature this flag is equivalent to @link #CAMERA_GET_IMAGE_PRIORITY_OLDEST @endlink + CAMERA_GET_IMAGE_PRIORITY_NEXT=2, +}emCameraGetImagePriority; + +/// @ingroup MV_ENUM_TYPE +/// \~chinese 软触发功能标志 +/// \~english Soft trigger function flag +typedef enum +{ + CAMERA_ST_CLEAR_BUFFER_BEFORE = 0x1, ///< \~chinese 在软触发之前先清空相机已缓存的帧 \~english Empty camera-cached frames before soft triggering +}emCameraSoftTriggerExFlags; + +//相机的设备信息 +typedef struct +{ + char acProductSeries[32]; // 产品系列 + char acProductName[32]; // 产品名称 + char acFriendlyName[32]; // 产品昵称,用户可自定义改昵称,保存在相机内,用于区分多个相机同时使用,可以用CameraSetFriendlyName接口改变该昵称,设备重启后生效。 + char acLinkName[32]; // 内核符号连接名,内部使用 + char acDriverVersion[32]; // 驱动版本 + char acSensorType[32]; // sensor类型 + char acPortType[32]; // 接口类型 + char acSn[32]; // 产品唯一序列号 + UINT uInstance; // 该型号相机在该电脑上的实例索引号,用于区分同型号多相机 +} tSdkCameraDevInfo; + +#define EXT_TRIG_MASK_GRR_SHUTTER 1 ///< \~chinese 快门支持GRR模式 \~english Shutter supports GRR mode +#define EXT_TRIG_MASK_LEVEL_MODE 2 ///< \~chinese 支持电平触发 \~english Support level trigger +#define EXT_TRIG_MASK_DOUBLE_EDGE 4 ///< \~chinese 支持双边沿触发 \~english Supports bilateral triggering +#define EXT_TRIG_MASK_BUFFERED_DELAY 8 ///< \~chinese 支持信号后延 \~english Supports signal delayed activation + +//tSdkResolutionRange结构体中SKIP、 BIN、RESAMPLE模式的掩码值 +#define MASK_2X2_HD (1<<0) //硬件SKIP、BIN、重采样 2X2 +#define MASK_3X3_HD (1<<1) +#define MASK_4X4_HD (1<<2) +#define MASK_5X5_HD (1<<3) +#define MASK_6X6_HD (1<<4) +#define MASK_7X7_HD (1<<5) +#define MASK_8X8_HD (1<<6) +#define MASK_9X9_HD (1<<7) +#define MASK_10X10_HD (1<<8) +#define MASK_11X11_HD (1<<9) +#define MASK_12X12_HD (1<<10) +#define MASK_13X13_HD (1<<11) +#define MASK_14X14_HD (1<<12) +#define MASK_15X15_HD (1<<13) +#define MASK_16X16_HD (1<<14) +#define MASK_17X17_HD (1<<15) +#define MASK_2X2_SW (1<<16) //硬件SKIP、BIN、重采样 2X2 +#define MASK_3X3_SW (1<<17) +#define MASK_4X4_SW (1<<18) +#define MASK_5X5_SW (1<<19) +#define MASK_6X6_SW (1<<20) +#define MASK_7X7_SW (1<<21) +#define MASK_8X8_SW (1<<22) +#define MASK_9X9_SW (1<<23) +#define MASK_10X10_SW (1<<24) +#define MASK_11X11_SW (1<<25) +#define MASK_12X12_SW (1<<26) +#define MASK_13X13_SW (1<<27) +#define MASK_14X14_SW (1<<28) +#define MASK_15X15_SW (1<<29) +#define MASK_16X16_SW (1<<30) +#define MASK_17X17_SW (1<<31) + +//相机的分辨率设定范围,用于构件UI +typedef struct +{ + INT iHeightMax; //图像最大高度 + INT iHeightMin; //图像最小高度 + INT iWidthMax; //图像最大宽度 + INT iWidthMin; //图像最小宽度 + UINT uSkipModeMask; //SKIP模式掩码,为0,表示不支持SKIP 。bit0为1,表示支持SKIP 2x2 ;bit1为1,表示支持SKIP 3x3.... + UINT uBinSumModeMask; //BIN(求和)模式掩码,为0,表示不支持BIN 。bit0为1,表示支持BIN 2x2 ;bit1为1,表示支持BIN 3x3.... + UINT uBinAverageModeMask; //BIN(求均值)模式掩码,为0,表示不支持BIN 。bit0为1,表示支持BIN 2x2 ;bit1为1,表示支持BIN 3x3.... + UINT uResampleMask; //硬件重采样的掩码 +} tSdkResolutionRange; + + +//相机的分辨率描述 +typedef struct +{ + INT iIndex; // 索引号,[0,N]表示预设的分辨率(N 为预设分辨率的最大个数,一般不超过20),OXFF 表示自定义分辨率(ROI) + char acDescription[32]; // 该分辨率的描述信息。仅预设分辨率时该信息有效。自定义分辨率可忽略该信息 + UINT uBinSumMode; // BIN(求和)的模式,范围不能超过tSdkResolutionRange中uBinSumModeMask + UINT uBinAverageMode; // BIN(求均值)的模式,范围不能超过tSdkResolutionRange中uBinAverageModeMask + UINT uSkipMode; // 是否SKIP的尺寸,为0表示禁止SKIP模式,范围不能超过tSdkResolutionRange中uSkipModeMask + UINT uResampleMask; // 硬件重采样的掩码 + INT iHOffsetFOV; // 采集视场相对于Sensor最大视场左上角的垂直偏移 + INT iVOffsetFOV; // 采集视场相对于Sensor最大视场左上角的水平偏移 + INT iWidthFOV; // 采集视场的宽度 + INT iHeightFOV; // 采集视场的高度 + INT iWidth; // 相机最终输出的图像的宽度 + INT iHeight; // 相机最终输出的图像的高度 + INT iWidthZoomHd; // 硬件缩放的宽度,不需要进行此操作的分辨率,此变量设置为0. + INT iHeightZoomHd; // 硬件缩放的高度,不需要进行此操作的分辨率,此变量设置为0. + INT iWidthZoomSw; // 软件缩放的宽度,不需要进行此操作的分辨率,此变量设置为0. + INT iHeightZoomSw; // 软件缩放的高度,不需要进行此操作的分辨率,此变量设置为0. +} tSdkImageResolution; + +//相机白平衡色温模式描述信息 +typedef struct +{ + INT iIndex; // 模式索引号 + char acDescription[32]; // 描述信息 +} tSdkColorTemperatureDes; + +//相机帧率描述信息 +typedef struct +{ + INT iIndex; // 帧率索引号,一般0对应于低速模式,1对应于普通模式,2对应于高速模式 + char acDescription[32]; // 描述信息 +} tSdkFrameSpeed; + +//相机曝光功能范围定义 +typedef struct +{ + UINT uiTargetMin; //自动曝光亮度目标最小值 + UINT uiTargetMax; //自动曝光亮度目标最大值 + UINT uiAnalogGainMin; //模拟增益的最小值,单位为fAnalogGainStep中定义 + UINT uiAnalogGainMax; //模拟增益的最大值,单位为fAnalogGainStep中定义 + float fAnalogGainStep; //模拟增益每增加1,对应的增加的放大倍数。例如,uiAnalogGainMin一般为16,fAnalogGainStep一般为0.125,那么最小放大倍数就是16*0.125 = 2倍 + UINT uiExposeTimeMin; //手动模式下,曝光时间的最小值,单位:行。根据CameraGetExposureLineTime可以获得一行对应的时间(微秒),从而得到整帧的曝光时间 + UINT uiExposeTimeMax; //手动模式下,曝光时间的最大值,单位:行 +} tSdkExpose; + +//触发模式描述 +typedef struct +{ + INT iIndex; //模式索引号 + char acDescription[32]; //该模式的描述信息 +} tSdkTrigger; + +//传输分包大小描述(主要是针对网络相机有效) +typedef struct +{ + INT iIndex; //分包大小索引号 + char acDescription[32]; //对应的描述信息 + UINT iPackSize; +} tSdkPackLength; + +//预设的LUT表描述 +typedef struct +{ + INT iIndex; //编号 + char acDescription[32]; //描述信息 +} tSdkPresetLut; + +//AE算法描述 +typedef struct +{ + INT iIndex; //编号 + char acDescription[32]; //描述信息 +} tSdkAeAlgorithm; + +//RAW转RGB算法描述 +typedef struct +{ + INT iIndex; //编号 + char acDescription[32]; //描述信息 +} tSdkBayerDecodeAlgorithm; + + +//帧率统计信息 +typedef struct +{ + INT iTotal; //当前采集的总帧数(包括错误帧) + INT iCapture; //当前采集的有效帧的数量 + INT iLost; //当前丢帧的数量 +} tSdkFrameStatistic; + +//相机输出的图像数据格式 +typedef struct +{ + INT iIndex; //格式种类编号 + char acDescription[32]; //描述信息 + UINT iMediaType; //对应的图像格式编码,如CAMERA_MEDIA_TYPE_BAYGR8,在本文件中有定义。 +} tSdkMediaType; + +//伽马的设定范围 +typedef struct +{ + INT iMin; //最小值 + INT iMax; //最大值 +} tGammaRange; + +//对比度的设定范围 +typedef struct +{ + INT iMin; //最小值 + INT iMax; //最大值 +} tContrastRange; + +//RGB三通道数字增益的设定范围 +typedef struct +{ + INT iRGainMin; //红色增益的最小值 + INT iRGainMax; //红色增益的最大值 + INT iGGainMin; //绿色增益的最小值 + INT iGGainMax; //绿色增益的最大值 + INT iBGainMin; //蓝色增益的最小值 + INT iBGainMax; //蓝色增益的最大值 +} tRgbGainRange; + +//饱和度设定的范围 +typedef struct +{ + INT iMin; //最小值 + INT iMax; //最大值 +} tSaturationRange; + +//锐化的设定范围 +typedef struct +{ + INT iMin; //最小值 + INT iMax; //最大值 +} tSharpnessRange; + +//ISP模块的使能信息 +typedef struct +{ + BOOL bMonoSensor; //表示该型号相机是否为黑白相机,如果是黑白相机,则颜色相关的功能都无法调节 + BOOL bWbOnce; //表示该型号相机是否支持手动白平衡功能 + BOOL bAutoWb; //表示该型号相机是否支持自动白平衡功能 + BOOL bAutoExposure; //表示该型号相机是否支持自动曝光功能 + BOOL bManualExposure; //表示该型号相机是否支持手动曝光功能 + BOOL bAntiFlick; //表示该型号相机是否支持抗频闪功能 + BOOL bDeviceIsp; //表示该型号相机是否支持硬件ISP功能 + BOOL bForceUseDeviceIsp;//bDeviceIsp和bForceUseDeviceIsp同时为TRUE时,表示强制只用硬件ISP,不可取消。 + BOOL bZoomHD; //相机硬件是否支持图像缩放输出(只能是缩小)。 +} tSdkIspCapacity; + +/* 定义整合的设备描述信息,这些信息可以用于动态构建UI */ +typedef struct +{ + + tSdkTrigger *pTriggerDesc; // 触发模式 + INT iTriggerDesc; // 触发模式的个数,即pTriggerDesc数组的大小 + + tSdkImageResolution *pImageSizeDesc;// 预设分辨率选择 + INT iImageSizeDesc; // 预设分辨率的个数,即pImageSizeDesc数组的大小 + + tSdkColorTemperatureDes *pClrTempDesc;// 预设色温模式,用于白平衡 + INT iClrTempDesc; + + tSdkMediaType *pMediaTypeDesc; // 相机输出图像格式 + INT iMediaTypdeDesc; // 相机输出图像格式的种类个数,即pMediaTypeDesc数组的大小。 + + tSdkFrameSpeed *pFrameSpeedDesc; // 可调节帧速类型,对应界面上普通 高速 和超级三种速度设置 + INT iFrameSpeedDesc; // 可调节帧速类型的个数,即pFrameSpeedDesc数组的大小。 + + tSdkPackLength *pPackLenDesc; // 传输包长度,一般用于网络设备 + INT iPackLenDesc; // 可供选择的传输分包长度的个数,即pPackLenDesc数组的大小。 + + INT iOutputIoCounts; // 可编程输出IO的个数 + INT iInputIoCounts; // 可编程输入IO的个数 + + tSdkPresetLut *pPresetLutDesc; // 相机预设的LUT表 + INT iPresetLut; // 相机预设的LUT表的个数,即pPresetLutDesc数组的大小 + + INT iUserDataMaxLen; // 指示该相机中用于保存用户数据区的最大长度。为0表示无。 + BOOL bParamInDevice; // 指示该设备是否支持从设备中读写参数组。1为支持,0不支持。 + + tSdkAeAlgorithm *pAeAlmSwDesc; // 软件自动曝光算法描述 + int iAeAlmSwDesc; // 软件自动曝光算法个数 + + tSdkAeAlgorithm *pAeAlmHdDesc; // 硬件自动曝光算法描述,为NULL表示不支持硬件自动曝光 + int iAeAlmHdDesc; // 硬件自动曝光算法个数,为0表示不支持硬件自动曝光 + + tSdkBayerDecodeAlgorithm *pBayerDecAlmSwDesc; // 软件Bayer转换为RGB数据的算法描述 + int iBayerDecAlmSwDesc; // 软件Bayer转换为RGB数据的算法个数 + + tSdkBayerDecodeAlgorithm *pBayerDecAlmHdDesc; // 硬件Bayer转换为RGB数据的算法描述,为NULL表示不支持 + int iBayerDecAlmHdDesc; // 硬件Bayer转换为RGB数据的算法个数,为0表示不支持 + + /* 图像参数的调节范围定义,用于动态构建UI*/ + tSdkExpose sExposeDesc; // 曝光的范围值 + tSdkResolutionRange sResolutionRange; // 分辨率范围描述 + tRgbGainRange sRgbGainRange; // 图像数字增益范围描述 + tSaturationRange sSaturationRange; // 饱和度范围描述 + tGammaRange sGammaRange; // 伽马范围描述 + tContrastRange sContrastRange; // 对比度范围描述 + tSharpnessRange sSharpnessRange; // 锐化范围描述 + tSdkIspCapacity sIspCapacity; // ISP能力描述 + + +} tSdkCameraCapbility; + + +//图像帧头信息 +typedef struct +{ + UINT uiMediaType; // 图像格式,Image Format + UINT uBytes; // 图像数据字节数,Total bytes + INT iWidth; // 图像的宽度,调用图像处理函数后,该变量可能被动态修改,来指示处理后的图像尺寸 + INT iHeight; // 图像的高度,调用图像处理函数后,该变量可能被动态修改,来指示处理后的图像尺寸 + INT iWidthZoomSw; // 软件缩放的宽度,不需要进行软件裁剪的图像,此变量设置为0. + INT iHeightZoomSw; // 软件缩放的高度,不需要进行软件裁剪的图像,此变量设置为0. + BOOL bIsTrigger; // 指示是否为触发帧 is trigger + UINT uiTimeStamp; // 该帧的采集时间,单位0.1毫秒 + UINT uiExpTime; // 当前图像的曝光值,单位为微秒us + float fAnalogGain; // 当前图像的模拟增益倍数 + INT iGamma; // 该帧图像的伽马设定值,仅当LUT模式为动态参数生成时有效,其余模式下为-1 + INT iContrast; // 该帧图像的对比度设定值,仅当LUT模式为动态参数生成时有效,其余模式下为-1 + INT iSaturation; // 该帧图像的饱和度设定值,对于黑白相机无意义,为0 + float fRgain; // 该帧图像处理的红色数字增益倍数,对于黑白相机无意义,为1 + float fGgain; // 该帧图像处理的绿色数字增益倍数,对于黑白相机无意义,为1 + float fBgain; // 该帧图像处理的蓝色数字增益倍数,对于黑白相机无意义,为1 +}tSdkFrameHead; + +//图像帧描述 +typedef struct sCameraFrame +{ + tSdkFrameHead head; //帧头 + BYTE * pBuffer; //数据区 +}tSdkFrame; + +/// \~chinese 帧事件 +/// \~english Frame Event +typedef struct tSdkFrameEvent_ +{ + UINT uType; ///< \~chinese 事件类型(1:帧开始 2:帧结束) \~english Event type (1:frame start 2:frame end) + UINT uStatus; ///< \~chinese 状态(0:成功 非0:错误) \~english Status (0:success, non-zero:error) + UINT uFrameID; ///< \~chinese 帧ID \~english Frame ID + UINT uWidth; ///< \~chinese 宽度 \~english Width + UINT uHeight; ///< \~chinese 高度 \~english Height + UINT uPixelFormat; ///< \~chinese 图像格式 \~english Image Format + UINT TimeStampL; ///< \~chinese 时间戳低32位 \~english Lower 32 bits of timestamp + UINT TimeStampH; ///< \~chinese 时间戳高32位 \~english High 32 bits of timestamp +}tSdkFrameEvent; + +//图像捕获的回调函数定义 +typedef void (*CAMERA_SNAP_PROC)(CameraHandle hCamera, BYTE *pFrameBuffer, tSdkFrameHead* pFrameHead,PVOID pContext); + +//SDK生成的相机配置页面的消息回调函数定义 +typedef void (*CAMERA_PAGE_MSG_PROC)(CameraHandle hCamera,UINT MSG,UINT uParam,PVOID pContext); + +/// @ingroup API_RECONNECT +/// \~chinese 相机连接状态回调 +/// \param [in] hCamera 相机句柄 +/// \param [in] MSG 消息,0: 相机连接断开 1: 相机连接恢复 +/// \param [in] uParam 附加信息 +/// \param [in] pContext 用户数据 +/// \return 无 +/// \note USB相机uParam取值: +/// \note 未定义 +/// \note 网口相机uParam取值: +/// \note 当MSG=0时:未定义 +/// \note 当MSG=1时: +/// \note 0:上次掉线原因,网络通讯失败 +/// \note 1:上次掉线原因,相机掉电 +/// \~english Camera connection status callback +/// \param [in] hCamera Camera handle +/// \param [in] MSG message, 0: Camera disconnected 1: Camera connection restored +/// \param [in] uParam Additional Information +/// \param [in] pContext user data +/// \return None +/// \note USB camera uParam value: +/// \note Undefined +/// \note network camera uParam value: +/// \note When MSG=0: Undefined +/// \note When MSG=1: +/// \note 0: The last dropped reason, network communication failed +/// \note 1: The last dropped reason, the camera lost power +typedef void (*CAMERA_CONNECTION_STATUS_CALLBACK)(CameraHandle hCamera,UINT MSG,UINT uParam,PVOID pContext); + +/// @ingroup API_ADVANCE +/// \~chinese 帧事件回调函数定义 +/// \~english Callback function definition for frame event +typedef void (*CAMERA_FRAME_EVENT_CALLBACK)(CameraHandle hCamera, tSdkFrameEvent* pEvent, PVOID pContext); + + +//----------------------------IMAGE FORMAT DEFINE------------------------------------ +//----------------------------图像格式定义------------------------------------------- +#define CAMERA_MEDIA_TYPE_MONO 0x01000000 +#define CAMERA_MEDIA_TYPE_RGB 0x02000000 +#define CAMERA_MEDIA_TYPE_COLOR 0x02000000 +#define CAMERA_MEDIA_TYPE_CUSTOM 0x80000000 +#define CAMERA_MEDIA_TYPE_COLOR_MASK 0xFF000000 +#define CAMERA_MEDIA_TYPE_OCCUPY1BIT 0x00010000 +#define CAMERA_MEDIA_TYPE_OCCUPY2BIT 0x00020000 +#define CAMERA_MEDIA_TYPE_OCCUPY4BIT 0x00040000 +#define CAMERA_MEDIA_TYPE_OCCUPY8BIT 0x00080000 +#define CAMERA_MEDIA_TYPE_OCCUPY10BIT 0x000A0000 +#define CAMERA_MEDIA_TYPE_OCCUPY12BIT 0x000C0000 +#define CAMERA_MEDIA_TYPE_OCCUPY16BIT 0x00100000 +#define CAMERA_MEDIA_TYPE_OCCUPY24BIT 0x00180000 +#define CAMERA_MEDIA_TYPE_OCCUPY32BIT 0x00200000 +#define CAMERA_MEDIA_TYPE_OCCUPY36BIT 0x00240000 +#define CAMERA_MEDIA_TYPE_OCCUPY48BIT 0x00300000 +#define CAMERA_MEDIA_TYPE_OCCUPY64BIT 0x00400000 + +#define CAMERA_MEDIA_TYPE_EFFECTIVE_PIXEL_SIZE_MASK 0x00FF0000 +#define CAMERA_MEDIA_TYPE_EFFECTIVE_PIXEL_SIZE_SHIFT 16 + +#define CAMERA_MEDIA_TYPE_PIXEL_SIZE(type) (((type) & CAMERA_MEDIA_TYPE_EFFECTIVE_PIXEL_SIZE_MASK)>>CAMERA_MEDIA_TYPE_EFFECTIVE_PIXEL_SIZE_SHIFT) + +#define CAMERA_MEDIA_TYPE_ID_MASK 0x0000FFFF +#define CAMERA_MEDIA_TYPE_COUNT 0x46 + +/*mono*/ +#define CAMERA_MEDIA_TYPE_MONO1P (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY1BIT | 0x0037) +#define CAMERA_MEDIA_TYPE_MONO2P (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY2BIT | 0x0038) +#define CAMERA_MEDIA_TYPE_MONO4P (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY4BIT | 0x0039) +#define CAMERA_MEDIA_TYPE_MONO8 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0001) +#define CAMERA_MEDIA_TYPE_MONO8S (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0002) +#define CAMERA_MEDIA_TYPE_MONO10 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0003) +#define CAMERA_MEDIA_TYPE_MONO10_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0004) +#define CAMERA_MEDIA_TYPE_MONO12 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0005) +#define CAMERA_MEDIA_TYPE_MONO12_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0006) +#define CAMERA_MEDIA_TYPE_MONO14 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0025) +#define CAMERA_MEDIA_TYPE_MONO16 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0007) + +/*Bayer */ +#define CAMERA_MEDIA_TYPE_BAYGR8 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0008) +#define CAMERA_MEDIA_TYPE_BAYRG8 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0009) +#define CAMERA_MEDIA_TYPE_BAYGB8 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x000A) +#define CAMERA_MEDIA_TYPE_BAYBG8 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x000B) + +#define CAMERA_MEDIA_TYPE_BAYGR10_MIPI (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY10BIT | 0x0026) +#define CAMERA_MEDIA_TYPE_BAYRG10_MIPI (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY10BIT | 0x0027) +#define CAMERA_MEDIA_TYPE_BAYGB10_MIPI (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY10BIT | 0x0028) +#define CAMERA_MEDIA_TYPE_BAYBG10_MIPI (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY10BIT | 0x0029) + + +#define CAMERA_MEDIA_TYPE_BAYGR10 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x000C) +#define CAMERA_MEDIA_TYPE_BAYRG10 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x000D) +#define CAMERA_MEDIA_TYPE_BAYGB10 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x000E) +#define CAMERA_MEDIA_TYPE_BAYBG10 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x000F) + +#define CAMERA_MEDIA_TYPE_BAYGR12 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0010) +#define CAMERA_MEDIA_TYPE_BAYRG12 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0011) +#define CAMERA_MEDIA_TYPE_BAYGB12 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0012) +#define CAMERA_MEDIA_TYPE_BAYBG12 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0013) + + +#define CAMERA_MEDIA_TYPE_BAYGR10_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0026) +#define CAMERA_MEDIA_TYPE_BAYRG10_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0027) +#define CAMERA_MEDIA_TYPE_BAYGB10_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0028) +#define CAMERA_MEDIA_TYPE_BAYBG10_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0029) + +#define CAMERA_MEDIA_TYPE_BAYGR12_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x002A) +#define CAMERA_MEDIA_TYPE_BAYRG12_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x002B) +#define CAMERA_MEDIA_TYPE_BAYGB12_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x002C) +#define CAMERA_MEDIA_TYPE_BAYBG12_PACKED (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x002D) + +#define CAMERA_MEDIA_TYPE_BAYGR16 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x002E) +#define CAMERA_MEDIA_TYPE_BAYRG16 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x002F) +#define CAMERA_MEDIA_TYPE_BAYGB16 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0030) +#define CAMERA_MEDIA_TYPE_BAYBG16 (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0031) + +/*RGB */ +#define CAMERA_MEDIA_TYPE_RGB8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x0014) +#define CAMERA_MEDIA_TYPE_BGR8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x0015) +#define CAMERA_MEDIA_TYPE_RGBA8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY32BIT | 0x0016) +#define CAMERA_MEDIA_TYPE_BGRA8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY32BIT | 0x0017) +#define CAMERA_MEDIA_TYPE_RGB10 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x0018) +#define CAMERA_MEDIA_TYPE_BGR10 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x0019) +#define CAMERA_MEDIA_TYPE_RGB12 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x001A) +#define CAMERA_MEDIA_TYPE_BGR12 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x001B) +#define CAMERA_MEDIA_TYPE_RGB16 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x0033) +#define CAMERA_MEDIA_TYPE_BGR16 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x004B) +#define CAMERA_MEDIA_TYPE_RGBA16 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY64BIT | 0x0064) +#define CAMERA_MEDIA_TYPE_BGRA16 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY64BIT | 0x0051) +#define CAMERA_MEDIA_TYPE_RGB10V1_PACKED (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY32BIT | 0x001C) +#define CAMERA_MEDIA_TYPE_RGB10P32 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY32BIT | 0x001D) +#define CAMERA_MEDIA_TYPE_RGB12V1_PACKED (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY36BIT | 0X0034) +#define CAMERA_MEDIA_TYPE_RGB565P (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0035) +#define CAMERA_MEDIA_TYPE_BGR565P (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0X0036) + +/*YUV and YCbCr*/ +#define CAMERA_MEDIA_TYPE_YUV411_8_UYYVYY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x001E) +#define CAMERA_MEDIA_TYPE_YUV422_8_UYVY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x001F) +#define CAMERA_MEDIA_TYPE_YUV422_8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0032) +#define CAMERA_MEDIA_TYPE_YUV8_UYV (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x0020) +#define CAMERA_MEDIA_TYPE_YCBCR8_CBYCR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x003A) +//CAMERA_MEDIA_TYPE_YCBCR422_8 : YYYYCbCrCbCr +#define CAMERA_MEDIA_TYPE_YCBCR422_8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x003B) +#define CAMERA_MEDIA_TYPE_YCBCR422_8_CBYCRY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0043) +#define CAMERA_MEDIA_TYPE_YCBCR411_8_CBYYCRYY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x003C) +#define CAMERA_MEDIA_TYPE_YCBCR601_8_CBYCR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x003D) +#define CAMERA_MEDIA_TYPE_YCBCR601_422_8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x003E) +#define CAMERA_MEDIA_TYPE_YCBCR601_422_8_CBYCRY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0044) +#define CAMERA_MEDIA_TYPE_YCBCR601_411_8_CBYYCRYY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x003F) +#define CAMERA_MEDIA_TYPE_YCBCR709_8_CBYCR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x0040) +#define CAMERA_MEDIA_TYPE_YCBCR709_422_8 (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0041) +#define CAMERA_MEDIA_TYPE_YCBCR709_422_8_CBYCRY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY16BIT | 0x0045) +#define CAMERA_MEDIA_TYPE_YCBCR709_411_8_CBYYCRYY (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0042) + +/*RGB Planar */ +#define CAMERA_MEDIA_TYPE_RGB8_PLANAR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY24BIT | 0x0021) +#define CAMERA_MEDIA_TYPE_RGB10_PLANAR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x0022) +#define CAMERA_MEDIA_TYPE_RGB12_PLANAR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x0023) +#define CAMERA_MEDIA_TYPE_RGB16_PLANAR (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY48BIT | 0x0024) + + + +/*MindVision 12bit packed bayer*/ +#define CAMERA_MEDIA_TYPE_BAYGR12_PACKED_MV (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0060) +#define CAMERA_MEDIA_TYPE_BAYRG12_PACKED_MV (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0061) +#define CAMERA_MEDIA_TYPE_BAYGB12_PACKED_MV (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0062) +#define CAMERA_MEDIA_TYPE_BAYBG12_PACKED_MV (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0063) + +/*MindVision 12bit packed monochome*/ +#define CAMERA_MEDIA_TYPE_MONO12_PACKED_MV (CAMERA_MEDIA_TYPE_MONO | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0064) +#define CAMERA_MEDIA_TYPE_YUV420P_MV (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0065) + +/*planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte V and the following byte U)*/ +#define CAMERA_MEDIA_TYPE_YUV_NV21_MV (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY12BIT | 0x0066) + +/* H264 H265 */ +#define CAMERA_MEDIA_TYPE_H264_MV (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0067) +#define CAMERA_MEDIA_TYPE_H265_MV (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0068) + +/* JPEG */ +#define CAMERA_MEDIA_TYPE_JPEG_MV (CAMERA_MEDIA_TYPE_COLOR | CAMERA_MEDIA_TYPE_OCCUPY8BIT | 0x0069) + +#endif diff --git a/io/mindvision/include/CameraStatus.h b/io/mindvision/include/CameraStatus.h new file mode 100644 index 0000000..e144809 --- /dev/null +++ b/io/mindvision/include/CameraStatus.h @@ -0,0 +1,118 @@ + +#ifndef __CAMERA_STATUS_DEF__ +#define __CAMERA_STATUS_DEF__ + +typedef int CameraSdkStatus; + +/*常用的宏*/ +#define SDK_SUCCESS(_FUC_) ((_FUC_) == CAMERA_STATUS_SUCCESS) + +#define SDK_UNSUCCESS(_FUC_) ((_FUC_) != CAMERA_STATUS_SUCCESS) + +#define SDK_UNSUCCESS_RETURN(_FUC_, RET) \ + if ((RET = (_FUC_)) != CAMERA_STATUS_SUCCESS) { \ + return RET; \ + } + +#define SDK_UNSUCCESS_BREAK(_FUC_) \ + if ((_FUC_) != CAMERA_STATUS_SUCCESS) { \ + break; \ + } + +/* 常用错误 */ + +#define CAMERA_STATUS_SUCCESS 0 // 操作成功 +#define CAMERA_STATUS_FAILED -1 // 操作失败 +#define CAMERA_STATUS_INTERNAL_ERROR -2 // 内部错误 +#define CAMERA_STATUS_UNKNOW -3 // 未知错误 +#define CAMERA_STATUS_NOT_SUPPORTED -4 // 不支持该功能 +#define CAMERA_STATUS_NOT_INITIALIZED -5 // 初始化未完成 +#define CAMERA_STATUS_PARAMETER_INVALID -6 // 参数无效 +#define CAMERA_STATUS_PARAMETER_OUT_OF_BOUND -7 // 参数越界 +#define CAMERA_STATUS_UNENABLED -8 // 未使能 +#define CAMERA_STATUS_USER_CANCEL -9 // 用户手动取消了,比如roi面板点击取消,返回 +#define CAMERA_STATUS_PATH_NOT_FOUND -10 // 注册表中没有找到对应的路径 +#define CAMERA_STATUS_SIZE_DISMATCH -11 // 获得图像数据长度和定义的尺寸不匹配 +#define CAMERA_STATUS_TIME_OUT -12 // 超时错误 +#define CAMERA_STATUS_IO_ERROR -13 // 硬件IO错误 +#define CAMERA_STATUS_COMM_ERROR -14 // 通讯错误 +#define CAMERA_STATUS_BUS_ERROR -15 // 总线错误 +#define CAMERA_STATUS_NO_DEVICE_FOUND -16 // 没有发现设备 +#define CAMERA_STATUS_NO_LOGIC_DEVICE_FOUND -17 // 未找到逻辑设备 +#define CAMERA_STATUS_DEVICE_IS_OPENED -18 // 设备已经打开 +#define CAMERA_STATUS_DEVICE_IS_CLOSED -19 // 设备已经关闭 +#define CAMERA_STATUS_DEVICE_VEDIO_CLOSED \ + -20 // 没有打开设备视频,调用录像相关的函数时,如果相机视频没有打开,则回返回该错误。 +#define CAMERA_STATUS_NO_MEMORY -21 // 没有足够系统内存 +#define CAMERA_STATUS_FILE_CREATE_FAILED -22 // 创建文件失败 +#define CAMERA_STATUS_FILE_INVALID -23 // 文件格式无效 +#define CAMERA_STATUS_WRITE_PROTECTED -24 // 写保护,不可写 +#define CAMERA_STATUS_GRAB_FAILED -25 // 数据采集失败 +#define CAMERA_STATUS_LOST_DATA -26 // 数据丢失,不完整 +#define CAMERA_STATUS_EOF_ERROR -27 // 未接收到帧结束符 +#define CAMERA_STATUS_BUSY -28 // 正忙(上一次操作还在进行中),此次操作不能进行 +#define CAMERA_STATUS_WAIT -29 // 需要等待(进行操作的条件不成立),可以再次尝试trf +#define CAMERA_STATUS_IN_PROCESS -30 // 正在进行,已经被操作过 +#define CAMERA_STATUS_IIC_ERROR -31 // IIC传输错误 +#define CAMERA_STATUS_SPI_ERROR -32 // SPI传输错误 +#define CAMERA_STATUS_USB_CONTROL_ERROR -33 // USB控制传输错误 +#define CAMERA_STATUS_USB_BULK_ERROR -34 // USB BULK传输错误 +#define CAMERA_STATUS_SOCKET_INIT_ERROR -35 // 网络传输套件初始化失败 +#define CAMERA_STATUS_GIGE_FILTER_INIT_ERROR \ + -36 // 网络相机内核过滤驱动初始化失败,请检查是否正确安装了驱动,或者重新安装。 +#define CAMERA_STATUS_NET_SEND_ERROR -37 // 网络数据发送错误 +#define CAMERA_STATUS_DEVICE_LOST -38 // 与网络相机失去连接,心跳检测超时 +#define CAMERA_STATUS_DATA_RECV_LESS -39 // 接收到的字节数比请求的少 +#define CAMERA_STATUS_FUNCTION_LOAD_FAILED -40 // 从文件中加载程序失败 +#define CAMERA_STATUS_CRITICAL_FILE_LOST -41 // 程序运行所必须的文件丢失。 +#define CAMERA_STATUS_SENSOR_ID_DISMATCH -42 // 固件和程序不匹配,原因是下载了错误的固件。 +#define CAMERA_STATUS_OUT_OF_RANGE -43 // 参数超出有效范围。 +#define CAMERA_STATUS_REGISTRY_ERROR \ + -44 // 安装程序注册错误。请重新安装程序,或者运行安装目录Setup/Installer.exe +#define CAMERA_STATUS_ACCESS_DENY \ + -45 // 禁止访问。指定相机已经被其他程序占用时,再申请访问该相机,会返回该状态。(一个相机不能被多个程序同时访问) +#define CAMERA_STATUS_CAMERA_NEED_RESET \ + -46 // 表示相机需要复位后才能正常使用,此时请让相机断电重启,或者重启操作系统后,便可正常使用。 +#define CAMERA_STATUS_ISP_MOUDLE_NOT_INITIALIZED -47 // ISP模块未初始化 +#define CAMERA_STATUS_ISP_DATA_CRC_ERROR -48 // 数据校验错误 +#define CAMERA_STATUS_MV_TEST_FAILED -49 // 数据测试失败 +#define CAMERA_STATUS_INTERNAL_ERR1 -50 // 内部错误1 +#define CAMERA_STATUS_U3V_NO_CONTROL_EP -51 // U3V控制端点未找到 +#define CAMERA_STATUS_U3V_CONTROL_ERROR -52 // U3V控制通讯错误 +#define CAMERA_STATUS_INVALID_FRIENDLY_NAME \ + -53 ///< \~chinese 无效的设备名,名字里不能包含以下字符(\/:*?"<>|") \~english Invalid device name, the name cannot contain the following characters (\/:*?"<>|") +#define CAMERA_STATUS_FORMAT_ERROR -54 ///< \~chinese 格式错误 \~english Format error +#define CAMERA_STATUS_PCIE_OPEN_ERROR \ + -55 ///< \~chinese PCIE设备打开失败 \~english PCIE device open failed +#define CAMERA_STATUS_PCIE_COMM_ERROR \ + -56 ///< \~chinese PCIE设备通讯失败 \~english PCIE device communication failed +#define CAMERA_STATUS_PCIE_DDR_ERROR -57 ///< \~chinese PCIE DDR错误 \~english PCIE DDR error + +//和AIA制定的标准相同 +/*#define CAMERA_AIA_SUCCESS 0x0000 */ +#define CAMERA_AIA_PACKET_RESEND 0x0100 //该帧需要重传 +#define CAMERA_AIA_NOT_IMPLEMENTED 0x8001 //设备不支持的命令 +#define CAMERA_AIA_INVALID_PARAMETER 0x8002 //命令参数非法 +#define CAMERA_AIA_INVALID_ADDRESS 0x8003 //不可访问的地址 +#define CAMERA_AIA_WRITE_PROTECT 0x8004 //访问的对象不可写 +#define CAMERA_AIA_BAD_ALIGNMENT 0x8005 //访问的地址没有按照要求对齐 +#define CAMERA_AIA_ACCESS_DENIED 0x8006 //没有访问权限 +#define CAMERA_AIA_BUSY 0x8007 //命令正在处理中 +#define CAMERA_AIA_DEPRECATED 0x8008 //0x8008-0x0800B 0x800F 该指令已经废弃 +#define CAMERA_AIA_PACKET_UNAVAILABLE 0x800C //包无效 +#define CAMERA_AIA_DATA_OVERRUN 0x800D //数据溢出,通常是收到的数据比需要的多 +#define CAMERA_AIA_INVALID_HEADER 0x800E //数据包头部中某些区域与协议不匹配 +#define CAMERA_AIA_PACKET_NOT_YET_AVAILABLE \ + 0x8010 //图像分包数据还未准备好,多用于触发模式,应用程序访问超时 +#define CAMERA_AIA_PACKET_AND_PREV_REMOVED_FROM_MEMORY \ + 0x8011 //需要访问的分包已经不存在。多用于重传时数据已经不在缓冲区中 +#define CAMERA_AIA_PACKET_REMOVED_FROM_MEMORY \ + 0x8012 //CAMERA_AIA_PACKET_AND_PREV_REMOVED_FROM_MEMORY +#define CAMERA_AIA_NO_REF_TIME 0x0813 //没有参考时钟源。多用于时间同步的命令执行时 +#define CAMERA_AIA_PACKET_TEMPORARILY_UNAVAILABLE \ + 0x0814 //由于信道带宽问题,当前分包暂时不可用,需稍后进行访问 +#define CAMERA_AIA_OVERFLOW 0x0815 //设备端数据溢出,通常是队列已满 +#define CAMERA_AIA_ACTION_LATE 0x0816 //命令执行已经超过有效的指定时间 +#define CAMERA_AIA_ERROR 0x8FFF //错误 + +#endif diff --git a/io/mindvision/lib/amd64/libMVSDK.so b/io/mindvision/lib/amd64/libMVSDK.so new file mode 100644 index 0000000..d1ff951 Binary files /dev/null and b/io/mindvision/lib/amd64/libMVSDK.so differ diff --git a/io/mindvision/lib/arm64/libMVSDK.so b/io/mindvision/lib/arm64/libMVSDK.so new file mode 100644 index 0000000..9b7ca05 Binary files /dev/null and b/io/mindvision/lib/arm64/libMVSDK.so differ diff --git a/io/mindvision/mindvision.cpp b/io/mindvision/mindvision.cpp new file mode 100644 index 0000000..0b6df3b --- /dev/null +++ b/io/mindvision/mindvision.cpp @@ -0,0 +1,171 @@ +#include "mindvision.hpp" + +#include + +#include + +#include "tools/logger.hpp" + +using namespace std::chrono_literals; + +namespace io +{ +MindVision::MindVision(double exposure_ms, double gamma, const std::string & vid_pid) +: exposure_ms_(exposure_ms), + gamma_(gamma), + handle_(-1), + quit_(false), + ok_(false), + queue_(1), + vid_(-1), + pid_(-1) +{ + set_vid_pid(vid_pid); + if (libusb_init(NULL)) tools::logger()->warn("Unable to init libusb!"); + + try_open(); + + // 守护线程 + daemon_thread_ = std::thread{[this] { + while (!quit_) { + std::this_thread::sleep_for(100ms); + + if (ok_) continue; + + if (capture_thread_.joinable()) capture_thread_.join(); + + close(); + reset_usb(); + try_open(); + } + }}; +} + +MindVision::~MindVision() +{ + quit_ = true; + if (daemon_thread_.joinable()) daemon_thread_.join(); + if (capture_thread_.joinable()) capture_thread_.join(); + close(); + tools::logger()->info("Mindvision destructed."); +} + +void MindVision::read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) +{ + CameraData data; + queue_.pop(data); + + img = data.img; + timestamp = data.timestamp; +} + +void MindVision::open() +{ + int camera_num = 1; + tSdkCameraDevInfo camera_info_list; + tSdkCameraCapbility camera_capbility; + CameraSdkInit(1); + CameraEnumerateDevice(&camera_info_list, &camera_num); + + if (camera_num == 0) throw std::runtime_error("Not found camera!"); + + if (CameraInit(&camera_info_list, -1, -1, &handle_) != CAMERA_STATUS_SUCCESS) + throw std::runtime_error("Failed to init camera!"); + + CameraGetCapability(handle_, &camera_capbility); + width_ = camera_capbility.sResolutionRange.iWidthMax; + height_ = camera_capbility.sResolutionRange.iHeightMax; + + CameraSetAeState(handle_, FALSE); // 关闭自动曝光 + CameraSetExposureTime(handle_, exposure_ms_ * 1e3); // 设置曝光 + CameraSetGamma(handle_, gamma_ * 1e2); // 设置伽马 + CameraSetIspOutFormat(handle_, CAMERA_MEDIA_TYPE_BGR8); // 设置输出格式为BGR + CameraSetTriggerMode(handle_, 0); // 设置为连续采集模式 + CameraSetFrameSpeed(handle_, 1); // 设置为低帧率模式 + + CameraPlay(handle_); + + // 取图线程 + capture_thread_ = std::thread{[this] { + tSdkFrameHead head; + BYTE * raw; + + ok_ = true; + while (!quit_) { + std::this_thread::sleep_for(1ms); + + auto img = cv::Mat(height_, width_, CV_8UC3); + + auto status = CameraGetImageBuffer(handle_, &head, &raw, 100); + auto timestamp = std::chrono::steady_clock::now(); + + if (status != CAMERA_STATUS_SUCCESS) { + tools::logger()->warn("Camera dropped!"); + ok_ = false; + break; + } + + CameraImageProcess(handle_, raw, img.data, &head); + CameraReleaseImageBuffer(handle_, raw); + + queue_.push({img, timestamp}); + } + }}; + + tools::logger()->info("Mindvision opened."); +} + +void MindVision::try_open() +{ + try { + open(); + } catch (const std::exception & e) { + tools::logger()->warn("{}", e.what()); + } +} + +void MindVision::close() +{ + if (handle_ == -1) return; + CameraUnInit(handle_); +} + +void MindVision::set_vid_pid(const std::string & vid_pid) +{ + auto index = vid_pid.find(':'); + if (index == std::string::npos) { + tools::logger()->warn("Invalid vid_pid: \"{}\"", vid_pid); + return; + } + + auto vid_str = vid_pid.substr(0, index); + auto pid_str = vid_pid.substr(index + 1); + + try { + vid_ = std::stoi(vid_str, 0, 16); + pid_ = std::stoi(pid_str, 0, 16); + } catch (const std::exception &) { + tools::logger()->warn("Invalid vid_pid: \"{}\"", vid_pid); + } +} + +void MindVision::reset_usb() const +{ + if (vid_ == -1 || pid_ == -1) return; + + // https://github.com/ralight/usb-reset/blob/master/usb-reset.c + auto handle = libusb_open_device_with_vid_pid(NULL, vid_, pid_); + if (!handle) { + tools::logger()->warn("Unable to open usb!"); + return; + } + + if (libusb_reset_device(handle)) + tools::logger()->warn("Unable to reset usb!"); + else + tools::logger()->info("Reset usb successfully :)"); + + libusb_close(handle); +} + +} // namespace io diff --git a/io/mindvision/mindvision.hpp b/io/mindvision/mindvision.hpp new file mode 100644 index 0000000..37f59e2 --- /dev/null +++ b/io/mindvision/mindvision.hpp @@ -0,0 +1,46 @@ +#ifndef IO__MINDVISION_HPP +#define IO__MINDVISION_HPP + +#include +#include +#include + +#include "CameraApi.h" +#include "io/camera.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace io +{ +class MindVision : public CameraBase +{ +public: + MindVision(double exposure_ms, double gamma, const std::string & vid_pid); + ~MindVision() override; + void read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) override; + +private: + struct CameraData + { + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + }; + + double exposure_ms_, gamma_; + CameraHandle handle_; + int height_, width_; + bool quit_, ok_; + std::thread capture_thread_; + std::thread daemon_thread_; + tools::ThreadSafeQueue queue_; + int vid_, pid_; + + void open(); + void try_open(); + void close(); + void set_vid_pid(const std::string & vid_pid); + void reset_usb() const; +}; + +} // namespace io + +#endif // IO__MINDVISION_HPP \ No newline at end of file diff --git a/io/ros2/publish2nav.cpp b/io/ros2/publish2nav.cpp new file mode 100644 index 0000000..725d136 --- /dev/null +++ b/io/ros2/publish2nav.cpp @@ -0,0 +1,48 @@ +#include "publish2nav.hpp" + +#include +#include +#include +#include + +#include "tools/logger.hpp" + +namespace io +{ + +Publish2Nav::Publish2Nav() : Node("auto_aim_target_pos_publisher") +{ + publisher_ = this->create_publisher("auto_aim_target_pos", 10); + + RCLCPP_INFO(this->get_logger(), "auto_aim_target_pos_publisher node initialized."); +} + +Publish2Nav::~Publish2Nav() +{ + RCLCPP_INFO(this->get_logger(), "auto_aim_target_pos_publisher node shutting down."); +} + +void Publish2Nav::send_data(const Eigen::Vector4d & target_pos) +{ + // 创建消息 + auto message = std::make_shared(); + + // 将 Eigen::Vector3d 数据转换为字符串并存储在消息中 + message->data = std::to_string(target_pos[0]) + "," + std::to_string(target_pos[1]) + "," + + std::to_string(target_pos[2]) + "," + std::to_string(target_pos[3]); + + // 发布消息 + publisher_->publish(*message); + + // RCLCPP_INFO( + // this->get_logger(), "auto_aim_target_pos_publisher node sent message: '%s'", + // message->data.c_str()); +} + +void Publish2Nav::start() +{ + RCLCPP_INFO(this->get_logger(), "auto_aim_target_pos_publisher node starting to spin..."); + rclcpp::spin(this->shared_from_this()); +} + +} // namespace io diff --git a/io/ros2/publish2nav.hpp b/io/ros2/publish2nav.hpp new file mode 100644 index 0000000..b738e73 --- /dev/null +++ b/io/ros2/publish2nav.hpp @@ -0,0 +1,35 @@ +#ifndef IO__PBLISH2NAV_HPP +#define IO__PBLISH2NAV_HPP + +#include // For Eigen::Vector3d +#include +#include +#include +#include +#include +#include + +#include "rclcpp/rclcpp.hpp" +#include "std_msgs/msg/string.hpp" + +namespace io +{ +class Publish2Nav : public rclcpp::Node +{ +public: + Publish2Nav(); + + ~Publish2Nav(); + + void start(); + + void send_data(const Eigen::Vector4d & data); + +private: + // ROS2 发布者 + rclcpp::Publisher::SharedPtr publisher_; +}; + +} // namespace io + +#endif // Publish2Nav_HPP_ diff --git a/io/ros2/ros2.cpp b/io/ros2/ros2.cpp new file mode 100644 index 0000000..4a25bfd --- /dev/null +++ b/io/ros2/ros2.cpp @@ -0,0 +1,36 @@ +#include "ros2.hpp" +namespace io +{ +ROS2::ROS2() +{ + rclcpp::init(0, nullptr); + + publish2nav_ = std::make_shared(); + + subscribe2nav_ = std::make_shared(); + + publish_spin_thread_ = std::make_unique([this]() { publish2nav_->start(); }); + + subscribe_spin_thread_ = std::make_unique([this]() { subscribe2nav_->start(); }); +} + +ROS2::~ROS2() +{ + rclcpp::shutdown(); + publish_spin_thread_->join(); + subscribe_spin_thread_->join(); +} + +void ROS2::publish(const Eigen::Vector4d & target_pos) { publish2nav_->send_data(target_pos); } + +std::vector ROS2::subscribe_enemy_status() +{ + return subscribe2nav_->subscribe_enemy_status(); +} + +std::vector ROS2::subscribe_autoaim_target() +{ + return subscribe2nav_->subscribe_autoaim_target(); +} + +} // namespace io diff --git a/io/ros2/ros2.hpp b/io/ros2/ros2.hpp new file mode 100644 index 0000000..848fa40 --- /dev/null +++ b/io/ros2/ros2.hpp @@ -0,0 +1,45 @@ +#ifndef IO__ROS2_HPP +#define IO__ROS2_HPP + +#include "publish2nav.hpp" +#include "subscribe2nav.hpp" + +namespace io +{ +class ROS2 +{ +public: + ROS2(); + + ~ROS2(); + + void publish(const Eigen::Vector4d & target_pos); + + std::vector subscribe_enemy_status(); + + std::vector subscribe_autoaim_target(); + + template + std::shared_ptr> create_publisher( + const std::string & node_name, const std::string & topic_name, size_t queue_size) + { + auto node = std::make_shared(node_name); + + auto publisher = node->create_publisher(topic_name, queue_size); + + // 运行一个单独的线程来 spin 这个节点,确保消息可以被正确发布 + std::thread([node]() { rclcpp::spin(node); }).detach(); + + return publisher; + } + +private: + std::shared_ptr publish2nav_; + std::shared_ptr subscribe2nav_; + + std::unique_ptr publish_spin_thread_; + std::unique_ptr subscribe_spin_thread_; +}; + +} // namespace io +#endif \ No newline at end of file diff --git a/io/ros2/subscribe2nav.cpp b/io/ros2/subscribe2nav.cpp new file mode 100644 index 0000000..bc5934e --- /dev/null +++ b/io/ros2/subscribe2nav.cpp @@ -0,0 +1,108 @@ +#include "subscribe2nav.hpp" + +#include +#include + +namespace io +{ + +Subscribe2Nav::Subscribe2Nav() +: Node("nav_subscriber"), + enemy_statue_queue_(1), + autoaim_target_queue_(1), + enemy_status_counter_(0), + autoaim_target_counter_(0) +{ + enemy_status_subscription_ = this->create_subscription( + "enemy_status", 10, + std::bind(&Subscribe2Nav::enemy_status_callback, this, std::placeholders::_1)); + + autoaim_target_subscription_ = this->create_subscription( + "autoaim_target", 10, + std::bind(&Subscribe2Nav::autoaim_target_callback, this, std::placeholders::_1)); + + RCLCPP_INFO(this->get_logger(), "nav_subscriber node initialized."); +} + +Subscribe2Nav::~Subscribe2Nav() +{ + RCLCPP_INFO(this->get_logger(), "nav_subscriber node shutting down."); +} + +void Subscribe2Nav::enemy_status_callback(const sp_msgs::msg::EnemyStatusMsg::SharedPtr msg) +{ + enemy_statue_queue_.clear(); + enemy_statue_queue_.push(*msg); + + enemy_status_counter_++; + + if (enemy_status_counter_ >= 2) { + if (enemy_status_timer_) { + enemy_status_timer_->cancel(); + } + enemy_status_timer_ = this->create_wall_timer(std::chrono::milliseconds(1500), [this]() { + enemy_statue_queue_.clear(); + enemy_status_counter_ = 0; + RCLCPP_INFO( + this->get_logger(), "Enemy status queue cleared due to inactivity after two messages."); + }); + } +} + +void Subscribe2Nav::autoaim_target_callback(const sp_msgs::msg::AutoaimTargetMsg::SharedPtr msg) +{ + autoaim_target_queue_.clear(); + autoaim_target_queue_.push(*msg); + + autoaim_target_counter_++; + + if (autoaim_target_counter_ >= 2) { + if (autoaim_target_timer_) { + autoaim_target_timer_->cancel(); + } + autoaim_target_timer_ = this->create_wall_timer(std::chrono::milliseconds(1500), [this]() { + autoaim_target_queue_.clear(); + autoaim_target_counter_ = 0; + RCLCPP_INFO( + this->get_logger(), "Autoaim target queue cleared due to inactivity after two messages."); + }); + } +} + +void Subscribe2Nav::start() +{ + RCLCPP_INFO(this->get_logger(), "nav_subscriber node Starting to spin..."); + rclcpp::spin(this->shared_from_this()); +} + +std::vector Subscribe2Nav::subscribe_enemy_status() +{ + if (enemy_statue_queue_.empty()) { + return std::vector(); + } + sp_msgs::msg::EnemyStatusMsg msg; + + enemy_statue_queue_.back(msg); + RCLCPP_INFO( + this->get_logger(), "Subscribe enemy_status at: %d.%09u", msg.timestamp.sec, + msg.timestamp.nanosec); + + return msg.invincible_enemy_ids; +} + +std::vector Subscribe2Nav::subscribe_autoaim_target() +{ + if (autoaim_target_queue_.empty()) { + return std::vector(); + } + sp_msgs::msg::AutoaimTargetMsg msg; + + autoaim_target_queue_.back(msg); + RCLCPP_INFO( + this->get_logger(), "Subscribe autoaim_target at: %d.%09u", msg.timestamp.sec, + msg.timestamp.nanosec); + + return msg.target_ids; +} + +} // namespace io \ No newline at end of file diff --git a/io/ros2/subscribe2nav.hpp b/io/ros2/subscribe2nav.hpp new file mode 100644 index 0000000..a9da253 --- /dev/null +++ b/io/ros2/subscribe2nav.hpp @@ -0,0 +1,45 @@ +#ifndef IO__SUBSCRIBE2NAV_HPP +#define IO__SUBSCRIBE2NAV_HPP + +#include +#include +#include +#include + +#include "sp_msgs/msg/autoaim_target_msg.hpp" +#include "sp_msgs/msg/enemy_status_msg.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace io +{ +class Subscribe2Nav : public rclcpp::Node +{ +public: + Subscribe2Nav(); + + ~Subscribe2Nav(); + + void start(); + + std::vector subscribe_enemy_status(); + std::vector subscribe_autoaim_target(); + +private: + void enemy_status_callback(const sp_msgs::msg::EnemyStatusMsg::SharedPtr msg); + void autoaim_target_callback(const sp_msgs::msg::AutoaimTargetMsg::SharedPtr msg); + + int enemy_status_counter_; + int autoaim_target_counter_; + + rclcpp::TimerBase::SharedPtr enemy_status_timer_; + rclcpp::TimerBase::SharedPtr autoaim_target_timer_; + + rclcpp::Subscription::SharedPtr enemy_status_subscription_; + rclcpp::Subscription::SharedPtr autoaim_target_subscription_; + + tools::ThreadSafeQueue enemy_statue_queue_; + tools::ThreadSafeQueue autoaim_target_queue_; +}; +} // namespace io + +#endif // IO__SUBSCRIBE2NAV_HPP diff --git a/io/serial/CMakeLists.txt b/io/serial/CMakeLists.txt new file mode 100644 index 0000000..01a1a14 --- /dev/null +++ b/io/serial/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.16) +project(serial) + +if(APPLE) + find_library(IOKIT_LIBRARY IOKit) + find_library(FOUNDATION_LIBRARY Foundation) +endif() + +if(UNIX AND NOT APPLE) + # If Linux, add rt and pthread + set(rt_LIBRARIES rt) + set(pthread_LIBRARIES pthread) +endif() + +## Sources +set(serial_SRCS + src/serial.cc + include/serial/serial.h + include/serial/v8stdint.h +) +if(APPLE) + # If OSX + list(APPEND serial_SRCS src/impl/unix.cc) + list(APPEND serial_SRCS src/impl/list_ports/list_ports_osx.cc) +elseif(UNIX) + # If unix + list(APPEND serial_SRCS src/impl/unix.cc) + list(APPEND serial_SRCS src/impl/list_ports/list_ports_linux.cc) +else() + # If windows + list(APPEND serial_SRCS src/impl/win.cc) + list(APPEND serial_SRCS src/impl/list_ports/list_ports_win.cc) +endif() + +## Add serial library +add_library(${PROJECT_NAME} ${serial_SRCS}) +if(APPLE) + target_link_libraries(${PROJECT_NAME} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY}) +elseif(UNIX) + target_link_libraries(${PROJECT_NAME} rt pthread) +else() + target_link_libraries(${PROJECT_NAME} setupapi) +endif() + +## Include headers +target_include_directories(${PROJECT_NAME} PUBLIC include) \ No newline at end of file diff --git a/io/serial/include/serial/impl/unix.h b/io/serial/include/serial/impl/unix.h new file mode 100644 index 0000000..fbbc089 --- /dev/null +++ b/io/serial/include/serial/impl/unix.h @@ -0,0 +1,183 @@ +/*! + * \file serial/impl/unix.h + * \author William Woodall + * \author John Harrison + * \version 0.1 + * + * \section LICENSE + * + * The MIT License + * + * Copyright (c) 2012 William Woodall, John Harrison + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * \section DESCRIPTION + * + * This provides a unix based pimpl for the Serial class. This implementation is + * based off termios.h and uses select for multiplexing the IO ports. + * + */ + +#if !defined(_WIN32) + +#ifndef SERIAL_IMPL_UNIX_H +#define SERIAL_IMPL_UNIX_H + +#include + +#include "serial/serial.h" + +namespace serial +{ + +using std::invalid_argument; +using std::size_t; +using std::string; + +using serial::IOException; +using serial::SerialException; + +class MillisecondTimer +{ +public: + MillisecondTimer(const uint32_t millis); + int64_t remaining(); + +private: + static timespec timespec_now(); + timespec expiry; +}; + +class serial::Serial::SerialImpl +{ +public: + SerialImpl( + const string & port, unsigned long baudrate, bytesize_t bytesize, parity_t parity, + stopbits_t stopbits, flowcontrol_t flowcontrol); + + virtual ~SerialImpl(); + + void open(); + + void close(); + + bool isOpen() const; + + size_t available(); + + bool waitReadable(uint32_t timeout); + + void waitByteTimes(size_t count); + + size_t read(uint8_t * buf, size_t size = 1); + + size_t write(const uint8_t * data, size_t length); + + void flush(); + + void flushInput(); + + void flushOutput(); + + void sendBreak(int duration); + + void setBreak(bool level); + + void setRTS(bool level); + + void setDTR(bool level); + + bool waitForChange(); + + bool getCTS(); + + bool getDSR(); + + bool getRI(); + + bool getCD(); + + void setPort(const string & port); + + string getPort() const; + + void setTimeout(Timeout & timeout); + + Timeout getTimeout() const; + + void setBaudrate(unsigned long baudrate); + + unsigned long getBaudrate() const; + + void setBytesize(bytesize_t bytesize); + + bytesize_t getBytesize() const; + + void setParity(parity_t parity); + + parity_t getParity() const; + + void setStopbits(stopbits_t stopbits); + + stopbits_t getStopbits() const; + + void setFlowcontrol(flowcontrol_t flowcontrol); + + flowcontrol_t getFlowcontrol() const; + + void readLock(); + + void readUnlock(); + + void writeLock(); + + void writeUnlock(); + +protected: + void reconfigurePort(); + +private: + string port_; // Path to the file descriptor + int fd_; // The current file descriptor + + bool is_open_; + bool xonxoff_; + bool rtscts_; + + Timeout timeout_; // Timeout for read operations + unsigned long baudrate_; // Baudrate + uint32_t byte_time_ns_; // Nanoseconds to transmit/receive a single byte + + parity_t parity_; // Parity + bytesize_t bytesize_; // Size of the bytes + stopbits_t stopbits_; // Stop Bits + flowcontrol_t flowcontrol_; // Flow Control + + // Mutex used to lock the read functions + pthread_mutex_t read_mutex; + // Mutex used to lock the write functions + pthread_mutex_t write_mutex; +}; + +} // namespace serial + +#endif // SERIAL_IMPL_UNIX_H + +#endif // !defined(_WIN32) \ No newline at end of file diff --git a/io/serial/include/serial/impl/win.h b/io/serial/include/serial/impl/win.h new file mode 100644 index 0000000..18c1174 --- /dev/null +++ b/io/serial/include/serial/impl/win.h @@ -0,0 +1,167 @@ +/*! + * \file serial/impl/windows.h + * \author William Woodall + * \author John Harrison + * \version 0.1 + * + * \section LICENSE + * + * The MIT License + * + * Copyright (c) 2012 William Woodall, John Harrison + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * \section DESCRIPTION + * + * This provides a windows implementation of the Serial class interface. + * + */ + +#if defined(_WIN32) + +#ifndef SERIAL_IMPL_WINDOWS_H +#define SERIAL_IMPL_WINDOWS_H + +#include "serial/serial.h" +#include "windows.h" + +namespace serial +{ + +using std::invalid_argument; +using std::string; +using std::wstring; + +using serial::IOException; +using serial::SerialException; + +class serial::Serial::SerialImpl +{ +public: + SerialImpl( + const string & port, unsigned long baudrate, bytesize_t bytesize, parity_t parity, + stopbits_t stopbits, flowcontrol_t flowcontrol); + + virtual ~SerialImpl(); + + void open(); + + void close(); + + bool isOpen() const; + + size_t available(); + + bool waitReadable(uint32_t timeout); + + void waitByteTimes(size_t count); + + size_t read(uint8_t * buf, size_t size = 1); + + size_t write(const uint8_t * data, size_t length); + + void flush(); + + void flushInput(); + + void flushOutput(); + + void sendBreak(int duration); + + void setBreak(bool level); + + void setRTS(bool level); + + void setDTR(bool level); + + bool waitForChange(); + + bool getCTS(); + + bool getDSR(); + + bool getRI(); + + bool getCD(); + + void setPort(const string & port); + + string getPort() const; + + void setTimeout(Timeout & timeout); + + Timeout getTimeout() const; + + void setBaudrate(unsigned long baudrate); + + unsigned long getBaudrate() const; + + void setBytesize(bytesize_t bytesize); + + bytesize_t getBytesize() const; + + void setParity(parity_t parity); + + parity_t getParity() const; + + void setStopbits(stopbits_t stopbits); + + stopbits_t getStopbits() const; + + void setFlowcontrol(flowcontrol_t flowcontrol); + + flowcontrol_t getFlowcontrol() const; + + void readLock(); + + void readUnlock(); + + void writeLock(); + + void writeUnlock(); + +protected: + void reconfigurePort(); + +private: + wstring port_; // Path to the file descriptor + HANDLE fd_; + + bool is_open_; + + Timeout timeout_; // Timeout for read operations + unsigned long baudrate_; // Baudrate + + parity_t parity_; // Parity + bytesize_t bytesize_; // Size of the bytes + stopbits_t stopbits_; // Stop Bits + flowcontrol_t flowcontrol_; // Flow Control + + // Mutex used to lock the read functions + HANDLE read_mutex; + // Mutex used to lock the write functions + HANDLE write_mutex; +}; + +} // namespace serial + +#endif // SERIAL_IMPL_WINDOWS_H + +#endif // if defined(_WIN32) \ No newline at end of file diff --git a/io/serial/include/serial/serial.h b/io/serial/include/serial/serial.h new file mode 100644 index 0000000..80e92ca --- /dev/null +++ b/io/serial/include/serial/serial.h @@ -0,0 +1,733 @@ +/*! + * \file serial/serial.h + * \author William Woodall + * \author John Harrison + * \version 0.1 + * + * \section LICENSE + * + * The MIT License + * + * Copyright (c) 2012 William Woodall + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * \section DESCRIPTION + * + * This provides a cross platform interface for interacting with Serial Ports. + */ + +#ifndef SERIAL_H +#define SERIAL_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message)) + +namespace serial +{ + +/*! + * Enumeration defines the possible bytesizes for the serial port. + */ +typedef enum +{ + fivebits = 5, + sixbits = 6, + sevenbits = 7, + eightbits = 8 +} bytesize_t; + +/*! + * Enumeration defines the possible parity types for the serial port. + */ +typedef enum +{ + parity_none = 0, + parity_odd = 1, + parity_even = 2, + parity_mark = 3, + parity_space = 4 +} parity_t; + +/*! + * Enumeration defines the possible stopbit types for the serial port. + */ +typedef enum +{ + stopbits_one = 1, + stopbits_two = 2, + stopbits_one_point_five +} stopbits_t; + +/*! + * Enumeration defines the possible flowcontrol types for the serial port. + */ +typedef enum +{ + flowcontrol_none = 0, + flowcontrol_software, + flowcontrol_hardware +} flowcontrol_t; + +/*! + * Structure for setting the timeout of the serial port, times are + * in milliseconds. + * + * In order to disable the interbyte timeout, set it to Timeout::max(). + */ +struct Timeout +{ +#ifdef max +#undef max +#endif + static uint32_t max() { return std::numeric_limits::max(); } + /*! + * Convenience function to generate Timeout structs using a + * single absolute timeout. + * + * \param timeout A long that defines the time in milliseconds until a + * timeout occurs after a call to read or write is made. + * + * \return Timeout struct that represents this simple timeout provided. + */ + static Timeout simpleTimeout(uint32_t timeout) { return Timeout(max(), timeout, 0, timeout, 0); } + + /*! Number of milliseconds between bytes received to timeout on. */ + uint32_t inter_byte_timeout; + /*! A constant number of milliseconds to wait after calling read. */ + uint32_t read_timeout_constant; + /*! A multiplier against the number of requested bytes to wait after + * calling read. + */ + uint32_t read_timeout_multiplier; + /*! A constant number of milliseconds to wait after calling write. */ + uint32_t write_timeout_constant; + /*! A multiplier against the number of requested bytes to wait after + * calling write. + */ + uint32_t write_timeout_multiplier; + + explicit Timeout( + uint32_t inter_byte_timeout_ = 0, uint32_t read_timeout_constant_ = 0, + uint32_t read_timeout_multiplier_ = 0, uint32_t write_timeout_constant_ = 0, + uint32_t write_timeout_multiplier_ = 0) + : inter_byte_timeout(inter_byte_timeout_), + read_timeout_constant(read_timeout_constant_), + read_timeout_multiplier(read_timeout_multiplier_), + write_timeout_constant(write_timeout_constant_), + write_timeout_multiplier(write_timeout_multiplier_) + { + } +}; + +/*! + * Class that provides a portable serial port interface. + */ +class Serial +{ +public: + /*! + * Creates a Serial object and opens the port if a port is specified, + * otherwise it remains closed until serial::Serial::open is called. + * + * \param port A std::string containing the address of the serial port, + * which would be something like 'COM1' on Windows and '/dev/ttyS0' + * on Linux. + * + * \param baudrate An unsigned 32-bit integer that represents the baudrate + * + * \param timeout A serial::Timeout struct that defines the timeout + * conditions for the serial port. \see serial::Timeout + * + * \param bytesize Size of each byte in the serial transmission of data, + * default is eightbits, possible values are: fivebits, sixbits, sevenbits, + * eightbits + * + * \param parity Method of parity, default is parity_none, possible values + * are: parity_none, parity_odd, parity_even + * + * \param stopbits Number of stop bits used, default is stopbits_one, + * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two + * + * \param flowcontrol Type of flowcontrol used, default is + * flowcontrol_none, possible values are: flowcontrol_none, + * flowcontrol_software, flowcontrol_hardware + * + * \throw serial::PortNotOpenedException + * \throw serial::IOException + * \throw std::invalid_argument + */ + Serial( + const std::string & port = "", uint32_t baudrate = 9600, Timeout timeout = Timeout(), + bytesize_t bytesize = eightbits, parity_t parity = parity_none, + stopbits_t stopbits = stopbits_one, flowcontrol_t flowcontrol = flowcontrol_none); + + /*! Destructor */ + virtual ~Serial(); + + /*! + * Opens the serial port as long as the port is set and the port isn't + * already open. + * + * If the port is provided to the constructor then an explicit call to open + * is not needed. + * + * \see Serial::Serial + * + * \throw std::invalid_argument + * \throw serial::SerialException + * \throw serial::IOException + */ + void open(); + + /*! Gets the open status of the serial port. + * + * \return Returns true if the port is open, false otherwise. + */ + bool isOpen() const; + + /*! Closes the serial port. */ + void close(); + + /*! Return the number of characters in the buffer. */ + size_t available(); + + /*! Block until there is serial data to read or read_timeout_constant + * number of milliseconds have elapsed. The return value is true when + * the function exits with the port in a readable state, false otherwise + * (due to timeout or select interruption). */ + bool waitReadable(); + + /*! Block for a period of time corresponding to the transmission time of + * count characters at present serial settings. This may be used in con- + * junction with waitReadable to read larger blocks of data from the + * port. */ + void waitByteTimes(size_t count); + + /*! Read a given amount of bytes from the serial port into a given buffer. + * + * The read function will return in one of three cases: + * * The number of requested bytes was read. + * * In this case the number of bytes requested will match the size_t + * returned by read. + * * A timeout occurred, in this case the number of bytes read will not + * match the amount requested, but no exception will be thrown. One of + * two possible timeouts occurred: + * * The inter byte timeout expired, this means that number of + * milliseconds elapsed between receiving bytes from the serial port + * exceeded the inter byte timeout. + * * The total timeout expired, which is calculated by multiplying the + * read timeout multiplier by the number of requested bytes and then + * added to the read timeout constant. If that total number of + * milliseconds elapses after the initial call to read a timeout will + * occur. + * * An exception occurred, in this case an actual exception will be thrown. + * + * \param buffer An uint8_t array of at least the requested size. + * \param size A size_t defining how many bytes to be read. + * + * \return A size_t representing the number of bytes read as a result of the + * call to read. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + size_t read(uint8_t * buffer, size_t size); + + /*! Read a given amount of bytes from the serial port into a give buffer. + * + * \param buffer A reference to a std::vector of uint8_t. + * \param size A size_t defining how many bytes to be read. + * + * \return A size_t representing the number of bytes read as a result of the + * call to read. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + size_t read(std::vector & buffer, size_t size = 1); + + /*! Read a given amount of bytes from the serial port into a give buffer. + * + * \param buffer A reference to a std::string. + * \param size A size_t defining how many bytes to be read. + * + * \return A size_t representing the number of bytes read as a result of the + * call to read. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + size_t read(std::string & buffer, size_t size = 1); + + /*! Read a given amount of bytes from the serial port and return a string + * containing the data. + * + * \param size A size_t defining how many bytes to be read. + * + * \return A std::string containing the data read from the port. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + std::string read(size_t size = 1); + + /*! Reads in a line or until a given delimiter has been processed. + * + * Reads from the serial port until a single line has been read. + * + * \param buffer A std::string reference used to store the data. + * \param size A maximum length of a line, defaults to 65536 (2^16) + * \param eol A string to match against for the EOL. + * + * \return A size_t representing the number of bytes read. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + size_t readline(std::string & buffer, size_t size = 65536, std::string eol = "\n"); + + /*! Reads in a line or until a given delimiter has been processed. + * + * Reads from the serial port until a single line has been read. + * + * \param size A maximum length of a line, defaults to 65536 (2^16) + * \param eol A string to match against for the EOL. + * + * \return A std::string containing the line. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + std::string readline(size_t size = 65536, std::string eol = "\n"); + + /*! Reads in multiple lines until the serial port times out. + * + * This requires a timeout > 0 before it can be run. It will read until a + * timeout occurs and return a list of strings. + * + * \param size A maximum length of combined lines, defaults to 65536 (2^16) + * + * \param eol A string to match against for the EOL. + * + * \return A vector containing the lines. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + */ + std::vector readlines(size_t size = 65536, std::string eol = "\n"); + + /*! Write a string to the serial port. + * + * \param data A const reference containing the data to be written + * to the serial port. + * + * \param size A size_t that indicates how many bytes should be written from + * the given data buffer. + * + * \return A size_t representing the number of bytes actually written to + * the serial port. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + * \throw serial::IOException + */ + size_t write(const uint8_t * data, size_t size); + + /*! Write a string to the serial port. + * + * \param data A const reference containing the data to be written + * to the serial port. + * + * \return A size_t representing the number of bytes actually written to + * the serial port. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + * \throw serial::IOException + */ + size_t write(const std::vector & data); + + /*! Write a string to the serial port. + * + * \param data A const reference containing the data to be written + * to the serial port. + * + * \return A size_t representing the number of bytes actually written to + * the serial port. + * + * \throw serial::PortNotOpenedException + * \throw serial::SerialException + * \throw serial::IOException + */ + size_t write(const std::string & data); + + /*! Sets the serial port identifier. + * + * \param port A const std::string reference containing the address of the + * serial port, which would be something like 'COM1' on Windows and + * '/dev/ttyS0' on Linux. + * + * \throw std::invalid_argument + */ + void setPort(const std::string & port); + + /*! Gets the serial port identifier. + * + * \see Serial::setPort + * + * \throw std::invalid_argument + */ + std::string getPort() const; + + /*! Sets the timeout for reads and writes using the Timeout struct. + * + * There are two timeout conditions described here: + * * The inter byte timeout: + * * The inter_byte_timeout component of serial::Timeout defines the + * maximum amount of time, in milliseconds, between receiving bytes on + * the serial port that can pass before a timeout occurs. Setting this + * to zero will prevent inter byte timeouts from occurring. + * * Total time timeout: + * * The constant and multiplier component of this timeout condition, + * for both read and write, are defined in serial::Timeout. This + * timeout occurs if the total time since the read or write call was + * made exceeds the specified time in milliseconds. + * * The limit is defined by multiplying the multiplier component by the + * number of requested bytes and adding that product to the constant + * component. In this way if you want a read call, for example, to + * timeout after exactly one second regardless of the number of bytes + * you asked for then set the read_timeout_constant component of + * serial::Timeout to 1000 and the read_timeout_multiplier to zero. + * This timeout condition can be used in conjunction with the inter + * byte timeout condition with out any problems, timeout will simply + * occur when one of the two timeout conditions is met. This allows + * users to have maximum control over the trade-off between + * responsiveness and efficiency. + * + * Read and write functions will return in one of three cases. When the + * reading or writing is complete, when a timeout occurs, or when an + * exception occurs. + * + * A timeout of 0 enables non-blocking mode. + * + * \param timeout A serial::Timeout struct containing the inter byte + * timeout, and the read and write timeout constants and multipliers. + * + * \see serial::Timeout + */ + void setTimeout(Timeout & timeout); + + /*! Sets the timeout for reads and writes. */ + void setTimeout( + uint32_t inter_byte_timeout, uint32_t read_timeout_constant, uint32_t read_timeout_multiplier, + uint32_t write_timeout_constant, uint32_t write_timeout_multiplier) + { + Timeout timeout( + inter_byte_timeout, read_timeout_constant, read_timeout_multiplier, write_timeout_constant, + write_timeout_multiplier); + return setTimeout(timeout); + } + + /*! Gets the timeout for reads in seconds. + * + * \return A Timeout struct containing the inter_byte_timeout, and read + * and write timeout constants and multipliers. + * + * \see Serial::setTimeout + */ + Timeout getTimeout() const; + + /*! Sets the baudrate for the serial port. + * + * Possible baudrates depends on the system but some safe baudrates include: + * 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000, + * 57600, 115200 + * Some other baudrates that are supported by some comports: + * 128000, 153600, 230400, 256000, 460800, 500000, 921600 + * + * \param baudrate An integer that sets the baud rate for the serial port. + * + * \throw std::invalid_argument + */ + void setBaudrate(uint32_t baudrate); + + /*! Gets the baudrate for the serial port. + * + * \return An integer that sets the baud rate for the serial port. + * + * \see Serial::setBaudrate + * + * \throw std::invalid_argument + */ + uint32_t getBaudrate() const; + + /*! Sets the bytesize for the serial port. + * + * \param bytesize Size of each byte in the serial transmission of data, + * default is eightbits, possible values are: fivebits, sixbits, sevenbits, + * eightbits + * + * \throw std::invalid_argument + */ + void setBytesize(bytesize_t bytesize); + + /*! Gets the bytesize for the serial port. + * + * \see Serial::setBytesize + * + * \throw std::invalid_argument + */ + bytesize_t getBytesize() const; + + /*! Sets the parity for the serial port. + * + * \param parity Method of parity, default is parity_none, possible values + * are: parity_none, parity_odd, parity_even + * + * \throw std::invalid_argument + */ + void setParity(parity_t parity); + + /*! Gets the parity for the serial port. + * + * \see Serial::setParity + * + * \throw std::invalid_argument + */ + parity_t getParity() const; + + /*! Sets the stopbits for the serial port. + * + * \param stopbits Number of stop bits used, default is stopbits_one, + * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two + * + * \throw std::invalid_argument + */ + void setStopbits(stopbits_t stopbits); + + /*! Gets the stopbits for the serial port. + * + * \see Serial::setStopbits + * + * \throw std::invalid_argument + */ + stopbits_t getStopbits() const; + + /*! Sets the flow control for the serial port. + * + * \param flowcontrol Type of flowcontrol used, default is flowcontrol_none, + * possible values are: flowcontrol_none, flowcontrol_software, + * flowcontrol_hardware + * + * \throw std::invalid_argument + */ + void setFlowcontrol(flowcontrol_t flowcontrol); + + /*! Gets the flow control for the serial port. + * + * \see Serial::setFlowcontrol + * + * \throw std::invalid_argument + */ + flowcontrol_t getFlowcontrol() const; + + /*! Flush the input and output buffers */ + void flush(); + + /*! Flush only the input buffer */ + void flushInput(); + + /*! Flush only the output buffer */ + void flushOutput(); + + /*! Sends the RS-232 break signal. See tcsendbreak(3). */ + void sendBreak(int duration); + + /*! Set the break condition to a given level. Defaults to true. */ + void setBreak(bool level = true); + + /*! Set the RTS handshaking line to the given level. Defaults to true. */ + void setRTS(bool level = true); + + /*! Set the DTR handshaking line to the given level. Defaults to true. */ + void setDTR(bool level = true); + + /*! + * Blocks until CTS, DSR, RI, CD changes or something interrupts it. + * + * Can throw an exception if an error occurs while waiting. + * You can check the status of CTS, DSR, RI, and CD once this returns. + * Uses TIOCMIWAIT via ioctl if available (mostly only on Linux) with a + * resolution of less than +-1ms and as good as +-0.2ms. Otherwise a + * polling method is used which can give +-2ms. + * + * \return Returns true if one of the lines changed, false if something else + * occurred. + * + * \throw SerialException + */ + bool waitForChange(); + + /*! Returns the current status of the CTS line. */ + bool getCTS(); + + /*! Returns the current status of the DSR line. */ + bool getDSR(); + + /*! Returns the current status of the RI line. */ + bool getRI(); + + /*! Returns the current status of the CD line. */ + bool getCD(); + +private: + // Disable copy constructors + Serial(const Serial &); + Serial & operator=(const Serial &); + + // Pimpl idiom, d_pointer + class SerialImpl; + SerialImpl * pimpl_; + + // Scoped Lock Classes + class ScopedReadLock; + class ScopedWriteLock; + + // Read common function + size_t read_(uint8_t * buffer, size_t size); + // Write common function + size_t write_(const uint8_t * data, size_t length); +}; + +class SerialException : public std::exception +{ + // Disable copy constructors + SerialException & operator=(const SerialException &); + std::string e_what_; + +public: + SerialException(const char * description) + { + std::stringstream ss; + ss << "SerialException " << description << " failed."; + e_what_ = ss.str(); + } + SerialException(const SerialException & other) : e_what_(other.e_what_) {} + virtual ~SerialException() throw() {} + virtual const char * what() const throw() { return e_what_.c_str(); } +}; + +class IOException : public std::exception +{ + // Disable copy constructors + IOException & operator=(const IOException &); + std::string file_; + int line_; + std::string e_what_; + int errno_; + +public: + explicit IOException(std::string file, int line, int errnum) + : file_(file), line_(line), errno_(errnum) + { + std::stringstream ss; +#if defined(_WIN32) && !defined(__MINGW32__) + char error_str[1024]; + strerror_s(error_str, 1024, errnum); +#else + char * error_str = strerror(errnum); +#endif + ss << "IO Exception (" << errno_ << "): " << error_str; + ss << ", file " << file_ << ", line " << line_ << "."; + e_what_ = ss.str(); + } + explicit IOException(std::string file, int line, const char * description) + : file_(file), line_(line), errno_(0) + { + std::stringstream ss; + ss << "IO Exception: " << description; + ss << ", file " << file_ << ", line " << line_ << "."; + e_what_ = ss.str(); + } + virtual ~IOException() throw() {} + IOException(const IOException & other) + : line_(other.line_), e_what_(other.e_what_), errno_(other.errno_) + { + } + + int getErrorNumber() const { return errno_; } + + virtual const char * what() const throw() { return e_what_.c_str(); } +}; + +class PortNotOpenedException : public std::exception +{ + // Disable copy constructors + const PortNotOpenedException & operator=(PortNotOpenedException); + std::string e_what_; + +public: + PortNotOpenedException(const char * description) + { + std::stringstream ss; + ss << "PortNotOpenedException " << description << " failed."; + e_what_ = ss.str(); + } + PortNotOpenedException(const PortNotOpenedException & other) : e_what_(other.e_what_) {} + virtual ~PortNotOpenedException() throw() {} + virtual const char * what() const throw() { return e_what_.c_str(); } +}; + +/*! + * Structure that describes a serial device. + */ +struct PortInfo +{ + /*! Address of the serial port (this can be passed to the constructor of Serial). */ + std::string port; + + /*! Human readable description of serial device if available. */ + std::string description; + + /*! Hardware ID (e.g. VID:PID of USB serial devices) or "n/a" if not available. */ + std::string hardware_id; +}; + +/* Lists the serial ports available on the system + * + * Returns a vector of available serial ports, each represented + * by a serial::PortInfo data structure: + * + * \return vector of serial::PortInfo. + */ +std::vector list_ports(); + +} // namespace serial + +#endif \ No newline at end of file diff --git a/io/serial/include/serial/v8stdint.h b/io/serial/include/serial/v8stdint.h new file mode 100644 index 0000000..16f9f12 --- /dev/null +++ b/io/serial/include/serial/v8stdint.h @@ -0,0 +1,57 @@ +// This header is from the v8 google project: +// http://code.google.com/p/v8/source/browse/trunk/include/v8stdint.h + +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Load definitions of standard types. + +#ifndef V8STDINT_H_ +#define V8STDINT_H_ + +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include + +#endif + +#endif // V8STDINT_H_ \ No newline at end of file diff --git a/io/serial/src/impl/list_ports/list_ports_linux.cc b/io/serial/src/impl/list_ports/list_ports_linux.cc new file mode 100644 index 0000000..efb627b --- /dev/null +++ b/io/serial/src/impl/list_ports/list_ports_linux.cc @@ -0,0 +1,292 @@ +#if defined(__linux__) + +/* + * Copyright (c) 2014 Craig Lilley + * This software is made available under the terms of the MIT licence. + * A copy of the licence can be obtained from: + * http://opensource.org/licenses/MIT + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial/serial.h" + +using serial::PortInfo; +using std::cout; +using std::endl; +using std::getline; +using std::ifstream; +using std::istringstream; +using std::string; +using std::vector; + +static vector glob(const vector & patterns); +static string basename(const string & path); +static string dirname(const string & path); +static bool path_exists(const string & path); +static string realpath(const string & path); +static string usb_sysfs_friendly_name(const string & sys_usb_path); +static vector get_sysfs_info(const string & device_path); +static string read_line(const string & file); +static string usb_sysfs_hw_string(const string & sysfs_path); +static string format(const char * format, ...); + +vector glob(const vector & patterns) +{ + vector paths_found; + + if (patterns.size() == 0) return paths_found; + + glob_t glob_results; + + int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results); + + vector::const_iterator iter = patterns.begin(); + + while (++iter != patterns.end()) { + glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results); + } + + for (int path_index = 0; path_index < glob_results.gl_pathc; path_index++) { + paths_found.push_back(glob_results.gl_pathv[path_index]); + } + + globfree(&glob_results); + + return paths_found; +} + +string basename(const string & path) +{ + size_t pos = path.rfind("/"); + + if (pos == std::string::npos) return path; + + return string(path, pos + 1, string::npos); +} + +string dirname(const string & path) +{ + size_t pos = path.rfind("/"); + + if (pos == std::string::npos) + return path; + else if (pos == 0) + return "/"; + + return string(path, 0, pos); +} + +bool path_exists(const string & path) +{ + struct stat sb; + + if (stat(path.c_str(), &sb) == 0) return true; + + return false; +} + +string realpath(const string & path) +{ + char * real_path = realpath(path.c_str(), NULL); + + string result; + + if (real_path != NULL) { + result = real_path; + + free(real_path); + } + + return result; +} + +string usb_sysfs_friendly_name(const string & sys_usb_path) +{ + unsigned int device_number = 0; + + istringstream(read_line(sys_usb_path + "/devnum")) >> device_number; + + string manufacturer = read_line(sys_usb_path + "/manufacturer"); + + string product = read_line(sys_usb_path + "/product"); + + string serial = read_line(sys_usb_path + "/serial"); + + if (manufacturer.empty() && product.empty() && serial.empty()) return ""; + + return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str()); +} + +vector get_sysfs_info(const string & device_path) +{ + string device_name = basename(device_path); + + string friendly_name; + + string hardware_id; + + string sys_device_path = format("/sys/class/tty/%s/device", device_name.c_str()); + + if (device_name.compare(0, 6, "ttyUSB") == 0) { + sys_device_path = dirname(dirname(realpath(sys_device_path))); + + if (path_exists(sys_device_path)) { + friendly_name = usb_sysfs_friendly_name(sys_device_path); + + hardware_id = usb_sysfs_hw_string(sys_device_path); + } + } else if (device_name.compare(0, 6, "ttyACM") == 0) { + sys_device_path = dirname(realpath(sys_device_path)); + + if (path_exists(sys_device_path)) { + friendly_name = usb_sysfs_friendly_name(sys_device_path); + + hardware_id = usb_sysfs_hw_string(sys_device_path); + } + } else { + // Try to read ID string of PCI device + + string sys_id_path = sys_device_path + "/id"; + + if (path_exists(sys_id_path)) hardware_id = read_line(sys_id_path); + } + + if (friendly_name.empty()) friendly_name = device_name; + + if (hardware_id.empty()) hardware_id = "n/a"; + + vector result; + result.push_back(friendly_name); + result.push_back(hardware_id); + + return result; +} + +string read_line(const string & file) +{ + ifstream ifs(file.c_str(), ifstream::in); + + string line; + + if (ifs) { + getline(ifs, line); + } + + return line; +} + +string format(const char * format, ...) +{ + va_list ap; + + size_t buffer_size_bytes = 256; + + string result; + + char * buffer = (char *)malloc(buffer_size_bytes); + + if (buffer == NULL) return result; + + bool done = false; + + unsigned int loop_count = 0; + + while (!done) { + va_start(ap, format); + + int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap); + + if (return_value < 0) { + done = true; + } else if (return_value >= buffer_size_bytes) { + // Realloc and try again. + + buffer_size_bytes = return_value + 1; + + char * new_buffer_ptr = (char *)realloc(buffer, buffer_size_bytes); + + if (new_buffer_ptr == NULL) { + done = true; + } else { + buffer = new_buffer_ptr; + } + } else { + result = buffer; + done = true; + } + + va_end(ap); + + if (++loop_count > 5) done = true; + } + + free(buffer); + + return result; +} + +string usb_sysfs_hw_string(const string & sysfs_path) +{ + string serial_number = read_line(sysfs_path + "/serial"); + + if (serial_number.length() > 0) { + serial_number = format("SNR=%s", serial_number.c_str()); + } + + string vid = read_line(sysfs_path + "/idVendor"); + + string pid = read_line(sysfs_path + "/idProduct"); + + return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str()); +} + +vector serial::list_ports() +{ + vector results; + + vector search_globs; + search_globs.push_back("/dev/ttyACM*"); + search_globs.push_back("/dev/ttyS*"); + search_globs.push_back("/dev/ttyUSB*"); + search_globs.push_back("/dev/tty.*"); + search_globs.push_back("/dev/cu.*"); + + vector devices_found = glob(search_globs); + + vector::iterator iter = devices_found.begin(); + + while (iter != devices_found.end()) { + string device = *iter++; + + vector sysfs_info = get_sysfs_info(device); + + string friendly_name = sysfs_info[0]; + + string hardware_id = sysfs_info[1]; + + PortInfo device_entry; + device_entry.port = device; + device_entry.description = friendly_name; + device_entry.hardware_id = hardware_id; + + results.push_back(device_entry); + } + + return results; +} + +#endif // defined(__linux__) \ No newline at end of file diff --git a/io/serial/src/impl/list_ports/list_ports_osx.cc b/io/serial/src/impl/list_ports/list_ports_osx.cc new file mode 100644 index 0000000..a10dcf7 --- /dev/null +++ b/io/serial/src/impl/list_ports/list_ports_osx.cc @@ -0,0 +1,234 @@ +#if defined(__APPLE__) + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "serial/serial.h" + +using serial::PortInfo; +using std::string; +using std::vector; + +#define HARDWARE_ID_STRING_LENGTH 128 + +string cfstring_to_string(CFStringRef cfstring); +string get_device_path(io_object_t & serial_port); +string get_class_name(io_object_t & obj); +io_registry_entry_t get_parent_iousb_device(io_object_t & serial_port); +string get_string_property(io_object_t & device, const char * property); +uint16_t get_int_property(io_object_t & device, const char * property); +string rtrim(const string & str); + +string cfstring_to_string(CFStringRef cfstring) +{ + char cstring[MAXPATHLEN]; + string result; + + if (cfstring) { + Boolean success = + CFStringGetCString(cfstring, cstring, sizeof(cstring), kCFStringEncodingASCII); + + if (success) result = cstring; + } + + return result; +} + +string get_device_path(io_object_t & serial_port) +{ + CFTypeRef callout_path; + string device_path; + + callout_path = IORegistryEntryCreateCFProperty( + serial_port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + + if (callout_path) { + if (CFGetTypeID(callout_path) == CFStringGetTypeID()) + device_path = cfstring_to_string(static_cast(callout_path)); + + CFRelease(callout_path); + } + + return device_path; +} + +string get_class_name(io_object_t & obj) +{ + string result; + io_name_t class_name; + kern_return_t kern_result; + + kern_result = IOObjectGetClass(obj, class_name); + + if (kern_result == KERN_SUCCESS) result = class_name; + + return result; +} + +io_registry_entry_t get_parent_iousb_device(io_object_t & serial_port) +{ + io_object_t device = serial_port; + io_registry_entry_t parent = 0; + io_registry_entry_t result = 0; + kern_return_t kern_result = KERN_FAILURE; + string name = get_class_name(device); + + // Walk the IO Registry tree looking for this devices parent IOUSBDevice. + while (name != "IOUSBDevice") { + kern_result = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent); + + if (kern_result != KERN_SUCCESS) { + result = 0; + break; + } + + device = parent; + + name = get_class_name(device); + } + + if (kern_result == KERN_SUCCESS) result = device; + + return result; +} + +string get_string_property(io_object_t & device, const char * property) +{ + string property_name; + + if (device) { + CFStringRef property_as_cfstring = + CFStringCreateWithCString(kCFAllocatorDefault, property, kCFStringEncodingASCII); + + CFTypeRef name_as_cfstring = + IORegistryEntryCreateCFProperty(device, property_as_cfstring, kCFAllocatorDefault, 0); + + if (name_as_cfstring) { + if (CFGetTypeID(name_as_cfstring) == CFStringGetTypeID()) + property_name = cfstring_to_string(static_cast(name_as_cfstring)); + + CFRelease(name_as_cfstring); + } + + if (property_as_cfstring) CFRelease(property_as_cfstring); + } + + return property_name; +} + +uint16_t get_int_property(io_object_t & device, const char * property) +{ + uint16_t result = 0; + + if (device) { + CFStringRef property_as_cfstring = + CFStringCreateWithCString(kCFAllocatorDefault, property, kCFStringEncodingASCII); + + CFTypeRef number = + IORegistryEntryCreateCFProperty(device, property_as_cfstring, kCFAllocatorDefault, 0); + + if (property_as_cfstring) CFRelease(property_as_cfstring); + + if (number) { + if (CFGetTypeID(number) == CFNumberGetTypeID()) { + bool success = + CFNumberGetValue(static_cast(number), kCFNumberSInt16Type, &result); + + if (!success) result = 0; + } + + CFRelease(number); + } + } + + return result; +} + +string rtrim(const string & str) +{ + string result = str; + + string whitespace = " \t\f\v\n\r"; + + std::size_t found = result.find_last_not_of(whitespace); + + if (found != std::string::npos) + result.erase(found + 1); + else + result.clear(); + + return result; +} + +vector serial::list_ports(void) +{ + vector devices_found; + CFMutableDictionaryRef classes_to_match; + io_iterator_t serial_port_iterator; + io_object_t serial_port; + mach_port_t master_port; + kern_return_t kern_result; + + kern_result = IOMasterPort(MACH_PORT_NULL, &master_port); + + if (kern_result != KERN_SUCCESS) return devices_found; + + classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue); + + if (classes_to_match == NULL) return devices_found; + + CFDictionarySetValue(classes_to_match, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); + + kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator); + + if (KERN_SUCCESS != kern_result) return devices_found; + + while ((serial_port = IOIteratorNext(serial_port_iterator))) { + string device_path = get_device_path(serial_port); + io_registry_entry_t parent = get_parent_iousb_device(serial_port); + IOObjectRelease(serial_port); + + if (device_path.empty()) continue; + + PortInfo port_info; + port_info.port = device_path; + port_info.description = "n/a"; + port_info.hardware_id = "n/a"; + + string device_name = rtrim(get_string_property(parent, "USB Product Name")); + string vendor_name = rtrim(get_string_property(parent, "USB Vendor Name")); + string description = rtrim(vendor_name + " " + device_name); + if (!description.empty()) port_info.description = description; + + string serial_number = rtrim(get_string_property(parent, "USB Serial Number")); + uint16_t vendor_id = get_int_property(parent, "idVendor"); + uint16_t product_id = get_int_property(parent, "idProduct"); + + if (vendor_id && product_id) { + char cstring[HARDWARE_ID_STRING_LENGTH]; + + if (serial_number.empty()) serial_number = "None"; + + int ret = snprintf( + cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s", vendor_id, product_id, + serial_number.c_str()); + + if ((ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH)) port_info.hardware_id = cstring; + } + + devices_found.push_back(port_info); + } + + IOObjectRelease(serial_port_iterator); + return devices_found; +} + +#endif // defined(__APPLE__) \ No newline at end of file diff --git a/io/serial/src/impl/list_ports/list_ports_win.cc b/io/serial/src/impl/list_ports/list_ports_win.cc new file mode 100644 index 0000000..e8a5859 --- /dev/null +++ b/io/serial/src/impl/list_ports/list_ports_win.cc @@ -0,0 +1,128 @@ +#if defined(_WIN32) + +/* + * Copyright (c) 2014 Craig Lilley + * This software is made available under the terms of the MIT licence. + * A copy of the licence can be obtained from: + * http://opensource.org/licenses/MIT + */ + +#include +#include +#include +#include +#include + +#include + +#include "serial/serial.h" + +using serial::PortInfo; +using std::string; +using std::vector; + +static const DWORD port_name_max_length = 256; +static const DWORD friendly_name_max_length = 256; +static const DWORD hardware_id_max_length = 256; + +// Convert a wide Unicode string to an UTF8 string +std::string utf8_encode(const std::wstring & wstr) +{ + int size_needed = + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +} + +vector serial::list_ports() +{ + vector devices_found; + + HDEVINFO device_info_set = + SetupDiGetClassDevs((const GUID *)&GUID_DEVCLASS_PORTS, NULL, NULL, DIGCF_PRESENT); + + unsigned int device_info_set_index = 0; + SP_DEVINFO_DATA device_info_data; + + device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + + while (SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data)) { + device_info_set_index++; + + // Get port name + + HKEY hkey = SetupDiOpenDevRegKey( + device_info_set, &device_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + + TCHAR port_name[port_name_max_length]; + DWORD port_name_length = port_name_max_length; + + LONG return_code = + RegQueryValueEx(hkey, _T("PortName"), NULL, NULL, (LPBYTE)port_name, &port_name_length); + + RegCloseKey(hkey); + + if (return_code != EXIT_SUCCESS) continue; + + if (port_name_length > 0 && port_name_length <= port_name_max_length) + port_name[port_name_length - 1] = '\0'; + else + port_name[0] = '\0'; + + // Ignore parallel ports + + if (_tcsstr(port_name, _T("LPT")) != NULL) continue; + + // Get port friendly name + + TCHAR friendly_name[friendly_name_max_length]; + DWORD friendly_name_actual_length = 0; + + BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty( + device_info_set, &device_info_data, SPDRP_FRIENDLYNAME, NULL, (PBYTE)friendly_name, + friendly_name_max_length, &friendly_name_actual_length); + + if (got_friendly_name == TRUE && friendly_name_actual_length > 0) + friendly_name[friendly_name_actual_length - 1] = '\0'; + else + friendly_name[0] = '\0'; + + // Get hardware ID + + TCHAR hardware_id[hardware_id_max_length]; + DWORD hardware_id_actual_length = 0; + + BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty( + device_info_set, &device_info_data, SPDRP_HARDWAREID, NULL, (PBYTE)hardware_id, + hardware_id_max_length, &hardware_id_actual_length); + + if (got_hardware_id == TRUE && hardware_id_actual_length > 0) + hardware_id[hardware_id_actual_length - 1] = '\0'; + else + hardware_id[0] = '\0'; + +#ifdef UNICODE + std::string portName = utf8_encode(port_name); + std::string friendlyName = utf8_encode(friendly_name); + std::string hardwareId = utf8_encode(hardware_id); +#else + std::string portName = port_name; + std::string friendlyName = friendly_name; + std::string hardwareId = hardware_id; +#endif + + PortInfo port_entry; + port_entry.port = portName; + port_entry.description = friendlyName; + port_entry.hardware_id = hardwareId; + + devices_found.push_back(port_entry); + } + + SetupDiDestroyDeviceInfoList(device_info_set); + + return devices_found; +} + +#endif // #if defined(_WIN32) \ No newline at end of file diff --git a/io/serial/src/impl/unix.cc b/io/serial/src/impl/unix.cc new file mode 100644 index 0000000..dbf605c --- /dev/null +++ b/io/serial/src/impl/unix.cc @@ -0,0 +1,1050 @@ +/* Copyright 2012 William Woodall and John Harrison + * + * Additional Contributors: Christopher Baker @bakercp + */ + +#if !defined(_WIN32) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(__linux__) +#include +#endif + +#include +#include +#include +#ifdef __MACH__ +#include +#include +#include +#endif + +#include "serial/impl/unix.h" + +#ifndef TIOCINQ +#ifdef FIONREAD +#define TIOCINQ FIONREAD +#else +#define TIOCINQ 0x541B +#endif +#endif + +#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3) +#include +#endif + +using serial::IOException; +using serial::MillisecondTimer; +using serial::PortNotOpenedException; +using serial::Serial; +using serial::SerialException; +using std::invalid_argument; +using std::string; +using std::stringstream; + +MillisecondTimer::MillisecondTimer(const uint32_t millis) : expiry(timespec_now()) +{ + int64_t tv_nsec = expiry.tv_nsec + (millis * 1e6); + if (tv_nsec >= 1e9) { + int64_t sec_diff = tv_nsec / static_cast(1e9); + expiry.tv_nsec = tv_nsec % static_cast(1e9); + expiry.tv_sec += sec_diff; + } else { + expiry.tv_nsec = tv_nsec; + } +} + +int64_t MillisecondTimer::remaining() +{ + timespec now(timespec_now()); + int64_t millis = (expiry.tv_sec - now.tv_sec) * 1e3; + millis += (expiry.tv_nsec - now.tv_nsec) / 1e6; + return millis; +} + +timespec MillisecondTimer::timespec_now() +{ + timespec time; +#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + time.tv_sec = mts.tv_sec; + time.tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_MONOTONIC, &time); +#endif + return time; +} + +timespec timespec_from_ms(const uint32_t millis) +{ + timespec time; + time.tv_sec = millis / 1e3; + time.tv_nsec = (millis - (time.tv_sec * 1e3)) * 1e6; + return time; +} + +Serial::SerialImpl::SerialImpl( + const string & port, unsigned long baudrate, bytesize_t bytesize, parity_t parity, + stopbits_t stopbits, flowcontrol_t flowcontrol) +: port_(port), + fd_(-1), + is_open_(false), + xonxoff_(false), + rtscts_(false), + baudrate_(baudrate), + parity_(parity), + bytesize_(bytesize), + stopbits_(stopbits), + flowcontrol_(flowcontrol) +{ + pthread_mutex_init(&this->read_mutex, NULL); + pthread_mutex_init(&this->write_mutex, NULL); + if (port_.empty() == false) open(); +} + +Serial::SerialImpl::~SerialImpl() +{ + close(); + pthread_mutex_destroy(&this->read_mutex); + pthread_mutex_destroy(&this->write_mutex); +} + +void Serial::SerialImpl::open() +{ + if (port_.empty()) { + throw invalid_argument("Empty port is invalid."); + } + if (is_open_ == true) { + throw SerialException("Serial port already open."); + } + + fd_ = ::open(port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd_ == -1) { + switch (errno) { + case EINTR: + // Recurse because this is a recoverable error. + open(); + return; + case ENFILE: + case EMFILE: + THROW(IOException, "Too many file handles open."); + default: + THROW(IOException, errno); + } + } + + reconfigurePort(); + is_open_ = true; +} + +void Serial::SerialImpl::reconfigurePort() +{ + if (fd_ == -1) { + // Can only operate on a valid file descriptor + THROW(IOException, "Invalid file descriptor, is the serial port open?"); + } + + struct termios options; // The options for the file descriptor + + if (tcgetattr(fd_, &options) == -1) { + THROW(IOException, "::tcgetattr"); + } + + // set up raw mode / no echo / binary + options.c_cflag |= (tcflag_t)(CLOCAL | CREAD); + options.c_lflag &= + (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN); //|ECHOPRT + + options.c_oflag &= (tcflag_t) ~(OPOST); + options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK); +#ifdef IUCLC + options.c_iflag &= (tcflag_t)~IUCLC; +#endif +#ifdef PARMRK + options.c_iflag &= (tcflag_t)~PARMRK; +#endif + + // setup baud rate + bool custom_baud = false; + speed_t baud; + switch (baudrate_) { +#ifdef B0 + case 0: + baud = B0; + break; +#endif +#ifdef B50 + case 50: + baud = B50; + break; +#endif +#ifdef B75 + case 75: + baud = B75; + break; +#endif +#ifdef B110 + case 110: + baud = B110; + break; +#endif +#ifdef B134 + case 134: + baud = B134; + break; +#endif +#ifdef B150 + case 150: + baud = B150; + break; +#endif +#ifdef B200 + case 200: + baud = B200; + break; +#endif +#ifdef B300 + case 300: + baud = B300; + break; +#endif +#ifdef B600 + case 600: + baud = B600; + break; +#endif +#ifdef B1200 + case 1200: + baud = B1200; + break; +#endif +#ifdef B1800 + case 1800: + baud = B1800; + break; +#endif +#ifdef B2400 + case 2400: + baud = B2400; + break; +#endif +#ifdef B4800 + case 4800: + baud = B4800; + break; +#endif +#ifdef B7200 + case 7200: + baud = B7200; + break; +#endif +#ifdef B9600 + case 9600: + baud = B9600; + break; +#endif +#ifdef B14400 + case 14400: + baud = B14400; + break; +#endif +#ifdef B19200 + case 19200: + baud = B19200; + break; +#endif +#ifdef B28800 + case 28800: + baud = B28800; + break; +#endif +#ifdef B57600 + case 57600: + baud = B57600; + break; +#endif +#ifdef B76800 + case 76800: + baud = B76800; + break; +#endif +#ifdef B38400 + case 38400: + baud = B38400; + break; +#endif +#ifdef B115200 + case 115200: + baud = B115200; + break; +#endif +#ifdef B128000 + case 128000: + baud = B128000; + break; +#endif +#ifdef B153600 + case 153600: + baud = B153600; + break; +#endif +#ifdef B230400 + case 230400: + baud = B230400; + break; +#endif +#ifdef B256000 + case 256000: + baud = B256000; + break; +#endif +#ifdef B460800 + case 460800: + baud = B460800; + break; +#endif +#ifdef B500000 + case 500000: + baud = B500000; + break; +#endif +#ifdef B576000 + case 576000: + baud = B576000; + break; +#endif +#ifdef B921600 + case 921600: + baud = B921600; + break; +#endif +#ifdef B1000000 + case 1000000: + baud = B1000000; + break; +#endif +#ifdef B1152000 + case 1152000: + baud = B1152000; + break; +#endif +#ifdef B1500000 + case 1500000: + baud = B1500000; + break; +#endif +#ifdef B2000000 + case 2000000: + baud = B2000000; + break; +#endif +#ifdef B2500000 + case 2500000: + baud = B2500000; + break; +#endif +#ifdef B3000000 + case 3000000: + baud = B3000000; + break; +#endif +#ifdef B3500000 + case 3500000: + baud = B3500000; + break; +#endif +#ifdef B4000000 + case 4000000: + baud = B4000000; + break; +#endif + default: + custom_baud = true; + // OS X support +#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) + // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates + // other than those specified by POSIX. The driver for the underlying serial hardware + // ultimately determines which baud rates can be used. This ioctl sets both the input + // and output speed. + speed_t new_baud = static_cast(baudrate_); + if (-1 == ioctl(fd_, IOSSIOSPEED, &new_baud, 1)) { + THROW(IOException, errno); + } + // Linux Support +#elif defined(__linux__) && defined(TIOCSSERIAL) + struct serial_struct ser; + + if (-1 == ioctl(fd_, TIOCGSERIAL, &ser)) { + THROW(IOException, errno); + } + + // set custom divisor + ser.custom_divisor = ser.baud_base / static_cast(baudrate_); + // update flags + ser.flags &= ~ASYNC_SPD_MASK; + ser.flags |= ASYNC_SPD_CUST; + + if (-1 == ioctl(fd_, TIOCSSERIAL, &ser)) { + THROW(IOException, errno); + } +#else + throw invalid_argument("OS does not currently support custom bauds"); +#endif + } + if (custom_baud == false) { +#ifdef _BSD_SOURCE + ::cfsetspeed(&options, baud); +#else + ::cfsetispeed(&options, baud); + ::cfsetospeed(&options, baud); +#endif + } + + // setup char len + options.c_cflag &= (tcflag_t)~CSIZE; + if (bytesize_ == eightbits) + options.c_cflag |= CS8; + else if (bytesize_ == sevenbits) + options.c_cflag |= CS7; + else if (bytesize_ == sixbits) + options.c_cflag |= CS6; + else if (bytesize_ == fivebits) + options.c_cflag |= CS5; + else + throw invalid_argument("invalid char len"); + // setup stopbits + if (stopbits_ == stopbits_one) + options.c_cflag &= (tcflag_t) ~(CSTOPB); + else if (stopbits_ == stopbits_one_point_five) + // ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5 + options.c_cflag |= (CSTOPB); + else if (stopbits_ == stopbits_two) + options.c_cflag |= (CSTOPB); + else + throw invalid_argument("invalid stop bit"); + // setup parity + options.c_iflag &= (tcflag_t) ~(INPCK | ISTRIP); + if (parity_ == parity_none) { + options.c_cflag &= (tcflag_t) ~(PARENB | PARODD); + } else if (parity_ == parity_even) { + options.c_cflag &= (tcflag_t) ~(PARODD); + options.c_cflag |= (PARENB); + } else if (parity_ == parity_odd) { + options.c_cflag |= (PARENB | PARODD); + } +#ifdef CMSPAR + else if (parity_ == parity_mark) { + options.c_cflag |= (PARENB | CMSPAR | PARODD); + } else if (parity_ == parity_space) { + options.c_cflag |= (PARENB | CMSPAR); + options.c_cflag &= (tcflag_t) ~(PARODD); + } +#else + // CMSPAR is not defined on OSX. So do not support mark or space parity. + else if (parity_ == parity_mark || parity_ == parity_space) { + throw invalid_argument("OS does not support mark or space parity"); + } +#endif // ifdef CMSPAR + else { + throw invalid_argument("invalid parity"); + } + // setup flow control + if (flowcontrol_ == flowcontrol_none) { + xonxoff_ = false; + rtscts_ = false; + } + if (flowcontrol_ == flowcontrol_software) { + xonxoff_ = true; + rtscts_ = false; + } + if (flowcontrol_ == flowcontrol_hardware) { + xonxoff_ = false; + rtscts_ = true; + } + // xonxoff +#ifdef IXANY + if (xonxoff_) + options.c_iflag |= (IXON | IXOFF); //|IXANY) + else + options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | IXANY); +#else + if (xonxoff_) + options.c_iflag |= (IXON | IXOFF); + else + options.c_iflag &= (tcflag_t) ~(IXON | IXOFF); +#endif + // rtscts +#ifdef CRTSCTS + if (rtscts_) + options.c_cflag |= (CRTSCTS); + else + options.c_cflag &= (unsigned long)~(CRTSCTS); +#elif defined CNEW_RTSCTS + if (rtscts_) + options.c_cflag |= (CNEW_RTSCTS); + else + options.c_cflag &= (unsigned long)~(CNEW_RTSCTS); +#else +#error "OS Support seems wrong." +#endif + + // http://www.unixwiz.net/techtips/termios-vmin-vtime.html + // this basically sets the read call up to be a polling read, + // but we are using select to ensure there is data available + // to read before each call, so we should never needlessly poll + options.c_cc[VMIN] = 0; + options.c_cc[VTIME] = 0; + + // activate settings + ::tcsetattr(fd_, TCSANOW, &options); + + // Update byte_time_ based on the new settings. + uint32_t bit_time_ns = 1e9 / baudrate_; + byte_time_ns_ = bit_time_ns * (1 + bytesize_ + parity_ + stopbits_); + + // Compensate for the stopbits_one_point_five enum being equal to int 3, + // and not 1.5. + if (stopbits_ == stopbits_one_point_five) { + byte_time_ns_ += ((1.5 - stopbits_one_point_five) * bit_time_ns); + } +} + +void Serial::SerialImpl::close() +{ + if (is_open_ == true) { + if (fd_ != -1) { + int ret; + ret = ::close(fd_); + if (ret == 0) { + fd_ = -1; + } else { + THROW(IOException, errno); + } + } + is_open_ = false; + } +} + +bool Serial::SerialImpl::isOpen() const { return is_open_; } + +size_t Serial::SerialImpl::available() +{ + if (!is_open_) { + return 0; + } + int count = 0; + if (-1 == ioctl(fd_, TIOCINQ, &count)) { + THROW(IOException, errno); + } else { + return static_cast(count); + } +} + +bool Serial::SerialImpl::waitReadable(uint32_t timeout) +{ + // Setup a select call to block for serial data or a timeout + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd_, &readfds); + timespec timeout_ts(timespec_from_ms(timeout)); + int r = pselect(fd_ + 1, &readfds, NULL, NULL, &timeout_ts, NULL); + + if (r < 0) { + // Select was interrupted + if (errno == EINTR) { + return false; + } + // Otherwise there was some error + THROW(IOException, errno); + } + // Timeout occurred + if (r == 0) { + return false; + } + // This shouldn't happen, if r > 0 our fd has to be in the list! + if (!FD_ISSET(fd_, &readfds)) { + THROW( + IOException, + "select reports ready to read, but our fd isn't" + " in the list, this shouldn't happen!"); + } + // Data available to read. + return true; +} + +void Serial::SerialImpl::waitByteTimes(size_t count) +{ + timespec wait_time = {0, static_cast(byte_time_ns_ * count)}; + pselect(0, NULL, NULL, NULL, &wait_time, NULL); +} + +size_t Serial::SerialImpl::read(uint8_t * buf, size_t size) +{ + // If the port is not open, throw + if (!is_open_) { + throw PortNotOpenedException("Serial::read"); + } + size_t bytes_read = 0; + + // Calculate total timeout in milliseconds t_c + (t_m * N) + long total_timeout_ms = timeout_.read_timeout_constant; + total_timeout_ms += timeout_.read_timeout_multiplier * static_cast(size); + MillisecondTimer total_timeout(total_timeout_ms); + + // Pre-fill buffer with available bytes + { + ssize_t bytes_read_now = ::read(fd_, buf, size); + if (bytes_read_now > 0) { + bytes_read = bytes_read_now; + } + } + + while (bytes_read < size) { + int64_t timeout_remaining_ms = total_timeout.remaining(); + if (timeout_remaining_ms <= 0) { + // Timed out + break; + } + // Timeout for the next select is whichever is less of the remaining + // total read timeout and the inter-byte timeout. + uint32_t timeout = + std::min(static_cast(timeout_remaining_ms), timeout_.inter_byte_timeout); + // Wait for the device to be readable, and then attempt to read. + if (waitReadable(timeout)) { + // If it's a fixed-length multi-byte read, insert a wait here so that + // we can attempt to grab the whole thing in a single IO call. Skip + // this wait if a non-max inter_byte_timeout is specified. + if (size > 1 && timeout_.inter_byte_timeout == Timeout::max()) { + size_t bytes_available = available(); + if (bytes_available + bytes_read < size) { + waitByteTimes(size - (bytes_available + bytes_read)); + } + } + // This should be non-blocking returning only what is available now + // Then returning so that select can block again. + ssize_t bytes_read_now = ::read(fd_, buf + bytes_read, size - bytes_read); + // read should always return some data as select reported it was + // ready to read when we get to this point. + if (bytes_read_now < 1) { + // Disconnected devices, at least on Linux, show the + // behavior that they are always ready to read immediately + // but reading returns nothing. + throw SerialException( + "device reports readiness to read but " + "returned no data (device disconnected?)"); + } + // Update bytes_read + bytes_read += static_cast(bytes_read_now); + // If bytes_read == size then we have read everything we need + if (bytes_read == size) { + break; + } + // If bytes_read < size then we have more to read + if (bytes_read < size) { + continue; + } + // If bytes_read > size then we have over read, which shouldn't happen + if (bytes_read > size) { + throw SerialException( + "read over read, too many bytes where " + "read, this shouldn't happen, might be " + "a logical error!"); + } + } + } + return bytes_read; +} + +size_t Serial::SerialImpl::write(const uint8_t * data, size_t length) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::write"); + } + fd_set writefds; + size_t bytes_written = 0; + + // Calculate total timeout in milliseconds t_c + (t_m * N) + long total_timeout_ms = timeout_.write_timeout_constant; + total_timeout_ms += timeout_.write_timeout_multiplier * static_cast(length); + MillisecondTimer total_timeout(total_timeout_ms); + + bool first_iteration = true; + while (bytes_written < length) { + int64_t timeout_remaining_ms = total_timeout.remaining(); + // Only consider the timeout if it's not the first iteration of the loop + // otherwise a timeout of 0 won't be allowed through + if (!first_iteration && (timeout_remaining_ms <= 0)) { + // Timed out + break; + } + first_iteration = false; + + timespec timeout(timespec_from_ms(timeout_remaining_ms)); + + FD_ZERO(&writefds); + FD_SET(fd_, &writefds); + + // Do the select + int r = pselect(fd_ + 1, NULL, &writefds, NULL, &timeout, NULL); + + // Figure out what happened by looking at select's response 'r' + /** Error **/ + if (r < 0) { + // Select was interrupted, try again + if (errno == EINTR) { + continue; + } + // Otherwise there was some error + THROW(IOException, errno); + } + /** Timeout **/ + if (r == 0) { + break; + } + /** Port ready to write **/ + if (r > 0) { + // Make sure our file descriptor is in the ready to write list + if (FD_ISSET(fd_, &writefds)) { + // This will write some + ssize_t bytes_written_now = ::write(fd_, data + bytes_written, length - bytes_written); + // write should always return some data as select reported it was + // ready to write when we get to this point. + if (bytes_written_now < 1) { + // Disconnected devices, at least on Linux, show the + // behavior that they are always ready to write immediately + // but writing returns nothing. + throw SerialException( + "device reports readiness to write but " + "returned no data (device disconnected?)"); + } + // Update bytes_written + bytes_written += static_cast(bytes_written_now); + // If bytes_written == size then we have written everything we need to + if (bytes_written == length) { + break; + } + // If bytes_written < size then we have more to write + if (bytes_written < length) { + continue; + } + // If bytes_written > size then we have over written, which shouldn't happen + if (bytes_written > length) { + throw SerialException( + "write over wrote, too many bytes where " + "written, this shouldn't happen, might be " + "a logical error!"); + } + } + // This shouldn't happen, if r > 0 our fd has to be in the list! + THROW( + IOException, + "select reports ready to write, but our fd isn't" + " in the list, this shouldn't happen!"); + } + } + return bytes_written; +} + +void Serial::SerialImpl::setPort(const string & port) { port_ = port; } + +string Serial::SerialImpl::getPort() const { return port_; } + +void Serial::SerialImpl::setTimeout(serial::Timeout & timeout) { timeout_ = timeout; } + +serial::Timeout Serial::SerialImpl::getTimeout() const { return timeout_; } + +void Serial::SerialImpl::setBaudrate(unsigned long baudrate) +{ + baudrate_ = baudrate; + if (is_open_) reconfigurePort(); +} + +unsigned long Serial::SerialImpl::getBaudrate() const { return baudrate_; } + +void Serial::SerialImpl::setBytesize(serial::bytesize_t bytesize) +{ + bytesize_ = bytesize; + if (is_open_) reconfigurePort(); +} + +serial::bytesize_t Serial::SerialImpl::getBytesize() const { return bytesize_; } + +void Serial::SerialImpl::setParity(serial::parity_t parity) +{ + parity_ = parity; + if (is_open_) reconfigurePort(); +} + +serial::parity_t Serial::SerialImpl::getParity() const { return parity_; } + +void Serial::SerialImpl::setStopbits(serial::stopbits_t stopbits) +{ + stopbits_ = stopbits; + if (is_open_) reconfigurePort(); +} + +serial::stopbits_t Serial::SerialImpl::getStopbits() const { return stopbits_; } + +void Serial::SerialImpl::setFlowcontrol(serial::flowcontrol_t flowcontrol) +{ + flowcontrol_ = flowcontrol; + if (is_open_) reconfigurePort(); +} + +serial::flowcontrol_t Serial::SerialImpl::getFlowcontrol() const { return flowcontrol_; } + +void Serial::SerialImpl::flush() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::flush"); + } + tcdrain(fd_); +} + +void Serial::SerialImpl::flushInput() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::flushInput"); + } + tcflush(fd_, TCIFLUSH); +} + +void Serial::SerialImpl::flushOutput() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::flushOutput"); + } + tcflush(fd_, TCOFLUSH); +} + +void Serial::SerialImpl::sendBreak(int duration) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::sendBreak"); + } + tcsendbreak(fd_, static_cast(duration / 4)); +} + +void Serial::SerialImpl::setBreak(bool level) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::setBreak"); + } + + if (level) { + if (-1 == ioctl(fd_, TIOCSBRK)) { + stringstream ss; + ss << "setBreak failed on a call to ioctl(TIOCSBRK): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + } else { + if (-1 == ioctl(fd_, TIOCCBRK)) { + stringstream ss; + ss << "setBreak failed on a call to ioctl(TIOCCBRK): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + } +} + +void Serial::SerialImpl::setRTS(bool level) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::setRTS"); + } + + int command = TIOCM_RTS; + + if (level) { + if (-1 == ioctl(fd_, TIOCMBIS, &command)) { + stringstream ss; + ss << "setRTS failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + } else { + if (-1 == ioctl(fd_, TIOCMBIC, &command)) { + stringstream ss; + ss << "setRTS failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + } +} + +void Serial::SerialImpl::setDTR(bool level) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::setDTR"); + } + + int command = TIOCM_DTR; + + if (level) { + if (-1 == ioctl(fd_, TIOCMBIS, &command)) { + stringstream ss; + ss << "setDTR failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + } else { + if (-1 == ioctl(fd_, TIOCMBIC, &command)) { + stringstream ss; + ss << "setDTR failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + } +} + +bool Serial::SerialImpl::waitForChange() +{ +#ifndef TIOCMIWAIT + + while (is_open_ == true) { + int status; + + if (-1 == ioctl(fd_, TIOCMGET, &status)) { + stringstream ss; + ss << "waitForChange failed on a call to ioctl(TIOCMGET): " << errno << " " + << strerror(errno); + throw(SerialException(ss.str().c_str())); + } else { + if ( + 0 != (status & TIOCM_CTS) || 0 != (status & TIOCM_DSR) || 0 != (status & TIOCM_RI) || + 0 != (status & TIOCM_CD)) { + return true; + } + } + + usleep(1000); + } + + return false; +#else + int command = (TIOCM_CD | TIOCM_DSR | TIOCM_RI | TIOCM_CTS); + + if (-1 == ioctl(fd_, TIOCMIWAIT, &command)) { + stringstream ss; + ss << "waitForDSR failed on a call to ioctl(TIOCMIWAIT): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } + return true; +#endif +} + +bool Serial::SerialImpl::getCTS() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getCTS"); + } + + int status; + + if (-1 == ioctl(fd_, TIOCMGET, &status)) { + stringstream ss; + ss << "getCTS failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } else { + return 0 != (status & TIOCM_CTS); + } +} + +bool Serial::SerialImpl::getDSR() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getDSR"); + } + + int status; + + if (-1 == ioctl(fd_, TIOCMGET, &status)) { + stringstream ss; + ss << "getDSR failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } else { + return 0 != (status & TIOCM_DSR); + } +} + +bool Serial::SerialImpl::getRI() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getRI"); + } + + int status; + + if (-1 == ioctl(fd_, TIOCMGET, &status)) { + stringstream ss; + ss << "getRI failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } else { + return 0 != (status & TIOCM_RI); + } +} + +bool Serial::SerialImpl::getCD() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getCD"); + } + + int status; + + if (-1 == ioctl(fd_, TIOCMGET, &status)) { + stringstream ss; + ss << "getCD failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); + throw(SerialException(ss.str().c_str())); + } else { + return 0 != (status & TIOCM_CD); + } +} + +void Serial::SerialImpl::readLock() +{ + int result = pthread_mutex_lock(&this->read_mutex); + if (result) { + THROW(IOException, result); + } +} + +void Serial::SerialImpl::readUnlock() +{ + int result = pthread_mutex_unlock(&this->read_mutex); + if (result) { + THROW(IOException, result); + } +} + +void Serial::SerialImpl::writeLock() +{ + int result = pthread_mutex_lock(&this->write_mutex); + if (result) { + THROW(IOException, result); + } +} + +void Serial::SerialImpl::writeUnlock() +{ + int result = pthread_mutex_unlock(&this->write_mutex); + if (result) { + THROW(IOException, result); + } +} + +#endif // !defined(_WIN32) \ No newline at end of file diff --git a/io/serial/src/impl/win.cc b/io/serial/src/impl/win.cc new file mode 100644 index 0000000..80e227a --- /dev/null +++ b/io/serial/src/impl/win.cc @@ -0,0 +1,631 @@ +#if defined(_WIN32) + +/* Copyright 2012 William Woodall and John Harrison */ + +#include "serial/impl/win.h" + +#include + +using serial::bytesize_t; +using serial::flowcontrol_t; +using serial::IOException; +using serial::parity_t; +using serial::PortNotOpenedException; +using serial::Serial; +using serial::SerialException; +using serial::stopbits_t; +using serial::Timeout; +using std::invalid_argument; +using std::string; +using std::stringstream; +using std::wstring; + +inline wstring _prefix_port_if_needed(const wstring & input) +{ + static wstring windows_com_port_prefix = L"\\\\.\\"; + if (input.compare(windows_com_port_prefix) != 0) { + return windows_com_port_prefix + input; + } + return input; +} + +Serial::SerialImpl::SerialImpl( + const string & port, unsigned long baudrate, bytesize_t bytesize, parity_t parity, + stopbits_t stopbits, flowcontrol_t flowcontrol) +: port_(port.begin(), port.end()), + fd_(INVALID_HANDLE_VALUE), + is_open_(false), + baudrate_(baudrate), + parity_(parity), + bytesize_(bytesize), + stopbits_(stopbits), + flowcontrol_(flowcontrol) +{ + if (port_.empty() == false) open(); + read_mutex = CreateMutex(NULL, false, NULL); + write_mutex = CreateMutex(NULL, false, NULL); +} + +Serial::SerialImpl::~SerialImpl() +{ + this->close(); + CloseHandle(read_mutex); + CloseHandle(write_mutex); +} + +void Serial::SerialImpl::open() +{ + if (port_.empty()) { + throw invalid_argument("Empty port is invalid."); + } + if (is_open_ == true) { + throw SerialException("Serial port already open."); + } + + // See: https://github.com/wjwwood/serial/issues/84 + wstring port_with_prefix = _prefix_port_if_needed(port_); + LPCWSTR lp_port = port_with_prefix.c_str(); + fd_ = CreateFileW( + lp_port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (fd_ == INVALID_HANDLE_VALUE) { + DWORD create_file_err = GetLastError(); + stringstream ss; + switch (create_file_err) { + case ERROR_FILE_NOT_FOUND: + // Use this->getPort to convert to a std::string + ss << "Specified port, " << this->getPort() << ", does not exist."; + THROW(IOException, ss.str().c_str()); + default: + ss << "Unknown error opening the serial port: " << create_file_err; + THROW(IOException, ss.str().c_str()); + } + } + + reconfigurePort(); + is_open_ = true; +} + +void Serial::SerialImpl::reconfigurePort() +{ + if (fd_ == INVALID_HANDLE_VALUE) { + // Can only operate on a valid file descriptor + THROW(IOException, "Invalid file descriptor, is the serial port open?"); + } + + DCB dcbSerialParams = {0}; + + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + + if (!GetCommState(fd_, &dcbSerialParams)) { + //error getting state + THROW(IOException, "Error getting the serial port state."); + } + + // setup baud rate + switch (baudrate_) { +#ifdef CBR_0 + case 0: + dcbSerialParams.BaudRate = CBR_0; + break; +#endif +#ifdef CBR_50 + case 50: + dcbSerialParams.BaudRate = CBR_50; + break; +#endif +#ifdef CBR_75 + case 75: + dcbSerialParams.BaudRate = CBR_75; + break; +#endif +#ifdef CBR_110 + case 110: + dcbSerialParams.BaudRate = CBR_110; + break; +#endif +#ifdef CBR_134 + case 134: + dcbSerialParams.BaudRate = CBR_134; + break; +#endif +#ifdef CBR_150 + case 150: + dcbSerialParams.BaudRate = CBR_150; + break; +#endif +#ifdef CBR_200 + case 200: + dcbSerialParams.BaudRate = CBR_200; + break; +#endif +#ifdef CBR_300 + case 300: + dcbSerialParams.BaudRate = CBR_300; + break; +#endif +#ifdef CBR_600 + case 600: + dcbSerialParams.BaudRate = CBR_600; + break; +#endif +#ifdef CBR_1200 + case 1200: + dcbSerialParams.BaudRate = CBR_1200; + break; +#endif +#ifdef CBR_1800 + case 1800: + dcbSerialParams.BaudRate = CBR_1800; + break; +#endif +#ifdef CBR_2400 + case 2400: + dcbSerialParams.BaudRate = CBR_2400; + break; +#endif +#ifdef CBR_4800 + case 4800: + dcbSerialParams.BaudRate = CBR_4800; + break; +#endif +#ifdef CBR_7200 + case 7200: + dcbSerialParams.BaudRate = CBR_7200; + break; +#endif +#ifdef CBR_9600 + case 9600: + dcbSerialParams.BaudRate = CBR_9600; + break; +#endif +#ifdef CBR_14400 + case 14400: + dcbSerialParams.BaudRate = CBR_14400; + break; +#endif +#ifdef CBR_19200 + case 19200: + dcbSerialParams.BaudRate = CBR_19200; + break; +#endif +#ifdef CBR_28800 + case 28800: + dcbSerialParams.BaudRate = CBR_28800; + break; +#endif +#ifdef CBR_57600 + case 57600: + dcbSerialParams.BaudRate = CBR_57600; + break; +#endif +#ifdef CBR_76800 + case 76800: + dcbSerialParams.BaudRate = CBR_76800; + break; +#endif +#ifdef CBR_38400 + case 38400: + dcbSerialParams.BaudRate = CBR_38400; + break; +#endif +#ifdef CBR_115200 + case 115200: + dcbSerialParams.BaudRate = CBR_115200; + break; +#endif +#ifdef CBR_128000 + case 128000: + dcbSerialParams.BaudRate = CBR_128000; + break; +#endif +#ifdef CBR_153600 + case 153600: + dcbSerialParams.BaudRate = CBR_153600; + break; +#endif +#ifdef CBR_230400 + case 230400: + dcbSerialParams.BaudRate = CBR_230400; + break; +#endif +#ifdef CBR_256000 + case 256000: + dcbSerialParams.BaudRate = CBR_256000; + break; +#endif +#ifdef CBR_460800 + case 460800: + dcbSerialParams.BaudRate = CBR_460800; + break; +#endif +#ifdef CBR_921600 + case 921600: + dcbSerialParams.BaudRate = CBR_921600; + break; +#endif + default: + // Try to blindly assign it + dcbSerialParams.BaudRate = baudrate_; + } + + // setup char len + if (bytesize_ == eightbits) + dcbSerialParams.ByteSize = 8; + else if (bytesize_ == sevenbits) + dcbSerialParams.ByteSize = 7; + else if (bytesize_ == sixbits) + dcbSerialParams.ByteSize = 6; + else if (bytesize_ == fivebits) + dcbSerialParams.ByteSize = 5; + else + throw invalid_argument("invalid char len"); + + // setup stopbits + if (stopbits_ == stopbits_one) + dcbSerialParams.StopBits = ONESTOPBIT; + else if (stopbits_ == stopbits_one_point_five) + dcbSerialParams.StopBits = ONE5STOPBITS; + else if (stopbits_ == stopbits_two) + dcbSerialParams.StopBits = TWOSTOPBITS; + else + throw invalid_argument("invalid stop bit"); + + // setup parity + if (parity_ == parity_none) { + dcbSerialParams.Parity = NOPARITY; + } else if (parity_ == parity_even) { + dcbSerialParams.Parity = EVENPARITY; + } else if (parity_ == parity_odd) { + dcbSerialParams.Parity = ODDPARITY; + } else if (parity_ == parity_mark) { + dcbSerialParams.Parity = MARKPARITY; + } else if (parity_ == parity_space) { + dcbSerialParams.Parity = SPACEPARITY; + } else { + throw invalid_argument("invalid parity"); + } + + // setup flowcontrol + if (flowcontrol_ == flowcontrol_none) { + dcbSerialParams.fOutxCtsFlow = false; + dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE; + dcbSerialParams.fOutX = false; + dcbSerialParams.fInX = false; + } + if (flowcontrol_ == flowcontrol_software) { + dcbSerialParams.fOutxCtsFlow = false; + dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE; + dcbSerialParams.fOutX = true; + dcbSerialParams.fInX = true; + } + if (flowcontrol_ == flowcontrol_hardware) { + dcbSerialParams.fOutxCtsFlow = true; + dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE; + dcbSerialParams.fOutX = false; + dcbSerialParams.fInX = false; + } + + // activate settings + if (!SetCommState(fd_, &dcbSerialParams)) { + CloseHandle(fd_); + THROW(IOException, "Error setting serial port settings."); + } + + // Setup timeouts + COMMTIMEOUTS timeouts = {0}; + timeouts.ReadIntervalTimeout = timeout_.inter_byte_timeout; + timeouts.ReadTotalTimeoutConstant = timeout_.read_timeout_constant; + timeouts.ReadTotalTimeoutMultiplier = timeout_.read_timeout_multiplier; + timeouts.WriteTotalTimeoutConstant = timeout_.write_timeout_constant; + timeouts.WriteTotalTimeoutMultiplier = timeout_.write_timeout_multiplier; + if (!SetCommTimeouts(fd_, &timeouts)) { + THROW(IOException, "Error setting timeouts."); + } +} + +void Serial::SerialImpl::close() +{ + if (is_open_ == true) { + if (fd_ != INVALID_HANDLE_VALUE) { + int ret; + ret = CloseHandle(fd_); + if (ret == 0) { + stringstream ss; + ss << "Error while closing serial port: " << GetLastError(); + THROW(IOException, ss.str().c_str()); + } else { + fd_ = INVALID_HANDLE_VALUE; + } + } + is_open_ = false; + } +} + +bool Serial::SerialImpl::isOpen() const { return is_open_; } + +size_t Serial::SerialImpl::available() +{ + if (!is_open_) { + return 0; + } + COMSTAT cs; + if (!ClearCommError(fd_, NULL, &cs)) { + stringstream ss; + ss << "Error while checking status of the serial port: " << GetLastError(); + THROW(IOException, ss.str().c_str()); + } + return static_cast(cs.cbInQue); +} + +bool Serial::SerialImpl::waitReadable(uint32_t /*timeout*/) +{ + THROW(IOException, "waitReadable is not implemented on Windows."); + return false; +} + +void Serial::SerialImpl::waitByteTimes(size_t /*count*/) +{ + THROW(IOException, "waitByteTimes is not implemented on Windows."); +} + +size_t Serial::SerialImpl::read(uint8_t * buf, size_t size) +{ + if (!is_open_) { + throw PortNotOpenedException("Serial::read"); + } + DWORD bytes_read; + if (!ReadFile(fd_, buf, static_cast(size), &bytes_read, NULL)) { + stringstream ss; + ss << "Error while reading from the serial port: " << GetLastError(); + THROW(IOException, ss.str().c_str()); + } + return (size_t)(bytes_read); +} + +size_t Serial::SerialImpl::write(const uint8_t * data, size_t length) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::write"); + } + DWORD bytes_written; + if (!WriteFile(fd_, data, static_cast(length), &bytes_written, NULL)) { + stringstream ss; + ss << "Error while writing to the serial port: " << GetLastError(); + THROW(IOException, ss.str().c_str()); + } + return (size_t)(bytes_written); +} + +void Serial::SerialImpl::setPort(const string & port) { port_ = wstring(port.begin(), port.end()); } + +string Serial::SerialImpl::getPort() const { return string(port_.begin(), port_.end()); } + +void Serial::SerialImpl::setTimeout(serial::Timeout & timeout) +{ + timeout_ = timeout; + if (is_open_) { + reconfigurePort(); + } +} + +serial::Timeout Serial::SerialImpl::getTimeout() const { return timeout_; } + +void Serial::SerialImpl::setBaudrate(unsigned long baudrate) +{ + baudrate_ = baudrate; + if (is_open_) { + reconfigurePort(); + } +} + +unsigned long Serial::SerialImpl::getBaudrate() const { return baudrate_; } + +void Serial::SerialImpl::setBytesize(serial::bytesize_t bytesize) +{ + bytesize_ = bytesize; + if (is_open_) { + reconfigurePort(); + } +} + +serial::bytesize_t Serial::SerialImpl::getBytesize() const { return bytesize_; } + +void Serial::SerialImpl::setParity(serial::parity_t parity) +{ + parity_ = parity; + if (is_open_) { + reconfigurePort(); + } +} + +serial::parity_t Serial::SerialImpl::getParity() const { return parity_; } + +void Serial::SerialImpl::setStopbits(serial::stopbits_t stopbits) +{ + stopbits_ = stopbits; + if (is_open_) { + reconfigurePort(); + } +} + +serial::stopbits_t Serial::SerialImpl::getStopbits() const { return stopbits_; } + +void Serial::SerialImpl::setFlowcontrol(serial::flowcontrol_t flowcontrol) +{ + flowcontrol_ = flowcontrol; + if (is_open_) { + reconfigurePort(); + } +} + +serial::flowcontrol_t Serial::SerialImpl::getFlowcontrol() const { return flowcontrol_; } + +void Serial::SerialImpl::flush() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::flush"); + } + FlushFileBuffers(fd_); +} + +void Serial::SerialImpl::flushInput() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::flushInput"); + } + PurgeComm(fd_, PURGE_RXCLEAR); +} + +void Serial::SerialImpl::flushOutput() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::flushOutput"); + } + PurgeComm(fd_, PURGE_TXCLEAR); +} + +void Serial::SerialImpl::sendBreak(int /*duration*/) +{ + THROW(IOException, "sendBreak is not supported on Windows."); +} + +void Serial::SerialImpl::setBreak(bool level) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::setBreak"); + } + if (level) { + EscapeCommFunction(fd_, SETBREAK); + } else { + EscapeCommFunction(fd_, CLRBREAK); + } +} + +void Serial::SerialImpl::setRTS(bool level) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::setRTS"); + } + if (level) { + EscapeCommFunction(fd_, SETRTS); + } else { + EscapeCommFunction(fd_, CLRRTS); + } +} + +void Serial::SerialImpl::setDTR(bool level) +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::setDTR"); + } + if (level) { + EscapeCommFunction(fd_, SETDTR); + } else { + EscapeCommFunction(fd_, CLRDTR); + } +} + +bool Serial::SerialImpl::waitForChange() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::waitForChange"); + } + DWORD dwCommEvent; + + if (!SetCommMask(fd_, EV_CTS | EV_DSR | EV_RING | EV_RLSD)) { + // Error setting communications mask + return false; + } + + if (!WaitCommEvent(fd_, &dwCommEvent, NULL)) { + // An error occurred waiting for the event. + return false; + } else { + // Event has occurred. + return true; + } +} + +bool Serial::SerialImpl::getCTS() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getCTS"); + } + DWORD dwModemStatus; + if (!GetCommModemStatus(fd_, &dwModemStatus)) { + THROW(IOException, "Error getting the status of the CTS line."); + } + + return (MS_CTS_ON & dwModemStatus) != 0; +} + +bool Serial::SerialImpl::getDSR() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getDSR"); + } + DWORD dwModemStatus; + if (!GetCommModemStatus(fd_, &dwModemStatus)) { + THROW(IOException, "Error getting the status of the DSR line."); + } + + return (MS_DSR_ON & dwModemStatus) != 0; +} + +bool Serial::SerialImpl::getRI() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getRI"); + } + DWORD dwModemStatus; + if (!GetCommModemStatus(fd_, &dwModemStatus)) { + THROW(IOException, "Error getting the status of the RI line."); + } + + return (MS_RING_ON & dwModemStatus) != 0; +} + +bool Serial::SerialImpl::getCD() +{ + if (is_open_ == false) { + throw PortNotOpenedException("Serial::getCD"); + } + DWORD dwModemStatus; + if (!GetCommModemStatus(fd_, &dwModemStatus)) { + // Error in GetCommModemStatus; + THROW(IOException, "Error getting the status of the CD line."); + } + + return (MS_RLSD_ON & dwModemStatus) != 0; +} + +void Serial::SerialImpl::readLock() +{ + if (WaitForSingleObject(read_mutex, INFINITE) != WAIT_OBJECT_0) { + THROW(IOException, "Error claiming read mutex."); + } +} + +void Serial::SerialImpl::readUnlock() +{ + if (!ReleaseMutex(read_mutex)) { + THROW(IOException, "Error releasing read mutex."); + } +} + +void Serial::SerialImpl::writeLock() +{ + if (WaitForSingleObject(write_mutex, INFINITE) != WAIT_OBJECT_0) { + THROW(IOException, "Error claiming write mutex."); + } +} + +void Serial::SerialImpl::writeUnlock() +{ + if (!ReleaseMutex(write_mutex)) { + THROW(IOException, "Error releasing write mutex."); + } +} + +#endif // #if defined(_WIN32) diff --git a/io/serial/src/serial.cc b/io/serial/src/serial.cc new file mode 100644 index 0000000..409df56 --- /dev/null +++ b/io/serial/src/serial.cc @@ -0,0 +1,293 @@ +/* Copyright 2012 William Woodall and John Harrison */ +#include + +#if !defined(_WIN32) && !defined(__OpenBSD__) && !defined(__FreeBSD__) +#include +#endif + +#if defined(__MINGW32__) +#define alloca __builtin_alloca +#endif + +#include "serial/serial.h" + +#ifdef _WIN32 +#include "serial/impl/win.h" +#else +#include "serial/impl/unix.h" +#endif + +using std::invalid_argument; +using std::min; +using std::numeric_limits; +using std::size_t; +using std::string; +using std::vector; + +using serial::bytesize_t; +using serial::flowcontrol_t; +using serial::IOException; +using serial::parity_t; +using serial::Serial; +using serial::SerialException; +using serial::stopbits_t; + +class Serial::ScopedReadLock +{ +public: + ScopedReadLock(SerialImpl * pimpl) : pimpl_(pimpl) { this->pimpl_->readLock(); } + ~ScopedReadLock() { this->pimpl_->readUnlock(); } + +private: + // Disable copy constructors + ScopedReadLock(const ScopedReadLock &); + const ScopedReadLock & operator=(ScopedReadLock); + + SerialImpl * pimpl_; +}; + +class Serial::ScopedWriteLock +{ +public: + ScopedWriteLock(SerialImpl * pimpl) : pimpl_(pimpl) { this->pimpl_->writeLock(); } + ~ScopedWriteLock() { this->pimpl_->writeUnlock(); } + +private: + // Disable copy constructors + ScopedWriteLock(const ScopedWriteLock &); + const ScopedWriteLock & operator=(ScopedWriteLock); + SerialImpl * pimpl_; +}; + +Serial::Serial( + const string & port, uint32_t baudrate, serial::Timeout timeout, bytesize_t bytesize, + parity_t parity, stopbits_t stopbits, flowcontrol_t flowcontrol) +: pimpl_(new SerialImpl(port, baudrate, bytesize, parity, stopbits, flowcontrol)) +{ + pimpl_->setTimeout(timeout); +} + +Serial::~Serial() { delete pimpl_; } + +void Serial::open() { pimpl_->open(); } + +void Serial::close() { pimpl_->close(); } + +bool Serial::isOpen() const { return pimpl_->isOpen(); } + +size_t Serial::available() { return pimpl_->available(); } + +bool Serial::waitReadable() +{ + serial::Timeout timeout(pimpl_->getTimeout()); + return pimpl_->waitReadable(timeout.read_timeout_constant); +} + +void Serial::waitByteTimes(size_t count) { pimpl_->waitByteTimes(count); } + +size_t Serial::read_(uint8_t * buffer, size_t size) { return this->pimpl_->read(buffer, size); } + +size_t Serial::read(uint8_t * buffer, size_t size) +{ + ScopedReadLock lock(this->pimpl_); + return this->pimpl_->read(buffer, size); +} + +size_t Serial::read(std::vector & buffer, size_t size) +{ + ScopedReadLock lock(this->pimpl_); + uint8_t * buffer_ = new uint8_t[size]; + size_t bytes_read = 0; + + try { + bytes_read = this->pimpl_->read(buffer_, size); + } catch (const std::exception & e) { + delete[] buffer_; + throw; + } + + buffer.insert(buffer.end(), buffer_, buffer_ + bytes_read); + delete[] buffer_; + return bytes_read; +} + +size_t Serial::read(std::string & buffer, size_t size) +{ + ScopedReadLock lock(this->pimpl_); + uint8_t * buffer_ = new uint8_t[size]; + size_t bytes_read = 0; + try { + bytes_read = this->pimpl_->read(buffer_, size); + } catch (const std::exception & e) { + delete[] buffer_; + throw; + } + buffer.append(reinterpret_cast(buffer_), bytes_read); + delete[] buffer_; + return bytes_read; +} + +string Serial::read(size_t size) +{ + std::string buffer; + this->read(buffer, size); + return buffer; +} + +size_t Serial::readline(string & buffer, size_t size, string eol) +{ + ScopedReadLock lock(this->pimpl_); + size_t eol_len = eol.length(); + uint8_t * buffer_ = static_cast(alloca(size * sizeof(uint8_t))); + size_t read_so_far = 0; + while (true) { + size_t bytes_read = this->read_(buffer_ + read_so_far, 1); + read_so_far += bytes_read; + if (bytes_read == 0) { + break; // Timeout occured on reading 1 byte + } + if (string(reinterpret_cast(buffer_ + read_so_far - eol_len), eol_len) == eol) { + break; // EOL found + } + if (read_so_far == size) { + break; // Reached the maximum read length + } + } + buffer.append(reinterpret_cast(buffer_), read_so_far); + return read_so_far; +} + +string Serial::readline(size_t size, string eol) +{ + std::string buffer; + this->readline(buffer, size, eol); + return buffer; +} + +vector Serial::readlines(size_t size, string eol) +{ + ScopedReadLock lock(this->pimpl_); + std::vector lines; + size_t eol_len = eol.length(); + uint8_t * buffer_ = static_cast(alloca(size * sizeof(uint8_t))); + size_t read_so_far = 0; + size_t start_of_line = 0; + while (read_so_far < size) { + size_t bytes_read = this->read_(buffer_ + read_so_far, 1); + read_so_far += bytes_read; + if (bytes_read == 0) { + if (start_of_line != read_so_far) { + lines.push_back(string( + reinterpret_cast(buffer_ + start_of_line), read_so_far - start_of_line)); + } + break; // Timeout occured on reading 1 byte + } + if (string(reinterpret_cast(buffer_ + read_so_far - eol_len), eol_len) == eol) { + // EOL found + lines.push_back(string( + reinterpret_cast(buffer_ + start_of_line), read_so_far - start_of_line)); + start_of_line = read_so_far; + } + if (read_so_far == size) { + if (start_of_line != read_so_far) { + lines.push_back(string( + reinterpret_cast(buffer_ + start_of_line), read_so_far - start_of_line)); + } + break; // Reached the maximum read length + } + } + return lines; +} + +size_t Serial::write(const string & data) +{ + ScopedWriteLock lock(this->pimpl_); + return this->write_(reinterpret_cast(data.c_str()), data.length()); +} + +size_t Serial::write(const std::vector & data) +{ + ScopedWriteLock lock(this->pimpl_); + return this->write_(&data[0], data.size()); +} + +size_t Serial::write(const uint8_t * data, size_t size) +{ + ScopedWriteLock lock(this->pimpl_); + return this->write_(data, size); +} + +size_t Serial::write_(const uint8_t * data, size_t length) { return pimpl_->write(data, length); } + +void Serial::setPort(const string & port) +{ + ScopedReadLock rlock(this->pimpl_); + ScopedWriteLock wlock(this->pimpl_); + bool was_open = pimpl_->isOpen(); + if (was_open) close(); + pimpl_->setPort(port); + if (was_open) open(); +} + +string Serial::getPort() const { return pimpl_->getPort(); } + +void Serial::setTimeout(serial::Timeout & timeout) { pimpl_->setTimeout(timeout); } + +serial::Timeout Serial::getTimeout() const { return pimpl_->getTimeout(); } + +void Serial::setBaudrate(uint32_t baudrate) { pimpl_->setBaudrate(baudrate); } + +uint32_t Serial::getBaudrate() const { return uint32_t(pimpl_->getBaudrate()); } + +void Serial::setBytesize(bytesize_t bytesize) { pimpl_->setBytesize(bytesize); } + +bytesize_t Serial::getBytesize() const { return pimpl_->getBytesize(); } + +void Serial::setParity(parity_t parity) { pimpl_->setParity(parity); } + +parity_t Serial::getParity() const { return pimpl_->getParity(); } + +void Serial::setStopbits(stopbits_t stopbits) { pimpl_->setStopbits(stopbits); } + +stopbits_t Serial::getStopbits() const { return pimpl_->getStopbits(); } + +void Serial::setFlowcontrol(flowcontrol_t flowcontrol) { pimpl_->setFlowcontrol(flowcontrol); } + +flowcontrol_t Serial::getFlowcontrol() const { return pimpl_->getFlowcontrol(); } + +void Serial::flush() +{ + ScopedReadLock rlock(this->pimpl_); + ScopedWriteLock wlock(this->pimpl_); + pimpl_->flush(); +} + +void Serial::flushInput() +{ + ScopedReadLock lock(this->pimpl_); + pimpl_->flushInput(); +} + +void Serial::flushOutput() +{ + ScopedWriteLock lock(this->pimpl_); + pimpl_->flushOutput(); +} + +void Serial::sendBreak(int duration) { pimpl_->sendBreak(duration); } + +void Serial::setBreak(bool level) { pimpl_->setBreak(level); } + +void Serial::setRTS(bool level) { pimpl_->setRTS(level); } + +void Serial::setDTR(bool level) { pimpl_->setDTR(level); } + +bool Serial::waitForChange() { return pimpl_->waitForChange(); } + +bool Serial::getCTS() { return pimpl_->getCTS(); } + +bool Serial::getDSR() { return pimpl_->getDSR(); } + +bool Serial::getRI() { return pimpl_->getRI(); } + +bool Serial::getCD() { return pimpl_->getCD(); } \ No newline at end of file diff --git a/io/socketcan.hpp b/io/socketcan.hpp new file mode 100644 index 0000000..b75bb75 --- /dev/null +++ b/io/socketcan.hpp @@ -0,0 +1,159 @@ +#ifndef IO__SOCKETCAN_HPP +#define IO__SOCKETCAN_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tools/logger.hpp" + +using namespace std::chrono_literals; + +constexpr int MAX_EVENTS = 10; + +namespace io +{ +class SocketCAN +{ +public: + SocketCAN(const std::string & interface, std::function rx_handler) + : interface_(interface), + socket_fd_(-1), + epoll_fd_(-1), + rx_handler_(rx_handler), + quit_(false), + ok_(false) + { + try_open(); + + // 守护线程 + daemon_thread_ = std::thread{[this] { + while (!quit_) { + std::this_thread::sleep_for(100ms); + + if (ok_) continue; + + if (read_thread_.joinable()) read_thread_.join(); + + close(); + try_open(); + } + }}; + } + + ~SocketCAN() + { + quit_ = true; + if (daemon_thread_.joinable()) daemon_thread_.join(); + if (read_thread_.joinable()) read_thread_.join(); + close(); + tools::logger()->info("SocketCAN destructed."); + } + + void write(can_frame * frame) const + { + if (::write(socket_fd_, frame, sizeof(can_frame)) == -1) + throw std::runtime_error("Unable to write!"); + } + +private: + std::string interface_; + int socket_fd_; + int epoll_fd_; + bool quit_; + bool ok_; + std::thread read_thread_; + std::thread daemon_thread_; + can_frame frame_; + epoll_event events_[MAX_EVENTS]; + std::function rx_handler_; + + void open() + { + socket_fd_ = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (socket_fd_ < 0) throw std::runtime_error("Error opening socket!"); + + ifreq ifr; + std::strncpy(ifr.ifr_name, interface_.c_str(), IFNAMSIZ - 1); + if (ioctl(socket_fd_, SIOCGIFINDEX, &ifr) < 0) + throw std::runtime_error("Error getting interface index!"); + + sockaddr_can addr; + std::memset(&addr, 0, sizeof(sockaddr_can)); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + if (bind(socket_fd_, (sockaddr *)&addr, sizeof(sockaddr_can)) < 0) { + ::close(socket_fd_); + throw std::runtime_error("Error binding socket to interface!"); + } + + epoll_event ev; + epoll_fd_ = epoll_create1(0); + if (epoll_fd_ == -1) throw std::runtime_error("Error creating epoll file descriptor!"); + + ev.events = EPOLLIN; + ev.data.fd = socket_fd_; + if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, ev.data.fd, &ev)) + throw std::runtime_error("Error adding socket to epoll file descriptor!"); + + // 接收线程 + read_thread_ = std::thread([this]() { + ok_ = true; + while (!quit_) { + std::this_thread::sleep_for(10us); + + try { + read(); + } catch (const std::exception & e) { + tools::logger()->warn("SocketCAN::read() failed: {}", e.what()); + ok_ = false; + break; + } + } + }); + + tools::logger()->info("SocketCAN opened."); + } + + void try_open() + { + try { + open(); + } catch (const std::exception & e) { + tools::logger()->warn("SocketCAN::open() failed: {}", e.what()); + } + } + + void read() + { + int num_events = epoll_wait(epoll_fd_, events_, MAX_EVENTS, 2); + if (num_events == -1) throw std::runtime_error("Error wating for events!"); + + for (int i = 0; i < num_events; i++) { + ssize_t num_bytes = recv(socket_fd_, &frame_, sizeof(can_frame), MSG_DONTWAIT); + if (num_bytes == -1) throw std::runtime_error("Error reading from SocketCAN!"); + + rx_handler_(frame_); + } + } + + void close() + { + if (socket_fd_ == -1) return; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, socket_fd_, NULL); + ::close(epoll_fd_); + ::close(socket_fd_); + } +}; + +} // namespace io + +#endif // IO__SOCKETCAN_HPP \ No newline at end of file diff --git a/io/usbcamera/usbcamera.cpp b/io/usbcamera/usbcamera.cpp new file mode 100644 index 0000000..d1123c9 --- /dev/null +++ b/io/usbcamera/usbcamera.cpp @@ -0,0 +1,174 @@ +#include "usbcamera.hpp" + +#include + +#include "tools/logger.hpp" +#include "tools/yaml.hpp" + +using namespace std::chrono_literals; + +namespace io +{ +USBCamera::USBCamera(const std::string & open_name, const std::string & config_path) +: open_name_(open_name), quit_(false), ok_(false), queue_(1), open_count_(0) +{ + auto yaml = tools::load(config_path); + image_width_ = tools::read(yaml, "image_width"); + image_height_ = tools::read(yaml, "image_height"); + usb_exposure_ = tools::read(yaml, "usb_exposure"); + usb_frame_rate_ = tools::read(yaml, "usb_frame_rate"); + usb_gamma_ = tools::read(yaml, "usb_gamma"); + usb_gain_ = tools::read(yaml, "usb_gain"); + try_open(); + + // 守护线程 + daemon_thread_ = std::thread{[this] { + // tools::logger()->info("daemon thread start"); + while (!quit_) { + std::this_thread::sleep_for(100ms); + + if (ok_) continue; + + if (open_count_ > 20) { + tools::logger()->warn("Give up to open {} USB camera", this->device_name); + quit_ = true; + + { + std::lock_guard lock(cap_mutex_); + close(); // 先关闭摄像头 + } + + if (capture_thread_.joinable()) { + tools::logger()->warn("Stopping capture thread"); + capture_thread_.join(); + } + + break; + } + + if (capture_thread_.joinable()) capture_thread_.join(); + + { + std::lock_guard lock(cap_mutex_); + close(); + } + try_open(); + } + // tools::logger()->info("daemon thread exit"); + }}; +} + +USBCamera::~USBCamera() +{ + quit_ = true; + { + std::lock_guard lock(cap_mutex_); + close(); + } + if (daemon_thread_.joinable()) daemon_thread_.join(); + if (capture_thread_.joinable()) capture_thread_.join(); + tools::logger()->info("USBCamera destructed."); +} + +cv::Mat USBCamera::read() +{ + std::lock_guard lock(cap_mutex_); + if (!cap_.isOpened()) { + tools::logger()->warn("Failed to read {} USB camera", this->device_name); + return cv::Mat(); + } + cap_ >> img_; + return img_; +} + +void USBCamera::read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp) +{ + CameraData data; + queue_.pop(data); + + img = data.img; + timestamp = data.timestamp; +} + +void USBCamera::open() +{ + std::lock_guard lock(cap_mutex_); + std::string true_device_name = "/dev/" + open_name_; + cap_.open(true_device_name, cv::CAP_V4L); + if (!cap_.isOpened()) { + tools::logger()->warn("Failed to open USB camera"); + return; + } + sharpness_ = cap_.get(cv::CAP_PROP_SHARPNESS); + cap_.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G')); + cap_.set(cv::CAP_PROP_FPS, usb_frame_rate_); + cap_.set(cv::CAP_PROP_AUTO_EXPOSURE, 1); + cap_.set(cv::CAP_PROP_GAMMA, usb_gamma_); + cap_.set(cv::CAP_PROP_GAIN, usb_gain_); + + if (sharpness_ == 2) { + device_name = "left"; + cap_.set(cv::CAP_PROP_FRAME_WIDTH, image_width_); + cap_.set(cv::CAP_PROP_FRAME_HEIGHT, image_height_); + cap_.set(cv::CAP_PROP_EXPOSURE, usb_exposure_); + } else if (sharpness_ == 3) { + device_name = "right"; + cap_.set(cv::CAP_PROP_FRAME_WIDTH, image_width_); + cap_.set(cv::CAP_PROP_FRAME_HEIGHT, image_height_); + cap_.set(cv::CAP_PROP_EXPOSURE, usb_exposure_); + } + + tools::logger()->info("{} USBCamera opened", device_name); + // tools::logger()->info("USBCamera exposure time:{}", cap_.get(cv::CAP_PROP_EXPOSURE)); + tools::logger()->info("USBCamera fps:{}", cap_.get(cv::CAP_PROP_FPS)); + // tools::logger()->info("USBCamera gamma:{}", cap_.get(cv::CAP_PROP_GAMMA)); + + // 取图线程 + capture_thread_ = std::thread{[this] { + ok_ = true; + std::this_thread::sleep_for(50ms); + tools::logger()->info("[{} USB camera] capture thread started ", this->device_name); + while (!quit_) { + std::this_thread::sleep_for(1ms); + + cv::Mat img; + bool success; + { + std::lock_guard lock(cap_mutex_); + if (!cap_.isOpened()) { + break; + } + success = cap_.read(img); + } + + if (!success) { + tools::logger()->warn("Failed to read frame, exiting capture thread"); + break; + } + + auto timestamp = std::chrono::steady_clock::now(); + queue_.push({img, timestamp}); + } + ok_ = false; + }}; +} + +void USBCamera::try_open() +{ + try { + open(); + open_count_++; + } catch (const std::exception & e) { + tools::logger()->warn("{}", e.what()); + } +} + +void USBCamera::close() +{ + if (cap_.isOpened()) { + cap_.release(); + tools::logger()->info("USB camera released."); + } +} + +} // namespace io \ No newline at end of file diff --git a/io/usbcamera/usbcamera.hpp b/io/usbcamera/usbcamera.hpp new file mode 100644 index 0000000..4917041 --- /dev/null +++ b/io/usbcamera/usbcamera.hpp @@ -0,0 +1,49 @@ +#ifndef IO__USBCamera_HPP +#define IO__USBCamera_HPP + +#include +#include +#include +#include + +#include "tools/thread_safe_queue.hpp" + +namespace io +{ +class USBCamera +{ +public: + USBCamera(const std::string & open_name, const std::string & config_path); + ~USBCamera(); + cv::Mat read(); + void read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp); + std::string device_name; + +private: + struct CameraData + { + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + }; + + std::mutex cap_mutex_; + cv::VideoCapture cap_; + cv::Mat img_; + std::string open_name_; + int usb_exposure_, usb_frame_rate_, sharpness_; + int open_count_; + double image_width_, image_height_; + int usb_gamma_, usb_gain_; + bool quit_, ok_; + std::thread capture_thread_; + std::thread daemon_thread_; + tools::ThreadSafeQueue queue_; + + void try_open(); + void open(); + void close(); +}; + +} // namespace io + +#endif \ No newline at end of file diff --git a/mpc_layout.xml b/mpc_layout.xml new file mode 100644 index 0000000..9389d72 --- /dev/null +++ b/mpc_layout.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/neo/intel-igc-core_1.0.13463.18_amd64.deb b/neo/intel-igc-core_1.0.13463.18_amd64.deb new file mode 100644 index 0000000..76685d8 Binary files /dev/null and b/neo/intel-igc-core_1.0.13463.18_amd64.deb differ diff --git a/neo/intel-igc-core_1.0.13463.18_amd64.deb.1 b/neo/intel-igc-core_1.0.13463.18_amd64.deb.1 new file mode 100644 index 0000000..8092b59 Binary files /dev/null and b/neo/intel-igc-core_1.0.13463.18_amd64.deb.1 differ diff --git a/neo/intel-igc-opencl_1.0.13463.18_amd64.deb b/neo/intel-igc-opencl_1.0.13463.18_amd64.deb new file mode 100644 index 0000000..7322079 Binary files /dev/null and b/neo/intel-igc-opencl_1.0.13463.18_amd64.deb differ diff --git a/neo/intel-igc-opencl_1.0.13463.18_amd64.deb.1 b/neo/intel-igc-opencl_1.0.13463.18_amd64.deb.1 new file mode 100644 index 0000000..844dbd3 Binary files /dev/null and b/neo/intel-igc-opencl_1.0.13463.18_amd64.deb.1 differ diff --git a/neo/intel-level-zero-gpu-dbgsym_1.3.25812.14_amd64.ddeb b/neo/intel-level-zero-gpu-dbgsym_1.3.25812.14_amd64.ddeb new file mode 100644 index 0000000..3c332e4 Binary files /dev/null and b/neo/intel-level-zero-gpu-dbgsym_1.3.25812.14_amd64.ddeb differ diff --git a/neo/intel-level-zero-gpu_1.3.25812.14_amd64.deb b/neo/intel-level-zero-gpu_1.3.25812.14_amd64.deb new file mode 100644 index 0000000..0a1fcc7 Binary files /dev/null and b/neo/intel-level-zero-gpu_1.3.25812.14_amd64.deb differ diff --git a/neo/intel-opencl-icd_23.09.25812.14_amd64.deb b/neo/intel-opencl-icd_23.09.25812.14_amd64.deb new file mode 100644 index 0000000..5629f87 Binary files /dev/null and b/neo/intel-opencl-icd_23.09.25812.14_amd64.deb differ diff --git a/neo/libigdgmm12_22.3.0_amd64.deb b/neo/libigdgmm12_22.3.0_amd64.deb new file mode 100644 index 0000000..263c666 Binary files /dev/null and b/neo/libigdgmm12_22.3.0_amd64.deb differ diff --git a/neo/ww09.sum b/neo/ww09.sum new file mode 100644 index 0000000..3ec23cb --- /dev/null +++ b/neo/ww09.sum @@ -0,0 +1,7 @@ +4612de24099fd2f7d5e045a836efb4dbabba1166bb27d68dd8f5b48ec93e410c intel-igc-core_1.0.13463.18_amd64.deb +bfde6a612a260b158210b99ff1fcb7870e4304620c0a997bc40aef6c54f4fdec intel-igc-opencl_1.0.13463.18_amd64.deb +020b604f7768a7b9af3d97001d237ba56b5db5cc281a017a2f6d5c56e34fca60 intel-level-zero-gpu-dbgsym_1.3.25812.14_amd64.ddeb +1a7b533a11fe21146964bfaba99276dc35a387897619c26a750cb8d735043b38 intel-level-zero-gpu_1.3.25812.14_amd64.deb +65bd286bcf379eaaf4e375007713129c46d3ce393048d79f87671416f07b36c8 intel-opencl-icd-dbgsym_23.09.25812.14_amd64.ddeb +a1c23ceabe4db028ba2f1ae1a5dfc79b0a52a2e29215b6098e9d998664944f47 intel-opencl-icd_23.09.25812.14_amd64.deb +de6ed9333a74506de43993eb14c8e0e0eb23314c42b03af66fe9c3df24a8973d libigdgmm12_22.3.0_amd64.deb diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..407d664 --- /dev/null +++ b/readme.md @@ -0,0 +1,351 @@ +# 同济大学SuperPower战队25赛季自瞄算法开源 + +## 本项目亮点 +- 无ROS依赖*,新同学无需学习ROS相关知识就能上手自瞄。 +- 完整工作流,包含开发、编译、调试、部署全流程。 +- 模块化架构,包含各模块的功能说明、代码实现、以及独立的测试程序。 +- 我们提出了“轨迹视角下的自瞄理论”,并据此设计了“自瞄轨迹规划算法”。相比传统自瞄决策算法,该算法实现更简洁、调试更方便、效果更好,击杀时间仅需10s(300HP,14rad/s,2m)。该理论也为后续自瞄技术的改进提供了方向。 + +*为实现哨兵“追击”功能,本项目预留了与导航通信的ROS接口。 + + +## 1 功能介绍 +自瞄的定义和意义。我们对自瞄的定义为“针对移动装甲板目标的自动瞄准和自动火控软件”。当操作手切换成自瞄模式后,自瞄会接管云台的控制权,通过对敌方运动轨迹的预测和弹道解算,控制云台进行追踪;同时,自瞄还会接管发射机构的控制权,判断开火时机。自瞄的意义在于提高我方作战能力,实现短击杀时间、高命中率的作战效果。 + +自瞄的模块组成。经过多年的发展,大部份参赛队伍都具备了自瞄的能力。通过和一些队伍的交流,目前各队的自瞄类似于如图1.1所示的模块划分。装甲板识别器和目标状态估计器作为“感知模块”,提供敌方运动信息。决策器根据上述信息,进行瞄准位置的计算和开火判断,并最终交由控制器进行执行。 +![自瞄模块划分](https://github.com/user-attachments/assets/0e3e960e-0bf0-430a-95f5-d0803d1a6ade) +图1.1 自瞄模块划分 + +自瞄是分布式软件。识别器、估计器、决策器由于算法较为复杂,一般运行在算力较高的小电脑上;出于对实时性和稳定性的考量,控制器一般运行在嵌入式单片机上。模块划分解耦了视觉组和电控组的职责,明确了分工,提高了开发效率。 + +本项目包含了本队自瞄相关的视觉组部分的全部代码,包括但不限于:多线程相机驱动程序、电控通信协议和具体实现、图像-四元数对齐算法、相机内参标定和手眼标定程序、装甲板识别算法(含传统、神经网络多种实现)、装甲板位姿解算、坐标变换和yaw优化算法、基于拓展卡尔曼的整车估计器、瞄准位置和开火决策逻辑、以及各模块独立测试程序等。 + +本项目建立在往年[sp_vision_23](https://github.com/TongjiSuperPower/sp_vision_23)、[sp_vision_24](https://github.com/TongjiSuperPower/sp_vision_24)项目的基础之上。其中,sp_vision_23完全基于Python开发,跑通了自瞄从识别到射击的完整流程,因其运行效率不佳(在导航运行时,帧率仅60FPS),sp_vision_24采用C++重写(帧率约100FPS),并且具备参数文件加载、日志记录、离线检测与重启、类似rosbag(时间戳+视频+四元数)录制等辅助功能,以及各个模块的独立测试程序,实现“赛前问题排查、赛后问题重现”的能力,保证了自瞄的可靠性和稳定性。 + +本项目在上述基础上改进了识别器和决策器。其中,识别器由传统的图像处理方案更换为其它战队开源的基于神经网络的四点检测模型[1,2],以期提高识别器的召回能力。而决策器则由基于经验主义的分段决策逻辑改为基于轨迹优化的规划器,简化代码的同时,实现了更优的云台控制效果和开火决策逻辑,其思路会在后文详细介绍。 + + +## 2 效果展示 +轨迹跟随效果。本项目创新性采用“自瞄轨迹规划算法”,降低控制难度的同时,提升轨迹跟随效果。如图2.1所示,跟随单块装甲板时,稳态误差小于0.01rad,装甲板切换时,具备提前减速的能力。 +![轨迹跟随效果](https://github.com/user-attachments/assets/ac65ab13-bc2c-45c0-9c06-613442fc104d) +图2.1 轨迹跟随效果(5rad/s,3m) + +调试场景演示。本项目容易上手,对新人友好。如视频2.1所示,我们通过无线局域网或插网线方式,使用NoMachine远程桌面软件连接机器人搭载的小电脑,配合PlotJuggler曲线图绘制软件,显示调试时常用的数据信息,如云台yaw关节执行情况、开火决策、击打目标的角速度等。 + +https://github.com/user-attachments/assets/606c2907-2f11-4392-b3fe-7ee4b7b6fd29 + +视频2.1 调试场景演示 + +量化指标统计。如图2.2所示,在25赛季国赛赛场上,搭载本算法的3、4号步兵的最高命中率为39.6%,发挥正常时,命中率不低于30%。除了命中率,我们还统计了击杀时间,面对2m处300血的敌方步兵,7rad/s转速下耗时约8s,14rad/s转速下耗时约10s。 +![国赛MVP结算数据](https://github.com/user-attachments/assets/4e17085d-3bc8-422e-b0df-b0fbe6e4c934) +图2.2 国赛MVP结算数据 + + +## 3 详细信息 +### 3.1 项目环境 +操作系统:Ubuntu 22.04\ +运算平台:NUC12WSKI7(i7-1260P,16GB)\ +相机型号:海康MV-CS016-10UC\ +镜头型号:海康官方6mm镜头\ +下位机型号:RoboMaster开发板C型(STM32F407)\ +IMU型号:使用C板内置BMI088作为IMU\ +通信方式:USB2CAN(旧)、MicroUSB虚拟串口(新)\ +辅助工具:NoMachine(远程桌面)、PlotJuggler(绘制曲线图) + +### 3.2 编译方式 +1. 安装依赖项: + - [MindVision SDK](https://mindvision.com.cn/category/software/sdk-installation-package/)或[HikRobot SDK](https://www.hikrobotics.com/cn2/source/support/software/MVS_STD_GML_V2.1.2_231116.zip) + - [OpenVINO](https://docs.openvino.ai/2024/get-started/install-openvino/install-openvino-archive-linux.html) + - [Ceres](http://ceres-solver.org/installation.html) + - 其余: + ```bash + sudo apt install -y \ + git \ + g++ \ + cmake \ + can-utils \ + libopencv-dev \ + libfmt-dev \ + libeigen3-dev \ + libspdlog-dev \ + libyaml-cpp-dev \ + libusb-1.0-0-dev \ + nlohmann-json3-dev \ + openssh-server \ + screen + ``` + +2. 编译: + ```bash + cmake -B build + make -C build/ -j`nproc` + ``` + +3. 运行demo: + ```bash + ./build/auto_aim_test + ``` + +4. 注册自启: + 1. 确保已安装`screen`: + ``` + sudo apt install screen + ``` + 2. 创建`.desktop`文件: + ``` + mkdir ~/.config/autostart/ + touch ~/.config/autostart/sp_vision.desktop + ``` + 3. 在该文件中写入: + ``` + [Desktop Entry] + Type=Application + Exec=/home/rm/Desktop/sp_vision_25/autostart.sh + Name=sp_vision + ``` + 注: [Exec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html)必须为绝对路径. + 4. 确保`autostart.sh`有可执行权限: + ``` + chmod +x autostart.sh + ``` + +5. USB2CAN设置(可选) + 1. 创建`.rules`文件: + ``` + sudo touch /etc/udev/rules.d/99-can-up.rules + ``` + 2. 在该文件中写入: + ``` + ACTION=="add", KERNEL=="can0", RUN+="/sbin/ip link set can0 up type can bitrate 1000000" + ACTION=="add", KERNEL=="can1", RUN+="/sbin/ip link set can1 up type can bitrate 1000000" + +6. 使用GPU推理(可选) + ``` + mkdir neo + cd neo + + wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.13463.18/intel-igc-core_1.0.13463.18_amd64.deb + wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.13463.18/intel-igc-opencl_1.0.13463.18_amd64.deb + wget https://github.com/intel/compute-runtime/releases/download/23.09.25812.14/intel-level-zero-gpu-dbgsym_1.3.25812.14_amd64.ddeb + wget https://github.com/intel/compute-runtime/releases/download/23.09.25812.14/intel-level-zero-gpu_1.3.25812.14_amd64.deb + wget https://github.com/intel/compute-runtime/releases/download/23.09.25812.14/intel-opencl-icd-dbgsym_23.09.25812.14_amd64.ddeb + wget https://github.com/intel/compute-runtime/releases/download/23.09.25812.14/intel-opencl-icd_23.09.25812.14_amd64.deb + wget https://github.com/intel/compute-runtime/releases/download/23.09.25812.14/libigdgmm12_22.3.0_amd64.deb + wget https://github.com/intel/compute-runtime/releases/download/23.09.25812.14/ww09.sum + + sha256sum -c ww09.sum + sudo dpkg -i *.deb + ``` + 注:如果使用 GPU 异步推理(async-infer),最高显示分辨率限制为 1920×1080 (24Hz) + +7. 串口设置 + 1. 授予权限 + ``` + sudo usermod -a -G dialout $USER + ``` + 2. 获取端口 ID(serial, idVendor, idProduct) + ``` + udevadm info -a -n /dev/ttyACM0 | grep -E '({serial}|{idVendor}|{idProduct})' + ``` + 将 /dev/ttyACM0 替换为实际设备名。 + 3. 创建 udev 规则文件 + ``` + sudo touch /etc/udev/rules.d/99-usb-serial.rules + ``` + 然后在文件中写入如下内容(用真实 ID 替换示例,SYMLINK 是规则应用后固定的串口名): + ``` + SUBSYSTEM=="tty", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="1234", ATTRS{serial}=="A1234567", SYMLINK+="gimbal" + ``` + 4. 重新加载 udev 规则 + ``` + sudo udevadm control --reload-rules + sudo udevadm trigger + ``` + 5. 检查结果 + ``` + ls -l /dev/gimbal + # Expected output (example): + # lrwxrwxrwx 1 root root 7 Jul 21 10:00 /dev/gimbal -> ttyACM0 + ``` + +### 3.3 数据流图 +视觉相关模块如图3.1所示。其中,相机线程产生图像、时间戳,通过下位机线程获取对应的云台姿态四元数;图像经过识别器,获得装甲板的四个顶点像素坐标,以及其图案类别;估计器根据装甲板信息,获得目标单位的运动状态;决策器则根据当前的目标运动状态信息,预测目标的运动轨迹,从而判断最佳瞄准位置和最佳开火时机,形成指令发送给下位机;最后控制器和执行机构则根据该指令进行执行,从而完成一个完整的自瞄流程。 +![数据流图](https://github.com/user-attachments/assets/b89ce42f-a769-49c5-b82a-d69aeac02925) +图3.1 数据流图 + +### 3.4 软件架构 +各兵种需要实现的功能往往有多个,但是每个功能不能作为独立的程序。例如步兵需要自瞄和打符,显然,这两个功能都需要从相机获取图像,但是相机只能被一个程序打开,这会导致另一个程序无法正常工作(相机被占用)。 + +为了解决这个问题,我们提出了视觉框架(sp_vision):自瞄、打符等功能,被拆解为该框架下的一个个功能组,每个功能组包含多个类或函数(如识别器、解算器、预测器等等);而运行的程序(main函数)只有一个,它负责从相机获取图像,再根据电控发来的信号(自瞄档or打符档),选择对应的功能组执行,不同兵种的执行逻辑各不相同,即不同的main函数。此外,为了方便大家合作开发,视觉框架还提供了许多常用的工具函数,减少因重复造轮子所导致的出错概率和时间成本。本框架的组成如图3.2所示。 +![软件架构](https://github.com/user-attachments/assets/2603a3b3-ae1d-4fa8-afbb-490efde30d77) +图3.2 软件架构 + +### 3.5 文件结构 +``` +sp_vision_25 +├── assets // 包含demo素材、网络权重等 +│   └── ... +├── calibration // 标定相关程序 +│   ├── calibrate_camera.cpp // 相机内参标定程序 +│   ├── calibrate_handeye.cpp // 手眼标定程序 +│   ├── calibrate_robotworld_handeye.cpp // 手眼标定程序(同时计算标定板位置) +│   └── capture.cpp // 相机标定数据采集程序 +├── CMakeLists.txt // CMake配置文件 +├── configs // 每台机器人的YAML配置文件 +│   └── ... +├── io // 硬件抽象层,见3.4软件架构 +│   └── ... +├── src // 应用层,见3.4软件架构 +│   └── ... +├── tasks // 功能层,见3.4软件架构 +│   ├── auto_aim // 自瞄相关算法实现 +│   │   └── ... +│   ├── auto_buff // 打符相关算法实现 +│   │   └── ... +│   └── omniperception // 全向感知相关算法实现 +│   │   └── ... +├── tests +│   ├── auto_aim_test.cpp // 自瞄录制视频测试程序 +│   ├── auto_buff_test.cpp // 打符录制视频测试程序 +│   ├── camera_detect_test.cpp // 识别器测试程序(工业相机) +│   ├── camera_test.cpp // 相机测试程序 +│   ├── camera_thread_test.cpp // 相机线程测试程序 +│   ├── cboard_test.cpp // C板测试程序 +│   ├── detector_video_test.cpp // 识别器测试程序(视频) +│   ├── dm_test.cpp // 达妙IMU测试程序 +│   ├── fire_test.cpp // 开火测试程序 +│   ├── gimbal_response_test.cpp // 云台响应测试程序 +│   ├── gimbal_test.cpp // 云台通信测试程序 +│   ├── handeye_test.cpp // 手眼标定测试程序 +│   ├── minimum_vision_system.cpp // 最小视觉系统测试程序 +│   ├── multi_usbcamera_test.cpp // 多USB摄像头测试程序 +│   ├── planner_test_offline.cpp // 规划器测试程序(离线) +│   ├── planner_test.cpp // 规划器测试程序(实车) +│   ├── publish_test.cpp // ROS发送测试程序 +│   ├── subscribe_test.cpp // ROS接收测试程序 +│   ├── topic_loop_test.cpp // ROS话题循环测试程序 +│   ├── usbcamera_detect_test.cpp // 识别器测试程序(USB相机) +│   ├── usbcamera_test.cpp // USB相机测试程序 +│   └── ... +└── tools // 工具层,见3.4软件架构 + ├── crc.hpp // CRC校验 + ├── exiter.hpp // 退出检测 + ├── extended_kalman_filter.hpp // 扩展卡尔曼滤波器 + ├── img_tools.hpp // 图像处理工具 + ├── logger.hpp // 日志记录器 + ├── math_tools.hpp // 数学工具 + ├── plotter.hpp // 曲线图绘制工具 + ├── recorder.hpp // 视频录制器 + ├── thread_safe_queue.hpp // 线程安全队列 + ├── trajectory.hpp // 弹道解算 + ├── yaml.hpp // YAML配置文件解析器 + └── ... +``` + + +## 4 轨迹视角下的自瞄理论 +### 4.1 引言 +在自瞄的研发和测试过程中,我们发现决策器和控制器是目前的短板。 + +决策器实现繁琐。不同兵种、不同敌方移动状态(平移、低速小陀螺、高速小陀螺)需要不同的“自瞄行为”,例如:英雄只瞄准旋转中心位置,根据时间误差判断是否开火;平移和低速时,步兵的瞄准位置则持续跟随敌方装甲板移动,同时依据位置误差判断是否开火,而高速时则退化为类似英雄的情况。不同的自瞄行为需要不同的代码实现,而且需要设计不同的判断条件,这一方面提高了代码维护的成本,另一方面也引入额外参数,增大了调试的负担。 + +控制调参困难。传统控制指标的输入一般为阶跃或斜坡函数,而自瞄控制器的输入更类似于三角波,且周期和幅值随敌方运动状态变化而变化,我们无法找到合适的理论将二者联系起来,因此调参过程中更依赖于经验猜测而不是理论指导。此外,我们无法判断云台的控制效果上限在哪里,甚至对于其影响因素都缺乏充分的认识。这导致电控调参十分“坐牢”,调试一台机器人需要消耗大量时间和资源,并且调试后的机器人表现效果参差不齐。 + +因此,我们希望能够找到一个“理论”,统一不同决策逻辑、精简代码,同时提高我们对于自瞄以及控制的认识,指导我们更加高效、可靠地调试自瞄,最终提高整体的自瞄效果。 + +### 4.2 前置概念 +命中率。命中率越高,意味着所发射的子弹中命中装甲板的数量越多,造成的伤害越高,是用来衡量自瞄效果最常见的指标。然而,“不谈射频的命中率是没有意义的”[3],因为如果自瞄只在最有把握的时候射击,只要时间足够长,早晚能打死对面,所以除了命中率还需要考虑击杀时间。 + +击杀时间。击杀时间衡量了打死敌方单位所需的时间,是衡量自瞄好坏的“金标准”:一方面,装甲板击打检测和发射机构射频均存在上限,杜绝了依靠超高射频刷击杀时间的可行性;另一方面,该指标便于测量,仅需要秒表和靶机即可,比赛时也可以通过回放视频来快速估算。在测量该指标时,需要控制敌方血量、距离和运动状态等变量,这样才能保证时间的可比较性。 + +目标运动状态。目标运动状态由估计器计算得出,用于决策器预测目标在一段时间后的位置。目前主流的估计算法为基于扩展卡尔曼滤波的整车估计器[3,4],其定义的目标运动状态包括目标旋转中心位置、速度、yaw旋转角度、yaw旋转角速度、各装甲板半径和高度差,根据这些信息,决策器可以计算出每个装甲板的位置,并在其中选择击打目标。为了方便后续的论述,这里有两点假设:假设一,估计器是完美的,无需考虑估计导致的误差;假设二,目标处于匀速运动状态,从而无需考虑预测时因目标变速导致的误差。上述两点假设简化了自瞄使用场景的复杂性,帮助我们专注于决策器的改进。 + +目标轨迹和云台轨迹。已知目标运动状态,根据匀速运动公式,可以计算任意时刻t下目标各装甲板位置,选择距离云台最近的装甲板,计算其相对于云台的方位角yaw,得到函数yaw_target(t),记为目标轨迹。由于pitch计算方式类似,这里的“轨迹”只讨论yaw。云台轨迹同理,代表对应时刻云台相对于初始位置所呈的夹角yaw,记为yaw_gimbal(t)。 + +射击轨迹。子弹出膛后需经过子弹飞行时间t_fly后到达装甲板,将目标轨迹提前t_fly可获得射击轨迹,即: +```math +yaw_{shoot}(t) = yaw_{target}(t + t_{fly}) +``` +如果t时刻云台角度yaw_gimbal和对应的射击角度yaw_shoot满足一定的误差范围(半个装甲板),则此时从枪管射出的子弹会命中装甲板。更进一步,如果云台轨迹和射击轨迹重合,则任意时刻从枪管射出的子弹均会命中装甲板,“子弹发射如水流一样”[3]。如图4.1所示,射击轨迹的瞄准位置是14ms后目标轨迹的位置。 +![轨迹示意图](https://github.com/user-attachments/assets/80019e43-8cdc-47d1-98cd-dd8ae3d5e20b) +图4.1 轨迹示意图 + +### 4.3 什么是好自瞄 +在轨迹视角下,“云台轨迹”和“射击轨迹”重合度越高,击杀时间越短。击杀时间反映了DPS(Damage Per Second),公式如下: +```math +击杀时间 = \frac{血量}{DPS} +``` +```math +DPS = 单位时间射击窗口占比 \times 射频 \times 单发子弹伤害 +``` +不难看出,重合度越高,单位时间射击窗口占比越高,DPS越高,击杀时间越短,自瞄效果越好。 + +轨迹重合度和云台控制能力息息相关。云台最大加速度的高低决定了云台控制能力的好坏,云台能产生的瞬时加速度越大,跟随轨迹变化的能力越强,轨迹重合度越高。云台最大加速度计算公式如下: +```math +云台最大加速度 = \frac{云台电机最大扭矩}{云台惯量} +``` +其中,云台电机最大扭矩一般由电机厂家给出,云台惯量通过系统辨识[5]得出。除了云台最大加速度,控制算法的设计也会影响最终的云台控制效果,为了尽可能降低控制难度,我们采取了多种措施: +1. 我们提出了“自瞄轨迹规划器”,根据云台最大加速度优化射击轨迹,使其更容易被云台跟随。 +2. 我们计算了轨迹对应的速度和加速度,作为前馈量一并发送给电控,提高云台的响应。 +3. 我们采用了计算力矩控制算法[6],其结合PID控制和动力学模型,相比双环PID调参更加简便。 + +除了击杀时间,命中率也可以在轨迹视角下进行分析。我们可以将云台轨迹划分为“重合射击轨迹段”和“偏离射击轨迹段”,二者的比例决定了理论命中率上限: +```math +命中率 \le 重合度 = \frac{重合射击轨迹段}{(重合射击轨迹段 + 偏离射击轨迹段)} +``` +云台在移动过程中,不可避免地会出现偏离射击轨迹的情况,当该情况发生时,应停止开火,减少弹丸的浪费,保证命中率。在自瞄轨迹规划器中,我们将上述情况作为开火决策的依据,在保证击杀时间的同时提高命中率。 + +击杀时间越短、命中率越高,自瞄效果越好。前者依赖良好的瞄准位置决策,后者则依赖良好的开火决策。我们从轨迹视角出发,用自瞄轨迹规划器替代传统自瞄决策器:通过考虑云台控制的能力,使得决策后的瞄准位置的更具可行性;通过考虑射击轨迹和规划后轨迹的偏差,结合开火延迟时间,使得开火决策更加简洁清晰,无需额外的分段参数,调试时更加方便。 + +### 4.4 轨迹规划器 +轨迹突变问题和提前减速策略。小陀螺时装甲板会发生切换,导致目标轨迹和射击轨迹突变,不连续点处的瞬时速度和加速度未定义,强行让控制算法跟随这样的轨迹会有明显的超调或滞后现象。针对轨迹突变问题,轨迹规划器采取“提前减速策略”:若未来一段时间后装甲板会发生切换,则提前减速向下一个装甲板过渡,使规划后轨迹的加速度小于云台最大加速度,如图4.2所示。 +![提前减速策略](https://github.com/user-attachments/assets/f3c60424-5ece-4eff-a14e-f30fd9ddd584) +图4.2 提前减速策略示意图 + +该策略的实现方式不止一种,我们尝试了两种方案: +1. 隐式搜索,以规划后轨迹加速度序列为变量,构造代价函数(重合度尽可能高)和约束条件(不超过云台最大加速度),将该问题转换为二次规划问题,调用第三方库TinyMPC[7]求解。该库针对MPC问题的求解进行了加速优化,实测求解耗时小于1ms。这里MPC并非闭环控制器,而是作为求解器寻找可行轨迹解,其输入不包含云台的实际状态。我们也尝试过使用MPC直接作为闭环控制器,通过下位机转发MPC求解后的力矩指令给电机,当自身小陀螺时,控制效果不如下位机方案。 +2. 显式搜索,以过渡时间为变量,确定切换点前后的起点和终点,使用五次多项式生成过渡段轨迹,调整时间长度,直到满足加速度限制。该方案确定性高,“跟随段”不会参与优化,和射击轨迹重合,在高转速下更加稳定。同时实现更加轻量,无需依赖第三方库和QR矩阵等额外参数。 + +由于时间有限,方案二仅进行了仿真验证,未上车测试,国赛上场采用了方案一。 + +开火延迟问题和提前开火决策。从开火命令发送到子弹出膛(摩擦轮掉速)存在时间延迟t_fire,仅考虑当前位置误差进行开火判断并不严谨。轨迹规划器会生成一段时间内的轨迹序列,通过查询t_fire时刻射击轨迹和对应规划后轨迹的误差,判断经过t_fire时间后子弹是否应该出膛,从而实现更加精细的开火决策。该方案的前提是开火延迟无波动,对机械和电控的要求较高。 + +预测时间偏移量。射击轨迹提前于目标轨迹的时间称为预测时间,预测时间除了子弹飞行时间外,还需考虑各个环节引入的时间延迟,包括: +- 图像传输延迟:图像时间戳对应接收完成的时刻,需要图像传输至小电脑的时间。 +- 图像处理延迟:神经网络推理耗时无法忽略,需要考虑图像开始处理到形成决策命令的时间。 +- 下位机通信延迟:向下位机传输决策命令所需的时间。 +- 下位机控制延迟:控制云台到目标位置所需的时间。 + +这里不需要考虑开火延迟,因为在射击轨迹上任意时间发射均会命中[3]。在实际代码实现中,除了图像处理延迟可以直接计算外,我们把其余延迟时间的总和作为一个调试参数[8],通过拍摄慢动作视频、比较击杀时间等方式进行调整,约15ms。 + + +## 5 未来优化方向 +将轨迹规划器部署到更多兵种上。由于时间有限,国赛中仅步兵使用了轨迹规划器。我们非常期待其在英雄(射频低)、哨兵(惯量大)、无人机(射频高)上的表现。 + +边跑边打。目前的自瞄并没有考虑自身的移动,无法边跑边打。在本赛季,步兵经常需要从狗洞冲下去杀对面静止的英雄,而此时自瞄会认为对面在移动,导致前几发打不准,限制了操作手的发挥。我们计划引入轮式里程计信息,改善在该场景下的表现。 + + +## 参考文献 +[1] Alan Day.【RM2024赛季-识别模型】深圳大学-RobotPilots[EB/OL]. RoboMaster论坛. https://bbs.robomaster.com/article/54091, 2025. + +[2] gaoxin.【RM2024-识别训练网络及推理代码开源】北京科技大学Reborn[EB/OL]. RoboMaster论坛. https://bbs.robomaster.com/article/9655, 2024. + +[3] 方俊杰. Linear Modelled Top Detector[EB/OL]. GitHub. https://github.com/julyfun/rm.cv.fans, 2023. + +[4] 陈君. rm_vision[EB/OL]. GitHub. https://github.com/chenjunnn/rm_vision, 2023. + +[5] 桂凯. 系统辨识心得分享[EB/OL]. 知乎. https://www.zhihu.com/question/57405191/answer/153098166, 2017. + +[6] Lynch K M, Park F C. Modern Robotics: Mechanics, Planning, and Control[M]. Cambridge: Cambridge University Press, 2017. + +[7] Nguyen K, Schoedel S, Alavilli A, et al. TinyMPC: Model-predictive control on resource-constrained microcontrollers[C]//2024 IEEE International Conference on Robotics and Automation (ICRA). IEEE, 2024: 1-7. + +[8] 王洪玺, 计泽贤, 张兰勇. 基于卡尔曼滤波的目标识别跟踪与射击系统设计[J]. Journal of Ordnance Equipment Engineering, 2022, 43(11). + + +## 项目成员 +王骁扬、杨佳轩、奚睿豪、俞选涛、吴圳楠、杨瑞灵、程翔宇 + + +## Star History +[![Star History Chart](https://api.star-history.com/svg?repos=TongjiSuperPower/sp_vision_25&type=Date)](https://www.star-history.com/#TongjiSuperPower/sp_vision_25&Date) diff --git a/src/.gitkeep b/src/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/auto_aim_debug_mpc.cpp b/src/auto_aim_debug_mpc.cpp new file mode 100644 index 0000000..8d7657f --- /dev/null +++ b/src/auto_aim_debug_mpc.cpp @@ -0,0 +1,150 @@ +#include + +#include +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/gimbal/gimbal.hpp" +#include "tasks/auto_aim/planner/planner.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/thread_safe_queue.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + tools::Exiter exiter; + tools::Plotter plotter; + + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + io::Gimbal gimbal(config_path); + io::Camera camera(config_path); + + auto_aim::YOLO yolo(config_path, true); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Planner planner(config_path); + + tools::ThreadSafeQueue, true> target_queue(1); + target_queue.push(std::nullopt); + + std::atomic quit = false; + auto plan_thread = std::thread([&]() { + auto t0 = std::chrono::steady_clock::now(); + uint16_t last_bullet_count = 0; + + while (!quit) { + auto target = target_queue.front(); + auto gs = gimbal.state(); + auto plan = planner.plan(target, gs.bullet_speed); + + gimbal.send( + plan.control, plan.fire, plan.yaw, plan.yaw_vel, plan.yaw_acc, plan.pitch, plan.pitch_vel, + plan.pitch_acc); + + auto fired = gs.bullet_count > last_bullet_count; + last_bullet_count = gs.bullet_count; + + nlohmann::json data; + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + + data["gimbal_yaw"] = gs.yaw; + data["gimbal_yaw_vel"] = gs.yaw_vel; + data["gimbal_pitch"] = gs.pitch; + data["gimbal_pitch_vel"] = gs.pitch_vel; + + data["target_yaw"] = plan.target_yaw; + data["target_pitch"] = plan.target_pitch; + + data["plan_yaw"] = plan.yaw; + data["plan_yaw_vel"] = plan.yaw_vel; + data["plan_yaw_acc"] = plan.yaw_acc; + + data["plan_pitch"] = plan.pitch; + data["plan_pitch_vel"] = plan.pitch_vel; + data["plan_pitch_acc"] = plan.pitch_acc; + + data["fire"] = plan.fire ? 1 : 0; + data["fired"] = fired ? 1 : 0; + + if (target.has_value()) { + data["target_z"] = target->ekf_x()[4]; //z + data["target_vz"] = target->ekf_x()[5]; //vz + } + + if (target.has_value()) { + data["w"] = target->ekf_x()[7]; + } else { + data["w"] = 0.0; + } + + plotter.plot(data); + + std::this_thread::sleep_for(10ms); + } + }); + + cv::Mat img; + std::chrono::steady_clock::time_point t; + + while (!exiter.exit()) { + camera.read(img, t); + auto q = gimbal.q(t); + + solver.set_R_gimbal2world(q); + auto armors = yolo.detect(img); + auto targets = tracker.track(armors, t); + if (!targets.empty()) + target_queue.push(targets.front()); + else + target_queue.push(std::nullopt); + + if (!targets.empty()) { + auto target = targets.front(); + + // 当前帧target更新后 + std::vector armor_xyza_list = target.armor_xyza_list(); + for (const Eigen::Vector4d & xyza : armor_xyza_list) { + auto image_points = + solver.reproject_armor(xyza.head(3), xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 255, 0}); + } + + Eigen::Vector4d aim_xyza = planner.debug_xyza; + auto image_points = + solver.reproject_armor(aim_xyza.head(3), aim_xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 0, 255}); + } + + cv::resize(img, img, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("reprojection", img); + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + quit = true; + if (plan_thread.joinable()) plan_thread.join(); + gimbal.send(false, false, 0, 0, 0, 0, 0, 0); + + return 0; +} \ No newline at end of file diff --git a/src/auto_buff_debug.cpp b/src/auto_buff_debug.cpp new file mode 100755 index 0000000..85b8f26 --- /dev/null +++ b/src/auto_buff_debug.cpp @@ -0,0 +1,158 @@ +#include + +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "tasks/auto_buff/buff_aimer.hpp" +#include "tasks/auto_buff/buff_detector.hpp" +#include "tasks/auto_buff/buff_solver.hpp" +#include "tasks/auto_buff/buff_target.hpp" +#include "tasks/auto_buff/buff_type.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" +#include "tools/trajectory.hpp" + +// 定义命令行参数 +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + // 初始化绘图器、录制器、退出器 + tools::Plotter plotter; + tools::Recorder recorder; + tools::Exiter exiter; + + // 初始化C板、相机 + io::CBoard cboard(config_path); + io::Camera camera(config_path); + + // 初始化识别器、解算器、追踪器、瞄准器 + auto_buff::Buff_Detector detector(config_path); + auto_buff::Solver solver(config_path); + auto_buff::SmallTarget target; + // auto_buff::BigTarget target; + auto_buff::Aimer aimer(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + while (!exiter.exit()) { + camera.read(img, t); + q = cboard.imu_at(t); + // recorder.record(img, q, t); + + // -------------- 打符核心逻辑 -------------- + + solver.set_R_gimbal2world(q); + + auto power_runes = detector.detect(img); + + solver.solve(power_runes); + + target.get_target(power_runes, t); + + auto target_copy = target; + + auto command = aimer.aim(target_copy, t, cboard.bullet_speed, true); + + cboard.send(command); + + // -------------- 调试输出 -------------- + + nlohmann::json data; + + // buff原始观测数据 + if (power_runes.has_value()) { + const auto & p = power_runes.value(); + data["buff_R_yaw"] = p.ypd_in_world[0]; + data["buff_R_pitch"] = p.ypd_in_world[1]; + data["buff_R_dis"] = p.ypd_in_world[2]; + data["buff_yaw"] = p.ypr_in_world[0] * 57.3; + data["buff_pitch"] = p.ypr_in_world[1] * 57.3; + data["buff_roll"] = p.ypr_in_world[2] * 57.3; + } + + if (!target.is_unsolve()) { + auto & p = power_runes.value(); + + // 显示 + for (int i = 0; i < 4; i++) tools::draw_point(img, p.target().points[i]); + tools::draw_point(img, p.target().center, {0, 0, 255}, 3); + tools::draw_point(img, p.r_center, {0, 0, 255}, 3); + + // 当前帧target更新后buff + auto Rxyz_in_world_now = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.0)); + auto image_points = + solver.reproject_buff(Rxyz_in_world_now, target.ekf_x()[4], target.ekf_x()[5]); + tools::draw_points( + img, std::vector(image_points.begin(), image_points.begin() + 4), {0, 255, 0}); + tools::draw_points( + img, std::vector(image_points.begin() + 4, image_points.end()), {0, 255, 0}); + + // buff瞄准位置(预测) + double dangle = target.ekf_x()[5] - target_copy.ekf_x()[5]; + auto Rxyz_in_world_pre = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.0)); + image_points = + solver.reproject_buff(Rxyz_in_world_pre, target_copy.ekf_x()[4], target_copy.ekf_x()[5]); + tools::draw_points( + img, std::vector(image_points.begin(), image_points.begin() + 4), {255, 0, 0}); + tools::draw_points( + img, std::vector(image_points.begin() + 4, image_points.end()), {255, 0, 0}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["R_yaw"] = x[0]; + data["R_V_yaw"] = x[1]; + data["R_pitch"] = x[2]; + data["R_dis"] = x[3]; + data["yaw"] = x[4] * 57.3; + + data["angle"] = x[5] * 57.3; + data["spd"] = x[6] * 57.3; + if (x.size() >= 10) { + data["spd"] = x[6]; + data["a"] = x[7]; + data["w"] = x[8]; + data["fi"] = x[9]; + data["spd0"] = target.spd; + } + } + + // 云台响应情况 + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + data["gimbal_yaw"] = ypr[0] * 57.3; + data["gimbal_pitch"] = ypr[1] * 57.3; + + if (command.control) { + data["cmd_yaw"] = command.yaw * 57.3; + data["cmd_pitch"] = command.pitch * 57.3; + data["shoot"] = command.shoot ? 1 : 0; + } + + plotter.plot(data); + + cv::resize(img, img, {}, 0.5, 0.5); + cv::imshow("result", img); + + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + return 0; +} \ No newline at end of file diff --git a/src/auto_buff_debug_mpc.cpp b/src/auto_buff_debug_mpc.cpp new file mode 100755 index 0000000..1de24b2 --- /dev/null +++ b/src/auto_buff_debug_mpc.cpp @@ -0,0 +1,165 @@ +#include + +#include + +#include "io/camera.hpp" +#include "io/gimbal/gimbal.hpp" +#include "tasks/auto_buff/buff_aimer.hpp" +#include "tasks/auto_buff/buff_detector.hpp" +#include "tasks/auto_buff/buff_solver.hpp" +#include "tasks/auto_buff/buff_target.hpp" +#include "tasks/auto_buff/buff_type.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" +#include "tools/trajectory.hpp" + +// 定义命令行参数 +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + // 初始化绘图器、录制器、退出器 + tools::Plotter plotter; + tools::Recorder recorder; + tools::Exiter exiter; + + // 初始化云台、相机 + io::Gimbal gimbal(config_path); + io::Camera camera(config_path); + + // 初始化识别器、解算器、追踪器、瞄准器 + auto_buff::Buff_Detector detector(config_path); + auto_buff::Solver solver(config_path); + auto_buff::SmallTarget target; + // auto_buff::BigTarget target; + auto_buff::Aimer aimer(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + while (!exiter.exit()) { + camera.read(img, t); + q = gimbal.q(t); + auto gs = gimbal.state(); + // recorder.record(img, q, t); + + // -------------- 打符核心逻辑 -------------- + + solver.set_R_gimbal2world(q); + + auto power_runes = detector.detect(img); + + solver.solve(power_runes); + + target.get_target(power_runes, t); + + auto target_copy = target; + + auto plan = aimer.mpc_aim(target_copy, t, gs, true); + + gimbal.send( + plan.control, plan.fire, plan.yaw, plan.yaw_vel, plan.yaw_acc, plan.pitch, plan.pitch_vel, + plan.pitch_acc); + // -------------- 调试输出 -------------- + + nlohmann::json data; + + // buff原始观测数据 + if (power_runes.has_value()) { + const auto & p = power_runes.value(); + data["buff_R_yaw"] = p.ypd_in_world[0]; + data["buff_R_pitch"] = p.ypd_in_world[1]; + data["buff_R_dis"] = p.ypd_in_world[2]; + data["buff_yaw"] = p.ypr_in_world[0] * 57.3; + data["buff_pitch"] = p.ypr_in_world[1] * 57.3; + data["buff_roll"] = p.ypr_in_world[2] * 57.3; + } + + if (!target.is_unsolve()) { + auto & p = power_runes.value(); + + // 显示 + for (int i = 0; i < 4; i++) tools::draw_point(img, p.target().points[i]); + tools::draw_point(img, p.target().center, {0, 0, 255}, 3); + tools::draw_point(img, p.r_center, {0, 0, 255}, 3); + + // 当前帧target更新后buff + auto Rxyz_in_world_now = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.0)); + auto image_points = + solver.reproject_buff(Rxyz_in_world_now, target.ekf_x()[4], target.ekf_x()[5]); + tools::draw_points( + img, std::vector(image_points.begin(), image_points.begin() + 4), {0, 255, 0}); + tools::draw_points( + img, std::vector(image_points.begin() + 4, image_points.end()), {0, 255, 0}); + + // buff瞄准位置(预测) + double dangle = target.ekf_x()[5] - target_copy.ekf_x()[5]; + auto Rxyz_in_world_pre = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.0)); + image_points = + solver.reproject_buff(Rxyz_in_world_pre, target_copy.ekf_x()[4], target_copy.ekf_x()[5]); + tools::draw_points( + img, std::vector(image_points.begin(), image_points.begin() + 4), {255, 0, 0}); + tools::draw_points( + img, std::vector(image_points.begin() + 4, image_points.end()), {255, 0, 0}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["R_yaw"] = x[0]; + data["R_V_yaw"] = x[1]; + data["R_pitch"] = x[2]; + data["R_dis"] = x[3]; + data["yaw"] = x[4] * 57.3; + + data["angle"] = x[5] * 57.3; + data["spd"] = x[6] * 57.3; + if (x.size() >= 10) { + data["spd"] = x[6]; + data["a"] = x[7]; + data["w"] = x[8]; + data["fi"] = x[9]; + data["spd0"] = target.spd; + } + } + + // 云台响应情况 + data["gimbal_yaw"] = gs.yaw * 57.3; + data["gimbal_pitch"] = gs.pitch * 57.3; + data["gimbal_yaw_vel"] = gs.yaw_vel * 57.3; + data["gimbal_pitch_vel"] = gs.pitch_vel * 57.3; + + if (plan.control) { + data["plan_yaw"] = plan.yaw * 57.3; + data["plan_pitch"] = plan.pitch * 57.3; + data["plan_yaw_vel"] = plan.yaw_vel * 57.3; + data["plan_pitch_vel"] = plan.pitch_vel * 57.3; + data["plan_yaw_acc"] = plan.yaw_acc * 57.3; + data["plan_pitch_acc"] = plan.pitch_acc * 57.3; + data["shoot"] = plan.fire ? 1 : 0; + } + + plotter.plot(data); + + cv::resize(img, img, {}, 0.5, 0.5); + cv::imshow("result", img); + + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + return 0; +} \ No newline at end of file diff --git a/src/mt_auto_aim_debug.cpp b/src/mt_auto_aim_debug.cpp new file mode 100644 index 0000000..e7badd7 --- /dev/null +++ b/src/mt_auto_aim_debug.cpp @@ -0,0 +1,173 @@ +#include + +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/multithread/commandgener.hpp" +#include "tasks/auto_aim/multithread/mt_detector.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +using namespace std::chrono; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder(100); //根据实际帧率调整 + + io::CBoard cboard(config_path); + io::Camera camera(config_path); + + auto_aim::multithread::MultiThreadDetector detector(config_path, true); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + auto_aim::multithread::CommandGener commandgener(shooter, aimer, cboard, plotter, true); + + auto detect_thread = std::thread([&]() { + cv::Mat img; + std::chrono::steady_clock::time_point t; + + while (!exiter.exit()) { + camera.read(img, t); + detector.push(img, t); + } + }); + + auto mode = io::Mode::idle; + auto last_mode = io::Mode::idle; + + while (!exiter.exit()) { + auto t0 = std::chrono::steady_clock::now(); + /// 自瞄核心逻辑 + auto [img, armors, t] = detector.debug_pop(); + Eigen::Quaterniond q = cboard.imu_at(t - 1ms); + mode = cboard.mode; + + if (last_mode != mode) { + tools::logger()->info("Switch to {}", io::MODES[mode]); + last_mode = mode; + } + + solver.set_R_gimbal2world(q); + + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto targets = tracker.track(armors, t); + + commandgener.push(targets, t, cboard.bullet_speed, ypr); // 发送给决策线程 + + /// debug + tools::draw_text(img, fmt::format("[{}]", tracker.state()), {10, 30}, {255, 255, 255}); + + nlohmann::json data; + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + + // 装甲板原始观测数据 + data["armor_num"] = armors.size(); + if (!armors.empty()) { + auto min_x = 1e10; + auto & armor = armors.front(); + for (auto & a : armors) { + if (a.center.x < min_x) { + min_x = a.center.x; + armor = a; + } + } //always left + solver.solve(armor); + data["armor_x"] = armor.xyz_in_world[0]; + data["armor_y"] = armor.xyz_in_world[1]; + data["armor_yaw"] = armor.ypr_in_world[0] * 57.3; + data["armor_yaw_raw"] = armor.yaw_raw * 57.3; + } + + if (!targets.empty()) { + auto target = targets.front(); + + // 当前帧target更新后 + std::vector armor_xyza_list = target.armor_xyza_list(); + for (const Eigen::Vector4d & xyza : armor_xyza_list) { + auto image_points = + solver.reproject_armor(xyza.head(3), xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 255, 0}); + } + + // aimer瞄准位置 + auto aim_point = aimer.debug_aim_point; + Eigen::Vector4d aim_xyza = aim_point.xyza; + auto image_points = + solver.reproject_armor(aim_xyza.head(3), aim_xyza[3], target.armor_type, target.name); + if (aim_point.valid) + tools::draw_points(img, image_points, {0, 0, 255}); + else + tools::draw_points(img, image_points, {255, 0, 0}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["x"] = x[0]; + data["vx"] = x[1]; + data["y"] = x[2]; + data["vy"] = x[3]; + data["z"] = x[4]; + data["vz"] = x[5]; + data["a"] = x[6] * 57.3; + data["w"] = x[7]; + data["r"] = x[8]; + data["l"] = x[9]; + data["h"] = x[10]; + data["last_id"] = target.last_id; + + // 卡方检验数据 + data["residual_yaw"] = target.ekf().data.at("residual_yaw"); + data["residual_pitch"] = target.ekf().data.at("residual_pitch"); + data["residual_distance"] = target.ekf().data.at("residual_distance"); + data["residual_angle"] = target.ekf().data.at("residual_angle"); + data["nis"] = target.ekf().data.at("nis"); + data["nees"] = target.ekf().data.at("nees"); + data["nis_fail"] = target.ekf().data.at("nis_fail"); + data["nees_fail"] = target.ekf().data.at("nees_fail"); + data["recent_nis_failures"] = target.ekf().data.at("recent_nis_failures"); + } + + // 云台响应情况 + data["gimbal_yaw"] = ypr[0] * 57.3; + data["gimbal_pitch"] = ypr[1] * 57.3; + data["bullet_speed"] = cboard.bullet_speed; + + plotter.plot(data); + + cv::resize(img, img, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("reprojection", img); + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + detect_thread.join(); + + return 0; +} \ No newline at end of file diff --git a/src/mt_standard.cpp b/src/mt_standard.cpp new file mode 100644 index 0000000..dc1c8fb --- /dev/null +++ b/src/mt_standard.cpp @@ -0,0 +1,138 @@ +#include +#include +#include + +#include "io/camera.hpp" +#include "io/dm_imu/dm_imu.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/multithread/commandgener.hpp" +#include "tasks/auto_aim/multithread/mt_detector.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_buff/buff_aimer.hpp" +#include "tasks/auto_buff/buff_detector.hpp" +#include "tasks/auto_buff/buff_solver.hpp" +#include "tasks/auto_buff/buff_target.hpp" +#include "tasks/auto_buff/buff_type.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | | yaml配置文件路径 }"; + +using namespace std::chrono_literals; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get("@config-path"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + io::Camera camera(config_path); + io::CBoard cboard(config_path); + + auto_aim::multithread::MultiThreadDetector detector(config_path); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + auto_buff::Buff_Detector buff_detector(config_path); + auto_buff::Solver buff_solver(config_path); + auto_buff::SmallTarget buff_small_target; + auto_buff::BigTarget buff_big_target; + auto_buff::Aimer buff_aimer(config_path); + + auto_aim::multithread::CommandGener commandgener(shooter, aimer, cboard, plotter); + + std::atomic mode{io::Mode::idle}; + auto last_mode{io::Mode::idle}; + + auto detect_thread = std::thread([&]() { + cv::Mat img; + std::chrono::steady_clock::time_point t; + + while (!exiter.exit()) { + if (mode.load() == io::Mode::auto_aim) { + camera.read(img, t); + detector.push(img, t); + } else + continue; + } + }); + + while (!exiter.exit()) { + mode = cboard.mode; + + if (last_mode != mode) { + tools::logger()->info("Switch to {}", io::MODES[mode]); + last_mode = mode.load(); + } + + /// 自瞄 + if (mode.load() == io::Mode::auto_aim) { + auto [img, armors, t] = detector.debug_pop(); + Eigen::Quaterniond q = cboard.imu_at(t - 1ms); + + // recorder.record(img, q, t); + + solver.set_R_gimbal2world(q); + + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto targets = tracker.track(armors, t); + + commandgener.push(targets, t, cboard.bullet_speed, ypr); // 发送给决策线程 + + } + + /// 打符 + else if (mode.load() == io::Mode::small_buff || mode.load() == io::Mode::big_buff) { + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + camera.read(img, t); + q = cboard.imu_at(t - 1ms); + + // recorder.record(img, q, t); + + buff_solver.set_R_gimbal2world(q); + + auto power_runes = buff_detector.detect(img); + + buff_solver.solve(power_runes); + + io::Command buff_command; + if (mode.load() == io::Mode::small_buff) { + buff_small_target.get_target(power_runes, t); + auto target_copy = buff_small_target; + buff_command = buff_aimer.aim(target_copy, t, cboard.bullet_speed, true); + } else if (mode.load() == io::Mode::big_buff) { + buff_big_target.get_target(power_runes, t); + auto target_copy = buff_big_target; + buff_command = buff_aimer.aim(target_copy, t, cboard.bullet_speed, true); + } + cboard.send(buff_command); + + } else + continue; + } + + detect_thread.join(); + + return 0; +} \ No newline at end of file diff --git a/src/sentry.cpp b/src/sentry.cpp new file mode 100644 index 0000000..95f7a01 --- /dev/null +++ b/src/sentry.cpp @@ -0,0 +1,106 @@ +#include + +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "io/ros2/publish2nav.hpp" +#include "io/ros2/ros2.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tasks/omniperception/decider.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +using namespace std::chrono; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + + io::ROS2 ros2; + io::CBoard cboard(config_path); + io::Camera camera(config_path); + io::Camera back_camera("configs/camera.yaml"); + io::USBCamera usbcam1("video0", config_path); + io::USBCamera usbcam2("video2", config_path); + + auto_aim::YOLO yolo(config_path, false); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + omniperception::Decider decider(config_path); + + cv::Mat img; + + std::chrono::steady_clock::time_point timestamp; + io::Command last_command; + + while (!exiter.exit()) { + camera.read(img, timestamp); + Eigen::Quaterniond q = cboard.imu_at(timestamp - 1ms); + // recorder.record(img, q, timestamp); + + /// 自瞄核心逻辑 + solver.set_R_gimbal2world(q); + + Eigen::Vector3d gimbal_pos = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = yolo.detect(img); + + decider.get_invincible_armor(ros2.subscribe_enemy_status()); + + decider.armor_filter(armors); + + // decider.get_auto_aim_target(armors, ros2.subscribe_autoaim_target()); + + decider.set_priority(armors); + + auto targets = tracker.track(armors, timestamp); + + io::Command command{false, false, 0, 0}; + + /// 全向感知逻辑 + if (tracker.state() == "lost") + command = decider.decide(yolo, gimbal_pos, usbcam1, usbcam2, back_camera); + else + command = aimer.aim(targets, timestamp, cboard.bullet_speed, cboard.shoot_mode); + + /// 发射逻辑 + command.shoot = shooter.shoot(command, aimer, targets, gimbal_pos); + + cboard.send(command); + + /// ROS2通信 + Eigen::Vector4d target_info = decider.get_target_info(armors, targets); + + ros2.publish(target_info); + } + return 0; +} \ No newline at end of file diff --git a/src/sentry_bp.cpp b/src/sentry_bp.cpp new file mode 100644 index 0000000..2a9054a --- /dev/null +++ b/src/sentry_bp.cpp @@ -0,0 +1,104 @@ +#include + +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "io/ros2/publish2nav.hpp" +#include "io/ros2/ros2.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tasks/omniperception/decider.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +using namespace std::chrono; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + + io::ROS2 ros2; + io::CBoard cboard(config_path); + io::Camera camera(config_path); + io::Camera back_camera("configs/camera.yaml"); + + auto_aim::YOLO yolo(config_path, false); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + omniperception::Decider decider(config_path); + + cv::Mat img; + + std::chrono::steady_clock::time_point timestamp; + io::Command last_command; + + while (!exiter.exit()) { + camera.read(img, timestamp); + Eigen::Quaterniond q = cboard.imu_at(timestamp - 1ms); + // recorder.record(img, q, timestamp); + + /// 自瞄核心逻辑 + solver.set_R_gimbal2world(q); + + Eigen::Vector3d gimbal_pos = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = yolo.detect(img); + + decider.get_invincible_armor(ros2.subscribe_enemy_status()); + + decider.armor_filter(armors); + + // decider.get_auto_aim_target(armors, ros2.subscribe_autoaim_target()); + + decider.set_priority(armors); + + auto targets = tracker.track(armors, timestamp); + + io::Command command{false, false, 0, 0}; + + /// 全向感知逻辑 + if (tracker.state() == "lost") + command = decider.decide(yolo, gimbal_pos, back_camera); + else + command = aimer.aim(targets, timestamp, cboard.bullet_speed, cboard.shoot_mode); + + /// 发射逻辑 + command.shoot = shooter.shoot(command, aimer, targets, gimbal_pos); + + cboard.send(command); + + /// ROS2通信 + Eigen::Vector4d target_info = decider.get_target_info(armors, targets); + + ros2.publish(target_info); + } + return 0; +} \ No newline at end of file diff --git a/src/sentry_debug.cpp b/src/sentry_debug.cpp new file mode 100644 index 0000000..fecc35b --- /dev/null +++ b/src/sentry_debug.cpp @@ -0,0 +1,196 @@ +#include + +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "io/ros2/publish2nav.hpp" +#include "io/ros2/ros2.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tasks/omniperception/decider.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +using namespace std::chrono; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + + io::ROS2 ros2; + io::CBoard cboard(config_path); + io::Camera camera(config_path); + io::Camera back_camera("configs/camera.yaml"); + io::USBCamera usbcam1("video0", config_path); + io::USBCamera usbcam2("video2", config_path); + + auto_aim::YOLO yolo(config_path, false); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + omniperception::Decider decider(config_path); + + cv::Mat img; + + std::chrono::steady_clock::time_point timestamp; + io::Command last_command; + + while (!exiter.exit()) { + camera.read(img, timestamp); + Eigen::Quaterniond q = cboard.imu_at(timestamp - 1ms); + // recorder.record(img, q, timestamp); + + /// 自瞄核心逻辑 + solver.set_R_gimbal2world(q); + + Eigen::Vector3d gimbal_pos = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = yolo.detect(img); + + decider.get_invincible_armor(ros2.subscribe_enemy_status()); + + decider.armor_filter(armors); + + decider.get_auto_aim_target(armors, ros2.subscribe_autoaim_target()); + + decider.set_priority(armors); + + auto targets = tracker.track(armors, timestamp); + + io::Command command{false, false, 0, 0}; + + /// 全向感知逻辑 + if (tracker.state() == "lost") + command = decider.decide(yolo, gimbal_pos, usbcam1, usbcam2, back_camera); + else + command = aimer.aim(targets, timestamp, cboard.bullet_speed, cboard.shoot_mode); + + /// 发射逻辑 + command.shoot = shooter.shoot(command, aimer, targets, gimbal_pos); + + cboard.send(command); + + /// ROS2通信 + Eigen::Vector4d target_info = decider.get_target_info(armors, targets); + + ros2.publish(target_info); + + /// debug + tools::draw_text(img, fmt::format("[{}]", tracker.state()), {10, 30}, {255, 255, 255}); + + nlohmann::json data; + + // 装甲板原始观测数据 + data["armor_num"] = armors.size(); + if (!armors.empty()) { + auto min_x = 1e10; + auto & armor = armors.front(); + for (auto & a : armors) { + if (a.center.x < min_x) { + min_x = a.center.x; + armor = a; + } + } //always left + solver.solve(armor); + data["armor_x"] = armor.xyz_in_world[0]; + data["armor_y"] = armor.xyz_in_world[1]; + data["armor_yaw"] = armor.ypr_in_world[0] * 57.3; + data["armor_yaw_raw"] = armor.yaw_raw * 57.3; + } + + if (!targets.empty()) { + auto target = targets.front(); + + // 当前帧target更新后 + std::vector armor_xyza_list = target.armor_xyza_list(); + for (const Eigen::Vector4d & xyza : armor_xyza_list) { + auto image_points = + solver.reproject_armor(xyza.head(3), xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 255, 0}); + } + + // aimer瞄准位置 + auto aim_point = aimer.debug_aim_point; + Eigen::Vector4d aim_xyza = aim_point.xyza; + auto image_points = + solver.reproject_armor(aim_xyza.head(3), aim_xyza[3], target.armor_type, target.name); + if (aim_point.valid) + tools::draw_points(img, image_points, {0, 0, 255}); + else + tools::draw_points(img, image_points, {255, 0, 0}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["x"] = x[0]; + data["vx"] = x[1]; + data["y"] = x[2]; + data["vy"] = x[3]; + data["z"] = x[4]; + data["vz"] = x[5]; + data["a"] = x[6] * 57.3; + data["w"] = x[7]; + data["r"] = x[8]; + data["l"] = x[9]; + data["h"] = x[10]; + data["last_id"] = target.last_id; + + // 卡方检验数据 + data["residual_yaw"] = target.ekf().data.at("residual_yaw"); + data["residual_pitch"] = target.ekf().data.at("residual_pitch"); + data["residual_distance"] = target.ekf().data.at("residual_distance"); + data["residual_angle"] = target.ekf().data.at("residual_angle"); + data["nis"] = target.ekf().data.at("nis"); + data["nees"] = target.ekf().data.at("nees"); + data["nis_fail"] = target.ekf().data.at("nis_fail"); + data["nees_fail"] = target.ekf().data.at("nees_fail"); + data["recent_nis_failures"] = target.ekf().data.at("recent_nis_failures"); + } + + // 云台响应情况 + data["gimbal_yaw"] = gimbal_pos[0] * 57.3; + data["gimbal_pitch"] = -gimbal_pos[1] * 57.3; + data["shootmode"] = cboard.shoot_mode; + if (command.control) { + data["cmd_yaw"] = command.yaw * 57.3; + data["cmd_pitch"] = command.pitch * 57.3; + data["cmd_shoot"] = command.shoot; + } + + data["bullet_speed"] = cboard.bullet_speed; + + plotter.plot(data); + + cv::resize(img, img, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("reprojection", img); + auto key = cv::waitKey(1); + if (key == 'q') break; + } + return 0; +} \ No newline at end of file diff --git a/src/sentry_multithread.cpp b/src/sentry_multithread.cpp new file mode 100644 index 0000000..e022ab3 --- /dev/null +++ b/src/sentry_multithread.cpp @@ -0,0 +1,125 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "io/ros2/ros2.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tasks/omniperception/decider.hpp" +#include "tasks/omniperception/perceptron.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +using namespace std::chrono; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + + io::ROS2 ros2; + io::CBoard cboard(config_path); + io::Camera camera(config_path); + io::USBCamera usbcam1("video0", config_path); + io::USBCamera usbcam2("video2", config_path); + io::USBCamera usbcam3("video4", config_path); + io::USBCamera usbcam4("video6", config_path); + + auto_aim::YOLO yolo(config_path, false); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + omniperception::Decider decider(config_path); + omniperception::Perceptron perceptron(&usbcam1, &usbcam2, &usbcam3, &usbcam4, config_path); + + omniperception::DetectionResult switch_target; + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + io::Command last_command; + + while (!exiter.exit()) { + camera.read(img, timestamp); + Eigen::Quaterniond q = cboard.imu_at(timestamp - 1ms); + recorder.record(img, q, timestamp); + /// 自瞄核心逻辑 + solver.set_R_gimbal2world(q); + + Eigen::Vector3d gimbal_pos = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = yolo.detect(img); + + decider.get_invincible_armor(ros2.subscribe_enemy_status()); + + decider.armor_filter(armors); + + decider.set_priority(armors); + + auto detection_queue = perceptron.get_detection_queue(); + + decider.sort(detection_queue); + + auto [switch_target, targets] = tracker.track(detection_queue, armors, timestamp); + + io::Command command{false, false, 0, 0}; + + /// 全向感知逻辑 + if (tracker.state() == "switching") { + command.control = switch_target.armors.empty() ? false : true; + command.shoot = false; + command.pitch = tools::limit_rad(switch_target.delta_pitch); + command.yaw = tools::limit_rad(switch_target.delta_yaw + gimbal_pos[0]); + } + + else if (tracker.state() == "lost") { + command = decider.decide(detection_queue); + command.yaw = tools::limit_rad(command.yaw + gimbal_pos[0]); + } + + else { + command = aimer.aim(targets, timestamp, cboard.bullet_speed); + } + + /// 发射逻辑 + command.shoot = shooter.shoot(command, aimer, targets, gimbal_pos); + // command.shoot = false; + + cboard.send(command); + + /// ROS2通信 + Eigen::Vector4d target_info = decider.get_target_info(armors, targets); + + ros2.publish(target_info); + } + + return 0; +} \ No newline at end of file diff --git a/src/standard.cpp b/src/standard.cpp new file mode 100644 index 0000000..9136f7a --- /dev/null +++ b/src/standard.cpp @@ -0,0 +1,83 @@ +#include + +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/multithread/commandgener.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +using namespace std::chrono; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/standard3.yaml | 位置参数,yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + io::CBoard cboard(config_path); + io::Camera camera(config_path); + + auto_aim::YOLO detector(config_path, false); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + auto mode = io::Mode::idle; + auto last_mode = io::Mode::idle; + + while (!exiter.exit()) { + camera.read(img, t); + q = cboard.imu_at(t - 1ms); + mode = cboard.mode; + + if (last_mode != mode) { + tools::logger()->info("Switch to {}", io::MODES[mode]); + last_mode = mode; + } + + // recorder.record(img, q, t); + + solver.set_R_gimbal2world(q); + + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = detector.detect(img); + + auto targets = tracker.track(armors, t); + + auto command = aimer.aim(targets, t, cboard.bullet_speed); + + cboard.send(command); + } + + return 0; +} \ No newline at end of file diff --git a/src/standard_mpc.cpp b/src/standard_mpc.cpp new file mode 100644 index 0000000..3e07a46 --- /dev/null +++ b/src/standard_mpc.cpp @@ -0,0 +1,145 @@ +#include +#include +#include + +#include "io/camera.hpp" +#include "io/dm_imu/dm_imu.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/multithread/commandgener.hpp" +#include "tasks/auto_aim/multithread/mt_detector.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_buff/buff_aimer.hpp" +#include "tasks/auto_buff/buff_detector.hpp" +#include "tasks/auto_buff/buff_solver.hpp" +#include "tasks/auto_buff/buff_target.hpp" +#include "tasks/auto_buff/buff_type.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | | yaml配置文件路径 }"; + +using namespace std::chrono_literals; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get("@config-path"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + io::Gimbal gimbal(config_path); + io::Camera camera(config_path); + + auto_aim::YOLO yolo(config_path, true); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Planner planner(config_path); + + tools::ThreadSafeQueue, true> target_queue(1); + target_queue.push(std::nullopt); + + auto_buff::Buff_Detector buff_detector(config_path); + auto_buff::Solver buff_solver(config_path); + auto_buff::SmallTarget buff_small_target; + auto_buff::BigTarget buff_big_target; + auto_buff::Aimer buff_aimer(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + std::atomic quit = false; + + std::atomic mode{io::GimbalMode::IDLE}; + auto last_mode{io::GimbalMode::IDLE}; + + auto plan_thread = std::thread([&]() { + auto t0 = std::chrono::steady_clock::now(); + uint16_t last_bullet_count = 0; + + while (!quit) { + if (!target_queue.empty() && mode == io::GimbalMode::AUTO_AIM) { + auto target = target_queue.front(); + auto gs = gimbal.state(); + auto plan = planner.plan(target, gs.bullet_speed); + + gimbal.send( + plan.control, plan.fire, plan.yaw, plan.yaw_vel, plan.yaw_acc, plan.pitch, plan.pitch_vel, + plan.pitch_acc); + + std::this_thread::sleep_for(10ms); + } else + std::this_thread::sleep_for(200ms); + } + }); + + while (!exiter.exit()) { + mode = gimbal.mode(); + + if (last_mode != mode) { + tools::logger()->info("Switch to {}", gimbal.str(mode)); + last_mode = mode.load(); + } + + camera.read(img, t); + auto q = gimbal.q(t); + auto gs = gimbal.state(); + recorder.record(img, q, t); + solver.set_R_gimbal2world(q); + + /// 自瞄 + if (mode.load() == io::GimbalMode::AUTO_AIM) { + auto armors = yolo.detect(img); + auto targets = tracker.track(armors, t); + if (!targets.empty()) + target_queue.push(targets.front()); + else + target_queue.push(std::nullopt); + } + + /// 打符 + else if (mode.load() == io::GimbalMode::SMALL_BUFF || mode.load() == io::GimbalMode::BIG_BUFF) { + buff_solver.set_R_gimbal2world(q); + + auto power_runes = buff_detector.detect(img); + + buff_solver.solve(power_runes); + + auto_aim::Plan buff_plan; + if (mode.load() == io::GimbalMode::SMALL_BUFF) { + buff_small_target.get_target(power_runes, t); + auto target_copy = buff_small_target; + buff_plan = buff_aimer.mpc_aim(target_copy, t, gs, true); + } else if (mode.load() == io::GimbalMode::BIG_BUFF) { + buff_big_target.get_target(power_runes, t); + auto target_copy = buff_big_target; + buff_plan = buff_aimer.mpc_aim(target_copy, t, gs, true); + } + gimbal.send( + buff_plan.control, buff_plan.fire, buff_plan.yaw, buff_plan.yaw_vel, buff_plan.yaw_acc, + buff_plan.pitch, buff_plan.pitch_vel, buff_plan.pitch_acc); + + } else + gimbal.send(false, false, 0, 0, 0, 0, 0, 0); + } + + quit = true; + if (plan_thread.joinable()) plan_thread.join(); + gimbal.send(false, false, 0, 0, 0, 0, 0, 0); + + return 0; +} \ No newline at end of file diff --git a/src/uav.cpp b/src/uav.cpp new file mode 100644 index 0000000..a3fec7d --- /dev/null +++ b/src/uav.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +#include "io/camera.hpp" +#include "io/dm_imu/dm_imu.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tasks/auto_buff/buff_aimer.hpp" +#include "tasks/auto_buff/buff_detector.hpp" +#include "tasks/auto_buff/buff_solver.hpp" +#include "tasks/auto_buff/buff_target.hpp" +#include "tasks/auto_buff/buff_type.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/uav.yaml | yaml配置文件路径 }"; + +using namespace std::chrono_literals; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get("@config-path"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + io::Camera camera(config_path); + io::CBoard cboard(config_path); + + auto_aim::Detector detector(config_path); + auto_aim::Solver solver(config_path); + // auto_aim::YOLO yolo(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + auto_buff::Buff_Detector buff_detector(config_path); + auto_buff::Solver buff_solver(config_path); + auto_buff::SmallTarget buff_small_target; + auto_buff::BigTarget buff_big_target; + auto_buff::Aimer buff_aimer(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + auto mode = io::Mode::idle; + auto last_mode = io::Mode::idle; + + while (!exiter.exit()) { + camera.read(img, t); + q = cboard.imu_at(t - 1ms); + mode = cboard.mode; + // recorder.record(img, q, t); + if (last_mode != mode) { + tools::logger()->info("Switch to {}", io::MODES[mode]); + last_mode = mode; + } + + /// 自瞄 + if (mode == io::Mode::auto_aim || mode == io::Mode::outpost) { + solver.set_R_gimbal2world(q); + + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = detector.detect(img); + + auto targets = tracker.track(armors, t); + + auto command = aimer.aim(targets, t, cboard.bullet_speed); + + command.shoot = shooter.shoot(command, aimer, targets, ypr); + + cboard.send(command); + } + + /// 打符 + else if (mode == io::Mode::small_buff || mode == io::Mode::big_buff) { + buff_solver.set_R_gimbal2world(q); + + auto power_runes = buff_detector.detect(img); + + buff_solver.solve(power_runes); + + io::Command buff_command; + if (mode == io::Mode::small_buff) { + buff_small_target.get_target(power_runes, t); + auto target_copy = buff_small_target; + buff_command = buff_aimer.aim(target_copy, t, cboard.bullet_speed, true); + } else if (mode == io::Mode::big_buff) { + buff_big_target.get_target(power_runes, t); + auto target_copy = buff_big_target; + buff_command = buff_aimer.aim(target_copy, t, cboard.bullet_speed, true); + } + cboard.send(buff_command); + } + + else + continue; + } + + return 0; +} \ No newline at end of file diff --git a/src/uav_debug.cpp b/src/uav_debug.cpp new file mode 100644 index 0000000..2be022e --- /dev/null +++ b/src/uav_debug.cpp @@ -0,0 +1,173 @@ +#include +#include +#include + +#include "io/camera.hpp" +#include "io/dm_imu/dm_imu.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/uav.yaml | yaml配置文件路径 }"; + +using namespace std::chrono_literals; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get("@config-path"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + tools::Recorder recorder; + + io::Camera camera(config_path); + io::CBoard cboard(config_path); + + auto_aim::Detector detector(config_path); + auto_aim::Solver solver(config_path); + auto_aim::YOLO yolo(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + + auto mode = io::Mode::idle; + auto last_mode = io::Mode::idle; + + auto t0 = std::chrono::steady_clock::now(); + + while (!exiter.exit()) { + camera.read(img, t); + q = cboard.imu_at(t - 1ms); + mode = cboard.mode; + // recorder.record(img, q, t); + if (last_mode != mode) { + tools::logger()->info("Switch to {}", io::MODES[mode]); + last_mode = mode; + } + + /// 自瞄 + solver.set_R_gimbal2world(q); + + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto armors = detector.detect(img); + + auto targets = tracker.track(armors, t); + + auto command = aimer.aim(targets, t, cboard.bullet_speed); + + command.shoot = shooter.shoot(command, aimer, targets, ypr); + + cboard.send(command); + + /// debug + tools::draw_text(img, fmt::format("[{}]", tracker.state()), {10, 30}, {255, 255, 255}); + + nlohmann::json data; + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + + // 装甲板原始观测数据 + data["armor_num"] = armors.size(); + if (!armors.empty()) { + auto min_x = 1e10; + auto & armor = armors.front(); + for (auto & a : armors) { + if (a.center.x < min_x) { + min_x = a.center.x; + armor = a; + } + } //always left + solver.solve(armor); + data["armor_x"] = armor.xyz_in_world[0]; + data["armor_y"] = armor.xyz_in_world[1]; + data["armor_yaw"] = armor.ypr_in_world[0] * 57.3; + data["armor_yaw_raw"] = armor.yaw_raw * 57.3; + } + + if (!targets.empty()) { + auto target = targets.front(); + + // 当前帧target更新后 + std::vector armor_xyza_list = target.armor_xyza_list(); + for (const Eigen::Vector4d & xyza : armor_xyza_list) { + auto image_points = + solver.reproject_armor(xyza.head(3), xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 255, 0}); + } + + // aimer瞄准位置 + auto aim_point = aimer.debug_aim_point; + Eigen::Vector4d aim_xyza = aim_point.xyza; + auto image_points = + solver.reproject_armor(aim_xyza.head(3), aim_xyza[3], target.armor_type, target.name); + if (aim_point.valid) + tools::draw_points(img, image_points, {0, 0, 255}); + else + tools::draw_points(img, image_points, {255, 0, 0}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["x"] = x[0]; + data["vx"] = x[1]; + data["y"] = x[2]; + data["vy"] = x[3]; + data["z"] = x[4]; + data["vz"] = x[5]; + data["a"] = x[6] * 57.3; + data["w"] = x[7]; + data["r"] = x[8]; + data["l"] = x[9]; + data["h"] = x[10]; + data["last_id"] = target.last_id; + + // 卡方检验数据 + data["residual_yaw"] = target.ekf().data.at("residual_yaw"); + data["residual_pitch"] = target.ekf().data.at("residual_pitch"); + data["residual_distance"] = target.ekf().data.at("residual_distance"); + data["residual_angle"] = target.ekf().data.at("residual_angle"); + data["nis"] = target.ekf().data.at("nis"); + data["nees"] = target.ekf().data.at("nees"); + data["nis_fail"] = target.ekf().data.at("nis_fail"); + data["nees_fail"] = target.ekf().data.at("nees_fail"); + data["recent_nis_failures"] = target.ekf().data.at("recent_nis_failures"); + } + + // 云台响应情况 + data["gimbal_yaw"] = ypr[0] * 57.3; + data["gimbal_pitch"] = ypr[1] * 57.3; + data["bullet_speed"] = cboard.bullet_speed; + if (command.control) { + data["cmd_yaw"] = command.yaw * 57.3; + data["cmd_pitch"] = command.pitch * 57.3; + data["cmd_shoot"] = command.shoot; + } + plotter.plot(data); + + cv::resize(img, img, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("reprojection", img); + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + return 0; +} \ No newline at end of file diff --git a/tasks/auto_aim/CMakeLists.txt b/tasks/auto_aim/CMakeLists.txt new file mode 100644 index 0000000..3775d42 --- /dev/null +++ b/tasks/auto_aim/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16) + +set(OpenVINO_DIR "/opt/intel/openvino_2024.6.0/runtime/cmake") + +find_package(OpenVINO REQUIRED COMPONENTS Runtime) + +add_subdirectory(planner/tinympc) + +add_library(auto_aim OBJECT + armor.cpp + classifier.cpp + detector.cpp + solver.cpp + aimer.cpp + target.cpp + tracker.cpp + voter.cpp + shooter.cpp + yolo.cpp + yolos/yolov5.cpp + yolos/yolov8.cpp + yolos/yolo11.cpp + multithread/commandgener.cpp + multithread/mt_detector.cpp + planner/planner.cpp +) + +target_link_libraries(auto_aim io openvino::runtime tinympcstatic) \ No newline at end of file diff --git a/tasks/auto_aim/aimer.cpp b/tasks/auto_aim/aimer.cpp new file mode 100644 index 0000000..b9194cd --- /dev/null +++ b/tasks/auto_aim/aimer.cpp @@ -0,0 +1,211 @@ +#include "aimer.hpp" + +#include + +#include +#include + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/trajectory.hpp" + +namespace auto_aim +{ +Aimer::Aimer(const std::string & config_path) +: left_yaw_offset_(std::nullopt), right_yaw_offset_(std::nullopt) +{ + auto yaml = YAML::LoadFile(config_path); + yaw_offset_ = yaml["yaw_offset"].as() / 57.3; // degree to rad + pitch_offset_ = yaml["pitch_offset"].as() / 57.3; // degree to rad + comming_angle_ = yaml["comming_angle"].as() / 57.3; // degree to rad + leaving_angle_ = yaml["leaving_angle"].as() / 57.3; // degree to rad + high_speed_delay_time_ = yaml["high_speed_delay_time"].as(); + low_speed_delay_time_ = yaml["low_speed_delay_time"].as(); + decision_speed_ = yaml["decision_speed"].as(); + if (yaml["left_yaw_offset"].IsDefined() && yaml["right_yaw_offset"].IsDefined()) { + left_yaw_offset_ = yaml["left_yaw_offset"].as() / 57.3; // degree to rad + right_yaw_offset_ = yaml["right_yaw_offset"].as() / 57.3; // degree to rad + tools::logger()->info("[Aimer] successfully loading shootmode"); + } +} + +io::Command Aimer::aim( + std::list targets, std::chrono::steady_clock::time_point timestamp, double bullet_speed, + bool to_now) +{ + if (targets.empty()) return {false, false, 0, 0}; + auto target = targets.front(); + + auto ekf = target.ekf(); + double delay_time = + target.ekf_x()[7] > decision_speed_ ? high_speed_delay_time_ : low_speed_delay_time_; + + if (bullet_speed < 14) bullet_speed = 23; + + // 考虑detecor和tracker所消耗的时间,此外假设aimer的用时可忽略不计 + auto future = timestamp; + if (to_now) { + double dt; + dt = tools::delta_time(std::chrono::steady_clock::now(), timestamp) + delay_time; + future += std::chrono::microseconds(int(dt * 1e6)); + target.predict(future); + } + + else { + auto dt = 0.005 + delay_time; //detector-aimer耗时0.005+发弹延时0.1 + // tools::logger()->info("dt is {:.4f} second", dt); + future += std::chrono::microseconds(int(dt * 1e6)); + target.predict(future); + } + + auto aim_point0 = choose_aim_point(target); + debug_aim_point = aim_point0; + if (!aim_point0.valid) { + // tools::logger()->debug("Invalid aim_point0."); + return {false, false, 0, 0}; + } + + Eigen::Vector3d xyz0 = aim_point0.xyza.head(3); + auto d0 = std::sqrt(xyz0[0] * xyz0[0] + xyz0[1] * xyz0[1]); + tools::Trajectory trajectory0(bullet_speed, d0, xyz0[2]); + if (trajectory0.unsolvable) { + tools::logger()->debug( + "[Aimer] Unsolvable trajectory0: {:.2f} {:.2f} {:.2f}", bullet_speed, d0, xyz0[2]); + debug_aim_point.valid = false; + return {false, false, 0, 0}; + } + + // 迭代求解飞行时间 (最多10次,收敛条件:相邻两次fly_time差 <0.001) + bool converged = false; + double prev_fly_time = trajectory0.fly_time; + tools::Trajectory current_traj = trajectory0; + std::vector iteration_target(10, target); // 创建10个目标副本用于迭代预测 + + for (int iter = 0; iter < 10; ++iter) { + // 预测目标在 future + prev_fly_time 时刻的位置 + auto predict_time = future + std::chrono::microseconds(static_cast(prev_fly_time * 1e6)); + iteration_target[iter].predict(predict_time); + + // 计算瞄准点 + auto aim_point = choose_aim_point(iteration_target[iter]); + debug_aim_point = aim_point; + if (!aim_point.valid) { + return {false, false, 0, 0}; + } + + // 计算新弹道 + Eigen::Vector3d xyz = aim_point.xyza.head(3); + double d = std::sqrt(xyz.x() * xyz.x() + xyz.y() * xyz.y()); + current_traj = tools::Trajectory(bullet_speed, d, xyz.z()); + + // 检查弹道是否可解 + if (current_traj.unsolvable) { + tools::logger()->debug( + "[Aimer] Unsolvable trajectory in iter {}: speed={:.2f}, d={:.2f}, z={:.2f}", iter + 1, + bullet_speed, d, xyz.z()); + debug_aim_point.valid = false; + return {false, false, 0, 0}; + } + + // 检查收敛条件 + if (std::abs(current_traj.fly_time - prev_fly_time) < 0.001) { + converged = true; + break; + } + prev_fly_time = current_traj.fly_time; + } + + // 计算最终角度 + Eigen::Vector3d final_xyz = debug_aim_point.xyza.head(3); + double yaw = std::atan2(final_xyz.y(), final_xyz.x()) + yaw_offset_; + double pitch = -(current_traj.pitch + pitch_offset_); //世界坐标系下pitch向上为负 + return {true, false, yaw, pitch}; +} + +io::Command Aimer::aim( + std::list targets, std::chrono::steady_clock::time_point timestamp, double bullet_speed, + io::ShootMode shoot_mode, bool to_now) +{ + double yaw_offset; + if (shoot_mode == io::left_shoot && left_yaw_offset_.has_value()) { + yaw_offset = left_yaw_offset_.value(); + } else if (shoot_mode == io::right_shoot && right_yaw_offset_.has_value()) { + yaw_offset = right_yaw_offset_.value(); + } else { + yaw_offset = yaw_offset_; + } + + auto command = aim(targets, timestamp, bullet_speed, to_now); + command.yaw = command.yaw - yaw_offset_ + yaw_offset; + + return command; +} + +AimPoint Aimer::choose_aim_point(const Target & target) +{ + Eigen::VectorXd ekf_x = target.ekf_x(); + std::vector armor_xyza_list = target.armor_xyza_list(); + auto armor_num = armor_xyza_list.size(); + // 如果装甲板未发生过跳变,则只有当前装甲板的位置已知 + if (!target.jumped) return {true, armor_xyza_list[0]}; + + // 整车旋转中心的球坐标yaw + auto center_yaw = std::atan2(ekf_x[2], ekf_x[0]); + + // 如果delta_angle为0,则该装甲板中心和整车中心的连线在世界坐标系的xy平面过原点 + std::vector delta_angle_list; + for (int i = 0; i < armor_num; i++) { + auto delta_angle = tools::limit_rad(armor_xyza_list[i][3] - center_yaw); + delta_angle_list.emplace_back(delta_angle); + } + + // 不考虑小陀螺 + if (std::abs(target.ekf_x()[8]) <= 2 && target.name != ArmorName::outpost) { + // 选择在可射击范围内的装甲板 + std::vector id_list; + for (int i = 0; i < armor_num; i++) { + if (std::abs(delta_angle_list[i]) > 60 / 57.3) continue; + id_list.push_back(i); + } + // 绝无可能 + if (id_list.empty()) { + tools::logger()->warn("Empty id list!"); + return {false, armor_xyza_list[0]}; + } + + // 锁定模式:防止在两个都呈45度的装甲板之间来回切换 + if (id_list.size() > 1) { + int id0 = id_list[0], id1 = id_list[1]; + + // 未处于锁定模式时,选择delta_angle绝对值较小的装甲板,进入锁定模式 + if (lock_id_ != id0 && lock_id_ != id1) + lock_id_ = (std::abs(delta_angle_list[id0]) < std::abs(delta_angle_list[id1])) ? id0 : id1; + + return {true, armor_xyza_list[lock_id_]}; + } + + // 只有一个装甲板在可射击范围内时,退出锁定模式 + lock_id_ = -1; + return {true, armor_xyza_list[id_list[0]]}; + } + + double coming_angle, leaving_angle; + if (target.name == ArmorName::outpost) { + coming_angle = 70 / 57.3; + leaving_angle = 30 / 57.3; + } else { + coming_angle = comming_angle_; + leaving_angle = leaving_angle_; + } + + // 在小陀螺时,一侧的装甲板不断出现,另一侧的装甲板不断消失,显然前者被打中的概率更高 + for (int i = 0; i < armor_num; i++) { + if (std::abs(delta_angle_list[i]) > coming_angle) continue; + if (ekf_x[7] > 0 && delta_angle_list[i] < leaving_angle) return {true, armor_xyza_list[i]}; + if (ekf_x[7] < 0 && delta_angle_list[i] > -leaving_angle) return {true, armor_xyza_list[i]}; + } + + return {false, armor_xyza_list[0]}; +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/aimer.hpp b/tasks/auto_aim/aimer.hpp new file mode 100644 index 0000000..26c1e81 --- /dev/null +++ b/tasks/auto_aim/aimer.hpp @@ -0,0 +1,50 @@ +#ifndef AUTO_AIM__AIMER_HPP +#define AUTO_AIM__AIMER_HPP + +#include +#include +#include + +#include "io/cboard.hpp" +#include "io/command.hpp" +#include "target.hpp" + +namespace auto_aim +{ + +struct AimPoint +{ + bool valid; + Eigen::Vector4d xyza; +}; + +class Aimer +{ +public: + AimPoint debug_aim_point; + explicit Aimer(const std::string & config_path); + io::Command aim( + std::list targets, std::chrono::steady_clock::time_point timestamp, double bullet_speed, + bool to_now = true); + + io::Command aim( + std::list targets, std::chrono::steady_clock::time_point timestamp, double bullet_speed, + io::ShootMode shoot_mode, bool to_now = true); + +private: + double yaw_offset_; + std::optional left_yaw_offset_, right_yaw_offset_; + double pitch_offset_; + double comming_angle_; + double leaving_angle_; + double lock_id_ = -1; + double high_speed_delay_time_; + double low_speed_delay_time_; + double decision_speed_; + + AimPoint choose_aim_point(const Target & target); +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__AIMER_HPP \ No newline at end of file diff --git a/tasks/auto_aim/armor.cpp b/tasks/auto_aim/armor.cpp new file mode 100644 index 0000000..e29ab05 --- /dev/null +++ b/tasks/auto_aim/armor.cpp @@ -0,0 +1,221 @@ +#include "armor.hpp" + +#include +#include +#include + +namespace auto_aim +{ +Lightbar::Lightbar(const cv::RotatedRect & rotated_rect, std::size_t id) +: id(id), rotated_rect(rotated_rect) +{ + std::vector corners(4); + rotated_rect.points(&corners[0]); + std::sort(corners.begin(), corners.end(), [](const cv::Point2f & a, const cv::Point2f & b) { + return a.y < b.y; + }); + + center = rotated_rect.center; + top = (corners[0] + corners[1]) / 2; + bottom = (corners[2] + corners[3]) / 2; + top2bottom = bottom - top; + + points.emplace_back(top); + points.emplace_back(bottom); + + width = cv::norm(corners[0] - corners[1]); + angle = std::atan2(top2bottom.y, top2bottom.x); + angle_error = std::abs(angle - CV_PI / 2); + length = cv::norm(top2bottom); + ratio = length / width; +} + +//传统构造函数 +Armor::Armor(const Lightbar & left, const Lightbar & right) +: left(left), right(right), duplicated(false) +{ + color = left.color; + center = (left.center + right.center) / 2; + + points.emplace_back(left.top); + points.emplace_back(right.top); + points.emplace_back(right.bottom); + points.emplace_back(left.bottom); + + auto left2right = right.center - left.center; + auto width = cv::norm(left2right); + auto max_lightbar_length = std::max(left.length, right.length); + auto min_lightbar_length = std::min(left.length, right.length); + ratio = width / max_lightbar_length; + side_ratio = max_lightbar_length / min_lightbar_length; + + auto roll = std::atan2(left2right.y, left2right.x); + auto left_rectangular_error = std::abs(left.angle - roll - CV_PI / 2); + auto right_rectangular_error = std::abs(right.angle - roll - CV_PI / 2); + rectangular_error = std::max(left_rectangular_error, right_rectangular_error); +} + +//神经网络构造函数 +Armor::Armor( + int class_id, float confidence, const cv::Rect & box, std::vector armor_keypoints) +: class_id(class_id), confidence(confidence), box(box), points(armor_keypoints) +{ + center = (armor_keypoints[0] + armor_keypoints[1] + armor_keypoints[2] + armor_keypoints[3]) / 4; + auto left_width = cv::norm(armor_keypoints[0] - armor_keypoints[3]); + auto right_width = cv::norm(armor_keypoints[1] - armor_keypoints[2]); + auto max_width = std::max(left_width, right_width); + auto top_length = cv::norm(armor_keypoints[0] - armor_keypoints[1]); + auto bottom_length = cv::norm(armor_keypoints[3] - armor_keypoints[2]); + auto max_length = std::max(top_length, bottom_length); + auto left_center = (armor_keypoints[0] + armor_keypoints[3]) / 2; + auto right_center = (armor_keypoints[1] + armor_keypoints[2]) / 2; + auto left2right = right_center - left_center; + auto roll = std::atan2(left2right.y, left2right.x); + auto left_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[3] - armor_keypoints[0]).y, (armor_keypoints[3] - armor_keypoints[0]).x) - + roll - CV_PI / 2); + auto right_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[2] - armor_keypoints[1]).y, (armor_keypoints[2] - armor_keypoints[1]).x) - + roll - CV_PI / 2); + rectangular_error = std::max(left_rectangular_error, right_rectangular_error); + + ratio = max_length / max_width; + // color = class_id == 0 ? Color::blue : Color::red; + + if (class_id >= 0 && class_id < armor_properties.size()) { + auto [color, name, type] = armor_properties[class_id]; + this->color = color; + this->name = name; + this->type = type; + } else { + this->color = blue; // Default + this->name = not_armor; // Default + this->type = small; // Default + } +} + +//神经网络ROI构造函数 +Armor::Armor( + int class_id, float confidence, const cv::Rect & box, std::vector armor_keypoints, + cv::Point2f offset) +: class_id(class_id), confidence(confidence), box(box), points(armor_keypoints) +{ + std::transform( + armor_keypoints.begin(), armor_keypoints.end(), armor_keypoints.begin(), + [&offset](const cv::Point2f & point) { return point + offset; }); + std::transform( + points.begin(), points.end(), points.begin(), + [&offset](const cv::Point2f & point) { return point + offset; }); + center = (armor_keypoints[0] + armor_keypoints[1] + armor_keypoints[2] + armor_keypoints[3]) / 4; + auto left_width = cv::norm(armor_keypoints[0] - armor_keypoints[3]); + auto right_width = cv::norm(armor_keypoints[1] - armor_keypoints[2]); + auto max_width = std::max(left_width, right_width); + auto top_length = cv::norm(armor_keypoints[0] - armor_keypoints[1]); + auto bottom_length = cv::norm(armor_keypoints[3] - armor_keypoints[2]); + auto max_length = std::max(top_length, bottom_length); + auto left_center = (armor_keypoints[0] + armor_keypoints[3]) / 2; + auto right_center = (armor_keypoints[1] + armor_keypoints[2]) / 2; + auto left2right = right_center - left_center; + auto roll = std::atan2(left2right.y, left2right.x); + auto left_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[3] - armor_keypoints[0]).y, (armor_keypoints[3] - armor_keypoints[0]).x) - + roll - CV_PI / 2); + auto right_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[2] - armor_keypoints[1]).y, (armor_keypoints[2] - armor_keypoints[1]).x) - + roll - CV_PI / 2); + rectangular_error = std::max(left_rectangular_error, right_rectangular_error); + + ratio = max_length / max_width; + // color = class_id == 0 ? Color::blue : Color::red; + + if (class_id >= 0 && class_id < armor_properties.size()) { + auto [color, name, type] = armor_properties[class_id]; + this->color = color; + this->name = name; + this->type = type; + } else { + this->color = blue; // Default + this->name = not_armor; // Default + this->type = small; // Default + } +} + +// YOLOV5构造函数 +Armor::Armor( + int color_id, int num_id, float confidence, const cv::Rect & box, + std::vector armor_keypoints) +: confidence(confidence), box(box), points(armor_keypoints) +{ + center = (armor_keypoints[0] + armor_keypoints[1] + armor_keypoints[2] + armor_keypoints[3]) / 4; + auto left_width = cv::norm(armor_keypoints[0] - armor_keypoints[3]); + auto right_width = cv::norm(armor_keypoints[1] - armor_keypoints[2]); + auto max_width = std::max(left_width, right_width); + auto top_length = cv::norm(armor_keypoints[0] - armor_keypoints[1]); + auto bottom_length = cv::norm(armor_keypoints[3] - armor_keypoints[2]); + auto max_length = std::max(top_length, bottom_length); + auto left_center = (armor_keypoints[0] + armor_keypoints[3]) / 2; + auto right_center = (armor_keypoints[1] + armor_keypoints[2]) / 2; + auto left2right = right_center - left_center; + auto roll = std::atan2(left2right.y, left2right.x); + auto left_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[3] - armor_keypoints[0]).y, (armor_keypoints[3] - armor_keypoints[0]).x) - + roll - CV_PI / 2); + auto right_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[2] - armor_keypoints[1]).y, (armor_keypoints[2] - armor_keypoints[1]).x) - + roll - CV_PI / 2); + rectangular_error = std::max(left_rectangular_error, right_rectangular_error); + + ratio = max_length / max_width; + color = color_id == 0 ? Color::blue : color_id == 1 ? Color::red : Color::extinguish; + name = num_id == 0 ? ArmorName::sentry + : num_id > 5 ? ArmorName(num_id) + : ArmorName(num_id - 1); //TODO 考虑Bb + type = num_id == 1 ? ArmorType::big : ArmorType::small; +} + +// YOLOV5+ROI构造函数 +Armor::Armor( + int color_id, int num_id, float confidence, const cv::Rect & box, + std::vector armor_keypoints, cv::Point2f offset) +: confidence(confidence), box(box), points(armor_keypoints) +{ + std::transform( + armor_keypoints.begin(), armor_keypoints.end(), armor_keypoints.begin(), + [&offset](const cv::Point2f & point) { return point + offset; }); + std::transform( + points.begin(), points.end(), points.begin(), + [&offset](const cv::Point2f & point) { return point + offset; }); + center = (armor_keypoints[0] + armor_keypoints[1] + armor_keypoints[2] + armor_keypoints[3]) / 4; + auto left_width = cv::norm(armor_keypoints[0] - armor_keypoints[3]); + auto right_width = cv::norm(armor_keypoints[1] - armor_keypoints[2]); + auto max_width = std::max(left_width, right_width); + auto top_length = cv::norm(armor_keypoints[0] - armor_keypoints[1]); + auto bottom_length = cv::norm(armor_keypoints[3] - armor_keypoints[2]); + auto max_length = std::max(top_length, bottom_length); + auto left_center = (armor_keypoints[0] + armor_keypoints[3]) / 2; + auto right_center = (armor_keypoints[1] + armor_keypoints[2]) / 2; + auto left2right = right_center - left_center; + auto roll = std::atan2(left2right.y, left2right.x); + auto left_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[3] - armor_keypoints[0]).y, (armor_keypoints[3] - armor_keypoints[0]).x) - + roll - CV_PI / 2); + auto right_rectangular_error = std::abs( + std::atan2( + (armor_keypoints[2] - armor_keypoints[1]).y, (armor_keypoints[2] - armor_keypoints[1]).x) - + roll - CV_PI / 2); + rectangular_error = std::max(left_rectangular_error, right_rectangular_error); + + ratio = max_length / max_width; + color = color_id == 0 ? Color::blue : color_id == 1 ? Color::red : Color::extinguish; + name = num_id == 0 ? ArmorName::sentry : num_id > 5 ? ArmorName(num_id) : ArmorName(num_id - 1); + type = num_id == 1 ? ArmorType::big : ArmorType::small; +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/armor.hpp b/tasks/auto_aim/armor.hpp new file mode 100644 index 0000000..4950582 --- /dev/null +++ b/tasks/auto_aim/armor.hpp @@ -0,0 +1,125 @@ +#ifndef AUTO_AIM__ARMOR_HPP +#define AUTO_AIM__ARMOR_HPP + +#include +#include +#include +#include + +namespace auto_aim +{ +enum Color +{ + red, + blue, + extinguish, + purple +}; +const std::vector COLORS = {"red", "blue", "extinguish", "purple"}; + +enum ArmorType +{ + big, + small +}; +const std::vector ARMOR_TYPES = {"big", "small"}; + +enum ArmorName +{ + one, + two, + three, + four, + five, + sentry, + outpost, + base, + not_armor +}; +const std::vector ARMOR_NAMES = {"one", "two", "three", "four", "five", + "sentry", "outpost", "base", "not_armor"}; + +enum ArmorPriority +{ + first = 1, + second, + third, + forth, + fifth +}; + +// clang-format off +const std::vector> armor_properties = { + {blue, sentry, small}, {red, sentry, small}, {extinguish, sentry, small}, + {blue, one, small}, {red, one, small}, {extinguish, one, small}, + {blue, two, small}, {red, two, small}, {extinguish, two, small}, + {blue, three, small}, {red, three, small}, {extinguish, three, small}, + {blue, four, small}, {red, four, small}, {extinguish, four, small}, + {blue, five, small}, {red, five, small}, {extinguish, five, small}, + {blue, outpost, small}, {red, outpost, small}, {extinguish, outpost, small}, + {blue, base, big}, {red, base, big}, {extinguish, base, big}, {purple, base, big}, + {blue, base, small}, {red, base, small}, {extinguish, base, small}, {purple, base, small}, + {blue, three, big}, {red, three, big}, {extinguish, three, big}, + {blue, four, big}, {red, four, big}, {extinguish, four, big}, + {blue, five, big}, {red, five, big}, {extinguish, five, big}}; +// clang-format on + +struct Lightbar +{ + std::size_t id; + Color color; + cv::Point2f center, top, bottom, top2bottom; + std::vector points; + double angle, angle_error, length, width, ratio; + cv::RotatedRect rotated_rect; + + Lightbar(const cv::RotatedRect & rotated_rect, std::size_t id); + Lightbar() {}; +}; + +struct Armor +{ + Color color; + Lightbar left, right; //used to be const + cv::Point2f center; // 不是对角线交点,不能作为实际中心! + cv::Point2f center_norm; // 归一化坐标 + std::vector points; + + double ratio; // 两灯条的中点连线与长灯条的长度之比 + double side_ratio; // 长灯条与短灯条的长度之比 + double rectangular_error; // 灯条和中点连线所成夹角与π/2的差值 + + ArmorType type; + ArmorName name; + ArmorPriority priority; + int class_id; + cv::Rect box; + cv::Mat pattern; + double confidence; + bool duplicated; + + Eigen::Vector3d xyz_in_gimbal; // 单位:m + Eigen::Vector3d xyz_in_world; // 单位:m + Eigen::Vector3d ypr_in_gimbal; // 单位:rad + Eigen::Vector3d ypr_in_world; // 单位:rad + Eigen::Vector3d ypd_in_world; // 球坐标系 + + double yaw_raw; // rad + + Armor(const Lightbar & left, const Lightbar & right); + Armor( + int class_id, float confidence, const cv::Rect & box, std::vector armor_keypoints); + Armor( + int class_id, float confidence, const cv::Rect & box, std::vector armor_keypoints, + cv::Point2f offset); + Armor( + int color_id, int num_id, float confidence, const cv::Rect & box, + std::vector armor_keypoints); + Armor( + int color_id, int num_id, float confidence, const cv::Rect & box, + std::vector armor_keypoints, cv::Point2f offset); +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__ARMOR_HPP \ No newline at end of file diff --git a/tasks/auto_aim/classifier.cpp b/tasks/auto_aim/classifier.cpp new file mode 100644 index 0000000..d94f0e9 --- /dev/null +++ b/tasks/auto_aim/classifier.cpp @@ -0,0 +1,114 @@ +#include "classifier.hpp" + +#include + +namespace auto_aim +{ +Classifier::Classifier(const std::string & config_path) +{ + auto yaml = YAML::LoadFile(config_path); + auto model = yaml["classify_model"].as(); + net_ = cv::dnn::readNetFromONNX(model); + auto ovmodel = core_.read_model(model); + compiled_model_ = core_.compile_model( + ovmodel, "AUTO", ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY)); +} + +void Classifier::classify(Armor & armor) +{ + if (armor.pattern.empty()) { + armor.name = ArmorName::not_armor; + return; + } + + cv::Mat gray; + cv::cvtColor(armor.pattern, gray, cv::COLOR_BGR2GRAY); + + auto input = cv::Mat(32, 32, CV_8UC1, cv::Scalar(0)); + auto x_scale = static_cast(32) / gray.cols; + auto y_scale = static_cast(32) / gray.rows; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(gray.rows * scale); + auto w = static_cast(gray.cols * scale); + + if (h == 0 || w == 0) { + armor.name = ArmorName::not_armor; + return; + } + auto roi = cv::Rect(0, 0, w, h); + cv::resize(gray, input(roi), {w, h}); + + auto blob = cv::dnn::blobFromImage(input, 1.0 / 255.0, cv::Size(), cv::Scalar()); + + net_.setInput(blob); + cv::Mat outputs = net_.forward(); + + // softmax + float max = *std::max_element(outputs.begin(), outputs.end()); + cv::exp(outputs - max, outputs); + float sum = cv::sum(outputs)[0]; + outputs /= sum; + + double confidence; + cv::Point label_point; + cv::minMaxLoc(outputs.reshape(1, 1), nullptr, &confidence, nullptr, &label_point); + int label_id = label_point.x; + + armor.confidence = confidence; + armor.name = static_cast(label_id); +} + +void Classifier::ovclassify(Armor & armor) +{ + if (armor.pattern.empty()) { + armor.name = ArmorName::not_armor; + return; + } + + cv::Mat gray; + cv::cvtColor(armor.pattern, gray, cv::COLOR_BGR2GRAY); + + // Resize image to 32x32 + auto input = cv::Mat(32, 32, CV_8UC1, cv::Scalar(0)); + auto x_scale = static_cast(32) / gray.cols; + auto y_scale = static_cast(32) / gray.rows; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(gray.rows * scale); + auto w = static_cast(gray.cols * scale); + + if (h == 0 || w == 0) { + armor.name = ArmorName::not_armor; + return; + } + + auto roi = cv::Rect(0, 0, w, h); + cv::resize(gray, input(roi), {w, h}); + // Normalize the input image to [0, 1] range + input.convertTo(input, CV_32F, 1.0 / 255.0); + + ov::Tensor input_tensor(ov::element::f32, {1, 1, 32, 32}, input.data); + + ov::InferRequest infer_request = compiled_model_.create_infer_request(); + infer_request.set_input_tensor(input_tensor); + infer_request.infer(); + + auto output_tensor = infer_request.get_output_tensor(); + auto output_shape = output_tensor.get_shape(); + cv::Mat outputs(1, 9, CV_32F, output_tensor.data()); + + // Softmax + float max = *std::max_element(outputs.begin(), outputs.end()); + cv::exp(outputs - max, outputs); + float sum = cv::sum(outputs)[0]; + outputs /= sum; + + double confidence; + cv::Point label_point; + cv::minMaxLoc(outputs.reshape(1, 1), nullptr, &confidence, nullptr, &label_point); + int label_id = label_point.x; + + armor.confidence = confidence; + armor.name = static_cast(label_id); +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/classifier.hpp b/tasks/auto_aim/classifier.hpp new file mode 100644 index 0000000..4c85264 --- /dev/null +++ b/tasks/auto_aim/classifier.hpp @@ -0,0 +1,29 @@ +#ifndef AUTO_AIM__CLASSIFIER_HPP +#define AUTO_AIM__CLASSIFIER_HPP + +#include +#include +#include + +#include "armor.hpp" + +namespace auto_aim +{ +class Classifier +{ +public: + explicit Classifier(const std::string & config_path); + + void classify(Armor & armor); + + void ovclassify(Armor & armor); + +private: + cv::dnn::Net net_; + ov::Core core_; + ov::CompiledModel compiled_model_; +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__CLASSIFIER_HPP \ No newline at end of file diff --git a/tasks/auto_aim/detector.cpp b/tasks/auto_aim/detector.cpp new file mode 100644 index 0000000..1a9728e --- /dev/null +++ b/tasks/auto_aim/detector.cpp @@ -0,0 +1,491 @@ +#include "detector.hpp" + +#include +#include + +#include + +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" + +namespace auto_aim +{ +Detector::Detector(const std::string & config_path, bool debug) +: classifier_(config_path), debug_(debug) +{ + auto yaml = YAML::LoadFile(config_path); + + threshold_ = yaml["threshold"].as(); + max_angle_error_ = yaml["max_angle_error"].as() / 57.3; // degree to rad + min_lightbar_ratio_ = yaml["min_lightbar_ratio"].as(); + max_lightbar_ratio_ = yaml["max_lightbar_ratio"].as(); + min_lightbar_length_ = yaml["min_lightbar_length"].as(); + min_armor_ratio_ = yaml["min_armor_ratio"].as(); + max_armor_ratio_ = yaml["max_armor_ratio"].as(); + max_side_ratio_ = yaml["max_side_ratio"].as(); + min_confidence_ = yaml["min_confidence"].as(); + max_rectangular_error_ = yaml["max_rectangular_error"].as() / 57.3; // degree to rad + + save_path_ = "patterns"; + std::filesystem::create_directory(save_path_); +} + +std::list Detector::detect(const cv::Mat & bgr_img, int frame_count) +{ + // 彩色图转灰度图 + cv::Mat gray_img; + cv::cvtColor(bgr_img, gray_img, cv::COLOR_BGR2GRAY); + + // 进行二值化 + cv::Mat binary_img; + cv::threshold(gray_img, binary_img, threshold_, 255, cv::THRESH_BINARY); + cv::imshow("binary_img", binary_img); + + // 获取轮廓点 + std::vector> contours; + cv::findContours(binary_img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + + // 获取灯条 + std::size_t lightbar_id = 0; + std::list lightbars; + for (const auto & contour : contours) { + auto rotated_rect = cv::minAreaRect(contour); + auto lightbar = Lightbar(rotated_rect, lightbar_id); + + if (!check_geometry(lightbar)) continue; + + lightbar.color = get_color(bgr_img, contour); + lightbars.emplace_back(lightbar); + lightbar_id += 1; + } + + // 将灯条从左到右排序 + lightbars.sort([](const Lightbar & a, const Lightbar & b) { return a.center.x < b.center.x; }); + + // 获取装甲板 + std::list armors; + for (auto left = lightbars.begin(); left != lightbars.end(); left++) { + for (auto right = std::next(left); right != lightbars.end(); right++) { + if (left->color != right->color) continue; + + auto armor = Armor(*left, *right); + if (!check_geometry(armor)) continue; + + armor.pattern = get_pattern(bgr_img, armor); + classifier_.classify(armor); + if (!check_name(armor)) continue; + + armor.type = get_type(armor); + if (!check_type(armor)) continue; + + armor.center_norm = get_center_norm(bgr_img, armor.center); + armors.emplace_back(armor); + } + } + + // 检查装甲板是否存在共用灯条的情况 + for (auto armor1 = armors.begin(); armor1 != armors.end(); armor1++) { + for (auto armor2 = std::next(armor1); armor2 != armors.end(); armor2++) { + if ( + armor1->left.id != armor2->left.id && armor1->left.id != armor2->right.id && + armor1->right.id != armor2->left.id && armor1->right.id != armor2->right.id) { + continue; + } + + // 装甲板重叠, 保留roi小的 + if (armor1->left.id == armor2->left.id || armor1->right.id == armor2->right.id) { + auto area1 = armor1->pattern.cols * armor1->pattern.rows; + auto area2 = armor2->pattern.cols * armor2->pattern.rows; + if (area1 < area2) + armor2->duplicated = true; + else + armor1->duplicated = true; + } + + // 装甲板相连,保留置信度大的 + if (armor1->left.id == armor2->right.id || armor1->right.id == armor2->left.id) { + if (armor1->confidence < armor2->confidence) + armor1->duplicated = true; + else + armor2->duplicated = true; + } + } + } + + armors.remove_if([&](const Armor & a) { return a.duplicated; }); + + if (debug_) show_result(binary_img, bgr_img, lightbars, armors, frame_count); + + return armors; +} + +bool Detector::detect(Armor & armor, const cv::Mat & bgr_img) +{ + // 取得四个角点 + auto tl = armor.points[0]; + auto tr = armor.points[1]; + auto br = armor.points[2]; + auto bl = armor.points[3]; + // 计算向量和调整后的点 + auto lt2b = bl - tl; + auto rt2b = br - tr; + auto tl1 = (tl + bl) / 2 - lt2b; + auto bl1 = (tl + bl) / 2 + lt2b; + auto br1 = (tr + br) / 2 + rt2b; + auto tr1 = (tr + br) / 2 - rt2b; + auto tl2tr = tr1 - tl1; + auto bl2br = br1 - bl1; + auto tl2 = (tl1 + tr) / 2 - 0.75 * tl2tr; + auto tr2 = (tl1 + tr) / 2 + 0.75 * tl2tr; + auto bl2 = (bl1 + br) / 2 - 0.75 * bl2br; + auto br2 = (bl1 + br) / 2 + 0.75 * bl2br; + // 构造新的四个角点 + std::vector points = {tl2, tr2, br2, bl2}; + auto armor_rotaterect = cv::minAreaRect(points); + cv::Rect boundingBox = armor_rotaterect.boundingRect(); + // 检查boundingBox是否超出图像边界 + if ( + boundingBox.x < 0 || boundingBox.y < 0 || boundingBox.x + boundingBox.width > bgr_img.cols || + boundingBox.y + boundingBox.height > bgr_img.rows) { + return false; + } + + // 在图像上裁剪出这个矩形区域(ROI) + cv::Mat armor_roi = bgr_img(boundingBox); + if (armor_roi.empty()) { + return false; + } + + // 彩色图转灰度图 + cv::Mat gray_img; + cv::cvtColor(armor_roi, gray_img, cv::COLOR_BGR2GRAY); + // 进行二值化 + cv::Mat binary_img; + cv::threshold(gray_img, binary_img, threshold_, 255, cv::THRESH_BINARY); + // cv::imshow("binary_img", binary_img); + // 获取轮廓点 + std::vector> contours; + cv::findContours(binary_img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + // 获取灯条 + std::size_t lightbar_id = 0; + std::list lightbars; + for (const auto & contour : contours) { + auto rotated_rect = cv::minAreaRect(contour); + auto lightbar = Lightbar(rotated_rect, lightbar_id); + + if (!check_geometry(lightbar)) continue; + + lightbar.color = get_color(bgr_img, contour); + // lightbar_points_corrector(lightbar, gray_img); //关闭PCA + lightbars.emplace_back(lightbar); + lightbar_id += 1; + } + + if (lightbars.size() < 2) return false; + + // 将灯条从左到右排序 + lightbars.sort([](const Lightbar & a, const Lightbar & b) { return a.center.x < b.center.x; }); + + // 计算与 tl_roi, bl_roi 和 br_roi, tr_roi 距离最近的灯条 + Lightbar * closest_left_lightbar = nullptr; + Lightbar * closest_right_lightbar = nullptr; + float min_distance_tl_bl = std::numeric_limits::max(); + float min_distance_br_tr = std::numeric_limits::max(); + for (auto & lightbar : lightbars) { + float distance_tl_bl = + cv::norm(tl - (lightbar.top + cv::Point2f(boundingBox.x, boundingBox.y))) + + cv::norm(bl - (lightbar.bottom + cv::Point2f(boundingBox.x, boundingBox.y))); + if (distance_tl_bl < min_distance_tl_bl) { + min_distance_tl_bl = distance_tl_bl; + closest_left_lightbar = &lightbar; + } + float distance_br_tr = + cv::norm(br - (lightbar.bottom + cv::Point2f(boundingBox.x, boundingBox.y))) + + cv::norm(tr - (lightbar.top + cv::Point2f(boundingBox.x, boundingBox.y))); + if (distance_br_tr < min_distance_br_tr) { + min_distance_br_tr = distance_br_tr; + closest_right_lightbar = &lightbar; + } + } + + // tools::logger()->debug( + // "min_distance_br_tr + min_distance_tl_bl is {}", min_distance_br_tr + min_distance_tl_bl); + // std::vector points2f{ + // closest_left_lightbar->top, closest_left_lightbar->bottom, closest_right_lightbar->bottom, + // closest_right_lightbar->top}; + // tools::draw_points(armor_roi, points2f, {0, 0, 255}, 2); + // cv::imshow("armor_roi", armor_roi); + + if ( + closest_left_lightbar && closest_right_lightbar && + min_distance_br_tr + min_distance_tl_bl < 15) { + // 将四个点从armor_roi坐标系转换到原始图像坐标系 + armor.points[0] = closest_left_lightbar->top + cv::Point2f(boundingBox.x, boundingBox.y); + armor.points[1] = closest_right_lightbar->top + cv::Point2f(boundingBox.x, boundingBox.y); + armor.points[2] = closest_right_lightbar->bottom + cv::Point2f(boundingBox.x, boundingBox.y); + armor.points[3] = closest_left_lightbar->bottom + cv::Point2f(boundingBox.x, boundingBox.y); + return true; + } + + return false; +} + +bool Detector::check_geometry(const Lightbar & lightbar) const +{ + auto angle_ok = lightbar.angle_error < max_angle_error_; + auto ratio_ok = lightbar.ratio > min_lightbar_ratio_ && lightbar.ratio < max_lightbar_ratio_; + auto length_ok = lightbar.length > min_lightbar_length_; + return angle_ok && ratio_ok && length_ok; +} + +bool Detector::check_geometry(const Armor & armor) const +{ + auto ratio_ok = armor.ratio > min_armor_ratio_ && armor.ratio < max_armor_ratio_; + auto side_ratio_ok = armor.side_ratio < max_side_ratio_; + auto rectangular_error_ok = armor.rectangular_error < max_rectangular_error_; + return ratio_ok && side_ratio_ok && rectangular_error_ok; +} + +bool Detector::check_name(const Armor & armor) const +{ + auto name_ok = armor.name != ArmorName::not_armor; + auto confidence_ok = armor.confidence > min_confidence_; + + // 保存不确定的图案,用于分类器的迭代 + if (name_ok && !confidence_ok) save(armor); + + // 出现 5号 则显示 debug 信息。但不过滤。 + if (armor.name == ArmorName::five) tools::logger()->debug("See pattern 5"); + + return name_ok && confidence_ok; +} + +bool Detector::check_type(const Armor & armor) const +{ + auto name_ok = armor.type == ArmorType::small + ? (armor.name != ArmorName::one && armor.name != ArmorName::base) + : (armor.name == ArmorName::one || armor.name == ArmorName::base); + + // 保存异常的图案,用于分类器的迭代 + if (!name_ok) { + tools::logger()->debug( + "see strange armor: {} {}", ARMOR_TYPES[armor.type], ARMOR_NAMES[armor.name]); + save(armor); + } + + return name_ok; +} + +Color Detector::get_color(const cv::Mat & bgr_img, const std::vector & contour) const +{ + int red_sum = 0, blue_sum = 0; + + for (const auto & point : contour) { + red_sum += bgr_img.at(point)[2]; + blue_sum += bgr_img.at(point)[0]; + } + + return blue_sum > red_sum ? Color::blue : Color::red; +} + +cv::Mat Detector::get_pattern(const cv::Mat & bgr_img, const Armor & armor) const +{ + // 延长灯条获得装甲板角点 + // 1.125 = 0.5 * armor_height / lightbar_length = 0.5 * 126mm / 56mm + auto tl = armor.left.center - armor.left.top2bottom * 1.125; + auto bl = armor.left.center + armor.left.top2bottom * 1.125; + auto tr = armor.right.center - armor.right.top2bottom * 1.125; + auto br = armor.right.center + armor.right.top2bottom * 1.125; + + auto roi_left = std::max(std::min(tl.x, bl.x), 0); + auto roi_top = std::max(std::min(tl.y, tr.y), 0); + auto roi_right = std::min(std::max(tr.x, br.x), bgr_img.cols); + auto roi_bottom = std::min(std::max(bl.y, br.y), bgr_img.rows); + auto roi_tl = cv::Point(roi_left, roi_top); + auto roi_br = cv::Point(roi_right, roi_bottom); + auto roi = cv::Rect(roi_tl, roi_br); + + return bgr_img(roi); +} + +ArmorType Detector::get_type(const Armor & armor) +{ + /// 优先根据当前armor.ratio判断 + /// TODO: 25赛季是否还需要根据比例判断大小装甲?能否根据图案直接判断? + + if (armor.ratio > 3.0) { + // tools::logger()->debug( + // "[Detector] get armor type by ratio: BIG {} {:.2f}", ARMOR_NAMES[armor.name], armor.ratio); + return ArmorType::big; + } + + if (armor.ratio < 2.5) { + // tools::logger()->debug( + // "[Detector] get armor type by ratio: SMALL {} {:.2f}", ARMOR_NAMES[armor.name], armor.ratio); + return ArmorType::small; + } + + // tools::logger()->debug("[Detector] get armor type by name: {}", ARMOR_NAMES[armor.name]); + + // 英雄、基地只能是大装甲板 + if (armor.name == ArmorName::one || armor.name == ArmorName::base) { + return ArmorType::big; + } + + // 其他所有(工程、哨兵、前哨站、步兵)都是小装甲板 + /// TODO: 基地顶装甲是小装甲板 + return ArmorType::small; +} + +cv::Point2f Detector::get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const +{ + auto h = bgr_img.rows; + auto w = bgr_img.cols; + return {center.x / w, center.y / h}; +} + +void Detector::save(const Armor & armor) const +{ + auto file_name = fmt::format("{:%Y-%m-%d_%H-%M-%S}", std::chrono::system_clock::now()); + auto img_path = fmt::format("{}/{}_{}.jpg", save_path_, armor.name, file_name); + cv::imwrite(img_path, armor.pattern); +} + +void Detector::show_result( + const cv::Mat & binary_img, const cv::Mat & bgr_img, const std::list & lightbars, + const std::list & armors, int frame_count) const +{ + auto detection = bgr_img.clone(); + tools::draw_text(detection, fmt::format("[{}]", frame_count), {10, 30}, {255, 255, 255}); + + for (const auto & lightbar : lightbars) { + auto info = fmt::format( + "{:.1f} {:.1f} {:.1f} {}", lightbar.angle_error * 57.3, lightbar.ratio, lightbar.length, + COLORS[lightbar.color]); + tools::draw_text(detection, info, lightbar.top, {0, 255, 255}); + tools::draw_points(detection, lightbar.points, {0, 255, 255}, 3); + } + + for (const auto & armor : armors) { + auto info = fmt::format( + "{:.2f} {:.2f} {:.1f} {:.2f} {} {}", armor.ratio, armor.side_ratio, + armor.rectangular_error * 57.3, armor.confidence, ARMOR_NAMES[armor.name], + ARMOR_TYPES[armor.type]); + tools::draw_points(detection, armor.points, {0, 255, 0}); + tools::draw_text(detection, info, armor.left.bottom, {0, 255, 0}); + } + + cv::Mat binary_img2; + cv::resize(binary_img, binary_img2, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::resize(detection, detection, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + + // cv::imshow("threshold", binary_img2); + cv::imshow("detection", detection); +} + +void Detector::lightbar_points_corrector(Lightbar & lightbar, const cv::Mat & gray_img) const +{ + // 配置参数 + constexpr float MAX_BRIGHTNESS = 25; // 归一化最大亮度值 + constexpr float ROI_SCALE = 0.07; // ROI扩展比例 + constexpr float SEARCH_START = 0.4; // 搜索起始位置比例(原0.8/2) + constexpr float SEARCH_END = 0.6; // 搜索结束位置比例(原1.2/2) + + // 扩展并裁剪ROI + cv::Rect roi_box = lightbar.rotated_rect.boundingRect(); + roi_box.x -= roi_box.width * ROI_SCALE; + roi_box.y -= roi_box.height * ROI_SCALE; + roi_box.width += 2 * roi_box.width * ROI_SCALE; + roi_box.height += 2 * roi_box.height * ROI_SCALE; + + // 边界约束 + roi_box &= cv::Rect(0, 0, gray_img.cols, gray_img.rows); + + // 归一化ROI + cv::Mat roi = gray_img(roi_box); + const float mean_val = cv::mean(roi)[0]; + roi.convertTo(roi, CV_32F); + cv::normalize(roi, roi, 0, MAX_BRIGHTNESS, cv::NORM_MINMAX); + + // 计算质心 + const cv::Moments moments = cv::moments(roi); + const cv::Point2f centroid( + moments.m10 / moments.m00 + roi_box.x, moments.m01 / moments.m00 + roi_box.y); + + // 生成稀疏点云(优化性能) + std::vector points; + for (int i = 0; i < roi.rows; ++i) { + for (int j = 0; j < roi.cols; ++j) { + const float weight = roi.at(i, j); + if (weight > 1e-3) { // 忽略极小值提升性能 + points.emplace_back(j, i); // 坐标相对于ROI区域 + } + } + } + + // PCA计算对称轴方向 + cv::PCA pca(cv::Mat(points).reshape(1), cv::Mat(), cv::PCA::DATA_AS_ROW); + cv::Point2f axis(pca.eigenvectors.at(0, 0), pca.eigenvectors.at(0, 1)); + axis /= cv::norm(axis); + if (axis.y > 0) axis = -axis; // 统一方向 + + const auto find_corner = [&](int direction) -> cv::Point2f { + const float dx = axis.x * direction; + const float dy = axis.y * direction; + const float search_length = lightbar.length * (SEARCH_END - SEARCH_START); + + std::vector candidates; + + // 横向采样多个候选线 + const int half_width = (lightbar.width - 2) / 2; + for (int i_offset = -half_width; i_offset <= half_width; ++i_offset) { + // 计算搜索起点 + cv::Point2f start_point( + centroid.x + lightbar.length * SEARCH_START * dx + i_offset, + centroid.y + lightbar.length * SEARCH_START * dy); + + // 沿轴搜索亮度跳变点 + cv::Point2f corner = start_point; + float max_diff = 0; + bool found = false; + + for (float step = 0; step < search_length; ++step) { + const cv::Point2f cur_point(start_point.x + dx * step, start_point.y + dy * step); + + // 边界检查 + if ( + cur_point.x < 0 || cur_point.x >= gray_img.cols || cur_point.y < 0 || + cur_point.y >= gray_img.rows) { + break; + } + + // 计算亮度差(使用双线性插值提升精度) + const auto prev_val = gray_img.at(cv::Point2i(cur_point - cv::Point2f(dx, dy))); + const auto cur_val = gray_img.at(cv::Point2i(cur_point)); + const float diff = prev_val - cur_val; + + if (diff > max_diff && prev_val > mean_val) { + max_diff = diff; + corner = cur_point - cv::Point2f(dx, dy); // 跳变发生在上一位置 + found = true; + } + } + + if (found) { + candidates.push_back(corner); + } + } + + // 返回候选点均值 + return candidates.empty() + ? cv::Point2f(-1, -1) + : std::accumulate(candidates.begin(), candidates.end(), cv::Point2f(0, 0)) / + static_cast(candidates.size()); + }; + + // 并行检测顶部和底部 + lightbar.top = find_corner(1); + lightbar.bottom = find_corner(-1); +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/detector.hpp b/tasks/auto_aim/detector.hpp new file mode 100644 index 0000000..3e00c6e --- /dev/null +++ b/tasks/auto_aim/detector.hpp @@ -0,0 +1,62 @@ +#ifndef AUTO_AIM__DETECTOR_HPP +#define AUTO_AIM__DETECTOR_HPP + +#include +#include +#include +#include + +#include "armor.hpp" +#include "classifier.hpp" + +namespace auto_aim +{ + +class Detector +{ +public: + Detector(const std::string & config_path, bool debug = true); + + std::list detect(const cv::Mat & bgr_img, int frame_count = -1); + + bool detect(Armor & armor, const cv::Mat & bgr_img); + + friend class YOLOV8; + +private: + Classifier classifier_; + + double threshold_; + double max_angle_error_; + double min_lightbar_ratio_, max_lightbar_ratio_; + double min_lightbar_length_; + double min_armor_ratio_, max_armor_ratio_; + double max_side_ratio_; + double min_confidence_; + double max_rectangular_error_; + + bool debug_; + std::string save_path_; + + // 利用PCA回归角点,参考自https://github.com/CSU-FYT-Vision/FYT2024_vision + void lightbar_points_corrector(Lightbar & lightbar, const cv::Mat & gray_img) const; + + bool check_geometry(const Lightbar & lightbar) const; + bool check_geometry(const Armor & armor) const; + bool check_name(const Armor & armor) const; + bool check_type(const Armor & armor) const; + + Color get_color(const cv::Mat & bgr_img, const std::vector & contour) const; + cv::Mat get_pattern(const cv::Mat & bgr_img, const Armor & armor) const; + ArmorType get_type(const Armor & armor); + cv::Point2f get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const; + + void save(const Armor & armor) const; + void show_result( + const cv::Mat & binary_img, const cv::Mat & bgr_img, const std::list & lightbars, + const std::list & armors, int frame_count) const; +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__DETECTOR_HPP \ No newline at end of file diff --git a/tasks/auto_aim/multithread/commandgener.cpp b/tasks/auto_aim/multithread/commandgener.cpp new file mode 100644 index 0000000..b907f91 --- /dev/null +++ b/tasks/auto_aim/multithread/commandgener.cpp @@ -0,0 +1,74 @@ +#include "commandgener.hpp" + +#include "tools/math_tools.hpp" + +namespace auto_aim +{ +namespace multithread +{ + +CommandGener::CommandGener( + auto_aim::Shooter & shooter, auto_aim::Aimer & aimer, io::CBoard & cboard, + tools::Plotter & plotter, bool debug) +: shooter_(shooter), aimer_(aimer), cboard_(cboard), plotter_(plotter), stop_(false), debug_(debug) +{ + thread_ = std::thread(&CommandGener::generate_command, this); +} + +CommandGener::~CommandGener() +{ + { + std::lock_guard lock(mtx_); + stop_ = true; + } + cv_.notify_all(); + if (thread_.joinable()) thread_.join(); +} + +void CommandGener::push( + const std::list & targets, const std::chrono::steady_clock::time_point & t, + double bullet_speed, const Eigen::Vector3d & gimbal_pos) +{ + std::lock_guard lock(mtx_); + latest_ = {targets, t, bullet_speed, gimbal_pos}; + cv_.notify_one(); +} + +void CommandGener::generate_command() +{ + auto t0 = std::chrono::steady_clock::now(); + while (!stop_) { + std::optional input; + { + std::lock_guard lock(mtx_); + if (latest_ && tools::delta_time(std::chrono::steady_clock::now(), latest_->t) < 0.2) { + input = latest_; + } else + input = std::nullopt; + } + if (input) { + auto command = aimer_.aim(input->targets_, input->t, input->bullet_speed); + command.shoot = shooter_.shoot(command, aimer_, input->targets_, input->gimbal_pos); + command.horizon_distance = input->targets_.empty() + ? 0 + : std::sqrt( + tools::square(input->targets_.front().ekf_x()[0]) + + tools::square(input->targets_.front().ekf_x()[2])); + cboard_.send(command); + if (debug_) { + nlohmann::json data; + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + data["cmd_yaw"] = command.yaw * 57.3; + data["cmd_pitch"] = command.pitch * 57.3; + data["shoot"] = command.shoot; + data["horizon_distance"] = command.horizon_distance; + plotter_.plot(data); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(2)); //approximately 500Hz + } +} + +} // namespace multithread + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/multithread/commandgener.hpp b/tasks/auto_aim/multithread/commandgener.hpp new file mode 100644 index 0000000..1efb5b8 --- /dev/null +++ b/tasks/auto_aim/multithread/commandgener.hpp @@ -0,0 +1,58 @@ +#ifndef AUTO_AIM_MULTITHREAD__HPP +#define AUTO_AIM_MULTITHREAD__HPP + +#include + +#include "io/cboard.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/omniperception/decider.hpp" +#include "tools/plotter.hpp" + +namespace auto_aim +{ +namespace multithread +{ + +class CommandGener +{ +public: + CommandGener( + auto_aim::Shooter & shooter, auto_aim::Aimer & aimer, io::CBoard & cboard, + tools::Plotter & plotter, bool debug = false); + + ~CommandGener(); + + void push( + const std::list & targets, const std::chrono::steady_clock::time_point & t, + double bullet_speed, const Eigen::Vector3d & gimbal_pos); + +private: + struct Input + { + std::list targets_; + std::chrono::steady_clock::time_point t; + // std::function decide; + double bullet_speed; + Eigen::Vector3d gimbal_pos; + }; + + io::CBoard & cboard_; + auto_aim::Shooter & shooter_; + auto_aim::Aimer & aimer_; + tools::Plotter & plotter_; + + std::optional latest_; + std::mutex mtx_; + std::condition_variable cv_; + std::thread thread_; + bool stop_, debug_; + + void generate_command(); +}; + +} // namespace multithread + +} // namespace auto_aim + +#endif // AUTO_AIM_MULTITHREAD__HPP \ No newline at end of file diff --git a/tasks/auto_aim/multithread/mt_detector.cpp b/tasks/auto_aim/multithread/mt_detector.cpp new file mode 100644 index 0000000..bf42928 --- /dev/null +++ b/tasks/auto_aim/multithread/mt_detector.cpp @@ -0,0 +1,102 @@ +#include "mt_detector.hpp" + +#include + +namespace auto_aim +{ +namespace multithread +{ + +MultiThreadDetector::MultiThreadDetector(const std::string & config_path, bool debug) +: yolo_(config_path, debug) +{ + auto yaml = YAML::LoadFile(config_path); + auto yolo_name = yaml["yolo_name"].as(); + auto model_path = yaml[yolo_name + "_model_path"].as(); + device_ = yaml["device"].as(); + + auto model = core_.read_model(model_path); + ov::preprocess::PrePostProcessor ppp(model); + auto & input = ppp.input(); + + input.tensor() + .set_element_type(ov::element::u8) + .set_shape({1, 640, 640, 3}) // TODO + .set_layout("NHWC") + .set_color_format(ov::preprocess::ColorFormat::BGR); + + input.model().set_layout("NCHW"); + + input.preprocess() + .convert_element_type(ov::element::f32) + .convert_color(ov::preprocess::ColorFormat::RGB) + // .resize(ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR) + .scale(255.0); + + model = ppp.build(); + compiled_model_ = core_.compile_model( + model, device_, ov::hint::performance_mode(ov::hint::PerformanceMode::THROUGHPUT)); + + tools::logger()->info("[MultiThreadDetector] initialized !"); +} + +void MultiThreadDetector::push(cv::Mat img, std::chrono::steady_clock::time_point t) +{ + auto x_scale = static_cast(640) / img.rows; + auto y_scale = static_cast(640) / img.cols; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(img.rows * scale); + auto w = static_cast(img.cols * scale); + + // preproces + auto input = cv::Mat(640, 640, CV_8UC3, cv::Scalar(0, 0, 0)); + auto roi = cv::Rect(0, 0, w, h); + cv::resize(img, input(roi), {w, h}); + + auto input_port = compiled_model_.input(); + auto infer_request = compiled_model_.create_infer_request(); + ov::Tensor input_tensor(ov::element::u8, {1, 640, 640, 3}, input.data); + + infer_request.set_input_tensor(input_tensor); + infer_request.start_async(); + queue_.push({img.clone(), t, std::move(infer_request)}); +} + +std::tuple, std::chrono::steady_clock::time_point> MultiThreadDetector::pop() +{ + auto [img, t, infer_request] = queue_.pop(); + infer_request.wait(); + + // postprocess + auto output_tensor = infer_request.get_output_tensor(); + auto output_shape = output_tensor.get_shape(); + cv::Mat output(output_shape[1], output_shape[2], CV_32F, output_tensor.data()); + auto x_scale = static_cast(640) / img.rows; + auto y_scale = static_cast(640) / img.cols; + auto scale = std::min(x_scale, y_scale); + auto armors = yolo_.postprocess(scale, output, img, 0); //暂不支持ROI + + return {std::move(armors), t}; +} + +std::tuple, std::chrono::steady_clock::time_point> +MultiThreadDetector::debug_pop() +{ + auto [img, t, infer_request] = queue_.pop(); + infer_request.wait(); + + // postprocess + auto output_tensor = infer_request.get_output_tensor(); + auto output_shape = output_tensor.get_shape(); + cv::Mat output(output_shape[1], output_shape[2], CV_32F, output_tensor.data()); + auto x_scale = static_cast(640) / img.rows; + auto y_scale = static_cast(640) / img.cols; + auto scale = std::min(x_scale, y_scale); + auto armors = yolo_.postprocess(scale, output, img, 0); //暂不支持ROI + + return {img, std::move(armors), t}; +} + +} // namespace multithread + +} // namespace auto_aim diff --git a/tasks/auto_aim/multithread/mt_detector.hpp b/tasks/auto_aim/multithread/mt_detector.hpp new file mode 100644 index 0000000..b92d9cf --- /dev/null +++ b/tasks/auto_aim/multithread/mt_detector.hpp @@ -0,0 +1,44 @@ +#ifndef AUTO_AIM__MT_DETECTOR_HPP +#define AUTO_AIM__MT_DETECTOR_HPP + +#include +#include +#include +#include + +#include "tasks/auto_aim/yolos/yolov5.hpp" +#include "tools/logger.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace auto_aim +{ +namespace multithread +{ + +class MultiThreadDetector +{ +public: + MultiThreadDetector(const std::string & config_path, bool debug = false); + + void push(cv::Mat img, std::chrono::steady_clock::time_point t); + + std::tuple, std::chrono::steady_clock::time_point> pop(); //暂时不支持yolov8 + + std::tuple, std::chrono::steady_clock::time_point> debug_pop(); + +private: + ov::Core core_; + ov::CompiledModel compiled_model_; + std::string device_; + YOLO yolo_; + + tools::ThreadSafeQueue< + std::tuple> + queue_{16, [] { tools::logger()->debug("[MultiThreadDetector] queue is full!"); }}; +}; + +} // namespace multithread + +} // namespace auto_aim + +#endif // AUTO_AIM__MT_DETECTOR_HPP \ No newline at end of file diff --git a/tasks/auto_aim/planner/planner.cpp b/tasks/auto_aim/planner/planner.cpp new file mode 100644 index 0000000..14295ef --- /dev/null +++ b/tasks/auto_aim/planner/planner.cpp @@ -0,0 +1,205 @@ +#include "planner.hpp" + +#include + +#include "tools/math_tools.hpp" +#include "tools/trajectory.hpp" +#include "tools/yaml.hpp" + +using namespace std::chrono_literals; + +namespace auto_aim +{ +Planner::Planner(const std::string & config_path) +{ + auto yaml = tools::load(config_path); + yaw_offset_ = tools::read(yaml, "yaw_offset") / 57.3; + pitch_offset_ = tools::read(yaml, "pitch_offset") / 57.3; + fire_thresh_ = tools::read(yaml, "fire_thresh"); + decision_speed_ = tools::read(yaml, "decision_speed"); + high_speed_delay_time_ = tools::read(yaml, "high_speed_delay_time"); + low_speed_delay_time_ = tools::read(yaml, "low_speed_delay_time"); + + setup_yaw_solver(config_path); + setup_pitch_solver(config_path); +} + +Plan Planner::plan(Target target, double bullet_speed) +{ + // 0. Check bullet speed + if (bullet_speed < 10 || bullet_speed > 25) { + bullet_speed = 22; + } + + // 1. Predict fly_time + Eigen::Vector3d xyz; + auto min_dist = 1e10; + for (auto & xyza : target.armor_xyza_list()) { + auto dist = xyza.head<2>().norm(); + if (dist < min_dist) { + min_dist = dist; + xyz = xyza.head<3>(); + } + } + auto bullet_traj = tools::Trajectory(bullet_speed, min_dist, xyz.z()); + target.predict(bullet_traj.fly_time); + + // 2. Get trajectory + double yaw0; + Trajectory traj; + try { + yaw0 = aim(target, bullet_speed)(0); + traj = get_trajectory(target, yaw0, bullet_speed); + } catch (const std::exception & e) { + tools::logger()->warn("Unsolvable target {:.2f}", bullet_speed); + return {false}; + } + + // 3. Solve yaw + Eigen::VectorXd x0(2); + x0 << traj(0, 0), traj(1, 0); + tiny_set_x0(yaw_solver_, x0); + + yaw_solver_->work->Xref = traj.block(0, 0, 2, HORIZON); + tiny_solve(yaw_solver_); + + // 4. Solve pitch + x0 << traj(2, 0), traj(3, 0); + tiny_set_x0(pitch_solver_, x0); + + pitch_solver_->work->Xref = traj.block(2, 0, 2, HORIZON); + tiny_solve(pitch_solver_); + + Plan plan; + plan.control = true; + + plan.target_yaw = tools::limit_rad(traj(0, HALF_HORIZON) + yaw0); + plan.target_pitch = traj(2, HALF_HORIZON); + + plan.yaw = tools::limit_rad(yaw_solver_->work->x(0, HALF_HORIZON) + yaw0); + plan.yaw_vel = yaw_solver_->work->x(1, HALF_HORIZON); + plan.yaw_acc = yaw_solver_->work->u(0, HALF_HORIZON); + + plan.pitch = pitch_solver_->work->x(0, HALF_HORIZON); + plan.pitch_vel = pitch_solver_->work->x(1, HALF_HORIZON); + plan.pitch_acc = pitch_solver_->work->u(0, HALF_HORIZON); + + auto shoot_offset_ = 2; + plan.fire = + std::hypot( + traj(0, HALF_HORIZON + shoot_offset_) - yaw_solver_->work->x(0, HALF_HORIZON + shoot_offset_), + traj(2, HALF_HORIZON + shoot_offset_) - + pitch_solver_->work->x(0, HALF_HORIZON + shoot_offset_)) < fire_thresh_; + return plan; +} + +Plan Planner::plan(std::optional target, double bullet_speed) +{ + if (!target.has_value()) return {false}; + + double delay_time = + std::abs(target->ekf_x()[7]) > decision_speed_ ? high_speed_delay_time_ : low_speed_delay_time_; + + auto future = std::chrono::steady_clock::now() + std::chrono::microseconds(int(delay_time * 1e6)); + + target->predict(future); + + return plan(*target, bullet_speed); +} + +void Planner::setup_yaw_solver(const std::string & config_path) +{ + auto yaml = tools::load(config_path); + auto max_yaw_acc = tools::read(yaml, "max_yaw_acc"); + auto Q_yaw = tools::read>(yaml, "Q_yaw"); + auto R_yaw = tools::read>(yaml, "R_yaw"); + + Eigen::MatrixXd A{{1, DT}, {0, 1}}; + Eigen::MatrixXd B{{0}, {DT}}; + Eigen::VectorXd f{{0, 0}}; + Eigen::Matrix Q(Q_yaw.data()); + Eigen::Matrix R(R_yaw.data()); + tiny_setup(&yaw_solver_, A, B, f, Q.asDiagonal(), R.asDiagonal(), 1.0, 2, 1, HORIZON, 0); + + Eigen::MatrixXd x_min = Eigen::MatrixXd::Constant(2, HORIZON, -1e17); + Eigen::MatrixXd x_max = Eigen::MatrixXd::Constant(2, HORIZON, 1e17); + Eigen::MatrixXd u_min = Eigen::MatrixXd::Constant(1, HORIZON - 1, -max_yaw_acc); + Eigen::MatrixXd u_max = Eigen::MatrixXd::Constant(1, HORIZON - 1, max_yaw_acc); + tiny_set_bound_constraints(yaw_solver_, x_min, x_max, u_min, u_max); + + yaw_solver_->settings->max_iter = 10; +} + +void Planner::setup_pitch_solver(const std::string & config_path) +{ + auto yaml = tools::load(config_path); + auto max_pitch_acc = tools::read(yaml, "max_pitch_acc"); + auto Q_pitch = tools::read>(yaml, "Q_pitch"); + auto R_pitch = tools::read>(yaml, "R_pitch"); + + Eigen::MatrixXd A{{1, DT}, {0, 1}}; + Eigen::MatrixXd B{{0}, {DT}}; + Eigen::VectorXd f{{0, 0}}; + Eigen::Matrix Q(Q_pitch.data()); + Eigen::Matrix R(R_pitch.data()); + tiny_setup(&pitch_solver_, A, B, f, Q.asDiagonal(), R.asDiagonal(), 1.0, 2, 1, HORIZON, 0); + + Eigen::MatrixXd x_min = Eigen::MatrixXd::Constant(2, HORIZON, -1e17); + Eigen::MatrixXd x_max = Eigen::MatrixXd::Constant(2, HORIZON, 1e17); + Eigen::MatrixXd u_min = Eigen::MatrixXd::Constant(1, HORIZON - 1, -max_pitch_acc); + Eigen::MatrixXd u_max = Eigen::MatrixXd::Constant(1, HORIZON - 1, max_pitch_acc); + tiny_set_bound_constraints(pitch_solver_, x_min, x_max, u_min, u_max); + + pitch_solver_->settings->max_iter = 10; +} + +Eigen::Matrix Planner::aim(const Target & target, double bullet_speed) +{ + Eigen::Vector3d xyz; + double yaw; + auto min_dist = 1e10; + + for (auto & xyza : target.armor_xyza_list()) { + auto dist = xyza.head<2>().norm(); + if (dist < min_dist) { + min_dist = dist; + xyz = xyza.head<3>(); + yaw = xyza[3]; + } + } + debug_xyza = Eigen::Vector4d(xyz.x(), xyz.y(), xyz.z(), yaw); + + auto azim = std::atan2(xyz.y(), xyz.x()); + auto bullet_traj = tools::Trajectory(bullet_speed, min_dist, xyz.z()); + if (bullet_traj.unsolvable) throw std::runtime_error("Unsolvable bullet trajectory!"); + + return {tools::limit_rad(azim + yaw_offset_), -bullet_traj.pitch - pitch_offset_}; +} + +Trajectory Planner::get_trajectory(Target & target, double yaw0, double bullet_speed) +{ + Trajectory traj; + + target.predict(-DT * (HALF_HORIZON + 1)); + auto yaw_pitch_last = aim(target, bullet_speed); + + target.predict(DT); // [0] = -HALF_HORIZON * DT -> [HHALF_HORIZON] = 0 + auto yaw_pitch = aim(target, bullet_speed); + + for (int i = 0; i < HORIZON; i++) { + target.predict(DT); + auto yaw_pitch_next = aim(target, bullet_speed); + + auto yaw_vel = tools::limit_rad(yaw_pitch_next(0) - yaw_pitch_last(0)) / (2 * DT); + auto pitch_vel = (yaw_pitch_next(1) - yaw_pitch_last(1)) / (2 * DT); + + traj.col(i) << tools::limit_rad(yaw_pitch(0) - yaw0), yaw_vel, yaw_pitch(1), pitch_vel; + + yaw_pitch_last = yaw_pitch; + yaw_pitch = yaw_pitch_next; + } + + return traj; +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/planner/planner.hpp b/tasks/auto_aim/planner/planner.hpp new file mode 100644 index 0000000..76974fd --- /dev/null +++ b/tasks/auto_aim/planner/planner.hpp @@ -0,0 +1,60 @@ +#ifndef AUTO_AIM__PLANNER_HPP +#define AUTO_AIM__PLANNER_HPP + +#include +#include +#include + +#include "tasks/auto_aim/target.hpp" +#include "tinympc/tiny_api.hpp" + +namespace auto_aim +{ +constexpr double DT = 0.01; +constexpr int HALF_HORIZON = 50; +constexpr int HORIZON = HALF_HORIZON * 2; + +using Trajectory = Eigen::Matrix; // yaw, yaw_vel, pitch, pitch_vel + +struct Plan +{ + bool control; + bool fire; + float target_yaw; + float target_pitch; + float yaw; + float yaw_vel; + float yaw_acc; + float pitch; + float pitch_vel; + float pitch_acc; +}; + +class Planner +{ +public: + Eigen::Vector4d debug_xyza; + Planner(const std::string & config_path); + + Plan plan(Target target, double bullet_speed); + Plan plan(std::optional target, double bullet_speed); + +private: + double yaw_offset_; + double pitch_offset_; + double fire_thresh_; + double low_speed_delay_time_, high_speed_delay_time_, decision_speed_; + + TinySolver * yaw_solver_; + TinySolver * pitch_solver_; + + void setup_yaw_solver(const std::string & config_path); + void setup_pitch_solver(const std::string & config_path); + + Eigen::Matrix aim(const Target & target, double bullet_speed); + Trajectory get_trajectory(Target & target, double yaw0, double bullet_speed); +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__PLANNER_HPP \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/CMakeLists.txt b/tasks/auto_aim/planner/tinympc/CMakeLists.txt new file mode 100755 index 0000000..b8654af --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/CMakeLists.txt @@ -0,0 +1,43 @@ +add_library(tinympcstatic STATIC + admm.cpp + tiny_api.cpp + codegen.cpp + rho_benchmark.cpp +) + +set_property(TARGET tinympcstatic PROPERTY POSITION_INDEPENDENT_CODE ON) + +# target_link_libraries(tinympcstatic PUBLIC Eigen) +target_include_directories(tinympcstatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) +target_include_directories(tinympcstatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../include/Eigen) + + + +if(USING_CODEGEN) # Defined in top-level CMakeLists.txt + +# Files that are needed for embedded code generation +list( APPEND EMBEDDED_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/admm.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/admm.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/tiny_api.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/tiny_api.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/types.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/tiny_api_constants.hpp" ) + + +foreach( f ${EMBEDDED_FILES} ) + get_filename_component( fname ${f} NAME ) + + set( dest_file "${EMBEDDED_BUILD_TINYMPC_DIR}/${fname}" ) + list( APPEND EMBEDDED_BUILD_TINYMPC_FILES "${dest_file}" ) + + add_custom_command(OUTPUT ${dest_file} + COMMAND ${CMAKE_COMMAND} -E copy "${f}" "${dest_file}" + DEPENDS ${f} + COMMENT "Copying ${fname}") +endforeach() + +add_custom_target( copy_codegen_tinympc_files DEPENDS ${EMBEDDED_BUILD_TINYMPC_FILES} ) +add_dependencies( copy_codegen_files copy_codegen_tinympc_files ) + +endif(USING_CODEGEN) diff --git a/tasks/auto_aim/planner/tinympc/admm.cpp b/tasks/auto_aim/planner/tinympc/admm.cpp new file mode 100755 index 0000000..7051a2b --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/admm.cpp @@ -0,0 +1,391 @@ +#include + +#include "admm.hpp" +#include "rho_benchmark.hpp" + +#define DEBUG_MODULE "TINYALG" + +extern "C" { + +/** + * Update linear terms from Riccati backward pass +*/ +void backward_pass_grad(TinySolver *solver) +{ + for (int i = solver->work->N - 2; i >= 0; i--) + { + (solver->work->d.col(i)).noalias() = solver->cache->Quu_inv * (solver->work->Bdyn.transpose() * solver->work->p.col(i + 1) + solver->work->r.col(i) + solver->cache->BPf); + (solver->work->p.col(i)).noalias() = solver->work->q.col(i) + solver->cache->AmBKt.lazyProduct(solver->work->p.col(i + 1)) - (solver->cache->Kinf.transpose()).lazyProduct(solver->work->r.col(i)) + solver->cache->APf; + } +} + +/** + * Use LQR feedback policy to roll out trajectory +*/ +void forward_pass(TinySolver *solver) +{ + for (int i = 0; i < solver->work->N - 1; i++) + { + (solver->work->u.col(i)).noalias() = -solver->cache->Kinf.lazyProduct(solver->work->x.col(i)) - solver->work->d.col(i); + (solver->work->x.col(i + 1)).noalias() = solver->work->Adyn.lazyProduct(solver->work->x.col(i)) + solver->work->Bdyn.lazyProduct(solver->work->u.col(i)) + solver->work->fdyn; + } +} + +/** + * Project a vector s onto the second order cone defined by mu + * @param s, mu + * @return projection onto cone if s is outside cone. Return s if s is inside cone. +*/ +tinyVector project_soc(tinyVector s, float mu) { + tinytype u0 = s(Eigen::placeholders::last) * mu; + tinyVector u1 = s.head(s.rows()-1); + float a = u1.norm(); + tinyVector cone_origin(s.rows()); + cone_origin.setZero(); + + if (a <= -u0) { // below cone + return cone_origin; + } + else if (a <= u0) { // in cone + return s; + } + else if (a >= abs(u0)) { // outside cone + Matrix u2(u1.size() + 1); + u2 << u1, a/mu; + return 0.5 * (1 + u0/a) * u2; + } + else { + return cone_origin; + } +} + +/** + * Project a vector z onto a hyperplane defined by a^T z = b + * Implements equation (21): ΠH(z) = z - (⟨z, a⟩ − b)/||a||² * a + * @param z Vector to project + * @param a Normal vector of the hyperplane + * @param b Offset of the hyperplane + * @return Projection of z onto the hyperplane + */ +tinyVector project_hyperplane(const tinyVector& z, const tinyVector& a, tinytype b) { + tinytype dist = (a.dot(z) - b) / a.squaredNorm(); + return z - dist * a; +} + +/** + * Project slack (auxiliary) variables into their feasible domain, defined by + * projection functions related to each constraint + * TODO: pass in meta information with each constraint assigning it to a + * projection function +*/ +void update_slack(TinySolver *solver) +{ + + // Update bound constraint slack variables for state + solver->work->vnew = solver->work->x + solver->work->g; + + // Update bound constraint slack variables for input + solver->work->znew = solver->work->u + solver->work->y; + + // Box constraints on state + if (solver->settings->en_state_bound) { + solver->work->vnew = solver->work->x_max.cwiseMin(solver->work->x_min.cwiseMax(solver->work->vnew)); + } + + // Box constraints on input + if (solver->settings->en_input_bound) { + solver->work->znew = solver->work->u_max.cwiseMin(solver->work->u_min.cwiseMax(solver->work->znew)); + } + + + // Update second order cone slack variables for state + if (solver->settings->en_state_soc && solver->work->numStateCones > 0) { + solver->work->vcnew = solver->work->x + solver->work->gc; + } + + // Update second order cone slack variables for input + if (solver->settings->en_input_soc && solver->work->numInputCones > 0) { + solver->work->zcnew = solver->work->u + solver->work->yc; + } + + // Cone constraints on state + if (solver->settings->en_state_soc) { + for (int i=0; iwork->N; i++) { + for (int k=0; kwork->numStateCones; k++) { + int start = solver->work->Acx(k); + int num_xs = solver->work->qcx(k); + tinytype mu = solver->work->cx(k); + tinyVector col = solver->work->vcnew.block(start, i, num_xs, 1); + solver->work->vcnew.block(start, i, num_xs, 1) = project_soc(col, mu); + } + } + } + + // Cone constraints on input + if (solver->settings->en_input_soc) { + for (int i=0; iwork->N-1; i++) { + for (int k=0; kwork->numInputCones; k++) { + int start = solver->work->Acu(k); + int num_us = solver->work->qcu(k); + tinytype mu = solver->work->cu(k); + tinyVector col = solver->work->zcnew.block(start, i, num_us, 1); + solver->work->zcnew.block(start, i, num_us, 1) = project_soc(col, mu); + } + } + } + + // Update linear constraint slack variables for state + if (solver->settings->en_state_linear) { + solver->work->vlnew = solver->work->x + solver->work->gl; + } + + // Update linear constraint slack variables for input + if (solver->settings->en_input_linear) { + solver->work->zlnew = solver->work->u + solver->work->yl; + } + + // Linear constraints on state + if (solver->settings->en_state_linear) { + for (int i=0; iwork->N; i++) { + for (int k=0; kwork->numStateLinear; k++) { + tinyVector a = solver->work->Alin_x.row(k); + tinytype b = solver->work->blin_x(k); + tinytype constraint_value = a.dot(solver->work->vlnew.col(i)); + if (constraint_value > b) { // Only project if constraint is violated + solver->work->vlnew.col(i) = project_hyperplane(solver->work->vlnew.col(i), a, b); + } + } + } + } + + // Linear constraints on input + if (solver->settings->en_input_linear) { + for (int i=0; iwork->N-1; i++) { + for (int k=0; kwork->numInputLinear; k++) { + tinyVector a = solver->work->Alin_u.row(k); + tinytype b = solver->work->blin_u(k); + tinytype constraint_value = a.dot(solver->work->zlnew.col(i)); + if (constraint_value > b) { // Only project if constraint is violated + solver->work->zlnew.col(i) = project_hyperplane(solver->work->zlnew.col(i), a, b); + } + } + } + } + +} + +/** + * Update next iteration of dual variables by performing the augmented + * lagrangian multiplier update +*/ +void update_dual(TinySolver *solver) +{ + // Update bound constraint dual variables for state + solver->work->g = solver->work->g + solver->work->x - solver->work->vnew; + + // Update bound constraint dual variables for input + solver->work->y = solver->work->y + solver->work->u - solver->work->znew; + + // Update second order cone dual variables for state + if (solver->settings->en_state_soc && solver->work->numStateCones > 0) { + solver->work->gc = solver->work->gc + solver->work->x - solver->work->vcnew; + } + + // Update second order cone dual variables for input + if (solver->settings->en_input_soc && solver->work->numInputCones > 0) { + solver->work->yc = solver->work->yc + solver->work->u - solver->work->zcnew; + } + + // Update linear constraint dual variables for state + if (solver->settings->en_state_linear) { + solver->work->gl = solver->work->gl + solver->work->x - solver->work->vlnew; + } + + // Update linear constraint dual variables for input + if (solver->settings->en_input_linear) { + solver->work->yl = solver->work->yl + solver->work->u - solver->work->zlnew; + } +} + +/** + * Update linear control cost terms in the Riccati feedback using the changing + * slack and dual variables from ADMM +*/ +void update_linear_cost(TinySolver *solver) +{ + + // Update state cost terms + solver->work->q = -(solver->work->Xref.array().colwise() * solver->work->Q.array()); + (solver->work->q).noalias() -= solver->cache->rho * (solver->work->vnew - solver->work->g); + if (solver->settings->en_state_soc && solver->work->numStateCones > 0) { + (solver->work->q).noalias() -= solver->cache->rho * (solver->work->vcnew - solver->work->gc); + } + if (solver->settings->en_state_linear) { + (solver->work->q).noalias() -= solver->cache->rho * (solver->work->vlnew - solver->work->gl); + } + + // Update input cost terms + solver->work->r = -(solver->work->Uref.array().colwise() * solver->work->R.array()); + (solver->work->r).noalias() -= solver->cache->rho * (solver->work->znew - solver->work->y); + if (solver->settings->en_input_soc && solver->work->numInputCones > 0) { + (solver->work->r).noalias() -= solver->cache->rho * (solver->work->zcnew - solver->work->yc); + } + if (solver->settings->en_input_linear) { + (solver->work->r).noalias() -= solver->cache->rho * (solver->work->zlnew - solver->work->yl); + } + + // Update terminal cost + solver->work->p.col(solver->work->N - 1) = -(solver->work->Xref.col(solver->work->N - 1).transpose().lazyProduct(solver->cache->Pinf)); + (solver->work->p.col(solver->work->N - 1)).noalias() -= solver->cache->rho * (solver->work->vnew.col(solver->work->N - 1) - solver->work->g.col(solver->work->N - 1)); + + if (solver->settings->en_state_soc && solver->work->numStateCones > 0) { + solver->work->p.col(solver->work->N - 1) -= solver->cache->rho * (solver->work->vcnew.col(solver->work->N - 1) - solver->work->gc.col(solver->work->N - 1)); + } + if (solver->settings->en_state_linear) { + solver->work->p.col(solver->work->N - 1) -= solver->cache->rho * (solver->work->vlnew.col(solver->work->N - 1) - solver->work->gl.col(solver->work->N - 1)); + } +} + +/** + * Check for termination condition by evaluating whether the largest absolute + * primal and dual residuals for states and inputs are below threhold. +*/ +bool termination_condition(TinySolver *solver) +{ + if (solver->work->iter % solver->settings->check_termination == 0) + { + solver->work->primal_residual_state = (solver->work->x - solver->work->vnew).cwiseAbs().maxCoeff(); + solver->work->dual_residual_state = ((solver->work->v - solver->work->vnew).cwiseAbs().maxCoeff()) * solver->cache->rho; + solver->work->primal_residual_input = (solver->work->u - solver->work->znew).cwiseAbs().maxCoeff(); + solver->work->dual_residual_input = ((solver->work->z - solver->work->znew).cwiseAbs().maxCoeff()) * solver->cache->rho; + + if (solver->work->primal_residual_state < solver->settings->abs_pri_tol && + solver->work->primal_residual_input < solver->settings->abs_pri_tol && + solver->work->dual_residual_state < solver->settings->abs_dua_tol && + solver->work->dual_residual_input < solver->settings->abs_dua_tol) + { + return true; + } + } + return false; +} + + +int solve(TinySolver *solver) +{ + // Initialize variables + solver->solution->solved = 0; + solver->solution->iter = 0; + solver->work->status = 11; // TINY_UNSOLVED + solver->work->iter = 0; + + // Setup for adaptive rho + RhoAdapter adapter; + adapter.rho_min = solver->settings->adaptive_rho_min; + adapter.rho_max = solver->settings->adaptive_rho_max; + adapter.clip = solver->settings->adaptive_rho_enable_clipping; + + RhoBenchmarkResult rho_result; + + // Store previous values for residuals + tinyMatrix v_prev = solver->work->vnew; + tinyMatrix z_prev = solver->work->znew; + + // Initialize SOC slack variables if needed + if (solver->settings->en_state_soc && solver->work->numStateCones > 0) { + solver->work->vcnew = solver->work->x; + } + + if (solver->settings->en_input_soc && solver->work->numInputCones > 0) { + solver->work->zcnew = solver->work->u; + } + + // Initialize linear constraint slack variables if needed + if (solver->settings->en_state_linear) { + solver->work->vlnew = solver->work->x; + } + + if (solver->settings->en_input_linear) { + solver->work->zlnew = solver->work->u; + } + + for (int i = 0; i < solver->settings->max_iter; i++) + { + // Solve linear system with Riccati and roll out to get new trajectory + backward_pass_grad(solver); + + forward_pass(solver); + + // Project slack variables into feasible domain + update_slack(solver); + + // Compute next iteration of dual variables + update_dual(solver); + + // Update linear control cost terms using reference trajectory, duals, and slack variables + update_linear_cost(solver); + + solver->work->iter += 1; + + // Handle adaptive rho if enabled + if (solver->settings->adaptive_rho) { + // Calculate residuals for adaptive rho + tinytype pri_res_input = (solver->work->u - solver->work->znew).cwiseAbs().maxCoeff(); + tinytype pri_res_state = (solver->work->x - solver->work->vnew).cwiseAbs().maxCoeff(); + tinytype dua_res_input = solver->cache->rho * (solver->work->znew - z_prev).cwiseAbs().maxCoeff(); + tinytype dua_res_state = solver->cache->rho * (solver->work->vnew - v_prev).cwiseAbs().maxCoeff(); + + // Update rho every 5 iterations + if (i > 0 && i % 5 == 0) { + benchmark_rho_adaptation( + &adapter, + solver->work->x, + solver->work->u, + solver->work->vnew, + solver->work->znew, + solver->work->g, + solver->work->y, + solver->cache, + solver->work, + solver->work->N, + &rho_result + ); + + // Update matrices using Taylor expansion + update_matrices_with_derivatives(solver->cache, rho_result.final_rho); + } + } + + // Store previous values for next iteration + z_prev = solver->work->znew; + v_prev = solver->work->vnew; + + // Check for whether cost is minimized by calculating residuals + if (termination_condition(solver)) { + solver->work->status = 1; // TINY_SOLVED + + // Save solution + solver->solution->iter = solver->work->iter; + solver->solution->solved = 1; + solver->solution->x = solver->work->vnew; + solver->solution->u = solver->work->znew; + + // std::cout << "Solver converged in " << solver->work->iter << " iterations" << std::endl; + + return 0; + } + + // Save previous slack variables + solver->work->v = solver->work->vnew; + solver->work->z = solver->work->znew; + + } + + solver->solution->iter = solver->work->iter; + solver->solution->solved = 0; + solver->solution->x = solver->work->vnew; + solver->solution->u = solver->work->znew; + return 1; +} + +} /* extern "C" */ diff --git a/tasks/auto_aim/planner/tinympc/admm.hpp b/tasks/auto_aim/planner/tinympc/admm.hpp new file mode 100755 index 0000000..f07536d --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/admm.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "types.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +int solve(TinySolver *solver); + +void update_primal(TinySolver *solver); +void backward_pass_grad(TinySolver *solver); +void forward_pass(TinySolver *solver); +void update_slack(TinySolver *solver); +void update_dual(TinySolver *solver); +void update_linear_cost(TinySolver *solver); +bool termination_condition(TinySolver *solver); + +/** + * Project a vector s onto the second order cone defined by mu + * @param s, mu + * @return projection onto cone if s is outside cone. Return s if s is inside cone. +*/ +tinyVector project_soc(tinyVector s, float mu); + +/** + * Project a vector z onto a hyperplane defined by a^T z = b + * Implements equation (21): ΠH(z) = z - (⟨z, a⟩ − b)/||a||² * a + * @param z Vector to project + * @param a Normal vector of the hyperplane + * @param b Offset of the hyperplane + * @return Projection of z onto the hyperplane + */ +tinyVector project_hyperplane(const tinyVector& z, const tinyVector& a, tinytype b); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/codegen.cpp b/tasks/auto_aim/planner/tinympc/codegen.cpp new file mode 100755 index 0000000..cdc91f3 --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/codegen.cpp @@ -0,0 +1,447 @@ +#include +#include +#include +#include +#include +#include +#include +//#include +#include "error.hpp" + +#include +#include + +// #include "types.hpp" +#include "codegen.hpp" + +#ifdef __MINGW32__ +#include +inline int mkdir(const char *pathname, int flags) { + return _mkdir(pathname); +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define the maximum allowed length of the path (directory + filename + extension) */ +#define PATH_LENGTH 2048 + +using namespace Eigen; + +static void print_matrix(FILE *f, MatrixXd mat, int num_elements) +{ + // Check if matrix is uninitialized or too small + if (mat.size() == 0 || mat.size() < num_elements) { + // Print zeros for all elements + for (int i = 0; i < num_elements; i++) { + fprintf(f, "(tinytype)0.0000000000000000"); + if (i < num_elements - 1) + fprintf(f, ","); + } + return; + } + + // Matrix is properly initialized and has enough elements + for (int i = 0; i < num_elements; i++) { + fprintf(f, "(tinytype)%.16f", mat.reshaped()[i]); + if (i < num_elements - 1) + fprintf(f, ","); + } +} + + +static void create_directory(const char* dir, int verbose) { + // Attempt to create directory + if (mkdir(dir, S_IRWXU|S_IRWXG|S_IROTH)) { + if (errno == EEXIST) { // Skip if directory already exists + if (verbose) + std::cout << dir << " already exists, skipping." << std::endl; + } else { + ERROR_MSG(EXIT_FAILURE, "Failed to create directory %s", dir); + } + } +} + +// TODO: Make this fail if tiny_setup has not already been called +int tiny_codegen(TinySolver* solver, const char* output_dir, int verbose) { + if (!solver) { + std::cout << "Error in tiny_codegen: solver is nullptr" << std::endl; + return 1; + } + int status = 0; + status |= codegen_create_directories(output_dir, verbose); + status |= codegen_data_header(output_dir, verbose); + status |= codegen_data_source(solver, output_dir, verbose); + status |= codegen_example(output_dir, verbose); + + return status; +} + +int tiny_codegen_with_sensitivity(TinySolver* solver, const char* output_dir, + tinyMatrix* dK, tinyMatrix* dP, + tinyMatrix* dC1, tinyMatrix* dC2, int verbose) { + if (!solver) { + std::cout << "Error in tiny_codegen_with_sensitivity: solver is nullptr" << std::endl; + return 1; + } + + // Only store sensitivity matrices if adaptive rho is enabled + if (solver->settings->adaptive_rho) { + // Store the sensitivity matrices in the solver's cache + solver->cache->dKinf_drho = *dK; + solver->cache->dPinf_drho = *dP; + solver->cache->dC1_drho = *dC1; + solver->cache->dC2_drho = *dC2; + } + + // Call the regular codegen function which will now include the sensitivity matrices if adaptive_rho is enabled + return tiny_codegen(solver, output_dir, verbose); +} + +// Create code generation folder structure in whichever directory the executable calling tiny_codegen was called +int codegen_create_directories(const char* output_dir, int verbose) { + + // Create output folder (root folder for code generation) + create_directory(output_dir, verbose); + + // Create src folder + char src_dir[PATH_LENGTH]; + sprintf(src_dir, "%s/src/", output_dir); + create_directory(src_dir, verbose); + + // Create tinympc folder + char tinympc_dir[PATH_LENGTH]; + sprintf(tinympc_dir, "%s/tinympc/", output_dir); + create_directory(tinympc_dir, verbose); + + // // Create include folder + // char inc_dir[PATH_LENGTH]; + // sprintf(inc_dir, "%s/include/", output_dir); + // create_directory(inc_dir, verbose); + + return EXIT_SUCCESS; +} + +// Create inc/tiny_data.hpp file +int codegen_data_header(const char* output_dir, int verbose) { + char data_hpp_fname[PATH_LENGTH]; + FILE *data_hpp_f; + + sprintf(data_hpp_fname, "%s/tinympc/tiny_data.hpp", output_dir); + + // Open data header file + data_hpp_f = fopen(data_hpp_fname, "w+"); + if (data_hpp_f == NULL) + ERROR_MSG(EXIT_FAILURE, "Failed to open file %s", data_hpp_fname); + + // Preamble + time_t start_time; + time(&start_time); + fprintf(data_hpp_f, "/*\n"); + fprintf(data_hpp_f, " * This file was autogenerated by TinyMPC on %s", ctime(&start_time)); + fprintf(data_hpp_f, " */\n\n"); + + fprintf(data_hpp_f, "#pragma once\n\n"); + + fprintf(data_hpp_f, "#include \"types.hpp\"\n\n"); + + fprintf(data_hpp_f, "#ifdef __cplusplus\n"); + fprintf(data_hpp_f, "extern \"C\" {\n"); + fprintf(data_hpp_f, "#endif\n\n"); + + fprintf(data_hpp_f, "extern TinySolver tiny_solver;\n\n"); + + fprintf(data_hpp_f, "#ifdef __cplusplus\n"); + fprintf(data_hpp_f, "}\n"); + fprintf(data_hpp_f, "#endif\n"); + + // Close codegen data header file + fclose(data_hpp_f); + + if (verbose) { + printf("Data header generated in %s\n", data_hpp_fname); + } + return 0; +} + +// Create src/tiny_data.cpp file +int codegen_data_source(TinySolver* solver, const char* output_dir, int verbose) { + char data_cpp_fname[PATH_LENGTH]; + FILE *data_cpp_f; + + int nx = solver->work->nx; + int nu = solver->work->nu; + int N = solver->work->N; + + sprintf(data_cpp_fname, "%s/src/tiny_data.cpp", output_dir); + + // Open data source file + data_cpp_f = fopen(data_cpp_fname, "w+"); + if (data_cpp_f == NULL) + ERROR_MSG(EXIT_FAILURE, "Failed to open file %s", data_cpp_fname); + + // Preamble + time_t start_time; + time(&start_time); + fprintf(data_cpp_f, "/*\n"); + fprintf(data_cpp_f, " * This file was autogenerated by TinyMPC on %s", ctime(&start_time)); + fprintf(data_cpp_f, " */\n\n"); + + // Open extern C + fprintf(data_cpp_f, "#include \"tinympc/tiny_data.hpp\"\n\n"); + fprintf(data_cpp_f, "#ifdef __cplusplus\n"); + fprintf(data_cpp_f, "extern \"C\" {\n"); + fprintf(data_cpp_f, "#endif\n\n"); + + // Solution + fprintf(data_cpp_f, "/* Solution */\n"); + fprintf(data_cpp_f, "TinySolution solution = {\n"); + + fprintf(data_cpp_f, "\t%d,\t\t// iter\n", solver->solution->iter); + fprintf(data_cpp_f, "\t%d,\t\t// solved\n", solver->solution->solved); + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// x\n"); // x solution + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N-1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// x\n"); // u solution + + fprintf(data_cpp_f, "};\n\n"); + + // Cache + fprintf(data_cpp_f, "/* Matrices that must be recomputed with changes in time step, rho */\n"); + fprintf(data_cpp_f, "TinyCache cache = {\n"); + + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// rho (step size/penalty)\n", solver->cache->rho); + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, nx); + print_matrix(data_cpp_f, solver->cache->Kinf, nu * nx); + fprintf(data_cpp_f, ").finished(),\t// Kinf\n"); // Kinf + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->Pinf, nx * nx); + fprintf(data_cpp_f, ").finished(),\t// Pinf\n"); // Pinf + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, nu); + print_matrix(data_cpp_f, solver->cache->Quu_inv, nu * nu); + fprintf(data_cpp_f, ").finished(),\t// Quu_inv\n"); // Quu_inv + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->AmBKt, nx * nx); + fprintf(data_cpp_f, ").finished(),\t// AmBKt\n"); // AmBKt + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->C1, nx * nx); + fprintf(data_cpp_f, ").finished(),\t// C1\n"); // C1 + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->C2, nx * nx); + fprintf(data_cpp_f, ").finished()"); // C2, no comma if no sensitivity matrices + + // Only print sensitivity matrices if adaptive rho is enabled + if (solver->settings->adaptive_rho) { + fprintf(data_cpp_f, ",\t// C2\n"); // Add comma and comment for C2 if we have more matrices + + // Add sensitivity matrices within the struct initialization + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, nx); + print_matrix(data_cpp_f, solver->cache->dKinf_drho, nu * nx); + fprintf(data_cpp_f, ").finished(),\t// dKinf_drho\n"); // dKinf_drho + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->dPinf_drho, nx * nx); + fprintf(data_cpp_f, ").finished(),\t// dPinf_drho\n"); // dPinf_drho + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->dC1_drho, nx * nx); + fprintf(data_cpp_f, ").finished(),\t// dC1_drho\n"); // dC1_drho + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->cache->dC2_drho, nx * nx); + fprintf(data_cpp_f, ").finished()\t// dC2_drho\n"); // dC2_drho + } else { + fprintf(data_cpp_f, "\t// C2\n"); // Just add comment for C2 + } + + fprintf(data_cpp_f, "};\n\n"); + + // Settings + fprintf(data_cpp_f, "/* User settings */\n"); + fprintf(data_cpp_f, "TinySettings settings = {\n"); + + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// primal tolerance\n", solver->settings->abs_pri_tol); + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// dual tolerance\n", solver->settings->abs_dua_tol); + fprintf(data_cpp_f, "\t%d,\t\t// max iterations\n", solver->settings->max_iter); + fprintf(data_cpp_f, "\t%d,\t\t// iterations per termination check\n", solver->settings->check_termination); + fprintf(data_cpp_f, "\t%d,\t\t// enable state constraints\n", solver->settings->en_state_bound); + fprintf(data_cpp_f, "\t%d\t\t// enable input constraints\n", solver->settings->en_input_bound); + + fprintf(data_cpp_f, "};\n\n"); + + // Workspace + fprintf(data_cpp_f, "/* Problem variables */\n"); + fprintf(data_cpp_f, "TinyWorkspace work = {\n"); + + fprintf(data_cpp_f, "\t%d,\t// Number of states\n", nx); + fprintf(data_cpp_f, "\t%d,\t// Number of control inputs\n", nu); + fprintf(data_cpp_f, "\t%d,\t// Number of knotpoints in the horizon\n", N); + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// x\n"); // x + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// u\n"); // u + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// q\n"); // q + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// r\n"); // r + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// p\n"); // p + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// d\n"); // d + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// v\n"); // v + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// vnew\n"); // vnew + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// z\n"); // z + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// znew\n"); // znew + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// g\n"); // g + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// y\n"); // y + + fprintf(data_cpp_f, "\t(tinyVector(%d) << ", nx); + print_matrix(data_cpp_f, solver->work->Q, nx); + fprintf(data_cpp_f, ").finished(),\t// Q\n"); // Q + fprintf(data_cpp_f, "\t(tinyVector(%d) << ", nu); + print_matrix(data_cpp_f, solver->work->R, nu); + fprintf(data_cpp_f, ").finished(),\t// R\n"); // R + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nx); + print_matrix(data_cpp_f, solver->work->Adyn, nx * nx); + fprintf(data_cpp_f, ").finished(),\t// Adyn\n"); // Adyn + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, nu); + print_matrix(data_cpp_f, solver->work->Bdyn, nx * nu); + fprintf(data_cpp_f, ").finished(),\t// Bdyn\n"); // Bdyn + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, solver->work->x_min, nx * N); + fprintf(data_cpp_f, ").finished(),\t// x_min\n"); // x_min + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, solver->work->x_max, nx * N); + fprintf(data_cpp_f, ").finished(),\t// x_max\n"); // x_max + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, solver->work->u_min, nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// u_min\n"); // u_min + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, solver->work->u_max, nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// u_max\n"); // u_max + + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nx, N); + print_matrix(data_cpp_f, MatrixXd::Zero(nx, N), nx * N); + fprintf(data_cpp_f, ").finished(),\t// Xref\n"); // Xref + fprintf(data_cpp_f, "\t(tinyMatrix(%d, %d) << ", nu, N-1); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, N - 1), nu * (N-1)); + fprintf(data_cpp_f, ").finished(),\t// Uref\n"); // Uref + + fprintf(data_cpp_f, "\t(tinyVector(%d) << ", nu); + print_matrix(data_cpp_f, MatrixXd::Zero(nu, 1), nu); + fprintf(data_cpp_f, ").finished(),\t// Qu\n"); // Qu + + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// state primal residual\n", 0.0); + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// input primal residual\n", 0.0); + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// state dual residual\n", 0.0); + fprintf(data_cpp_f, "\t(tinytype)%.16f,\t// input dual residual\n", 0.0); + fprintf(data_cpp_f, "\t%d,\t// solve status\n", 0); + fprintf(data_cpp_f, "\t%d,\t// solve iteration\n", 0); + + fprintf(data_cpp_f, "};\n\n"); + + // Write solver struct definition to workspace file + fprintf(data_cpp_f, "TinySolver tiny_solver = {&solution, &settings, &cache, &work};\n\n"); + + // Close extern C + fprintf(data_cpp_f, "#ifdef __cplusplus\n"); + fprintf(data_cpp_f, "}\n"); + fprintf(data_cpp_f, "#endif\n\n"); + + // Close codegen data file + fclose(data_cpp_f); + if (verbose) { + printf("Data generated in %s\n", data_cpp_fname); + } + return 0; +} + +int codegen_example(const char* output_dir, int verbose) { + char example_cpp_fname[PATH_LENGTH]; + FILE *example_cpp_f; + + sprintf(example_cpp_fname, "%s/src/tiny_main.cpp", output_dir); + + // Open example file + example_cpp_f = fopen(example_cpp_fname, "w+"); + if (example_cpp_f == NULL) + ERROR_MSG(EXIT_FAILURE, "Failed to open file %s", example_cpp_fname); + + // Preamble + time_t start_time; + time(&start_time); + fprintf(example_cpp_f, "/*\n"); + fprintf(example_cpp_f, " * This file was autogenerated by TinyMPC on %s", ctime(&start_time)); + fprintf(example_cpp_f, " */\n\n"); + + fprintf(example_cpp_f, "#include \n\n"); + + fprintf(example_cpp_f, "#include \n"); + fprintf(example_cpp_f, "#include \n\n"); + + fprintf(example_cpp_f, "using namespace Eigen;\n"); + fprintf(example_cpp_f, "IOFormat TinyFmt(4, 0, \", \", \"\\n\", \"[\", \"]\");\n\n"); + + fprintf(example_cpp_f, "#ifdef __cplusplus\n"); + fprintf(example_cpp_f, "extern \"C\" {\n"); + fprintf(example_cpp_f, "#endif\n\n"); + + fprintf(example_cpp_f, "int main()\n"); + fprintf(example_cpp_f, "{\n"); + fprintf(example_cpp_f, "\tint exitflag = 1;\n"); + fprintf(example_cpp_f, "\t// Double check some data\n"); + fprintf(example_cpp_f, "\tstd::cout << \"rho: \" << tiny_solver.cache->rho << std::endl;\n"); + fprintf(example_cpp_f, "\tstd::cout << \"\\nmax iters: \" << tiny_solver.settings->max_iter << std::endl;\n"); + fprintf(example_cpp_f, "\tstd::cout << \"\\nState transition matrix:\\n\" << tiny_solver.work->Adyn.format(TinyFmt) << std::endl;\n"); + fprintf(example_cpp_f, "\tstd::cout << \"\\nInput/control matrix:\\n\" << tiny_solver.work->Bdyn.format(TinyFmt) << std::endl;\n\n"); + + fprintf(example_cpp_f, "\t// Visit https://tinympc.org/ to see how to set the initial condition and update the reference trajectory.\n\n"); + + fprintf(example_cpp_f, "\tstd::cout << \"\\nSolving...\\n\" << std::endl;\n\n"); + fprintf(example_cpp_f, "\texitflag = tiny_solve(&tiny_solver);\n\n"); + fprintf(example_cpp_f, "\tif (exitflag == 0) printf(\"Hooray! Solved with no error!\\n\");\n"); + fprintf(example_cpp_f, "\telse printf(\"Oops! Something went wrong!\\n\");\n"); + + fprintf(example_cpp_f, "\treturn 0;\n"); + fprintf(example_cpp_f, "}\n\n"); + + fprintf(example_cpp_f, "#ifdef __cplusplus\n"); + fprintf(example_cpp_f, "} /* extern \"C\" */\n"); + fprintf(example_cpp_f, "#endif\n"); + + // Close codegen example main file + fclose(example_cpp_f); + if (verbose) { + printf("Example tinympc main generated in %s\n", example_cpp_fname); + } + return 0; +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/codegen.hpp b/tasks/auto_aim/planner/tinympc/codegen.hpp new file mode 100755 index 0000000..4495d30 --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/codegen.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "types.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + + int tiny_codegen(TinySolver* solver, const char* output_dir, int verbose); + + int tiny_codegen_with_sensitivity(TinySolver* solver, const char* output_dir, + tinyMatrix* dK, tinyMatrix* dP, + tinyMatrix* dC1, tinyMatrix* dC2, int verbose); + + int codegen_create_directories(const char* output_dir, int verbose); + int codegen_data_header(const char* output_dir, int verbose); + int codegen_data_source(TinySolver* solver, const char* output_dir, int verbose); + int codegen_example(const char* output_dir, int verbose); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/error.hpp b/tasks/auto_aim/planner/tinympc/error.hpp new file mode 100644 index 0000000..713c1ce --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/error.hpp @@ -0,0 +1,29 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +// #if defined(__linux__) || defined(__unix__)// Check if Linux +// #include +// #define ERROR_MSG(exit_code, format, ...) error(exit_code, errno, format, ##__VA_ARGS__) + +// #elif defined(__APPLE__) || defined(__MACH__) // Check if macOS +#define ERROR_MSG(exit_code, format, ...) \ + { \ + fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); \ + exit(exit_code); \ + } + +// #else +// #error "Unsupported operating system" +// #endif + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/rho_benchmark.cpp b/tasks/auto_aim/planner/tinympc/rho_benchmark.cpp new file mode 100644 index 0000000..f10848d --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/rho_benchmark.cpp @@ -0,0 +1,250 @@ +#include "rho_benchmark.hpp" +#include +#include +#include +#ifdef ARDUINO +#include +#else +// For non-Arduino platforms +uint32_t micros() { + return 0; // Replace with appropriate timing function +} +#endif + +void initialize_format_matrices(RhoAdapter* adapter, int nx, int nu, int N) { + // Calculate dimensions + int x_decision_size = nx * N + nu * (N-1); + int constraint_rows = (nx + nu) * (N-1); + + // Pre-allocate matrices + adapter->A_matrix = tinyMatrix::Zero(constraint_rows, x_decision_size); + adapter->z_vector = tinyMatrix::Zero(constraint_rows, 1); + adapter->y_vector = tinyMatrix::Zero(constraint_rows, 1); + adapter->x_decision = tinyMatrix::Zero(x_decision_size, 1); + + // Pre-compute P matrix structure + adapter->P_matrix = tinyMatrix::Zero(x_decision_size, x_decision_size); + adapter->q_vector = tinyMatrix::Zero(x_decision_size, 1); + + // Pre-allocate residual computation matrices + adapter->Ax_vector = tinyMatrix::Zero(constraint_rows, 1); + adapter->r_prim_vector = tinyMatrix::Zero(constraint_rows, 1); + adapter->r_dual_vector = tinyMatrix::Zero(x_decision_size, 1); + adapter->Px_vector = tinyMatrix::Zero(x_decision_size, 1); + adapter->ATy_vector = tinyMatrix::Zero(x_decision_size, 1); + + // Store dimensions + adapter->format_nx = nx; + adapter->format_nu = nu; + adapter->format_N = N; + + adapter->matrices_initialized = true; +} + +void format_matrices( + RhoAdapter* adapter, + const tinyMatrix& x_prev, + const tinyMatrix& u_prev, + const tinyMatrix& v_prev, + const tinyMatrix& z_prev, + const tinyMatrix& g_prev, + const tinyMatrix& y_prev, + TinyCache* cache, + TinyWorkspace* work, + int N +) { + if (!adapter->matrices_initialized) { + initialize_format_matrices(adapter, x_prev.rows(), u_prev.rows(), N); + } + + int nx = adapter->format_nx; + int nu = adapter->format_nu; + + // Fill x_decision + int x_idx = 0; + for (int i = 0; i < N; i++) { + adapter->x_decision.block(x_idx, 0, nx, 1) = x_prev.col(i); + x_idx += nx; + if (i < N-1) { + adapter->x_decision.block(x_idx, 0, nu, 1) = u_prev.col(i); + x_idx += nu; + } + } + + // Clear A matrix for reuse + adapter->A_matrix.setZero(); + + // Fill A matrix with dynamics and input constraints + for (int i = 0; i < N-1; i++) { + // Input constraints + int row_start = i * nu; + int col_start = i * (nx+nu) + nx; + adapter->A_matrix.block(row_start, col_start, nu, nu) = tinyMatrix::Identity(nu, nu); + + // Dynamics constraints + row_start = (N-1) * nu + i * nx; + col_start = i * (nx+nu); + adapter->A_matrix.block(row_start, col_start, nx, nx) = work->Adyn; + adapter->A_matrix.block(row_start, col_start+nx, nx, nu) = work->Bdyn; + + int next_state_idx = col_start + nx + nu; + if (next_state_idx < adapter->A_matrix.cols()) { + adapter->A_matrix.block(row_start, next_state_idx, nx, nx) = -tinyMatrix::Identity(nx, nx); + } + } + + // Fill z and y vectors + for (int i = 0; i < N-1; i++) { + adapter->z_vector.block(i*nu, 0, nu, 1) = z_prev.col(i); + adapter->z_vector.block((N-1)*nu+i*nx, 0, nx, 1) = v_prev.col(i+1); + + adapter->y_vector.block(i*nu, 0, nu, 1) = y_prev.col(i); + adapter->y_vector.block((N-1)*nu+i*nx, 0, nx, 1) = g_prev.col(i+1); + } + + // Build P matrix (cost matrix) + adapter->P_matrix.setZero(); + + // Fill diagonal blocks + x_idx = 0; + for (int i = 0; i < N; i++) { + // State cost + if (i == N-1) { + adapter->P_matrix.block(x_idx, x_idx, nx, nx) = cache->Pinf; + } else { + adapter->P_matrix.block(x_idx, x_idx, nx, nx) = work->Q.asDiagonal(); + } + x_idx += nx; + + // Input cost + if (i < N-1) { + adapter->P_matrix.block(x_idx, x_idx, nu, nu) = work->R.asDiagonal(); + x_idx += nu; + } + } + + // Create q vector (linear cost vector) + x_idx = 0; + for (int i = 0; i < N; i++) { + // For simplicity, we'll use zero reference for now + // In a real implementation, you'd use your reference trajectory + tinyMatrix x_ref = tinyMatrix::Zero(nx, 1); + tinyMatrix delta_x = x_prev.col(i) - x_ref; + adapter->q_vector.block(x_idx, 0, nx, 1) = work->Q.asDiagonal() * delta_x; + x_idx += nx; + + if (i < N-1) { + // For simplicity, we'll use zero reference for now + tinyMatrix u_ref = tinyMatrix::Zero(nu, 1); + tinyMatrix delta_u = u_prev.col(i) - u_ref; + adapter->q_vector.block(x_idx, 0, nu, 1) = work->R.asDiagonal() * delta_u; + x_idx += nu; + } + } +} + +void compute_residuals( + RhoAdapter* adapter, + tinytype* pri_res, + tinytype* dual_res, + tinytype* pri_norm, + tinytype* dual_norm +) { + // Compute Ax + adapter->Ax_vector = adapter->A_matrix * adapter->x_decision; + + // Compute primal residual + adapter->r_prim_vector = adapter->Ax_vector - adapter->z_vector; + *pri_res = adapter->r_prim_vector.cwiseAbs().maxCoeff(); + *pri_norm = std::max(adapter->Ax_vector.cwiseAbs().maxCoeff(), adapter->z_vector.cwiseAbs().maxCoeff()); + + // Compute dual residual components + adapter->Px_vector = adapter->P_matrix * adapter->x_decision; + adapter->ATy_vector = adapter->A_matrix.transpose() * adapter->y_vector; + + // Compute full dual residual + adapter->r_dual_vector = adapter->Px_vector + adapter->q_vector + adapter->ATy_vector; + *dual_res = adapter->r_dual_vector.cwiseAbs().maxCoeff(); + + // Compute normalization + *dual_norm = std::max(std::max(adapter->Px_vector.cwiseAbs().maxCoeff(), + adapter->ATy_vector.cwiseAbs().maxCoeff()), + adapter->q_vector.cwiseAbs().maxCoeff()); +} + +tinytype predict_rho( + RhoAdapter* adapter, + tinytype pri_res, + tinytype dual_res, + tinytype pri_norm, + tinytype dual_norm, + tinytype current_rho +) { + const tinytype eps = 1e-10; + + tinytype normalized_pri = pri_res / (pri_norm + eps); + tinytype normalized_dual = dual_res / (dual_norm + eps); + + tinytype ratio = normalized_pri / (normalized_dual + eps); + + tinytype new_rho = current_rho * std::sqrt(ratio); + + if (adapter->clip) { + new_rho = std::min(std::max(new_rho, adapter->rho_min), adapter->rho_max); + } + + return new_rho; +} + +void update_matrices_with_derivatives(TinyCache* cache, tinytype new_rho) { + tinytype delta_rho = new_rho - cache->rho; + + + + cache->Kinf = cache->Kinf + delta_rho * cache->dKinf_drho; + cache->Pinf = cache->Pinf + delta_rho * cache->dPinf_drho; + cache->C1 = cache->C1 + delta_rho * cache->dC1_drho; + cache->C2 = cache->C2 + delta_rho * cache->dC2_drho; + + cache->rho = new_rho; + + +} + +void benchmark_rho_adaptation( + RhoAdapter* adapter, + const tinyMatrix& x_prev, + const tinyMatrix& u_prev, + const tinyMatrix& v_prev, + const tinyMatrix& z_prev, + const tinyMatrix& g_prev, + const tinyMatrix& y_prev, + TinyCache* cache, + TinyWorkspace* work, + int N, + RhoBenchmarkResult* result +) { + uint32_t start_time = micros(); + + // Format matrices + format_matrices(adapter, x_prev, u_prev, v_prev, z_prev, g_prev, y_prev, cache, work, N); + + // Compute residuals + tinytype pri_res, dual_res, pri_norm, dual_norm; + compute_residuals(adapter, &pri_res, &dual_res, &pri_norm, &dual_norm); + + // Predict new rho + tinytype new_rho = predict_rho(adapter, pri_res, dual_res, pri_norm, dual_norm, cache->rho); + + // Update matrices + update_matrices_with_derivatives(cache, new_rho); + + // Store results + result->time_us = micros() - start_time; + result->initial_rho = cache->rho; + result->final_rho = new_rho; + result->pri_res = pri_res; + result->dual_res = dual_res; + result->pri_norm = pri_norm; + result->dual_norm = dual_norm; +} \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/rho_benchmark.hpp b/tasks/auto_aim/planner/tinympc/rho_benchmark.hpp new file mode 100644 index 0000000..7f69230 --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/rho_benchmark.hpp @@ -0,0 +1,95 @@ +#pragma once +#include +#include "types.hpp" + +struct RhoAdapter { + tinytype rho_min; + tinytype rho_max; + bool clip; + bool matrices_initialized; + + // Pre-allocated matrices for formatting + tinyMatrix A_matrix; + tinyMatrix z_vector; + tinyMatrix y_vector; + tinyMatrix x_decision; + tinyMatrix P_matrix; + tinyMatrix q_vector; + + // Pre-allocated matrices for residual computation + tinyMatrix Ax_vector; + tinyMatrix r_prim_vector; + tinyMatrix r_dual_vector; + tinyMatrix Px_vector; + tinyMatrix ATy_vector; + + // Dimensions + int format_nx; + int format_nu; + int format_N; +}; + +struct RhoBenchmarkResult { + uint32_t time_us; + tinytype initial_rho; + tinytype final_rho; + tinytype pri_res; + tinytype dual_res; + tinytype pri_norm; + tinytype dual_norm; +}; + +// Initialize matrices for formatting +void initialize_format_matrices(RhoAdapter* adapter, int nx, int nu, int N); + +// Format matrices for residual computation +void format_matrices( + RhoAdapter* adapter, + const tinyMatrix& x_prev, + const tinyMatrix& u_prev, + const tinyMatrix& v_prev, + const tinyMatrix& z_prev, + const tinyMatrix& g_prev, + const tinyMatrix& y_prev, + TinyCache* cache, + TinyWorkspace* work, + int N +); + +// Compute residuals +void compute_residuals( + RhoAdapter* adapter, + tinytype* pri_res, + tinytype* dual_res, + tinytype* pri_norm, + tinytype* dual_norm +); + +// Predict new rho value +tinytype predict_rho( + RhoAdapter* adapter, + tinytype pri_res, + tinytype dual_res, + tinytype pri_norm, + tinytype dual_norm, + tinytype current_rho +); + +// Update matrices using derivatives +void update_matrices_with_derivatives(TinyCache* cache, tinytype new_rho); + + +// Main benchmark function +void benchmark_rho_adaptation( + RhoAdapter* adapter, + const tinyMatrix& x_prev, + const tinyMatrix& u_prev, + const tinyMatrix& v_prev, + const tinyMatrix& z_prev, + const tinyMatrix& g_prev, + const tinyMatrix& y_prev, + TinyCache* cache, + TinyWorkspace* work, + int N, + RhoBenchmarkResult* result +); \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/tiny_api.cpp b/tasks/auto_aim/planner/tinympc/tiny_api.cpp new file mode 100755 index 0000000..d05f40f --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/tiny_api.cpp @@ -0,0 +1,476 @@ +#include "tiny_api.hpp" +#include "tiny_api_constants.hpp" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +using namespace Eigen; +IOFormat TinyApiFmt(4, 0, ", ", "\n", "[", "]"); + +static int check_dimension(std::string matrix_name, std::string rows_or_columns, int actual, int expected) { + if (actual != expected) { + std::cout << matrix_name << " has " << actual << " " << rows_or_columns << ". Expected " << expected << "." << std::endl; + return 1; + } + return 0; +} + +int tiny_setup(TinySolver** solverp, + tinyMatrix Adyn, tinyMatrix Bdyn, tinyMatrix fdyn, tinyMatrix Q, tinyMatrix R, + tinytype rho, int nx, int nu, int N, int verbose) { + + TinySolution *solution = new TinySolution(); + TinyCache *cache = new TinyCache(); + TinySettings *settings = new TinySettings(); + TinyWorkspace *work = new TinyWorkspace(); + TinySolver *solver = new TinySolver(); + + solver->solution = solution; + solver->cache = cache; + solver->settings = settings; + solver->work = work; + + *solverp = solver; + + // Initialize solution + solution->iter = 0; + solution->solved = 0; + solution->x = tinyMatrix::Zero(nx, N); + solution->u = tinyMatrix::Zero(nu, N-1); + + // Initialize settings + tiny_set_default_settings(settings); + + // Initialize workspace + work->nx = nx; + work->nu = nu; + work->N = N; + + // Make sure arguments are the correct shapes + int status = 0; + status |= check_dimension("State transition matrix (A)", "rows", Adyn.rows(), nx); + status |= check_dimension("State transition matrix (A)", "columns", Adyn.cols(), nx); + status |= check_dimension("Input matrix (B)", "rows", Bdyn.rows(), nx); + status |= check_dimension("Input matrix (B)", "columns", Bdyn.cols(), nu); + status |= check_dimension("Affine vector (f)", "rows", fdyn.rows(), nx); + status |= check_dimension("Affine vector (f)", "columns", fdyn.cols(), 1); + status |= check_dimension("State stage cost (Q)", "rows", Q.rows(), nx); + status |= check_dimension("State stage cost (Q)", "columns", Q.cols(), nx); + status |= check_dimension("State input cost (R)", "rows", R.rows(), nu); + status |= check_dimension("State input cost (R)", "columns", R.cols(), nu); + if (status) { + return status; + } + + work->x = tinyMatrix::Zero(nx, N); + work->u = tinyMatrix::Zero(nu, N-1); + + work->q = tinyMatrix::Zero(nx, N); + work->r = tinyMatrix::Zero(nu, N-1); + + work->p = tinyMatrix::Zero(nx, N); + work->d = tinyMatrix::Zero(nu, N-1); + + // Bound constraint slack variables + work->v = tinyMatrix::Zero(nx, N); + work->vnew = tinyMatrix::Zero(nx, N); + work->z = tinyMatrix::Zero(nu, N-1); + work->znew = tinyMatrix::Zero(nu, N-1); + + // Bound constraint dual variables + work->g = tinyMatrix::Zero(nx, N); + work->y = tinyMatrix::Zero(nu, N-1); + + // Cone constraint slack variables + work->vc = tinyMatrix::Zero(nx, N); + work->vcnew = tinyMatrix::Zero(nx, N); + work->zc = tinyMatrix::Zero(nu, N-1); + work->zcnew = tinyMatrix::Zero(nu, N-1); + + // Cone constraint dual variables + work->gc = tinyMatrix::Zero(nx, N); + work->yc = tinyMatrix::Zero(nu, N-1); + + // Linear constraint slack variables + work->vl = tinyMatrix::Zero(nx, N); + work->vlnew = tinyMatrix::Zero(nx, N); + work->zl = tinyMatrix::Zero(nu, N-1); + work->zlnew = tinyMatrix::Zero(nu, N-1); + + // Linear constraint dual variables + work->gl = tinyMatrix::Zero(nx, N); + work->yl = tinyMatrix::Zero(nu, N-1); + + work->Q = (Q + rho * tinyMatrix::Identity(nx, nx)).diagonal(); + work->R = (R + rho * tinyMatrix::Identity(nu, nu)).diagonal(); + work->Adyn = Adyn; // State transition matrix + work->Bdyn = Bdyn; // Input matrix + work->fdyn = fdyn; // Affine offset vector + + work->Xref = tinyMatrix::Zero(nx, N); + work->Uref = tinyMatrix::Zero(nu, N-1); + + work->Qu = tinyVector::Zero(nu); + + work->primal_residual_state = 0; + work->primal_residual_input = 0; + work->dual_residual_state = 0; + work->dual_residual_input = 0; + work->status = 0; + work->iter = 0; + + // Initialize cache + status = tiny_precompute_and_set_cache(cache, Adyn, Bdyn, fdyn, work->Q.asDiagonal(), work->R.asDiagonal(), nx, nu, rho, verbose); + if (status) { + return status; + } + + // Initialize sensitivity matrices for adaptive rho + if (solver->settings->adaptive_rho) { + tiny_initialize_sensitivity_matrices(solver); + } + + return 0; +} + +int tiny_set_bound_constraints(TinySolver* solver, + tinyMatrix x_min, tinyMatrix x_max, + tinyMatrix u_min, tinyMatrix u_max) { + if (!solver) { + std::cout << "Error in tiny_set_bound_constraints: solver is nullptr" << std::endl; + return 1; + } + + // Make sure all bound constraint matrix sizes are self-consistent + int status = 0; + status |= check_dimension("Lower state bounds (x_min)", "rows", x_min.rows(), solver->work->nx); + status |= check_dimension("Lower state bounds (x_min)", "cols", x_min.cols(), solver->work->N); + status |= check_dimension("Lower state bounds (x_max)", "rows", x_max.rows(), solver->work->nx); + status |= check_dimension("Lower state bounds (x_max)", "cols", x_max.cols(), solver->work->N); + status |= check_dimension("Lower input bounds (u_min)", "rows", u_min.rows(), solver->work->nu); + status |= check_dimension("Lower input bounds (u_min)", "cols", u_min.cols(), solver->work->N-1); + status |= check_dimension("Lower input bounds (u_max)", "rows", u_max.rows(), solver->work->nu); + status |= check_dimension("Lower input bounds (u_max)", "cols", u_max.cols(), solver->work->N-1); + + solver->work->x_min = x_min; + solver->work->x_max = x_max; + solver->work->u_min = u_min; + solver->work->u_max = u_max; + + return 0; +} + +int tiny_set_cone_constraints(TinySolver* solver, + VectorXi Acx, VectorXi qcx, tinyVector cx, + VectorXi Acu, VectorXi qcu, tinyVector cu) { + if (!solver) { + std::cout << "Error in tiny_set_cone_constraints: solver is nullptr" << std::endl; + return 1; + } + + // Make sure all cone constraint vector sizes are self-consistent + int num_state_cones = Acx.rows(); + int num_input_cones = Acu.rows(); + int status = 0; + status |= check_dimension("Cone state size (qcx)", "rows", qcx.rows(), num_state_cones); + status |= check_dimension("Cone mu value for state (cx)", "rows", cx.rows(), num_state_cones); + status |= check_dimension("Cone input size (qcu)", "rows", qcu.rows(), num_input_cones); + status |= check_dimension("Cone mu value for input (cu)", "rows", cu.rows(), num_input_cones); + if (status) { + return status; + } + + solver->work->numStateCones = num_state_cones; + solver->work->numInputCones = num_input_cones; + + solver->work->Acx = Acx; + solver->work->qcx = qcx; + solver->work->cx = cx; + + solver->work->Acu = Acu; + solver->work->qcu = qcu; + solver->work->cu = cu; + + return 0; +} + +int tiny_set_linear_constraints(TinySolver* solver, + tinyMatrix Alin_x, tinyVector blin_x, + tinyMatrix Alin_u, tinyVector blin_u) { + if (!solver) { + std::cout << "Error in tiny_set_linear_constraints: solver is nullptr" << std::endl; + return 1; + } + + // Make sure all linear constraint matrix sizes are self-consistent + int num_state_linear = Alin_x.rows(); + int num_input_linear = Alin_u.rows(); + int status = 0; + + // Check state constraint dimensions + if (num_state_linear > 0) { + status |= check_dimension("State linear constraint matrix (Alin_x)", "rows", Alin_x.rows(), num_state_linear); + status |= check_dimension("State linear constraint matrix (Alin_x)", "columns", Alin_x.cols(), solver->work->nx); + status |= check_dimension("State linear constraint vector (blin_x)", "rows", blin_x.rows(), num_state_linear); + status |= check_dimension("State linear constraint vector (blin_x)", "columns", blin_x.cols(), 1); + } + + // Check input constraint dimensions + if (num_input_linear > 0) { + status |= check_dimension("Input linear constraint matrix (Alin_u)", "rows", Alin_u.rows(), num_input_linear); + status |= check_dimension("Input linear constraint matrix (Alin_u)", "columns", Alin_u.cols(), solver->work->nu); + status |= check_dimension("Input linear constraint vector (blin_u)", "rows", blin_u.rows(), num_input_linear); + status |= check_dimension("Input linear constraint vector (blin_u)", "columns", blin_u.cols(), 1); + } + + if (status) { + return status; + } + + solver->work->numStateLinear = num_state_linear; + solver->work->numInputLinear = num_input_linear; + + solver->work->Alin_x = Alin_x; + solver->work->blin_x = blin_x; + solver->work->Alin_u = Alin_u; + solver->work->blin_u = blin_u; + + return 0; +} + +int tiny_precompute_and_set_cache(TinyCache *cache, + tinyMatrix Adyn, tinyMatrix Bdyn, tinyMatrix fdyn, tinyMatrix Q, tinyMatrix R, + int nx, int nu, tinytype rho, int verbose) { + + if (!cache) { + std::cout << "Error in tiny_precompute_and_set_cache: cache is nullptr" << std::endl; + return 1; + } + + // Update by adding rho * identity matrix to Q, R + tinyMatrix Q1 = Q + rho * tinyMatrix::Identity(nx, nx); + tinyMatrix R1 = R + rho * tinyMatrix::Identity(nu, nu); + + // Printing + if (verbose) { + std::cout << "A = " << Adyn.format(TinyApiFmt) << std::endl; + std::cout << "B = " << Bdyn.format(TinyApiFmt) << std::endl; + std::cout << "Q = " << Q1.format(TinyApiFmt) << std::endl; + std::cout << "R = " << R1.format(TinyApiFmt) << std::endl; + std::cout << "rho = " << rho << std::endl; + } + + // Riccati recursion to get Kinf, Pinf + tinyMatrix Ktp1 = tinyMatrix::Zero(nu, nx); + tinyMatrix Ptp1 = rho * tinyMatrix::Ones(nx, 1).array().matrix().asDiagonal(); + tinyMatrix Kinf = tinyMatrix::Zero(nu, nx); + tinyMatrix Pinf = tinyMatrix::Zero(nx, nx); + + for (int i = 0; i < 1000; i++) + { + Kinf = (R1 + Bdyn.transpose() * Ptp1 * Bdyn).inverse() * Bdyn.transpose() * Ptp1 * Adyn; + Pinf = Q1 + Adyn.transpose() * Ptp1 * (Adyn - Bdyn * Kinf); + // if Kinf converges, break + if ((Kinf - Ktp1).cwiseAbs().maxCoeff() < 1e-5) + { + if (verbose) { + std::cout << "Kinf converged after " << i + 1 << " iterations" << std::endl; + } + break; + } + Ktp1 = Kinf; + Ptp1 = Pinf; + } + + // Compute cached matrices + tinyMatrix Quu_inv = (R1 + Bdyn.transpose() * Pinf * Bdyn).inverse(); + tinyMatrix AmBKt = (Adyn - Bdyn * Kinf).transpose(); + + // Precomputation for affine term + tinyVector APf = AmBKt*Pinf*fdyn; + tinyVector BPf = Bdyn.transpose()*Pinf*fdyn; + + if (verbose) { + std::cout << "Kinf = " << Kinf.format(TinyApiFmt) << std::endl; + std::cout << "Pinf = " << Pinf.format(TinyApiFmt) << std::endl; + std::cout << "Quu_inv = " << Quu_inv.format(TinyApiFmt) << std::endl; + std::cout << "AmBKt = " << AmBKt.format(TinyApiFmt) << std::endl; + std::cout << "APf = " << APf.format(TinyApiFmt) << std::endl; + std::cout << "BPf = " << BPf.format(TinyApiFmt) << std::endl; + + std::cout << "\nPrecomputation finished!\n" << std::endl; + } + + cache->rho = rho; + cache->Kinf = Kinf; + cache->Pinf = Pinf; + cache->Quu_inv = Quu_inv; + cache->AmBKt = AmBKt; + cache->C1 = Quu_inv; + cache->C2 = AmBKt; + cache->APf = APf; + cache->BPf = BPf; + + return 0; // return success +} + + +int tiny_solve(TinySolver* solver) { + return solve(solver); +} + +int tiny_update_settings(TinySettings* settings, tinytype abs_pri_tol, tinytype abs_dua_tol, + int max_iter, int check_termination, + int en_state_bound, int en_input_bound, + int en_state_soc, int en_input_soc, + int en_state_linear, int en_input_linear) { + if (!settings) { + std::cout << "Error in tiny_update_settings: settings is nullptr" << std::endl; + return 1; + } + settings->abs_pri_tol = abs_pri_tol; + settings->abs_dua_tol = abs_dua_tol; + settings->max_iter = max_iter; + settings->check_termination = check_termination; + settings->en_state_bound = en_state_bound; + settings->en_input_bound = en_input_bound; + settings->en_state_soc = en_state_soc; + settings->en_input_soc = en_input_soc; + settings->en_state_linear = en_state_linear; + settings->en_input_linear = en_input_linear; + return 0; +} + +int tiny_set_default_settings(TinySettings* settings) { + if (!settings) { + std::cout << "Error in tiny_set_default_settings: settings is nullptr" << std::endl; + return 1; + } + settings->abs_pri_tol = TINY_DEFAULT_ABS_PRI_TOL; + settings->abs_dua_tol = TINY_DEFAULT_ABS_DUA_TOL; + settings->max_iter = TINY_DEFAULT_MAX_ITER; + settings->check_termination = TINY_DEFAULT_CHECK_TERMINATION; + + // Turn off constraints until they are set by tiny_set_bound_constraints or tiny_set_cone_constraints + settings->en_state_bound = TINY_DEFAULT_EN_STATE_BOUND; + settings->en_input_bound = TINY_DEFAULT_EN_INPUT_BOUND; + settings->en_state_soc = TINY_DEFAULT_EN_STATE_SOC; + settings->en_input_soc = TINY_DEFAULT_EN_INPUT_SOC; + settings->en_state_linear = TINY_DEFAULT_EN_STATE_LINEAR; + settings->en_input_linear = TINY_DEFAULT_EN_INPUT_LINEAR; + + // Initialize adaptive rho settings + // NOTE : Adaptive rho currently supports only quadrotor system + settings->adaptive_rho = 0; // Disabled by default + settings->adaptive_rho_min = 1.0; + settings->adaptive_rho_max = 100.0; + settings->adaptive_rho_enable_clipping = 1; + + return 0; +} + +int tiny_set_x0(TinySolver* solver, tinyVector x0) { + if (!solver) { + std::cout << "Error in tiny_set_x0: solver is nullptr" << std::endl; + return 1; + } + if (x0.rows() != solver->work->nx) { + perror("Error in tiny_set_x0: x0 is not the correct length"); + } + solver->work->x.col(0) = x0; + return 0; +} + +int tiny_set_x_ref(TinySolver* solver, tinyMatrix x_ref) { + if (!solver) { + std::cout << "Error in tiny_set_x_ref: solver is nullptr" << std::endl; + return 1; + } + int status = 0; + status |= check_dimension("State reference trajectory (x_ref)", "rows", x_ref.rows(), solver->work->nx); + status |= check_dimension("State reference trajectory (x_ref)", "columns", x_ref.cols(), solver->work->N); + solver->work->Xref = x_ref; + return 0; +} + +int tiny_set_u_ref(TinySolver* solver, tinyMatrix u_ref) { + if (!solver) { + std::cout << "Error in tiny_set_u_ref: solver is nullptr" << std::endl; + return 1; + } + int status = 0; + status |= check_dimension("Control/input reference trajectory (u_ref)", "rows", u_ref.rows(), solver->work->nu); + status |= check_dimension("Control/input reference trajectory (u_ref)", "columns",u_ref.cols(), solver->work->N-1); + solver->work->Uref = u_ref; + return 0; +} + +void tiny_initialize_sensitivity_matrices(TinySolver *solver) { + + int nu = solver->work->nu; + int nx = solver->work->nx; + // Initialize matrices with zeros + solver->cache->dKinf_drho = tinyMatrix::Zero(nu, nx); + solver->cache->dPinf_drho = tinyMatrix::Zero(nx, nx); + solver->cache->dC1_drho = tinyMatrix::Zero(nu, nu); + solver->cache->dC2_drho = tinyMatrix::Zero(nx, nx); + + const float dKinf_drho[4][12] = { + { 0.0001, -0.0001, -0.0025, 0.0003, 0.0007, 0.0050, 0.0001, -0.0001, -0.0008, 0.0000, 0.0001, 0.0008}, + { -0.0001, -0.0000, -0.0025, -0.0001, -0.0006, -0.0050, -0.0001, 0.0000, -0.0008, -0.0000, -0.0001, -0.0008}, + { 0.0000, 0.0000, -0.0025, 0.0001, 0.0004, 0.0050, 0.0000, 0.0000, -0.0008, 0.0000, 0.0000, 0.0008}, + { -0.0000, 0.0001, -0.0025, -0.0003, -0.0004, -0.0050, -0.0000, 0.0001, -0.0008, -0.0000, -0.0000, -0.0008} + }; + + const float dPinf_drho[12][12] = { + { 0.0494, -0.0045, -0.0000, 0.0110, 0.1300, -0.0283, 0.0280, -0.0026, -0.0000, 0.0004, 0.0070, -0.0094}, + { -0.0045, 0.0491, 0.0000, -0.1320, -0.0111, 0.0114, -0.0026, 0.0279, 0.0000, -0.0076, -0.0004, 0.0038}, + { -0.0000, 0.0000, 2.4450, 0.0000, -0.0000, -0.0000, -0.0000, 0.0000, 1.2593, 0.0000, 0.0000, 0.0000}, + { 0.0110, -0.1320, 0.0000, 0.3913, 0.0592, 0.3108, 0.0080, -0.0776, 0.0000, 0.0254, 0.0068, 0.0750}, + { 0.1300, -0.0111, -0.0000, 0.0592, 0.4420, 0.7771, 0.0797, -0.0081, -0.0000, 0.0068, 0.0350, 0.1875}, + { -0.0283, 0.0114, -0.0000, 0.3108, 0.7771, 10.0441, 0.0272, -0.0109, 0.0000, 0.0655, 0.1639, 2.6362}, + { 0.0280, -0.0026, -0.0000, 0.0080, 0.0797, 0.0272, 0.0163, -0.0016, -0.0000, 0.0005, 0.0047, 0.0032}, + { -0.0026, 0.0279, 0.0000, -0.0776, -0.0081, -0.0109, -0.0016, 0.0161, 0.0000, -0.0046, -0.0005, -0.0013}, + { -0.0000, 0.0000, 1.2593, 0.0000, -0.0000, 0.0000, -0.0000, 0.0000, 0.9232, 0.0000, 0.0000, 0.0000}, + { 0.0004, -0.0076, 0.0000, 0.0254, 0.0068, 0.0655, 0.0005, -0.0046, 0.0000, 0.0022, 0.0017, 0.0244}, + { 0.0070, -0.0004, 0.0000, 0.0068, 0.0350, 0.1639, 0.0047, -0.0005, 0.0000, 0.0017, 0.0054, 0.0610}, + { -0.0094, 0.0038, 0.0000, 0.0750, 0.1875, 2.6362, 0.0032, -0.0013, 0.0000, 0.0244, 0.0610, 0.9869} + }; + + const float dC1_drho[4][4] = { + { -0.0000, 0.0000, -0.0000, 0.0000}, + { 0.0000, -0.0000, 0.0000, -0.0000}, + { -0.0000, 0.0000, -0.0000, 0.0000}, + { 0.0000, -0.0000, 0.0000, -0.0000} + }; + + const float dC2_drho[12][12] = { + { 0.0000, -0.0000, 0.0000, 0.0000, 0.0000, -0.0000, 0.0000, -0.0000, 0.0000, 0.0000, 0.0000, -0.0000}, + { -0.0000, 0.0000, 0.0000, -0.0000, -0.0000, 0.0000, -0.0000, 0.0000, 0.0000, -0.0000, -0.0000, 0.0000}, + { -0.0000, 0.0000, 0.0001, 0.0000, -0.0000, -0.0000, -0.0000, 0.0000, 0.0000, 0.0000, -0.0000, -0.0000}, + { 0.0000, -0.0000, -0.0000, 0.0001, 0.0000, -0.0000, 0.0000, -0.0000, -0.0000, 0.0000, 0.0000, -0.0000}, + { 0.0000, -0.0000, -0.0000, 0.0000, 0.0001, -0.0000, 0.0000, -0.0000, -0.0000, 0.0000, 0.0000, -0.0000}, + { -0.0000, 0.0000, -0.0000, -0.0000, 0.0000, 0.0001, -0.0000, 0.0000, -0.0000, 0.0000, 0.0000, 0.0000}, + { 0.0000, -0.0000, 0.0000, 0.0000, 0.0000, -0.0000, 0.0000, -0.0000, 0.0000, 0.0000, 0.0000, -0.0000}, + { -0.0000, 0.0000, 0.0000, -0.0000, -0.0000, 0.0000, -0.0000, 0.0000, 0.0000, -0.0000, -0.0000, 0.0000}, + { -0.0000, 0.0000, 0.0021, 0.0000, -0.0000, -0.0000, -0.0000, 0.0000, 0.0006, 0.0000, -0.0000, -0.0000}, + { 0.0002, -0.0027, -0.0000, 0.0068, 0.0005, -0.0005, 0.0001, -0.0015, -0.0000, 0.0004, 0.0000, -0.0001}, + { 0.0027, -0.0002, 0.0000, 0.0005, 0.0066, -0.0011, 0.0015, -0.0001, 0.0000, 0.0000, 0.0004, -0.0002}, + { -0.0001, 0.0001, 0.0000, -0.0000, 0.0000, 0.0041, -0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0006} + }; + + + + // Map arrays to Eigen matrices + solver->cache->dKinf_drho = Map>(dKinf_drho[0]).cast(); + solver->cache->dPinf_drho = Map>(dPinf_drho[0]).cast(); + solver->cache->dC1_drho = Map>(dC1_drho[0]).cast(); + solver->cache->dC2_drho = Map>(dC2_drho[0]).cast(); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/tiny_api.hpp b/tasks/auto_aim/planner/tinympc/tiny_api.hpp new file mode 100755 index 0000000..5d9ce04 --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/tiny_api.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include "admm.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +int tiny_setup(TinySolver** solverp, + tinyMatrix Adyn, tinyMatrix Bdyn, tinyMatrix fdyn, tinyMatrix Q, tinyMatrix R, + tinytype rho, int nx, int nu, int N, int verbose); +int tiny_set_bound_constraints(TinySolver* solver, + tinyMatrix x_min, tinyMatrix x_max, + tinyMatrix u_min, tinyMatrix u_max); +int tiny_set_cone_constraints(TinySolver* solver, + VectorXi Acu, VectorXi qcu, tinyVector cu, + VectorXi Acx, VectorXi qcx, tinyVector cx); +int tiny_set_linear_constraints(TinySolver* solver, + tinyMatrix Alin_x, tinyVector blin_x, + tinyMatrix Alin_u, tinyVector blin_u); +int tiny_precompute_and_set_cache(TinyCache *cache, + tinyMatrix Adyn, tinyMatrix Bdyn, tinyMatrix fdyn, tinyMatrix Q, tinyMatrix R, + int nx, int nu, tinytype rho, int verbose); + +void compute_sensitivity_matrices(TinyCache *cache, + tinyMatrix Adyn, tinyMatrix Bdyn, tinyMatrix Q, tinyMatrix R, + int nx, int nu, tinytype rho, int verbose); + +int tiny_update_matrices_with_derivatives(TinyCache *cache, tinytype delta_rho); +int tiny_solve(TinySolver *solver); + +int tiny_update_settings(TinySettings* settings, + tinytype abs_pri_tol, tinytype abs_dua_tol, + int max_iter, int check_termination, + int en_state_bound, int en_input_bound, + int en_state_soc, int en_input_soc, + int en_state_linear, int en_input_linear); +int tiny_set_default_settings(TinySettings* settings); + +int tiny_set_x0(TinySolver* solver, tinyVector x0); +int tiny_set_x_ref(TinySolver* solver, tinyMatrix x_ref); +int tiny_set_u_ref(TinySolver* solver, tinyMatrix u_ref); + +/** + * Initialize sensitivity matrices for adaptive rho + * + * @param solver Pointer to solver + */ +void tiny_initialize_sensitivity_matrices(TinySolver *solver); + +int tiny_setup_state_soc_constraints(TinySolver *solver, + tinyVector Acx, tinyVector qcx, tinyVector cx, + int numStateCones); + +int tiny_setup_input_soc_constraints(TinySolver *solver, + tinyVector Acu, tinyVector qcu, tinyVector cu, + int numInputCones); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tasks/auto_aim/planner/tinympc/tiny_api_constants.hpp b/tasks/auto_aim/planner/tinympc/tiny_api_constants.hpp new file mode 100644 index 0000000..d99e96a --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/tiny_api_constants.hpp @@ -0,0 +1,14 @@ +#pragma once + + +// Default settings +#define TINY_DEFAULT_ABS_PRI_TOL (1e-03) +#define TINY_DEFAULT_ABS_DUA_TOL (1e-03) +#define TINY_DEFAULT_MAX_ITER (1000) +#define TINY_DEFAULT_CHECK_TERMINATION (1) +#define TINY_DEFAULT_EN_STATE_BOUND (1) +#define TINY_DEFAULT_EN_INPUT_BOUND (1) +#define TINY_DEFAULT_EN_STATE_SOC (0) +#define TINY_DEFAULT_EN_INPUT_SOC (0) +#define TINY_DEFAULT_EN_STATE_LINEAR (0) +#define TINY_DEFAULT_EN_INPUT_LINEAR (0) diff --git a/tasks/auto_aim/planner/tinympc/types.hpp b/tasks/auto_aim/planner/tinympc/types.hpp new file mode 100755 index 0000000..409023a --- /dev/null +++ b/tasks/auto_aim/planner/tinympc/types.hpp @@ -0,0 +1,204 @@ +#pragma once + +#include +// #include +// #include + +using namespace Eigen; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef double tinytype; // should be double if you want to generate code +typedef Matrix tinyMatrix; +typedef Matrix tinyVector; + +// typedef Matrix tiny_VectorNx; +// typedef Matrix tiny_VectorNu; +// typedef Matrix tiny_MatrixNxNx; +// typedef Matrix tiny_MatrixNxNu; +// typedef Matrix tiny_MatrixNuNx; +// typedef Matrix tiny_MatrixNuNu; + +// typedef Matrix tiny_MatrixNxNh; // Nu x Nh +// typedef Matrix tiny_MatrixNuNhm1; // Nu x Nh-1 + +/** + * Solution + */ +typedef struct { + int iter; + int solved; + tinyMatrix x; // nx x N + tinyMatrix u; // nu x N-1 +} TinySolution; + + +/** +* Matrices that must be recomputed with changes in time step, rho +*/ +typedef struct { + tinytype rho; + tinyMatrix Kinf; // nu x nx + tinyMatrix Pinf; // nx x nx + tinyMatrix Quu_inv; // nu x nu + tinyMatrix AmBKt; // nx x nx + tinyVector APf; // nx x 1 + tinyVector BPf; // nu x 1 + tinyMatrix C1; // From adaptive rho + tinyMatrix C2; // From adaptive rho + + // Sensitivity matrices for adaptive rho + tinyMatrix dKinf_drho; + tinyMatrix dPinf_drho; + tinyMatrix dC1_drho; + tinyMatrix dC2_drho; +} TinyCache; +/** +* User settings +*/ +typedef struct { + tinytype abs_pri_tol; + tinytype abs_dua_tol; + int max_iter; + int check_termination; + int en_state_bound; + int en_input_bound; + int en_state_soc; + int en_input_soc; + int en_state_linear; + int en_input_linear; + + // Add adaptive rho parameters + int adaptive_rho; // Enable/disable adaptive rho (1/0) + tinytype adaptive_rho_min; // Minimum value for rho + tinytype adaptive_rho_max; // Maximum value for rho + int adaptive_rho_enable_clipping; // Enable/disable clipping of rho (1/0) +} TinySettings; + + +/** + * Problem variables + */ +typedef struct { + int nx; // Number of states + int nu; // Number of control inputs + int N; // Number of knotpoints in the horizon + + // State and input + tinyMatrix x; // nx x N + tinyMatrix u; // nu x N-1 + + // Linear control cost terms + tinyMatrix q; // nx x N + tinyMatrix r; // nu x N-1 + + // Linear Riccati backward pass terms + tinyMatrix p; // nx x N + tinyMatrix d; // nu x N-1 + + // Bound constraint variables + // Slack variables + tinyMatrix v; // nx x N + tinyMatrix vnew; // nx x N + tinyMatrix z; // nu x N-1 + tinyMatrix znew; // nu x N-1 + + // Dual variables + tinyMatrix g; // nx x N + tinyMatrix y; // nu x N-1 + + // State and input bounds + tinyMatrix x_min; // nx x N + tinyMatrix x_max; // nx x N + tinyMatrix u_min; // nu x N-1 + tinyMatrix u_max; // nu x N-1 + + // Cone constraint variables + // Variables to keep track of general cone information + int numStateCones; // Number of cone constraints on states at each time step + int numInputCones; // Number of cone constraints on inputs at each time step + tinyVector cx; // One coefficient for each state cone + tinyVector cu; // One coefficient for each input cone + VectorXi Acx; // Start indices for each state cone + VectorXi Acu; // Start indices for each input cone + VectorXi qcx; // Dimension for each state cone + VectorXi qcu; // Dimension for each input cone + + // Slack variables + tinyMatrix vc; // nx x N + tinyMatrix vcnew; // nx x N + tinyMatrix zc; // nu x N-1 + tinyMatrix zcnew; // nu x N-1 + + // Dual variables + tinyMatrix gc; // nx x N + tinyMatrix yc; // nu x N-1 + + // Linear constraint variables + // Variables to keep track of general linear constraint information + int numStateLinear; // Number of linear constraints on states at each time step + int numInputLinear; // Number of linear constraints on inputs at each time step + + // Constraint matrices and vectors + tinyMatrix Alin_x; // Normal vectors for state linear constraints (numStateLinear x nx) + tinyVector blin_x; // Offset values for state linear constraints (numStateLinear x 1) + tinyMatrix Alin_u; // Normal vectors for input linear constraints (numInputLinear x nu) + tinyVector blin_u; // Offset values for input linear constraints (numInputLinear x 1) + + // Slack variables for linear constraints + tinyMatrix vl; // nx x N + tinyMatrix vlnew; // nx x N + tinyMatrix zl; // nu x N-1 + tinyMatrix zlnew; // nu x N-1 + + // Dual variables for linear constraints + tinyMatrix gl; // nx x N + tinyMatrix yl; // nu x N-1 + + + + // Q, R, A, B, f given by user + tinyVector Q; // nx x 1 + tinyVector R; // nu x 1 + tinyMatrix Adyn; // nx x nx (state transition matrix) + tinyMatrix Bdyn; // nx x nu (control matrix) + tinyVector fdyn; // nx x 1 (affine vector) + + // Reference trajectory to track for one horizon + tinyMatrix Xref; // nx x N + tinyMatrix Uref; // nu x N-1 + + // Temporaries + tinyVector Qu; // nu x 1 + + + + // Variables for keeping track of solve status + tinytype primal_residual_state; + tinytype primal_residual_input; + tinytype dual_residual_state; + tinytype dual_residual_input; + int status; + int iter; +} TinyWorkspace; + +/** + * Main TinyMPC solver structure that holds all information. + */ +typedef struct { + TinySolution *solution; // Solution + TinySettings *settings; // Problem settings + TinyCache *cache; // Problem cache + TinyWorkspace *work; // Solver workspace +} TinySolver; + + +// Add at the top with other definitions +#define BENCH_NX 12 +#define BENCH_NU 4 + +#ifdef __cplusplus +} +#endif diff --git a/tasks/auto_aim/shooter.cpp b/tasks/auto_aim/shooter.cpp new file mode 100644 index 0000000..faeefb6 --- /dev/null +++ b/tasks/auto_aim/shooter.cpp @@ -0,0 +1,43 @@ +#include "shooter.hpp" + +#include + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +namespace auto_aim +{ +Shooter::Shooter(const std::string & config_path) : last_command_{false, false, 0, 0} +{ + auto yaml = YAML::LoadFile(config_path); + first_tolerance_ = yaml["first_tolerance"].as() / 57.3; // degree to rad + second_tolerance_ = yaml["second_tolerance"].as() / 57.3; // degree to rad + judge_distance_ = yaml["judge_distance"].as(); + auto_fire_ = yaml["auto_fire"].as(); +} + +bool Shooter::shoot( + const io::Command & command, const auto_aim::Aimer & aimer, + const std::list & targets, const Eigen::Vector3d & gimbal_pos) +{ + if (!command.control || targets.empty() || !auto_fire_) return false; + + auto target_x = targets.front().ekf_x()[0]; + auto target_y = targets.front().ekf_x()[2]; + auto tolerance = std::sqrt(tools::square(target_x) + tools::square(target_y)) > judge_distance_ + ? second_tolerance_ + : first_tolerance_; + // tools::logger()->debug("d(command.yaw) is {:.4f}", std::abs(last_command_.yaw - command.yaw)); + if ( + std::abs(last_command_.yaw - command.yaw) < tolerance * 2 && //此时认为command突变不应该射击 + std::abs(gimbal_pos[0] - last_command_.yaw) < tolerance && //应该减去上一次command的yaw值 + aimer.debug_aim_point.valid) { + last_command_ = command; + return true; + } + + last_command_ = command; + return false; +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/shooter.hpp b/tasks/auto_aim/shooter.hpp new file mode 100644 index 0000000..2b4cb78 --- /dev/null +++ b/tasks/auto_aim/shooter.hpp @@ -0,0 +1,29 @@ +#ifndef AUTO_AIM__SHOOTER_HPP +#define AUTO_AIM__SHOOTER_HPP + +#include + +#include "io/command.hpp" +#include "tasks/auto_aim/aimer.hpp" + +namespace auto_aim +{ +class Shooter +{ +public: + Shooter(const std::string & config_path); + + bool shoot( + const io::Command & command, const auto_aim::Aimer & aimer, + const std::list & targets, const Eigen::Vector3d & gimbal_pos); + +private: + io::Command last_command_; + double judge_distance_; + double first_tolerance_; + double second_tolerance_; + bool auto_fire_; +}; +} // namespace auto_aim + +#endif // AUTO_AIM__SHOOTER_HPP \ No newline at end of file diff --git a/tasks/auto_aim/solver.cpp b/tasks/auto_aim/solver.cpp new file mode 100644 index 0000000..d7ce7c8 --- /dev/null +++ b/tasks/auto_aim/solver.cpp @@ -0,0 +1,293 @@ +#include "solver.hpp" + +#include + +#include + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +namespace auto_aim +{ +constexpr double LIGHTBAR_LENGTH = 56e-3; // m +constexpr double BIG_ARMOR_WIDTH = 230e-3; // m +constexpr double SMALL_ARMOR_WIDTH = 135e-3; // m + +const std::vector BIG_ARMOR_POINTS{ + {0, BIG_ARMOR_WIDTH / 2, LIGHTBAR_LENGTH / 2}, + {0, -BIG_ARMOR_WIDTH / 2, LIGHTBAR_LENGTH / 2}, + {0, -BIG_ARMOR_WIDTH / 2, -LIGHTBAR_LENGTH / 2}, + {0, BIG_ARMOR_WIDTH / 2, -LIGHTBAR_LENGTH / 2}}; +const std::vector SMALL_ARMOR_POINTS{ + {0, SMALL_ARMOR_WIDTH / 2, LIGHTBAR_LENGTH / 2}, + {0, -SMALL_ARMOR_WIDTH / 2, LIGHTBAR_LENGTH / 2}, + {0, -SMALL_ARMOR_WIDTH / 2, -LIGHTBAR_LENGTH / 2}, + {0, SMALL_ARMOR_WIDTH / 2, -LIGHTBAR_LENGTH / 2}}; + +Solver::Solver(const std::string & config_path) : R_gimbal2world_(Eigen::Matrix3d::Identity()) +{ + auto yaml = YAML::LoadFile(config_path); + + auto R_gimbal2imubody_data = yaml["R_gimbal2imubody"].as>(); + auto R_camera2gimbal_data = yaml["R_camera2gimbal"].as>(); + auto t_camera2gimbal_data = yaml["t_camera2gimbal"].as>(); + R_gimbal2imubody_ = Eigen::Matrix(R_gimbal2imubody_data.data()); + R_camera2gimbal_ = Eigen::Matrix(R_camera2gimbal_data.data()); + t_camera2gimbal_ = Eigen::Matrix(t_camera2gimbal_data.data()); + + auto camera_matrix_data = yaml["camera_matrix"].as>(); + auto distort_coeffs_data = yaml["distort_coeffs"].as>(); + Eigen::Matrix camera_matrix(camera_matrix_data.data()); + Eigen::Matrix distort_coeffs(distort_coeffs_data.data()); + cv::eigen2cv(camera_matrix, camera_matrix_); + cv::eigen2cv(distort_coeffs, distort_coeffs_); +} + +Eigen::Matrix3d Solver::R_gimbal2world() const { return R_gimbal2world_; } + +void Solver::set_R_gimbal2world(const Eigen::Quaterniond & q) +{ + Eigen::Matrix3d R_imubody2imuabs = q.toRotationMatrix(); + R_gimbal2world_ = R_gimbal2imubody_.transpose() * R_imubody2imuabs * R_gimbal2imubody_; +} + +//solvePnP(获得姿态) +void Solver::solve(Armor & armor) const +{ + const auto & object_points = + (armor.type == ArmorType::big) ? BIG_ARMOR_POINTS : SMALL_ARMOR_POINTS; + + cv::Vec3d rvec, tvec; + cv::solvePnP( + object_points, armor.points, camera_matrix_, distort_coeffs_, rvec, tvec, false, + cv::SOLVEPNP_IPPE); + + Eigen::Vector3d xyz_in_camera; + cv::cv2eigen(tvec, xyz_in_camera); + armor.xyz_in_gimbal = R_camera2gimbal_ * xyz_in_camera + t_camera2gimbal_; + armor.xyz_in_world = R_gimbal2world_ * armor.xyz_in_gimbal; + + cv::Mat rmat; + cv::Rodrigues(rvec, rmat); + Eigen::Matrix3d R_armor2camera; + cv::cv2eigen(rmat, R_armor2camera); + Eigen::Matrix3d R_armor2gimbal = R_camera2gimbal_ * R_armor2camera; + Eigen::Matrix3d R_armor2world = R_gimbal2world_ * R_armor2gimbal; + armor.ypr_in_gimbal = tools::eulers(R_armor2gimbal, 2, 1, 0); + armor.ypr_in_world = tools::eulers(R_armor2world, 2, 1, 0); + + armor.ypd_in_world = tools::xyz2ypd(armor.xyz_in_world); + + // 平衡不做yaw优化,因为pitch假设不成立 + auto is_balance = (armor.type == ArmorType::big) && + (armor.name == ArmorName::three || armor.name == ArmorName::four || + armor.name == ArmorName::five); + if (is_balance) return; + + optimize_yaw(armor); +} + +std::vector Solver::reproject_armor( + const Eigen::Vector3d & xyz_in_world, double yaw, ArmorType type, ArmorName name) const +{ + auto sin_yaw = std::sin(yaw); + auto cos_yaw = std::cos(yaw); + + auto pitch = (name == ArmorName::outpost) ? -15.0 * CV_PI / 180.0 : 15.0 * CV_PI / 180.0; + auto sin_pitch = std::sin(pitch); + auto cos_pitch = std::cos(pitch); + + // clang-format off + const Eigen::Matrix3d R_armor2world { + {cos_yaw * cos_pitch, -sin_yaw, cos_yaw * sin_pitch}, + {sin_yaw * cos_pitch, cos_yaw, sin_yaw * sin_pitch}, + { -sin_pitch, 0, cos_pitch} + }; + // clang-format on + + // get R_armor2camera t_armor2camera + const Eigen::Vector3d & t_armor2world = xyz_in_world; + Eigen::Matrix3d R_armor2camera = + R_camera2gimbal_.transpose() * R_gimbal2world_.transpose() * R_armor2world; + Eigen::Vector3d t_armor2camera = + R_camera2gimbal_.transpose() * (R_gimbal2world_.transpose() * t_armor2world - t_camera2gimbal_); + + // get rvec tvec + cv::Vec3d rvec; + cv::Mat R_armor2camera_cv; + cv::eigen2cv(R_armor2camera, R_armor2camera_cv); + cv::Rodrigues(R_armor2camera_cv, rvec); + cv::Vec3d tvec(t_armor2camera[0], t_armor2camera[1], t_armor2camera[2]); + + // reproject + std::vector image_points; + const auto & object_points = (type == ArmorType::big) ? BIG_ARMOR_POINTS : SMALL_ARMOR_POINTS; + cv::projectPoints(object_points, rvec, tvec, camera_matrix_, distort_coeffs_, image_points); + return image_points; +} + +double Solver::oupost_reprojection_error(Armor armor, const double & pitch) +{ + // solve + const auto & object_points = + (armor.type == ArmorType::big) ? BIG_ARMOR_POINTS : SMALL_ARMOR_POINTS; + + cv::Vec3d rvec, tvec; + cv::solvePnP( + object_points, armor.points, camera_matrix_, distort_coeffs_, rvec, tvec, false, + cv::SOLVEPNP_IPPE); + + Eigen::Vector3d xyz_in_camera; + cv::cv2eigen(tvec, xyz_in_camera); + armor.xyz_in_gimbal = R_camera2gimbal_ * xyz_in_camera + t_camera2gimbal_; + armor.xyz_in_world = R_gimbal2world_ * armor.xyz_in_gimbal; + + cv::Mat rmat; + cv::Rodrigues(rvec, rmat); + Eigen::Matrix3d R_armor2camera; + cv::cv2eigen(rmat, R_armor2camera); + Eigen::Matrix3d R_armor2gimbal = R_camera2gimbal_ * R_armor2camera; + Eigen::Matrix3d R_armor2world = R_gimbal2world_ * R_armor2gimbal; + armor.ypr_in_gimbal = tools::eulers(R_armor2gimbal, 2, 1, 0); + armor.ypr_in_world = tools::eulers(R_armor2world, 2, 1, 0); + + armor.ypd_in_world = tools::xyz2ypd(armor.xyz_in_world); + + auto yaw = armor.ypr_in_world[0]; + auto xyz_in_world = armor.xyz_in_world; + + auto sin_yaw = std::sin(yaw); + auto cos_yaw = std::cos(yaw); + + auto sin_pitch = std::sin(pitch); + auto cos_pitch = std::cos(pitch); + + // clang-format off + const Eigen::Matrix3d _R_armor2world { + {cos_yaw * cos_pitch, -sin_yaw, cos_yaw * sin_pitch}, + {sin_yaw * cos_pitch, cos_yaw, sin_yaw * sin_pitch}, + { -sin_pitch, 0, cos_pitch} + }; + // clang-format on + + // get R_armor2camera t_armor2camera + const Eigen::Vector3d & t_armor2world = xyz_in_world; + Eigen::Matrix3d _R_armor2camera = + R_camera2gimbal_.transpose() * R_gimbal2world_.transpose() * _R_armor2world; + Eigen::Vector3d t_armor2camera = + R_camera2gimbal_.transpose() * (R_gimbal2world_.transpose() * t_armor2world - t_camera2gimbal_); + + // get rvec tvec + cv::Vec3d _rvec; + cv::Mat R_armor2camera_cv; + cv::eigen2cv(_R_armor2camera, R_armor2camera_cv); + cv::Rodrigues(R_armor2camera_cv, _rvec); + cv::Vec3d _tvec(t_armor2camera[0], t_armor2camera[1], t_armor2camera[2]); + + // reproject + std::vector image_points; + cv::projectPoints(object_points, _rvec, _tvec, camera_matrix_, distort_coeffs_, image_points); + + auto error = 0.0; + for (int i = 0; i < 4; i++) error += cv::norm(armor.points[i] - image_points[i]); + return error; +} + +void Solver::optimize_yaw(Armor & armor) const +{ + Eigen::Vector3d gimbal_ypr = tools::eulers(R_gimbal2world_, 2, 1, 0); + + constexpr double SEARCH_RANGE = 140; // degree + auto yaw0 = tools::limit_rad(gimbal_ypr[0] - SEARCH_RANGE / 2 * CV_PI / 180.0); + + auto min_error = 1e10; + auto best_yaw = armor.ypr_in_world[0]; + + for (int i = 0; i < SEARCH_RANGE; i++) { + double yaw = tools::limit_rad(yaw0 + i * CV_PI / 180.0); + auto error = armor_reprojection_error(armor, yaw, (i - SEARCH_RANGE / 2) * CV_PI / 180.0); + + if (error < min_error) { + min_error = error; + best_yaw = yaw; + } + } + + armor.yaw_raw = armor.ypr_in_world[0]; + armor.ypr_in_world[0] = best_yaw; +} + +double Solver::SJTU_cost( + const std::vector & cv_refs, const std::vector & cv_pts, + const double & inclined) const +{ + std::size_t size = cv_refs.size(); + std::vector refs; + std::vector pts; + for (std::size_t i = 0u; i < size; ++i) { + refs.emplace_back(cv_refs[i].x, cv_refs[i].y); + pts.emplace_back(cv_pts[i].x, cv_pts[i].y); + } + double cost = 0.; + for (std::size_t i = 0u; i < size; ++i) { + std::size_t p = (i + 1u) % size; + // i - p 构成线段。过程:先移动起点,再补长度,再旋转 + Eigen::Vector2d ref_d = refs[p] - refs[i]; // 标准 + Eigen::Vector2d pt_d = pts[p] - pts[i]; + // 长度差代价 + 起点差代价(1 / 2)(0 度左右应该抛弃) + double pixel_dis = // dis 是指方差平面内到原点的距离 + (0.5 * ((refs[i] - pts[i]).norm() + (refs[p] - pts[p]).norm()) + + std::fabs(ref_d.norm() - pt_d.norm())) / + ref_d.norm(); + double angular_dis = ref_d.norm() * tools::get_abs_angle(ref_d, pt_d) / ref_d.norm(); + // 平方可能是为了配合 sin 和 cos + // 弧度差代价(0 度左右占比应该大) + double cost_i = + tools::square(pixel_dis * std::sin(inclined)) + + tools::square(angular_dis * std::cos(inclined)) * 2.0; // DETECTOR_ERROR_PIXEL_BY_SLOPE + // 重投影像素误差越大,越相信斜率 + cost += std::sqrt(cost_i); + } + return cost; +} + +double Solver::armor_reprojection_error( + const Armor & armor, double yaw, const double & inclined) const +{ + auto image_points = reproject_armor(armor.xyz_in_world, yaw, armor.type, armor.name); + auto error = 0.0; + for (int i = 0; i < 4; i++) error += cv::norm(armor.points[i] - image_points[i]); + // auto error = SJTU_cost(image_points, armor.points, inclined); + + return error; +} + +// 世界坐标到像素坐标的转换 +std::vector Solver::world2pixel(const std::vector & worldPoints) +{ + Eigen::Matrix3d R_world2camera = R_camera2gimbal_.transpose() * R_gimbal2world_.transpose(); + Eigen::Vector3d t_world2camera = -R_camera2gimbal_.transpose() * t_camera2gimbal_; + + cv::Mat rvec; + cv::Mat tvec; + cv::eigen2cv(R_world2camera, rvec); + cv::eigen2cv(t_world2camera, tvec); + + std::vector valid_world_points; + for (const auto & world_point : worldPoints) { + Eigen::Vector3d world_point_eigen(world_point.x, world_point.y, world_point.z); + Eigen::Vector3d camera_point = R_world2camera * world_point_eigen + t_world2camera; + + if (camera_point.z() > 0) { + valid_world_points.push_back(world_point); + } + } + // 如果没有有效点,返回空vector + if (valid_world_points.empty()) { + return std::vector(); + } + std::vector pixelPoints; + cv::projectPoints(valid_world_points, rvec, tvec, camera_matrix_, distort_coeffs_, pixelPoints); + return pixelPoints; +} +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/solver.hpp b/tasks/auto_aim/solver.hpp new file mode 100644 index 0000000..602af72 --- /dev/null +++ b/tasks/auto_aim/solver.hpp @@ -0,0 +1,48 @@ +#ifndef AUTO_AIM__SOLVER_HPP +#define AUTO_AIM__SOLVER_HPP + +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include + +#include "armor.hpp" + +namespace auto_aim +{ +class Solver +{ +public: + explicit Solver(const std::string & config_path); + + Eigen::Matrix3d R_gimbal2world() const; + + void set_R_gimbal2world(const Eigen::Quaterniond & q); + + void solve(Armor & armor) const; + + std::vector reproject_armor( + const Eigen::Vector3d & xyz_in_world, double yaw, ArmorType type, ArmorName name) const; + + double oupost_reprojection_error(Armor armor, const double & picth); + + std::vector world2pixel(const std::vector & worldPoints); + +private: + cv::Mat camera_matrix_; + cv::Mat distort_coeffs_; + Eigen::Matrix3d R_gimbal2imubody_; + Eigen::Matrix3d R_camera2gimbal_; + Eigen::Vector3d t_camera2gimbal_; + Eigen::Matrix3d R_gimbal2world_; + + void optimize_yaw(Armor & armor) const; + + double armor_reprojection_error(const Armor & armor, double yaw, const double & inclined) const; + double SJTU_cost( + const std::vector & cv_refs, const std::vector & cv_pts, + const double & inclined) const; +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__SOLVER_HPP \ No newline at end of file diff --git a/tasks/auto_aim/target.cpp b/tasks/auto_aim/target.cpp new file mode 100644 index 0000000..f1121bb --- /dev/null +++ b/tasks/auto_aim/target.cpp @@ -0,0 +1,321 @@ +#include "target.hpp" + +#include + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +namespace auto_aim +{ +Target::Target( + const Armor & armor, std::chrono::steady_clock::time_point t, double radius, int armor_num, + Eigen::VectorXd P0_dig) +: name(armor.name), + armor_type(armor.type), + jumped(false), + last_id(0), + update_count_(0), + armor_num_(armor_num), + t_(t), + is_switch_(false), + is_converged_(false), + switch_count_(0) +{ + auto r = radius; + priority = armor.priority; + const Eigen::VectorXd & xyz = armor.xyz_in_world; + const Eigen::VectorXd & ypr = armor.ypr_in_world; + + // 旋转中心的坐标 + auto center_x = xyz[0] + r * std::cos(ypr[0]); + auto center_y = xyz[1] + r * std::sin(ypr[0]); + auto center_z = xyz[2]; + + // x vx y vy z vz a w r l h + // a: angle + // w: angular velocity + // l: r2 - r1 + // h: z2 - z1 + Eigen::VectorXd x0{{center_x, 0, center_y, 0, center_z, 0, ypr[0], 0, r, 0, 0}}; //初始化预测量 + Eigen::MatrixXd P0 = P0_dig.asDiagonal(); + + // 防止夹角求和出现异常值 + auto x_add = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a + b; + c[6] = tools::limit_rad(c[6]); + return c; + }; + + ekf_ = tools::ExtendedKalmanFilter(x0, P0, x_add); //初始化滤波器(预测量、预测量协方差) +} + +Target::Target(double x, double vyaw, double radius, double h) : armor_num_(4) +{ + Eigen::VectorXd x0{{x, 0, 0, 0, 0, 0, 0, vyaw, radius, 0, h}}; + Eigen::VectorXd P0_dig{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + Eigen::MatrixXd P0 = P0_dig.asDiagonal(); + + // 防止夹角求和出现异常值 + auto x_add = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a + b; + c[6] = tools::limit_rad(c[6]); + return c; + }; + + ekf_ = tools::ExtendedKalmanFilter(x0, P0, x_add); //初始化滤波器(预测量、预测量协方差) +} + +void Target::predict(std::chrono::steady_clock::time_point t) +{ + auto dt = tools::delta_time(t, t_); + predict(dt); + t_ = t; +} + +void Target::predict(double dt) +{ + // 状态转移矩阵 + // clang-format off + Eigen::MatrixXd F{ + {1, dt, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, dt, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 1, dt, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 1, dt, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + }; + // clang-format on + + // Piecewise White Noise Model + // https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python/blob/master/07-Kalman-Filter-Math.ipynb + double v1, v2; + if (name == ArmorName::outpost) { + v1 = 10; // 前哨站加速度方差 + v2 = 0.1; // 前哨站角加速度方差 + } else { + v1 = 100; // 加速度方差 + v2 = 400; // 角加速度方差 + } + auto a = dt * dt * dt * dt / 4; + auto b = dt * dt * dt / 2; + auto c = dt * dt; + // 预测过程噪声偏差的方差 + // clang-format off + Eigen::MatrixXd Q{ + {a * v1, b * v1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {b * v1, c * v1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, a * v1, b * v1, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, b * v1, c * v1, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, a * v1, b * v1, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, b * v1, c * v1, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, a * v2, b * v2, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, b * v2, c * v2, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + // clang-format on + + // 防止夹角求和出现异常值 + auto f = [&](const Eigen::VectorXd & x) -> Eigen::VectorXd { + Eigen::VectorXd x_prior = F * x; + x_prior[6] = tools::limit_rad(x_prior[6]); + return x_prior; + }; + + // 前哨站转速特判 + if (this->convergened() && this->name == ArmorName::outpost && std::abs(this->ekf_.x[7]) > 2) + this->ekf_.x[7] = this->ekf_.x[7] > 0 ? 2.51 : -2.51; + + ekf_.predict(F, Q, f); +} + +void Target::update(const Armor & armor) +{ + // 装甲板匹配 + int id; + auto min_angle_error = 1e10; + const std::vector & xyza_list = armor_xyza_list(); + + std::vector> xyza_i_list; + for (int i = 0; i < armor_num_; i++) { + xyza_i_list.push_back({xyza_list[i], i}); + } + + std::sort( + xyza_i_list.begin(), xyza_i_list.end(), + [](const std::pair & a, const std::pair & b) { + Eigen::Vector3d ypd1 = tools::xyz2ypd(a.first.head(3)); + Eigen::Vector3d ypd2 = tools::xyz2ypd(b.first.head(3)); + return ypd1[2] < ypd2[2]; + }); + + // 取前3个distance最小的装甲板 + for (int i = 0; i < 3; i++) { + const auto & xyza = xyza_i_list[i].first; + Eigen::Vector3d ypd = tools::xyz2ypd(xyza.head(3)); + auto angle_error = std::abs(tools::limit_rad(armor.ypr_in_world[0] - xyza[3])) + + std::abs(tools::limit_rad(armor.ypd_in_world[0] - ypd[0])); + + if (std::abs(angle_error) < std::abs(min_angle_error)) { + id = xyza_i_list[i].second; + min_angle_error = angle_error; + } + } + + if (id != 0) jumped = true; + + if (id != last_id) { + is_switch_ = true; + } else { + is_switch_ = false; + } + + if (is_switch_) switch_count_++; + + last_id = id; + update_count_++; + + update_ypda(armor, id); +} + +void Target::update_ypda(const Armor & armor, int id) +{ + //观测jacobi + Eigen::MatrixXd H = h_jacobian(ekf_.x, id); + // Eigen::VectorXd R_dig{{4e-3, 4e-3, 1, 9e-2}}; + auto center_yaw = std::atan2(armor.xyz_in_world[1], armor.xyz_in_world[0]); + auto delta_angle = tools::limit_rad(armor.ypr_in_world[0] - center_yaw); + Eigen::VectorXd R_dig{ + {4e-3, 4e-3, log(std::abs(delta_angle) + 1) + 1, + log(std::abs(armor.ypd_in_world[2]) + 1) / 200 + 9e-2}}; + + //测量过程噪声偏差的方差 + Eigen::MatrixXd R = R_dig.asDiagonal(); + + // 定义非线性转换函数h: x -> z + auto h = [&](const Eigen::VectorXd & x) -> Eigen::Vector4d { + Eigen::VectorXd xyz = h_armor_xyz(x, id); + Eigen::VectorXd ypd = tools::xyz2ypd(xyz); + auto angle = tools::limit_rad(x[6] + id * 2 * CV_PI / armor_num_); + return {ypd[0], ypd[1], ypd[2], angle}; + }; + + // 防止夹角求差出现异常值 + auto z_subtract = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a - b; + c[0] = tools::limit_rad(c[0]); + c[1] = tools::limit_rad(c[1]); + c[3] = tools::limit_rad(c[3]); + return c; + }; + + const Eigen::VectorXd & ypd = armor.ypd_in_world; + const Eigen::VectorXd & ypr = armor.ypr_in_world; + Eigen::VectorXd z{{ypd[0], ypd[1], ypd[2], ypr[0]}}; //获得观测量 + + ekf_.update(z, H, R, h, z_subtract); +} + +Eigen::VectorXd Target::ekf_x() const { return ekf_.x; } + +const tools::ExtendedKalmanFilter & Target::ekf() const { return ekf_; } + +std::vector Target::armor_xyza_list() const +{ + std::vector _armor_xyza_list; + + for (int i = 0; i < armor_num_; i++) { + auto angle = tools::limit_rad(ekf_.x[6] + i * 2 * CV_PI / armor_num_); + Eigen::Vector3d xyz = h_armor_xyz(ekf_.x, i); + _armor_xyza_list.push_back({xyz[0], xyz[1], xyz[2], angle}); + } + return _armor_xyza_list; +} + +bool Target::diverged() const +{ + auto r_ok = ekf_.x[8] > 0.05 && ekf_.x[8] < 0.5; + auto l_ok = ekf_.x[8] + ekf_.x[9] > 0.05 && ekf_.x[8] + ekf_.x[9] < 0.5; + + if (r_ok && l_ok) return false; + + tools::logger()->debug("[Target] r={:.3f}, l={:.3f}", ekf_.x[8], ekf_.x[9]); + return true; +} + +bool Target::convergened() +{ + if (this->name != ArmorName::outpost && update_count_ > 3 && !this->diverged()) { + is_converged_ = true; + } + + //前哨站特殊判断 + if (this->name == ArmorName::outpost && update_count_ > 10 && !this->diverged()) { + is_converged_ = true; + } + + return is_converged_; +} + +// 计算出装甲板中心的坐标(考虑长短轴) +Eigen::Vector3d Target::h_armor_xyz(const Eigen::VectorXd & x, int id) const +{ + auto angle = tools::limit_rad(x[6] + id * 2 * CV_PI / armor_num_); + auto use_l_h = (armor_num_ == 4) && (id == 1 || id == 3); + + auto r = (use_l_h) ? x[8] + x[9] : x[8]; + auto armor_x = x[0] - r * std::cos(angle); + auto armor_y = x[2] - r * std::sin(angle); + auto armor_z = (use_l_h) ? x[4] + x[10] : x[4]; + + return {armor_x, armor_y, armor_z}; +} + +Eigen::MatrixXd Target::h_jacobian(const Eigen::VectorXd & x, int id) const +{ + auto angle = tools::limit_rad(x[6] + id * 2 * CV_PI / armor_num_); + auto use_l_h = (armor_num_ == 4) && (id == 1 || id == 3); + + auto r = (use_l_h) ? x[8] + x[9] : x[8]; + auto dx_da = r * std::sin(angle); + auto dy_da = -r * std::cos(angle); + + auto dx_dr = -std::cos(angle); + auto dy_dr = -std::sin(angle); + auto dx_dl = (use_l_h) ? -std::cos(angle) : 0.0; + auto dy_dl = (use_l_h) ? -std::sin(angle) : 0.0; + + auto dz_dh = (use_l_h) ? 1.0 : 0.0; + + // clang-format off + Eigen::MatrixXd H_armor_xyza{ + {1, 0, 0, 0, 0, 0, dx_da, 0, dx_dr, dx_dl, 0}, + {0, 0, 1, 0, 0, 0, dy_da, 0, dy_dr, dy_dl, 0}, + {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, dz_dh}, + {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0} + }; + // clang-format on + + Eigen::VectorXd armor_xyz = h_armor_xyz(x, id); + Eigen::MatrixXd H_armor_ypd = tools::xyz2ypd_jacobian(armor_xyz); + // clang-format off + Eigen::MatrixXd H_armor_ypda{ + {H_armor_ypd(0, 0), H_armor_ypd(0, 1), H_armor_ypd(0, 2), 0}, + {H_armor_ypd(1, 0), H_armor_ypd(1, 1), H_armor_ypd(1, 2), 0}, + {H_armor_ypd(2, 0), H_armor_ypd(2, 1), H_armor_ypd(2, 2), 0}, + { 0, 0, 0, 1} + }; + // clang-format on + + return H_armor_ypda * H_armor_xyza; +} + +bool Target::checkinit() { return isinit; } + +} // namespace auto_aim diff --git a/tasks/auto_aim/target.hpp b/tasks/auto_aim/target.hpp new file mode 100644 index 0000000..c881d79 --- /dev/null +++ b/tasks/auto_aim/target.hpp @@ -0,0 +1,66 @@ +#ifndef AUTO_AIM__TARGET_HPP +#define AUTO_AIM__TARGET_HPP + +#include +#include +#include +#include +#include +#include + +#include "armor.hpp" +#include "tools/extended_kalman_filter.hpp" + +namespace auto_aim +{ + +class Target +{ +public: + ArmorName name; + ArmorType armor_type; + ArmorPriority priority; + bool jumped; + int last_id; // debug only + + Target() = default; + Target( + const Armor & armor, std::chrono::steady_clock::time_point t, double radius, int armor_num, + Eigen::VectorXd P0_dig); + Target(double x, double vyaw, double radius, double h); + + void predict(std::chrono::steady_clock::time_point t); + void predict(double dt); + void update(const Armor & armor); + + Eigen::VectorXd ekf_x() const; + const tools::ExtendedKalmanFilter & ekf() const; + std::vector armor_xyza_list() const; + + bool diverged() const; + + bool convergened(); + + bool isinit = false; + + bool checkinit(); + +private: + int armor_num_; + int switch_count_; + int update_count_; + + bool is_switch_, is_converged_; + + tools::ExtendedKalmanFilter ekf_; + std::chrono::steady_clock::time_point t_; + + void update_ypda(const Armor & armor, int id); // yaw pitch distance angle + + Eigen::Vector3d h_armor_xyz(const Eigen::VectorXd & x, int id) const; + Eigen::MatrixXd h_jacobian(const Eigen::VectorXd & x, int id) const; +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__TARGET_HPP \ No newline at end of file diff --git a/tasks/auto_aim/tracker.cpp b/tasks/auto_aim/tracker.cpp new file mode 100644 index 0000000..cffd8aa --- /dev/null +++ b/tasks/auto_aim/tracker.cpp @@ -0,0 +1,295 @@ +#include "tracker.hpp" + +#include + +#include + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +namespace auto_aim +{ +Tracker::Tracker(const std::string & config_path, Solver & solver) +: solver_{solver}, + detect_count_(0), + temp_lost_count_(0), + state_{"lost"}, + pre_state_{"lost"}, + last_timestamp_(std::chrono::steady_clock::now()), + omni_target_priority_{ArmorPriority::fifth} +{ + auto yaml = YAML::LoadFile(config_path); + enemy_color_ = (yaml["enemy_color"].as() == "red") ? Color::red : Color::blue; + min_detect_count_ = yaml["min_detect_count"].as(); + max_temp_lost_count_ = yaml["max_temp_lost_count"].as(); + outpost_max_temp_lost_count_ = yaml["outpost_max_temp_lost_count"].as(); + normal_temp_lost_count_ = max_temp_lost_count_; +} + +std::string Tracker::state() const { return state_; } + +std::list Tracker::track( + std::list & armors, std::chrono::steady_clock::time_point t, bool use_enemy_color) +{ + auto dt = tools::delta_time(t, last_timestamp_); + last_timestamp_ = t; + + // 时间间隔过长,说明可能发生了相机离线 + if (state_ != "lost" && dt > 0.1) { + tools::logger()->warn("[Tracker] Large dt: {:.3f}s", dt); + state_ = "lost"; + } + // 过滤掉非我方装甲板 + armors.remove_if([&](const auto_aim::Armor & a) { return a.color != enemy_color_; }); + + // 过滤前哨站顶部装甲板 + // armors.remove_if([this](const auto_aim::Armor & a) { + // return a.name == ArmorName::outpost && + // solver_.oupost_reprojection_error(a, 27.5 * CV_PI / 180.0) < + // solver_.oupost_reprojection_error(a, -15 * CV_PI / 180.0); + // }); + + // 优先选择靠近图像中心的装甲板 + armors.sort([](const Armor & a, const Armor & b) { + cv::Point2f img_center(1440 / 2, 1080 / 2); // TODO + auto distance_1 = cv::norm(a.center - img_center); + auto distance_2 = cv::norm(b.center - img_center); + return distance_1 < distance_2; + }); + + // 按优先级排序,优先级最高在首位(优先级越高数字越小,1的优先级最高) + armors.sort( + [](const auto_aim::Armor & a, const auto_aim::Armor & b) { return a.priority < b.priority; }); + + bool found; + if (state_ == "lost") { + found = set_target(armors, t); + } + + else { + found = update_target(armors, t); + } + + state_machine(found); + + // 发散检测 + if (state_ != "lost" && target_.diverged()) { + tools::logger()->debug("[Tracker] Target diverged!"); + state_ = "lost"; + return {}; + } + + // 收敛效果检测: + if ( + std::accumulate( + target_.ekf().recent_nis_failures.begin(), target_.ekf().recent_nis_failures.end(), 0) >= + (0.4 * target_.ekf().window_size)) { + tools::logger()->debug("[Target] Bad Converge Found!"); + state_ = "lost"; + return {}; + } + + if (state_ == "lost") return {}; + + std::list targets = {target_}; + return targets; +} + +std::tuple> Tracker::track( + const std::vector & detection_queue, std::list & armors, + std::chrono::steady_clock::time_point t, bool use_enemy_color) +{ + omniperception::DetectionResult switch_target{std::list(), t, 0, 0}; + omniperception::DetectionResult temp_target{std::list(), t, 0, 0}; + if (!detection_queue.empty()) { + temp_target = detection_queue.front(); + } + + auto dt = tools::delta_time(t, last_timestamp_); + last_timestamp_ = t; + + // 时间间隔过长,说明可能发生了相机离线 + if (state_ != "lost" && dt > 0.1) { + tools::logger()->warn("[Tracker] Large dt: {:.3f}s", dt); + state_ = "lost"; + } + + // 优先选择靠近图像中心的装甲板 + armors.sort([](const Armor & a, const Armor & b) { + cv::Point2f img_center(1440 / 2, 1080 / 2); // TODO + auto distance_1 = cv::norm(a.center - img_center); + auto distance_2 = cv::norm(b.center - img_center); + return distance_1 < distance_2; + }); + + // 按优先级排序,优先级最高在首位(优先级越高数字越小,1的优先级最高) + armors.sort([](const Armor & a, const Armor & b) { return a.priority < b.priority; }); + + bool found; + if (state_ == "lost") { + found = set_target(armors, t); + } + + // 此时主相机画面中出现了优先级更高的装甲板,切换目标 + else if (state_ == "tracking" && !armors.empty() && armors.front().priority < target_.priority) { + found = set_target(armors, t); + tools::logger()->debug("auto_aim switch target to {}", ARMOR_NAMES[armors.front().name]); + } + + // 此时全向感知相机画面中出现了优先级更高的装甲板,切换目标 + else if ( + state_ == "tracking" && !temp_target.armors.empty() && + temp_target.armors.front().priority < target_.priority && target_.convergened()) { + state_ = "switching"; + switch_target = omniperception::DetectionResult{ + temp_target.armors, t, temp_target.delta_yaw, temp_target.delta_pitch}; + omni_target_priority_ = temp_target.armors.front().priority; + found = false; + tools::logger()->debug("omniperception find higher priority target"); + } + + else if (state_ == "switching") { + found = !armors.empty() && armors.front().priority == omni_target_priority_; + } + + else if (state_ == "detecting" && pre_state_ == "switching") { + found = set_target(armors, t); + } + + else { + found = update_target(armors, t); + } + + pre_state_ = state_; + // 更新状态机 + state_machine(found); + + // 发散检测 + if (state_ != "lost" && target_.diverged()) { + tools::logger()->debug("[Tracker] Target diverged!"); + state_ = "lost"; + return {switch_target, {}}; // 返回switch_target和空的targets + } + + if (state_ == "lost") return {switch_target, {}}; // 返回switch_target和空的targets + + std::list targets = {target_}; + return {switch_target, targets}; +} + +void Tracker::state_machine(bool found) +{ + if (state_ == "lost") { + if (!found) return; + + state_ = "detecting"; + detect_count_ = 1; + } + + else if (state_ == "detecting") { + if (found) { + detect_count_++; + if (detect_count_ >= min_detect_count_) state_ = "tracking"; + } else { + detect_count_ = 0; + state_ = "lost"; + } + } + + else if (state_ == "tracking") { + if (found) return; + + temp_lost_count_ = 1; + state_ = "temp_lost"; + } + + else if (state_ == "switching") { + if (found) { + state_ = "detecting"; + } else { + temp_lost_count_++; + if (temp_lost_count_ > 200) state_ = "lost"; + } + } + + else if (state_ == "temp_lost") { + if (found) { + state_ = "tracking"; + } else { + temp_lost_count_++; + if (target_.name == ArmorName::outpost) + //前哨站的temp_lost_count需要设置的大一些 + max_temp_lost_count_ = outpost_max_temp_lost_count_; + else + max_temp_lost_count_ = normal_temp_lost_count_; + + if (temp_lost_count_ > max_temp_lost_count_) state_ = "lost"; + } + } +} + +bool Tracker::set_target(std::list & armors, std::chrono::steady_clock::time_point t) +{ + if (armors.empty()) return false; + + auto & armor = armors.front(); + solver_.solve(armor); + + // 根据兵种优化初始化参数 + auto is_balance = (armor.type == ArmorType::big) && + (armor.name == ArmorName::three || armor.name == ArmorName::four || + armor.name == ArmorName::five); + + if (is_balance) { + Eigen::VectorXd P0_dig{{1, 64, 1, 64, 1, 64, 0.4, 100, 1, 1, 1}}; + target_ = Target(armor, t, 0.2, 2, P0_dig); + } + + else if (armor.name == ArmorName::outpost) { + Eigen::VectorXd P0_dig{{1, 64, 1, 64, 1, 81, 0.4, 100, 1e-4, 0, 0}}; + target_ = Target(armor, t, 0.2765, 3, P0_dig); + } + + else if (armor.name == ArmorName::base) { + Eigen::VectorXd P0_dig{{1, 64, 1, 64, 1, 64, 0.4, 100, 1e-4, 0, 0}}; + target_ = Target(armor, t, 0.3205, 3, P0_dig); + } + + else { + Eigen::VectorXd P0_dig{{1, 64, 1, 64, 1, 64, 0.4, 100, 1, 1, 1}}; + target_ = Target(armor, t, 0.2, 4, P0_dig); + } + + return true; +} + +bool Tracker::update_target(std::list & armors, std::chrono::steady_clock::time_point t) +{ + target_.predict(t); + + int found_count = 0; + double min_x = 1e10; // 画面最左侧 + for (const auto & armor : armors) { + if (armor.name != target_.name || armor.type != target_.armor_type) continue; + found_count++; + min_x = armor.center.x < min_x ? armor.center.x : min_x; + } + + if (found_count == 0) return false; + + for (auto & armor : armors) { + if ( + armor.name != target_.name || armor.type != target_.armor_type + // || armor.center.x != min_x + ) + continue; + + solver_.solve(armor); + + target_.update(armor); + } + + return true; +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/tracker.hpp b/tasks/auto_aim/tracker.hpp new file mode 100644 index 0000000..2c47de8 --- /dev/null +++ b/tasks/auto_aim/tracker.hpp @@ -0,0 +1,55 @@ +#ifndef AUTO_AIM__TRACKER_HPP +#define AUTO_AIM__TRACKER_HPP + +#include +#include +#include +#include + +#include "armor.hpp" +#include "solver.hpp" +#include "target.hpp" +#include "tasks/omniperception/perceptron.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace auto_aim +{ +class Tracker +{ +public: + Tracker(const std::string & config_path, Solver & solver); + + std::string state() const; + + std::list track( + std::list & armors, std::chrono::steady_clock::time_point t, + bool use_enemy_color = true); + + std::tuple> track( + const std::vector & detection_queue, std::list & armors, + std::chrono::steady_clock::time_point t, bool use_enemy_color = true); + +private: + Solver & solver_; + Color enemy_color_; + int min_detect_count_; + int max_temp_lost_count_; + int detect_count_; + int temp_lost_count_; + int outpost_max_temp_lost_count_; + int normal_temp_lost_count_; + std::string state_, pre_state_; + Target target_; + std::chrono::steady_clock::time_point last_timestamp_; + ArmorPriority omni_target_priority_; + + void state_machine(bool found); + + bool set_target(std::list & armors, std::chrono::steady_clock::time_point t); + + bool update_target(std::list & armors, std::chrono::steady_clock::time_point t); +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__TRACKER_HPP \ No newline at end of file diff --git a/tasks/auto_aim/voter.cpp b/tasks/auto_aim/voter.cpp new file mode 100644 index 0000000..581a680 --- /dev/null +++ b/tasks/auto_aim/voter.cpp @@ -0,0 +1,21 @@ +#include "voter.hpp" + +namespace auto_aim +{ +Voter::Voter() : count_(COLORS.size() * ARMOR_NAMES.size() * ARMOR_TYPES.size(), 0) {} + +void Voter::vote(const Color color, const ArmorName name, const ArmorType type) +{ + count_[index(color, name, type)] += 1; +} + +std::size_t Voter::count(const Color color, const ArmorName name, const ArmorType type) +{ + return count_[index(color, name, type)]; +} + +std::size_t Voter::index(const Color color, const ArmorName name, const ArmorType type) const +{ + return color * (ARMOR_NAMES.size() * ARMOR_TYPES.size()) + name * ARMOR_TYPES.size() + type; +} +} \ No newline at end of file diff --git a/tasks/auto_aim/voter.hpp b/tasks/auto_aim/voter.hpp new file mode 100644 index 0000000..ab26bf8 --- /dev/null +++ b/tasks/auto_aim/voter.hpp @@ -0,0 +1,24 @@ +#ifndef AUTO_AIM__VOTER_HPP +#define AUTO_AIM__VOTER_HPP + +#include + +#include "armor.hpp" + +namespace auto_aim +{ + +class Voter +{ +public: + Voter(); + void vote(const Color color, const ArmorName name, const ArmorType type); + std::size_t count(const Color color, const ArmorName name, const ArmorType type); + +private: + std::vector count_; + std::size_t index(const Color color, const ArmorName name, const ArmorType type) const; +}; +} // namespace auto_aim + +#endif \ No newline at end of file diff --git a/tasks/auto_aim/yolo.cpp b/tasks/auto_aim/yolo.cpp new file mode 100644 index 0000000..cfe1c2e --- /dev/null +++ b/tasks/auto_aim/yolo.cpp @@ -0,0 +1,44 @@ +#include "yolo.hpp" + +#include + +#include "yolos/yolo11.hpp" +#include "yolos/yolov5.hpp" +#include "yolos/yolov8.hpp" + +namespace auto_aim +{ +YOLO::YOLO(const std::string & config_path, bool debug) +{ + auto yaml = YAML::LoadFile(config_path); + auto yolo_name = yaml["yolo_name"].as(); + + if (yolo_name == "yolov8") { + yolo_ = std::make_unique(config_path, debug); + } + + else if (yolo_name == "yolo11") { + yolo_ = std::make_unique(config_path, debug); + } + + else if (yolo_name == "yolov5") { + yolo_ = std::make_unique(config_path, debug); + } + + else { + throw std::runtime_error("Unknown yolo name: " + yolo_name + "!"); + } +} + +std::list YOLO::detect(const cv::Mat & img, int frame_count) +{ + return yolo_->detect(img, frame_count); +} + +std::list YOLO::postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ + return yolo_->postprocess(scale, output, bgr_img, frame_count); +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/yolo.hpp b/tasks/auto_aim/yolo.hpp new file mode 100644 index 0000000..888e4a1 --- /dev/null +++ b/tasks/auto_aim/yolo.hpp @@ -0,0 +1,35 @@ +#ifndef AUTO_AIM__YOLO_HPP +#define AUTO_AIM__YOLO_HPP + +#include + +#include "armor.hpp" + +namespace auto_aim +{ +class YOLOBase +{ +public: + virtual std::list detect(const cv::Mat & img, int frame_count) = 0; + + virtual std::list postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) = 0; +}; + +class YOLO +{ +public: + YOLO(const std::string & config_path, bool debug = true); + + std::list detect(const cv::Mat & img, int frame_count = -1); + + std::list postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count); + +private: + std::unique_ptr yolo_; +}; + +} // namespace auto_aim + +#endif // AUTO_AIM__YOLO_HPP \ No newline at end of file diff --git a/tasks/auto_aim/yolos/yolo11.cpp b/tasks/auto_aim/yolos/yolo11.cpp new file mode 100644 index 0000000..a12ba76 --- /dev/null +++ b/tasks/auto_aim/yolos/yolo11.cpp @@ -0,0 +1,271 @@ +#include "yolo11.hpp" + +#include +#include + +#include + +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" + +namespace auto_aim +{ +YOLO11::YOLO11(const std::string & config_path, bool debug) +: debug_(debug), detector_(config_path, false) +{ + auto yaml = YAML::LoadFile(config_path); + + model_path_ = yaml["yolo11_model_path"].as(); + device_ = yaml["device"].as(); + binary_threshold_ = yaml["threshold"].as(); + min_confidence_ = yaml["min_confidence"].as(); + int x = 0, y = 0, width = 0, height = 0; + x = yaml["roi"]["x"].as(); + y = yaml["roi"]["y"].as(); + width = yaml["roi"]["width"].as(); + height = yaml["roi"]["height"].as(); + use_roi_ = yaml["use_roi"].as(); + roi_ = cv::Rect(x, y, width, height); + offset_ = cv::Point2f(x, y); + + save_path_ = "imgs"; + std::filesystem::create_directory(save_path_); + auto model = core_.read_model(model_path_); + ov::preprocess::PrePostProcessor ppp(model); + auto & input = ppp.input(); + + input.tensor() + .set_element_type(ov::element::u8) + .set_shape({1, 640, 640, 3}) + .set_layout("NHWC") + .set_color_format(ov::preprocess::ColorFormat::BGR); + + input.model().set_layout("NCHW"); + + input.preprocess() + .convert_element_type(ov::element::f32) + .convert_color(ov::preprocess::ColorFormat::RGB) + .scale(255.0); + + // TODO: ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY) + model = ppp.build(); + compiled_model_ = core_.compile_model( + model, device_, ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY)); +} + +std::list YOLO11::detect(const cv::Mat & raw_img, int frame_count) +{ + if (raw_img.empty()) { + tools::logger()->warn("Empty img!, camera drop!"); + return std::list(); + } + + cv::Mat bgr_img; + tmp_img_ = raw_img; + if (use_roi_) { + if (roi_.width == -1) { // -1 表示该维度不裁切 + roi_.width = raw_img.cols; + } + if (roi_.height == -1) { // -1 表示该维度不裁切 + roi_.height = raw_img.rows; + } + bgr_img = raw_img(roi_); + } else { + bgr_img = raw_img; + } + + auto x_scale = static_cast(640) / bgr_img.rows; + auto y_scale = static_cast(640) / bgr_img.cols; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(bgr_img.rows * scale); + auto w = static_cast(bgr_img.cols * scale); + + // preproces + auto input = cv::Mat(640, 640, CV_8UC3, cv::Scalar(0, 0, 0)); + auto roi = cv::Rect(0, 0, w, h); + cv::resize(bgr_img, input(roi), {w, h}); + ov::Tensor input_tensor(ov::element::u8, {1, 640, 640, 3}, input.data); + + /// infer + auto infer_request = compiled_model_.create_infer_request(); + infer_request.set_input_tensor(input_tensor); + infer_request.infer(); + + // postprocess + auto output_tensor = infer_request.get_output_tensor(); + auto output_shape = output_tensor.get_shape(); + cv::Mat output(output_shape[1], output_shape[2], CV_32F, output_tensor.data()); + + return parse(scale, output, raw_img, frame_count); +} + +std::list YOLO11::parse( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ // for each row: xywh + classess + cv::transpose(output, output); + + std::vector ids; + std::vector confidences; + std::vector boxes; + std::vector> armors_key_points; + for (int r = 0; r < output.rows; r++) { + auto xywh = output.row(r).colRange(0, 4); + auto scores = output.row(r).colRange(4, 4 + class_num_); + auto one_key_points = output.row(r).colRange(4 + class_num_, 50); + + std::vector armor_key_points; + + double score; + cv::Point max_point; + cv::minMaxLoc(scores, nullptr, &score, nullptr, &max_point); + + if (score < score_threshold_) continue; + + auto x = xywh.at(0); + auto y = xywh.at(1); + auto w = xywh.at(2); + auto h = xywh.at(3); + auto left = static_cast((x - 0.5 * w) / scale); + auto top = static_cast((y - 0.5 * h) / scale); + auto width = static_cast(w / scale); + auto height = static_cast(h / scale); + + for (int i = 0; i < 4; i++) { + float x = one_key_points.at(0, i * 2 + 0) / scale; + float y = one_key_points.at(0, i * 2 + 1) / scale; + cv::Point2f kp = {x, y}; + armor_key_points.push_back(kp); + } + ids.emplace_back(max_point.x); + confidences.emplace_back(score); + boxes.emplace_back(left, top, width, height); + armors_key_points.emplace_back(armor_key_points); + } + + std::vector indices; + cv::dnn::NMSBoxes(boxes, confidences, score_threshold_, nms_threshold_, indices); + + std::list armors; + for (const auto & i : indices) { + sort_keypoints(armors_key_points[i]); + if (use_roi_) { + armors.emplace_back(ids[i], confidences[i], boxes[i], armors_key_points[i], offset_); + } else { + armors.emplace_back(ids[i], confidences[i], boxes[i], armors_key_points[i]); + } + } + + for (auto it = armors.begin(); it != armors.end();) { + if (!check_name(*it)) { + it = armors.erase(it); + continue; + } + + if (!check_type(*it)) { + it = armors.erase(it); + continue; + } + + it->center_norm = get_center_norm(bgr_img, it->center); + ++it; + } + + if (debug_) draw_detections(bgr_img, armors, frame_count); + + return armors; +} + +bool YOLO11::check_name(const Armor & armor) const +{ + auto name_ok = armor.name != ArmorName::not_armor; + auto confidence_ok = armor.confidence > min_confidence_; + + // 保存不确定的图案,用于神经网络的迭代 + // if (name_ok && !confidence_ok) save(armor); + + return name_ok && confidence_ok; +} + +bool YOLO11::check_type(const Armor & armor) const +{ + auto name_ok = (armor.type == ArmorType::small) + ? (armor.name != ArmorName::one && armor.name != ArmorName::base) + : (armor.name != ArmorName::two && armor.name != ArmorName::sentry && + armor.name != ArmorName::outpost); + + // 保存异常的图案,用于神经网络的迭代 + // if (!name_ok) save(armor); + + return name_ok; +} + +cv::Point2f YOLO11::get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const +{ + auto h = bgr_img.rows; + auto w = bgr_img.cols; + return {center.x / w, center.y / h}; +} + +void YOLO11::sort_keypoints(std::vector & keypoints) +{ + if (keypoints.size() != 4) { + std::cout << "beyond 4!!" << std::endl; + return; + } + + std::sort(keypoints.begin(), keypoints.end(), [](const cv::Point2f & a, const cv::Point2f & b) { + return a.y < b.y; + }); + + std::vector top_points = {keypoints[0], keypoints[1]}; + std::vector bottom_points = {keypoints[2], keypoints[3]}; + + std::sort(top_points.begin(), top_points.end(), [](const cv::Point2f & a, const cv::Point2f & b) { + return a.x < b.x; + }); + + std::sort( + bottom_points.begin(), bottom_points.end(), + [](const cv::Point2f & a, const cv::Point2f & b) { return a.x < b.x; }); + + keypoints[0] = top_points[0]; // top-left + keypoints[1] = top_points[1]; // top-right + keypoints[2] = bottom_points[1]; // bottom-right + keypoints[3] = bottom_points[0]; // bottom-left +} + +void YOLO11::draw_detections( + const cv::Mat & img, const std::list & armors, int frame_count) const +{ + auto detection = img.clone(); + tools::draw_text(detection, fmt::format("[{}]", frame_count), {10, 30}, {255, 255, 255}); + for (const auto & armor : armors) { + auto info = fmt::format( + "{:.2f} {} {} {}", armor.confidence, COLORS[armor.color], ARMOR_NAMES[armor.name], + ARMOR_TYPES[armor.type]); + tools::draw_points(detection, armor.points, {0, 255, 0}); + tools::draw_text(detection, info, armor.center, {0, 255, 0}); + } + + if (use_roi_) { + cv::Scalar green(0, 255, 0); + cv::rectangle(detection, roi_, green, 2); + } + cv::resize(detection, detection, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("detection", detection); +} + +void YOLO11::save(const Armor & armor) const +{ + auto file_name = fmt::format("{:%Y-%m-%d_%H-%M-%S}", std::chrono::system_clock::now()); + auto img_path = fmt::format("{}/{}_{}.jpg", save_path_, armor.name, file_name); + cv::imwrite(img_path, tmp_img_); +} + +std::list YOLO11::postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ + return parse(scale, output, bgr_img, frame_count); +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/yolos/yolo11.hpp b/tasks/auto_aim/yolos/yolo11.hpp new file mode 100644 index 0000000..f66d6ca --- /dev/null +++ b/tasks/auto_aim/yolos/yolo11.hpp @@ -0,0 +1,59 @@ +#ifndef AUTO_AIM__YOLO11_HPP +#define AUTO_AIM__YOLO11_HPP + +#include +#include +#include +#include +#include + +#include "tasks/auto_aim/armor.hpp" +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/yolo.hpp" + +namespace auto_aim +{ +class YOLO11 : public YOLOBase +{ +public: + YOLO11(const std::string & config_path, bool debug); + + std::list detect(const cv::Mat & bgr_img, int frame_count) override; + + std::list postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) override; + +private: + std::string device_, model_path_; + std::string save_path_, debug_path_; + bool debug_, use_roi_; + + const int class_num_ = 38; + const float nms_threshold_ = 0.3; + const float score_threshold_ = 0.7; + double min_confidence_, binary_threshold_; + + ov::Core core_; + ov::CompiledModel compiled_model_; + + cv::Rect roi_; + cv::Point2f offset_; + cv::Mat tmp_img_; + + Detector detector_; + + bool check_name(const Armor & armor) const; + bool check_type(const Armor & armor) const; + + cv::Point2f get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const; + + std::list parse(double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count); + + void save(const Armor & armor) const; + void draw_detections(const cv::Mat & img, const std::list & armors, int frame_count) const; + void sort_keypoints(std::vector & keypoints); +}; + +} // namespace auto_aim + +#endif //AUTO_AIM__YOLO11_HPP \ No newline at end of file diff --git a/tasks/auto_aim/yolos/yolov5.cpp b/tasks/auto_aim/yolos/yolov5.cpp new file mode 100644 index 0000000..eb01717 --- /dev/null +++ b/tasks/auto_aim/yolos/yolov5.cpp @@ -0,0 +1,268 @@ +#include "yolov5.hpp" + +#include +#include + +#include + +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" + +namespace auto_aim +{ +YOLOV5::YOLOV5(const std::string & config_path, bool debug) +: debug_(debug), detector_(config_path, false) +{ + auto yaml = YAML::LoadFile(config_path); + + model_path_ = yaml["yolov5_model_path"].as(); + device_ = yaml["device"].as(); + binary_threshold_ = yaml["threshold"].as(); + min_confidence_ = yaml["min_confidence"].as(); + int x = 0, y = 0, width = 0, height = 0; + x = yaml["roi"]["x"].as(); + y = yaml["roi"]["y"].as(); + width = yaml["roi"]["width"].as(); + height = yaml["roi"]["height"].as(); + use_roi_ = yaml["use_roi"].as(); + use_traditional_ = yaml["use_traditional"].as(); + roi_ = cv::Rect(x, y, width, height); + offset_ = cv::Point2f(x, y); + + save_path_ = "imgs"; + std::filesystem::create_directory(save_path_); + auto model = core_.read_model(model_path_); + ov::preprocess::PrePostProcessor ppp(model); + auto & input = ppp.input(); + + input.tensor() + .set_element_type(ov::element::u8) + .set_shape({1, 640, 640, 3}) + .set_layout("NHWC") + .set_color_format(ov::preprocess::ColorFormat::BGR); + + input.model().set_layout("NCHW"); + + input.preprocess() + .convert_element_type(ov::element::f32) + .convert_color(ov::preprocess::ColorFormat::RGB) + .scale(255.0); + + // TODO: ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY) + model = ppp.build(); + compiled_model_ = core_.compile_model( + model, device_, ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY)); +} + +std::list YOLOV5::detect(const cv::Mat & raw_img, int frame_count) +{ + if (raw_img.empty()) { + tools::logger()->warn("Empty img!, camera drop!"); + return std::list(); + } + + cv::Mat bgr_img; + if (use_roi_) { + if (roi_.width == -1) { // -1 表示该维度不裁切 + roi_.width = raw_img.cols; + } + if (roi_.height == -1) { // -1 表示该维度不裁切 + roi_.height = raw_img.rows; + } + bgr_img = raw_img(roi_); + } else { + bgr_img = raw_img; + } + + auto x_scale = static_cast(640) / bgr_img.rows; + auto y_scale = static_cast(640) / bgr_img.cols; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(bgr_img.rows * scale); + auto w = static_cast(bgr_img.cols * scale); + + // preproces + auto input = cv::Mat(640, 640, CV_8UC3, cv::Scalar(0, 0, 0)); + auto roi = cv::Rect(0, 0, w, h); + cv::resize(bgr_img, input(roi), {w, h}); + ov::Tensor input_tensor(ov::element::u8, {1, 640, 640, 3}, input.data); + + // infer + auto infer_request = compiled_model_.create_infer_request(); + infer_request.set_input_tensor(input_tensor); + infer_request.infer(); + + // postprocess + auto output_tensor = infer_request.get_output_tensor(); + auto output_shape = output_tensor.get_shape(); + cv::Mat output(output_shape[1], output_shape[2], CV_32F, output_tensor.data()); + + return parse(scale, output, raw_img, frame_count); +} + +std::list YOLOV5::parse( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ + // for each row: xywh + classess + std::vector color_ids, num_ids; + std::vector confidences; + std::vector boxes; + std::vector> armors_key_points; + for (int r = 0; r < output.rows; r++) { + double score = output.at(r, 8); + score = sigmoid(score); + + if (score < score_threshold_) continue; + + std::vector armor_key_points; + + //颜色和类别独热向量 + cv::Mat color_scores = output.row(r).colRange(9, 13); //color + cv::Mat classes_scores = output.row(r).colRange(13, 22); //num + cv::Point class_id, color_id; + int _class_id, _color_id; + double score_color, score_num; + cv::minMaxLoc(classes_scores, NULL, &score_num, NULL, &class_id); + cv::minMaxLoc(color_scores, NULL, &score_color, NULL, &color_id); + _class_id = class_id.x; + _color_id = color_id.x; + + armor_key_points.push_back( + cv::Point2f(output.at(r, 0) / scale, output.at(r, 1) / scale)); + armor_key_points.push_back( + cv::Point2f(output.at(r, 6) / scale, output.at(r, 7) / scale)); + armor_key_points.push_back( + cv::Point2f(output.at(r, 4) / scale, output.at(r, 5) / scale)); + armor_key_points.push_back( + cv::Point2f(output.at(r, 2) / scale, output.at(r, 3) / scale)); + + float min_x = armor_key_points[0].x; + float max_x = armor_key_points[0].x; + float min_y = armor_key_points[0].y; + float max_y = armor_key_points[0].y; + + for (int i = 1; i < armor_key_points.size(); i++) { + if (armor_key_points[i].x < min_x) min_x = armor_key_points[i].x; + if (armor_key_points[i].x > max_x) max_x = armor_key_points[i].x; + if (armor_key_points[i].y < min_y) min_y = armor_key_points[i].y; + if (armor_key_points[i].y > max_y) max_y = armor_key_points[i].y; + } + + cv::Rect rect(min_x, min_y, max_x - min_x, max_y - min_y); + + color_ids.emplace_back(_color_id); + num_ids.emplace_back(_class_id); + boxes.emplace_back(rect); + confidences.emplace_back(score); + armors_key_points.emplace_back(armor_key_points); + } + + std::vector indices; + cv::dnn::NMSBoxes(boxes, confidences, score_threshold_, nms_threshold_, indices); + + std::list armors; + for (const auto & i : indices) { + if (use_roi_) { + armors.emplace_back( + color_ids[i], num_ids[i], confidences[i], boxes[i], armors_key_points[i], offset_); + } else { + armors.emplace_back(color_ids[i], num_ids[i], confidences[i], boxes[i], armors_key_points[i]); + } + } + + tmp_img_ = bgr_img; + for (auto it = armors.begin(); it != armors.end();) { + if (!check_name(*it)) { + it = armors.erase(it); + continue; + } + + if (!check_type(*it)) { + it = armors.erase(it); + continue; + } + // 使用传统方法二次矫正角点 + if (use_traditional_) detector_.detect(*it, bgr_img); + + it->center_norm = get_center_norm(bgr_img, it->center); + ++it; + } + + if (debug_) draw_detections(bgr_img, armors, frame_count); + + return armors; +} + +bool YOLOV5::check_name(const Armor & armor) const +{ + auto name_ok = armor.name != ArmorName::not_armor; + auto confidence_ok = armor.confidence > min_confidence_; + + // 保存不确定的图案,用于神经网络的迭代 + // if (name_ok && !confidence_ok) save(armor); + + return name_ok && confidence_ok; +} + +bool YOLOV5::check_type(const Armor & armor) const +{ + auto name_ok = (armor.type == ArmorType::small) + ? (armor.name != ArmorName::one && armor.name != ArmorName::base) + : (armor.name != ArmorName::two && armor.name != ArmorName::sentry && + armor.name != ArmorName::outpost); + + // 保存异常的图案,用于神经网络的迭代 + // if (!name_ok) save(armor); + + return name_ok; +} + +cv::Point2f YOLOV5::get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const +{ + auto h = bgr_img.rows; + auto w = bgr_img.cols; + return {center.x / w, center.y / h}; +} + +void YOLOV5::draw_detections( + const cv::Mat & img, const std::list & armors, int frame_count) const +{ + auto detection = img.clone(); + tools::draw_text(detection, fmt::format("[{}]", frame_count), {10, 30}, {255, 255, 255}); + for (const auto & armor : armors) { + auto info = fmt::format( + "{:.2f} {} {} {}", armor.confidence, COLORS[armor.color], ARMOR_NAMES[armor.name], + ARMOR_TYPES[armor.type]); + tools::draw_points(detection, armor.points, {0, 255, 0}); + tools::draw_text(detection, info, armor.center, {0, 255, 0}); + } + + if (use_roi_) { + cv::Scalar green(0, 255, 0); + cv::rectangle(detection, roi_, green, 2); + } + cv::resize(detection, detection, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("detection", detection); +} + +void YOLOV5::save(const Armor & armor) const +{ + auto file_name = fmt::format("{:%Y-%m-%d_%H-%M-%S}", std::chrono::system_clock::now()); + auto img_path = fmt::format("{}/{}_{}.jpg", save_path_, armor.name, file_name); + cv::imwrite(img_path, tmp_img_); +} + +double YOLOV5::sigmoid(double x) +{ + if (x > 0) + return 1.0 / (1.0 + exp(-x)); + else + return exp(x) / (1.0 + exp(x)); +} + +std::list YOLOV5::postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ + return parse(scale, output, bgr_img, frame_count); +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/yolos/yolov5.hpp b/tasks/auto_aim/yolos/yolov5.hpp new file mode 100644 index 0000000..fcd0ea2 --- /dev/null +++ b/tasks/auto_aim/yolos/yolov5.hpp @@ -0,0 +1,60 @@ +#ifndef AUTO_AIM__YOLOV5_HPP +#define AUTO_AIM__YOLOV5_HPP + +#include +#include +#include +#include +#include + +#include "tasks/auto_aim/armor.hpp" +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/yolo.hpp" + +namespace auto_aim +{ +class YOLOV5 : public YOLOBase +{ +public: + YOLOV5(const std::string & config_path, bool debug); + + std::list detect(const cv::Mat & bgr_img, int frame_count) override; + + std::list postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) override; + +private: + std::string device_, model_path_; + std::string save_path_, debug_path_; + bool debug_, use_roi_, use_traditional_; + + const int class_num_ = 13; + const float nms_threshold_ = 0.3; + const float score_threshold_ = 0.7; + double min_confidence_, binary_threshold_; + + ov::Core core_; + ov::CompiledModel compiled_model_; + + cv::Rect roi_; + cv::Point2f offset_; + cv::Mat tmp_img_; + + Detector detector_; + friend class MultiThreadDetector; + + bool check_name(const Armor & armor) const; + bool check_type(const Armor & armor) const; + + cv::Point2f get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const; + + std::list parse(double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count); + + void save(const Armor & armor) const; + void draw_detections(const cv::Mat & img, const std::list & armors, int frame_count) const; + double sigmoid(double x); +}; + +} // namespace auto_aim + +#endif //AUTO_AIM__YOLOV5_HPP \ No newline at end of file diff --git a/tasks/auto_aim/yolos/yolov8.cpp b/tasks/auto_aim/yolos/yolov8.cpp new file mode 100644 index 0000000..f686d3c --- /dev/null +++ b/tasks/auto_aim/yolos/yolov8.cpp @@ -0,0 +1,330 @@ +#include "yolov8.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "tasks/auto_aim/classifier.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" + +namespace auto_aim +{ +YOLOV8::YOLOV8(const std::string & config_path, bool debug) +: classifier_(config_path), detector_(config_path), debug_(debug) +{ + auto yaml = YAML::LoadFile(config_path); + + model_path_ = yaml["yolov8_model_path"].as(); + device_ = yaml["device"].as(); + binary_threshold_ = yaml["threshold"].as(); + min_confidence_ = yaml["min_confidence"].as(); + int x = 0, y = 0, width = 0, height = 0; + x = yaml["roi"]["x"].as(); + y = yaml["roi"]["y"].as(); + width = yaml["roi"]["width"].as(); + height = yaml["roi"]["height"].as(); + use_roi_ = yaml["use_roi"].as(); + roi_ = cv::Rect(x, y, width, height); + offset_ = cv::Point2f(x, y); + + save_path_ = "imgs"; + std::filesystem::create_directory(save_path_); + + auto model = core_.read_model(model_path_); + ov::preprocess::PrePostProcessor ppp(model); + auto & input = ppp.input(); + + input.tensor() + .set_element_type(ov::element::u8) + .set_shape({1, 416, 416, 3}) + .set_layout("NHWC") + .set_color_format(ov::preprocess::ColorFormat::BGR); + + input.model().set_layout("NCHW"); + + input.preprocess() + .convert_element_type(ov::element::f32) + .convert_color(ov::preprocess::ColorFormat::RGB) + .scale(255.0); + + // TODO: ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY) + model = ppp.build(); + compiled_model_ = core_.compile_model( + model, device_, ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY)); +} + +std::list YOLOV8::detect(const cv::Mat & raw_img, int frame_count) +{ + if (raw_img.empty()) { + tools::logger()->warn("Empty img!, camera drop!"); + return std::list(); + } + + cv::Mat bgr_img; + if (use_roi_) { + if (roi_.width == -1) { // -1 表示该维度不裁切 + roi_.width = raw_img.cols; + } + if (roi_.height == -1) { // -1 表示该维度不裁切 + roi_.height = raw_img.rows; + } + bgr_img = raw_img(roi_); + } else { + bgr_img = raw_img; + } + + auto x_scale = static_cast(416) / bgr_img.rows; + auto y_scale = static_cast(416) / bgr_img.cols; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(bgr_img.rows * scale); + auto w = static_cast(bgr_img.cols * scale); + + // preproces + auto input = cv::Mat(416, 416, CV_8UC3, cv::Scalar(0, 0, 0)); + auto roi = cv::Rect(0, 0, w, h); + cv::resize(bgr_img, input(roi), {w, h}); + ov::Tensor input_tensor(ov::element::u8, {1, 416, 416, 3}, input.data); + + /// infer + auto infer_request = compiled_model_.create_infer_request(); + infer_request.set_input_tensor(input_tensor); + infer_request.infer(); + + // postprocess + auto output_tensor = infer_request.get_output_tensor(); + auto output_shape = output_tensor.get_shape(); + cv::Mat output(output_shape[1], output_shape[2], CV_32F, output_tensor.data()); + + return parse(scale, output, raw_img, frame_count); +} + +std::list YOLOV8::parse( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ + // for each row: xywh + classess + cv::transpose(output, output); + + std::vector ids; + std::vector confidences; + std::vector boxes; + std::vector> armors_key_points; + for (int r = 0; r < output.rows; r++) { + auto xywh = output.row(r).colRange(0, 4); + auto scores = output.row(r).colRange(4, 4 + class_num_); + auto one_key_points = output.row(r).colRange(4 + class_num_, 14); + + std::vector armor_key_points; + + double score; + cv::Point max_point; + cv::minMaxLoc(scores, nullptr, &score, nullptr, &max_point); + + if (score < score_threshold_) continue; + + auto x = xywh.at(0); + auto y = xywh.at(1); + auto w = xywh.at(2); + auto h = xywh.at(3); + auto left = static_cast((x - 0.5 * w) / scale); + auto top = static_cast((y - 0.5 * h) / scale); + auto width = static_cast(w / scale); + auto height = static_cast(h / scale); + + for (int i = 0; i < 4; i++) { + float x = one_key_points.at(0, i * 2 + 0) / scale; + float y = one_key_points.at(0, i * 2 + 1) / scale; + cv::Point2f kp = {x, y}; + armor_key_points.push_back(kp); + } + ids.emplace_back(max_point.x); + confidences.emplace_back(score); + boxes.emplace_back(left, top, width, height); + armors_key_points.emplace_back(armor_key_points); + } + + std::vector indices; + cv::dnn::NMSBoxes(boxes, confidences, score_threshold_, nms_threshold_, indices); + + std::list armors; + for (const auto & i : indices) { + sort_keypoints(armors_key_points[i]); + if (use_roi_) { + armors.emplace_back(ids[i], confidences[i], boxes[i], armors_key_points[i], offset_); + } else { + armors.emplace_back(ids[i], confidences[i], boxes[i], armors_key_points[i]); + } + } + + for (auto it = armors.begin(); it != armors.end();) { + it->pattern = get_pattern(bgr_img, *it); + classifier_.classify(*it); + + if (!check_name(*it)) { + it = armors.erase(it); + continue; + } + + it->type = get_type(*it); + if (!check_type(*it)) { + it = armors.erase(it); + continue; + } + + it->center_norm = get_center_norm(bgr_img, it->center); + ++it; + } + + if (debug_) draw_detections(bgr_img, armors, frame_count); + + return armors; +} + +bool YOLOV8::check_name(const Armor & armor) const +{ + auto name_ok = armor.name != ArmorName::not_armor; + auto confidence_ok = armor.confidence > min_confidence_; + + // 保存不确定的图案,用于分类器的迭代 + // if (name_ok && !confidence_ok) save(armor); + + return name_ok && confidence_ok; +} + +bool YOLOV8::check_type(const Armor & armor) const +{ + auto name_ok = (armor.type == ArmorType::small) + ? (armor.name != ArmorName::one && armor.name != ArmorName::base) + : (armor.name != ArmorName::two && armor.name != ArmorName::sentry && + armor.name != ArmorName::outpost); + + // 保存异常的图案,用于分类器的迭代 + // if (!name_ok) save(armor); + + return name_ok; +} + +ArmorType YOLOV8::get_type(const Armor & armor) +{ + // 英雄、基地只能是大装甲板 + if (armor.name == ArmorName::one || armor.name == ArmorName::base) { + return ArmorType::big; + } + + // 工程、哨兵、前哨站只能是小装甲板 + if ( + armor.name == ArmorName::two || armor.name == ArmorName::sentry || + armor.name == ArmorName::outpost) { + return ArmorType::small; + } + + // 步兵假设为小装甲板 + return ArmorType::small; +} + +cv::Point2f YOLOV8::get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const +{ + auto h = bgr_img.rows; + auto w = bgr_img.cols; + return {center.x / w, center.y / h}; +} + +cv::Mat YOLOV8::get_pattern(const cv::Mat & bgr_img, const Armor & armor) const +{ + // 延长灯条获得装甲板角点 + // 1.125 = 0.5 * armor_height / lightbar_length = 0.5 * 126mm / 56mm + auto tl = (armor.points[0] + armor.points[3]) / 2 - (armor.points[3] - armor.points[0]) * 1.125; + auto bl = (armor.points[0] + armor.points[3]) / 2 + (armor.points[3] - armor.points[0]) * 1.125; + auto tr = (armor.points[2] + armor.points[1]) / 2 - (armor.points[2] - armor.points[1]) * 1.125; + auto br = (armor.points[2] + armor.points[1]) / 2 + (armor.points[2] - armor.points[1]) * 1.125; + + auto roi_left = std::max(std::min(tl.x, bl.x), 0); + auto roi_top = std::max(std::min(tl.y, tr.y), 0); + auto roi_right = std::min(std::max(tr.x, br.x), bgr_img.cols); + auto roi_bottom = std::min(std::max(bl.y, br.y), bgr_img.rows); + auto roi_tl = cv::Point(roi_left, roi_top); + auto roi_br = cv::Point(roi_right, roi_bottom); + auto roi = cv::Rect(roi_tl, roi_br); + + // 检查ROI是否有效 + if (roi_left < 0 || roi_top < 0 || roi_right <= roi_left || roi_bottom <= roi_top) { + // std::cerr << "Invalid ROI: " << roi << std::endl; + return cv::Mat(); // 返回一个空的Mat对象 + } + + // 检查ROI是否超出图像边界 + if (roi_right > bgr_img.cols || roi_bottom > bgr_img.rows) { + // std::cerr << "ROI out of image bounds: " << roi << " Image size: " << bgr_img.size() + // << std::endl; + return cv::Mat(); // 返回一个空的Mat对象 + } + + return bgr_img(roi); +} + +void YOLOV8::save(const Armor & armor) const +{ + auto file_name = fmt::format("{:%Y-%m-%d_%H-%M-%S}", std::chrono::system_clock::now()); + auto img_path = fmt::format("{}/{}_{}.jpg", save_path_, armor.name, file_name); + cv::imwrite(img_path, armor.pattern); +} + +void YOLOV8::draw_detections( + const cv::Mat & img, const std::list & armors, int frame_count) const +{ + auto detection = img.clone(); + tools::draw_text(detection, fmt::format("[{}]", frame_count), {10, 30}, {255, 255, 255}); + for (const auto & armor : armors) { + auto info = fmt::format( + "{:.2f} {} {}", armor.confidence, ARMOR_NAMES[armor.name], ARMOR_TYPES[armor.type]); + tools::draw_points(detection, armor.points, {0, 255, 0}); + tools::draw_text(detection, info, armor.center, {0, 255, 0}); + } + + if (use_roi_) { + cv::Scalar green(0, 255, 0); + cv::rectangle(detection, roi_, green, 2); + } + cv::resize(detection, detection, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("detection", detection); +} + +void YOLOV8::sort_keypoints(std::vector & keypoints) +{ + if (keypoints.size() != 4) { + std::cout << "beyond 4!!" << std::endl; + return; + } + + std::sort(keypoints.begin(), keypoints.end(), [](const cv::Point2f & a, const cv::Point2f & b) { + return a.y < b.y; + }); + + std::vector top_points = {keypoints[0], keypoints[1]}; + std::vector bottom_points = {keypoints[2], keypoints[3]}; + + std::sort(top_points.begin(), top_points.end(), [](const cv::Point2f & a, const cv::Point2f & b) { + return a.x < b.x; + }); + + std::sort( + bottom_points.begin(), bottom_points.end(), + [](const cv::Point2f & a, const cv::Point2f & b) { return a.x < b.x; }); + + keypoints[0] = top_points[0]; // top-left + keypoints[1] = top_points[1]; // top-right + keypoints[2] = bottom_points[1]; // bottom-right + keypoints[3] = bottom_points[0]; // bottom-left +} + +std::list YOLOV8::postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) +{ + return parse(scale, output, bgr_img, frame_count); +} + +} // namespace auto_aim \ No newline at end of file diff --git a/tasks/auto_aim/yolos/yolov8.hpp b/tasks/auto_aim/yolos/yolov8.hpp new file mode 100644 index 0000000..faf2fb9 --- /dev/null +++ b/tasks/auto_aim/yolos/yolov8.hpp @@ -0,0 +1,63 @@ +#ifndef AUTO_AIM__YOLOV8_HPP +#define AUTO_AIM__YOLOV8_HPP + +#include +#include +#include +#include +#include + +#include "tasks/auto_aim/armor.hpp" +#include "tasks/auto_aim/classifier.hpp" +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/yolo.hpp" + +namespace auto_aim +{ + +class YOLOV8 : public YOLOBase +{ +public: + YOLOV8(const std::string & config_path, bool debug); + + std::list detect(const cv::Mat & bgr_img, int frame_count) override; + + std::list postprocess( + double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count) override; + +private: + Classifier classifier_; + Detector detector_; + + std::string device_, model_path_; + std::string save_path_, debug_path_; + bool debug_, use_roi_; + + const int class_num_ = 2; + const float nms_threshold_ = 0.3; + const float score_threshold_ = 0.7; + double min_confidence_, binary_threshold_; + + ov::Core core_; + ov::CompiledModel compiled_model_; + + cv::Rect roi_; + cv::Point2f offset_; + + bool check_name(const Armor & armor) const; + bool check_type(const Armor & armor) const; + + cv::Mat get_pattern(const cv::Mat & bgr_img, const Armor & armor) const; + ArmorType get_type(const Armor & armor); + cv::Point2f get_center_norm(const cv::Mat & bgr_img, const cv::Point2f & center) const; + + std::list parse(double scale, cv::Mat & output, const cv::Mat & bgr_img, int frame_count); + + void save(const Armor & armor) const; + void draw_detections(const cv::Mat & img, const std::list & armors, int frame_count) const; + void sort_keypoints(std::vector & keypoints); +}; + +} // namespace auto_aim + +#endif // TOOLS__YOLOV8_HPP \ No newline at end of file diff --git a/tasks/auto_buff/CMakeLists.txt b/tasks/auto_buff/CMakeLists.txt new file mode 100644 index 0000000..702bbb1 --- /dev/null +++ b/tasks/auto_buff/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.16) +set(OpenVINO_DIR "/opt/intel/openvino_2024.6.0/runtime/cmake") + +find_package(OpenVINO REQUIRED) +find_package(Ceres REQUIRED) + +add_library(auto_buff OBJECT + buff_type.cpp + buff_detector.cpp + buff_solver.cpp + buff_aimer.cpp + buff_target.cpp + yolo11_buff.cpp +) + +target_link_libraries(auto_buff auto_aim openvino::runtime ${CERES_LIBRARIES}) \ No newline at end of file diff --git a/tasks/auto_buff/buff_aimer.cpp b/tasks/auto_buff/buff_aimer.cpp new file mode 100644 index 0000000..d47f333 --- /dev/null +++ b/tasks/auto_buff/buff_aimer.cpp @@ -0,0 +1,201 @@ +#include "buff_aimer.hpp" + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/trajectory.hpp" + +namespace auto_buff +{ +Aimer::Aimer(const std::string & config_path) +{ + auto yaml = YAML::LoadFile(config_path); + yaw_offset_ = yaml["yaw_offset"].as() / 57.3; // degree to rad + pitch_offset_ = yaml["pitch_offset"].as() / 57.3; // degree to rad + fire_gap_time_ = yaml["fire_gap_time"].as(); + predict_time_ = yaml["predict_time"].as(); + + last_fire_t_ = std::chrono::steady_clock::now(); +} + +io::Command Aimer::aim( + auto_buff::Target & target, std::chrono::steady_clock::time_point & timestamp, + double bullet_speed, bool to_now) +{ + io::Command command = {false, false, 0, 0}; + if (target.is_unsolve()) return command; + + // 如果子弹速度小于10,将其设为24 + if (bullet_speed < 10) bullet_speed = 24; + + auto now = std::chrono::steady_clock::now(); + + auto detect_now_gap = tools::delta_time(now, timestamp); + auto future = to_now ? (detect_now_gap + predict_time_) : 0.1 + predict_time_; + double yaw, pitch; + + bool angle_changed = + std::abs(last_yaw_ - yaw) > 5 / 57.3 || std::abs(last_pitch_ - pitch) > 5 / 57.3; + if (get_send_angle(target, future, bullet_speed, to_now, yaw, pitch)) { + command.yaw = yaw; + command.pitch = -pitch; //世界坐标系下的pitch向上为负 + if (mistake_count_ > 3) { + switch_fanblade_ = true; + mistake_count_ = 0; + command.control = true; + } else if (std::abs(last_yaw_ - yaw) > 5 / 57.3 || std::abs(last_pitch_ - pitch) > 5 / 57.3) { + switch_fanblade_ = true; + mistake_count_++; + command.control = false; + } else { + switch_fanblade_ = false; + mistake_count_ = 0; + command.control = true; + } + last_yaw_ = yaw; + last_pitch_ = pitch; + } + + if (switch_fanblade_) { + command.shoot = false; + last_fire_t_ = now; + } else if (!switch_fanblade_ && tools::delta_time(now, last_fire_t_) > fire_gap_time_) { + command.shoot = true; + last_fire_t_ = now; + } + + return command; +} + +auto_aim::Plan Aimer::mpc_aim( + auto_buff::Target & target, std::chrono::steady_clock::time_point & timestamp, io::GimbalState gs, + bool to_now) +{ + auto_aim::Plan plan = {false, false, 0, 0, 0, 0, 0, 0, 0, 0}; + if (target.is_unsolve()) return plan; + + double bullet_speed; + // 如果子弹速度小于10,将其设为24 + if (gs.bullet_speed < 10) + bullet_speed = 24; + else + bullet_speed = gs.bullet_speed; + + auto now = std::chrono::steady_clock::now(); + + auto detect_now_gap = tools::delta_time(now, timestamp); + auto future = to_now ? (detect_now_gap + predict_time_) : 0.1 + predict_time_; + double yaw, pitch; + + bool angle_changed = + std::abs(last_yaw_ - yaw) > 5 / 57.3 || std::abs(last_pitch_ - pitch) > 5 / 57.3; + if (get_send_angle(target, future, bullet_speed, to_now, yaw, pitch)) { + plan.yaw = yaw; + plan.pitch = -pitch; //世界坐标系下的pitch向上为负 + if (mistake_count_ > 3) { + switch_fanblade_ = true; + mistake_count_ = 0; + plan.control = true; + first_in_aimer_ = true; + } else if (std::abs(last_yaw_ - yaw) > 5 / 57.3 || std::abs(last_pitch_ - pitch) > 5 / 57.3) { + switch_fanblade_ = true; + mistake_count_++; + plan.control = false; + + first_in_aimer_ = true; + } else { + switch_fanblade_ = false; + mistake_count_ = 0; + plan.control = true; + } + last_yaw_ = yaw; + last_pitch_ = pitch; + + if (plan.control) { + if (first_in_aimer_) { + plan.yaw_vel = 0; + plan.yaw_acc = 0; + plan.pitch_vel = 0; + plan.pitch_acc = 0; + first_in_aimer_ = false; + } else { + auto dt = predict_time_; + double last_yaw_mpc, last_pitch_mpc; + get_send_angle( + target, predict_time_ * -1, bullet_speed, to_now, last_yaw_mpc, last_pitch_mpc); + plan.yaw_vel = tools::limit_rad(yaw - last_yaw_mpc) / (2 * dt); + // plan.yaw_vel = tools::limit_min_max(plan.yaw_vel, -6.28, 6.28); + plan.yaw_acc = (tools::limit_rad(yaw - gs.yaw) - tools::limit_rad(gs.yaw - last_yaw_mpc)) / + std::pow(dt, 2); + // plan.yaw_acc = tools::limit_min_max(plan.yaw_acc, -50, 50); + + plan.pitch_vel = tools::limit_rad(-pitch + last_pitch_mpc) / (2 * dt); + // plan.pitch_vel = tools::limit_min_max(plan.pitch_vel, -6.28, 6.28); + plan.pitch_acc = (-pitch - gs.pitch - (gs.pitch + last_pitch_mpc)) / std::pow(dt, 2); + // plan.pitch_acc = tools::limit_min_max(plan.pitch_acc, -100, 100); + } + } + } + + if (switch_fanblade_) { + plan.fire = false; + last_fire_t_ = now; + } else if (!switch_fanblade_ && tools::delta_time(now, last_fire_t_) > fire_gap_time_) { + plan.fire = true; + last_fire_t_ = now; + } + + return plan; +} + +bool Aimer::get_send_angle( + auto_buff::Target & target, const double predict_time, const double bullet_speed, + const bool to_now, double & yaw, double & pitch) +{ + // 考虑detecor所消耗的时间,此外假设aimer的用时可忽略不计 + // 如果 to_now 为 true,则根据当前时间和时间戳预测目标位置,deltatime = 现在时间减去当时照片时间,加上0.1 + target.predict(predict_time); + // std::cout << "gap: " << detect_now_gap << std::endl; + angle = target.ekf_x()[5]; + + // 计算目标点的空间坐标 + auto aim_in_world = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + double d = std::sqrt(aim_in_world[0] * aim_in_world[0] + aim_in_world[1] * aim_in_world[1]); + double h = aim_in_world[2]; + + // 创建弹道对象 + tools::Trajectory trajectory0(bullet_speed, d, h); + if (trajectory0.unsolvable) { // 如果弹道无法解算,返回未命中结果 + tools::logger()->debug( + "[Aimer] Unsolvable trajectory0: {:.2f} {:.2f} {:.2f}", bullet_speed, d, h); + return false; + } + + // 根据第一个弹道飞行时间预测目标位置 + target.predict(trajectory0.fly_time); + angle = target.ekf_x()[5]; + + // 计算新的目标点的空间坐标 + aim_in_world = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + d = fsqrt(aim_in_world[0] * aim_in_world[0] + aim_in_world[1] * aim_in_world[1]); + h = aim_in_world[2]; + tools::Trajectory trajectory1(bullet_speed, d, h); + if (trajectory1.unsolvable) { // 如果弹道无法解算,返回未命中结果 + tools::logger()->debug( + "[Aimer] Unsolvable trajectory1: {:.2f} {:.2f} {:.2f}", bullet_speed, d, h); + return false; + } + + // 计算时间误差 + auto time_error = trajectory1.fly_time - trajectory0.fly_time; + if (std::abs(time_error) > 0.01) { // 如果时间误差过大,返回未命中结果 + tools::logger()->debug("[Aimer] Large time error: {:.3f}", time_error); + return false; + } + + // 计算偏航角和俯仰角,并返回命中结果 + yaw = std::atan2(aim_in_world[1], aim_in_world[0]) + yaw_offset_; + pitch = trajectory1.pitch + pitch_offset_; + return true; +}; + +} // namespace auto_buff \ No newline at end of file diff --git a/tasks/auto_buff/buff_aimer.hpp b/tasks/auto_buff/buff_aimer.hpp new file mode 100644 index 0000000..44ce958 --- /dev/null +++ b/tasks/auto_buff/buff_aimer.hpp @@ -0,0 +1,59 @@ +#ifndef AUTO_BUFF__AIMER_HPP +#define AUTO_BUFF__AIMER_HPP + +#include + +#include +#include +#include +#include + +#include "../auto_aim/planner/planner.hpp" +#include "buff_target.hpp" +#include "buff_type.hpp" +#include "io/command.hpp" +#include "io/gimbal/gimbal.hpp" + +namespace auto_buff +{ +class Aimer +{ +public: + Aimer(const std::string & config_path); + + io::Command aim( + Target & target, std::chrono::steady_clock::time_point & timestamp, double bullet_speed, + bool to_now = true); + + auto_aim::Plan mpc_aim( + Target & target, std::chrono::steady_clock::time_point & timestamp, io::GimbalState gs, + bool to_now = true); + + double angle; /// + double t_gap = 0; /// + +private: + SmallTarget target_; + double yaw_offset_; + double pitch_offset_; + + double fire_gap_time_; + double predict_time_; + + int mistake_count_ = 0; + bool switch_fanblade_; + + double last_yaw_ = 0; + double last_pitch_ = 0; + + // for mpc + bool first_in_aimer_ = true; + + std::chrono::steady_clock::time_point last_fire_t_; + + bool get_send_angle( + auto_buff::Target & target, const double predict_time, const double bullet_speed, + const bool to_now, double & yaw, double & pitch); +}; +} // namespace auto_buff +#endif // AUTO_AIM__AIMER_HPP \ No newline at end of file diff --git a/tasks/auto_buff/buff_detector.cpp b/tasks/auto_buff/buff_detector.cpp new file mode 100644 index 0000000..72c5774 --- /dev/null +++ b/tasks/auto_buff/buff_detector.cpp @@ -0,0 +1,195 @@ +#include "buff_detector.hpp" + +#include "tools/logger.hpp" + +namespace auto_buff +{ +Buff_Detector::Buff_Detector(const std::string & config) : status_(LOSE), lose_(0), MODE_(config) {} + +void Buff_Detector::handle_img(const cv::Mat & bgr_img, cv::Mat & dilated_img) +{ + // 彩色图转灰度图 + cv::Mat gray_img; + cv::cvtColor(bgr_img, gray_img, cv::COLOR_BGR2GRAY); // 彩色图转灰度图 + // cv::imshow("gray", gray_img); // 调试用 + + // 进行二值化 :把高于100变成255,低于100变成0 + cv::Mat binary_img; + cv::threshold(gray_img, binary_img, 100, 255, cv::THRESH_BINARY); + // cv::imshow("binary", binary_img); // 调试用 + + // 膨胀 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); // 使用矩形核 + cv::dilate(binary_img, dilated_img, kernel, cv::Point(-1, -1), 1); + // cv::imshow("Dilated Image", dilated_img); // 调试用 +} + +cv::Point2f Buff_Detector::get_r_center(std::vector & fanblades, cv::Mat & bgr_img) +{ + /// error + + if (fanblades.empty()) { + tools::logger()->debug("[Buff_Detector] 无法计算r_center!"); + return {0, 0}; + } + + /// 算出大概位置 + + cv::Point2f r_center_t = {0, 0}; + for (auto & fanblade : fanblades) { + auto point5 = fanblade.points[4]; // point5是扇叶的中心 + auto point6 = fanblade.points[5]; + r_center_t += (point6 - point5) * 1.4 + point5; // TODO + // r_center_t += 4.7 * point - (4.7 - 1) * fanblade.center; + } + r_center_t /= float(fanblades.size()); + + /// 处理图片,mask选出大概范围 + + cv::Mat dilated_img; + handle_img(bgr_img, dilated_img); + double radius = cv::norm(fanblades[0].points[2] - fanblades[0].center) * 0.8; + cv::Mat mask = cv::Mat::zeros(dilated_img.size(), CV_8U); // mask + circle(mask, r_center_t, radius, cv::Scalar(255), -1); + bitwise_and(dilated_img, mask, dilated_img); // 将遮罩应用于二值化图像 + tools::draw_point(bgr_img, r_center_t, {255, 255, 0}, 5); // 调试用 + // cv::imshow("Dilated Image", dilated_img); // 调试用 + + /// 获取轮廓点,矩阵框筛选 TODO + + std::vector> contours; + auto r_center = r_center_t; + cv::findContours( + dilated_img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); // external找外部区域 + double ratio_1 = INF; + for (auto & it : contours) { + auto rotated_rect = cv::minAreaRect(it); + double ratio = rotated_rect.size.height > rotated_rect.size.width + ? rotated_rect.size.height / rotated_rect.size.width + : rotated_rect.size.width / rotated_rect.size.height; + ratio += cv::norm(rotated_rect.center - r_center_t) / (radius / 3); + if (ratio < ratio_1) { + ratio_1 = ratio; + r_center = rotated_rect.center; + } + } + return r_center; +}; + +void Buff_Detector::handle_lose() +{ + lose_++; + if (lose_ >= LOSE_MAX) { + status_ = LOSE; + last_powerrune_ = std::nullopt; + } + status_ = TEM_LOSE; +} + +std::optional Buff_Detector::detect_24(cv::Mat & bgr_img) +{ + /// onnx 模型检测 + + std::vector results = MODE_.get_multicandidateboxes(bgr_img); + + /// 处理未获得的情况 + + if (results.empty()) { + handle_lose(); + return std::nullopt; + } + + /// results转扇叶FanBlade + + std::vector fanblades; + for (auto & result : results) fanblades.emplace_back(FanBlade(result.kpt, result.kpt[4], _light)); + + /// 生成PowerRune + auto r_center = get_r_center(fanblades, bgr_img); + PowerRune powerrune(fanblades, r_center, last_powerrune_); + + /// handle error + if (powerrune.is_unsolve()) { + handle_lose(); + return std::nullopt; + } + + status_ = TRACK; + lose_ = 0; + std::optional P; + P.emplace(powerrune); + last_powerrune_ = P; + return P; +} + +std::optional Buff_Detector::detect(cv::Mat & bgr_img) +{ + /// onnx 模型检测 + + std::vector results = MODE_.get_onecandidatebox(bgr_img); + + /// 处理未获得的情况 + + if (results.empty()) { + handle_lose(); + return std::nullopt; + } + + /// results转扇叶FanBlade + + std::vector fanblades; + auto result = results[0]; + fanblades.emplace_back(FanBlade(result.kpt, result.kpt[4], _light)); + + /// 生成PowerRune + auto r_center = get_r_center(fanblades, bgr_img); + PowerRune powerrune(fanblades, r_center, last_powerrune_); + + /// handle error + if (powerrune.is_unsolve()) { + handle_lose(); + return std::nullopt; + } + + status_ = TRACK; + lose_ = 0; + std::optional P; + P.emplace(powerrune); + last_powerrune_ = P; + return P; +} + +std::optional Buff_Detector::detect_debug(cv::Mat & bgr_img, cv::Point2f v) +{ + /// onnx 模型检测 + + std::vector results = MODE_.get_multicandidateboxes(bgr_img); + + /// 处理未获得的情况 + + if (results.empty()) return std::nullopt; + + /// results转扇叶FanBlade + + std::vector fanblades_t; + for (auto & result : results) + fanblades_t.emplace_back(FanBlade(result.kpt, result.kpt[4], _light)); + + /// 计算r_center,筛选fanblade + auto r_center = get_r_center(fanblades_t, bgr_img); + std::vector fanblades; + for (auto & fanblade : fanblades_t) { + if (cv::norm((fanblade.center - r_center) - v) < 10 || results.size() == 1) { + fanblades.emplace_back(fanblade); + break; + } + } + if (fanblades.empty()) return std::nullopt; + PowerRune powerrune(fanblades, r_center, std::nullopt); + + std::optional P; + P.emplace(powerrune); + return P; +} + +} // namespace auto_buff \ No newline at end of file diff --git a/tasks/auto_buff/buff_detector.hpp b/tasks/auto_buff/buff_detector.hpp new file mode 100644 index 0000000..a5b823a --- /dev/null +++ b/tasks/auto_buff/buff_detector.hpp @@ -0,0 +1,40 @@ +#ifndef AUTO_BUFF__TRACK_HPP +#define AUTO_BUFF__TRACK_HPP + +#include + +#include +#include + +#include "buff_type.hpp" +#include "tools/img_tools.hpp" +#include "yolo11_buff.hpp" +const int LOSE_MAX = 20; // 丢失的阙值 +namespace auto_buff +{ +class Buff_Detector +{ +public: + Buff_Detector(const std::string & config); + + std::optional detect_24(cv::Mat & bgr_img); + + std::optional detect(cv::Mat & bgr_img); + +std::optional detect_debug(cv::Mat & bgr_img, cv::Point2f v); + +private: + void handle_img(const cv::Mat & bgr_img, cv::Mat & dilated_img); + + cv::Point2f get_r_center(std::vector & fanblades, cv::Mat & bgr_img); + + void handle_lose(); + + YOLO11_BUFF MODE_; + Track_status status_; + int lose_; // 丢失的次数 + double lastlen_; + std::optional last_powerrune_ = std::nullopt; +}; +} // namespace auto_buff +#endif // DETECTOR_HPP \ No newline at end of file diff --git a/tasks/auto_buff/buff_predict.hpp b/tasks/auto_buff/buff_predict.hpp new file mode 100644 index 0000000..c2ea370 --- /dev/null +++ b/tasks/auto_buff/buff_predict.hpp @@ -0,0 +1,344 @@ +#ifndef BUFF__PREDICT_HPP +#define BUFF__PREDICT_HPP + +#include +#include +#include +#include +#include + +#include "tools/extended_kalman_filter.hpp" +#include "tools/img_tools.hpp" +#include "tools/plotter.hpp" +const double SMALL_W = CV_PI / 3; + +// Predictor 基类 +class Predictor +{ +public: + Predictor(){}; + virtual void update(double angle, double nowtime) = 0; // 纯虚函数 + virtual double predict(double delta_time) = 0; // 纯虚函数 + virtual bool is_unsolve() const = 0; // 纯虚函数 + virtual Eigen::VectorXd getX_best() const = 0; // 纯虚函数 +protected: + Eigen::VectorXd x0; + Eigen::MatrixXd P0; + Eigen::MatrixXd A; + Eigen::MatrixXd Q; + Eigen::MatrixXd H; + Eigen::MatrixXd R; + tools::ExtendedKalmanFilter ekf; + Eigen::VectorXd X_best; + double lastangle = 0; + double lasttime = 0; + int cw_ccw = 0; // 逆时针-1 顺时针1 + bool first_in = true; + bool unsolvable = true; +}; + +class Small_Predictor : public Predictor +{ +public: + Small_Predictor() + : Predictor() //, x0(Eigen::VectorXd(1)), P0(1, 1), A(1, 1), Q(1, 1), H(1, 1), R(1, 1) + { + // 初始状态协方差矩阵 + x0.resize(1); + P0.resize(1, 1); + A.resize(1, 1); + Q.resize(1, 1); + H.resize(1, 1); + R.resize(1, 1); + // 初始状态 + x0 << 0.0; // 初始角度为 0,初始角速度为 1 + // 初始状态协方差矩阵 + P0 << 1.0; + // 状态转移矩阵 + A << 1.0; + // 过程噪声协方差矩阵 //// 调整 + Q << 0.0; + // 测量方程矩阵 + H << 1.0; + // 测量噪声协方差矩阵 //// 调整 + R << 0.05; + // 创建扩展卡尔曼滤波器对象 + ekf = tools::ExtendedKalmanFilter(x0, P0); + } + + virtual void update(double angle, double nowtime) override + { + // [angle] + // [w ] w=CV_PI/6 + + // 初始化angle + if (first_in) { + first_in = false; + lasttime = nowtime; + lastangle = angle; + ekf.x[0] = angle; + unsolvable = true; + } + + // 处理扇叶跳变 + if (abs(angle - lastangle) > CV_PI / 12) { + for (int i = -5; i <= 5; i++) { + double angle_c = lastangle + i * 2 * CV_PI / 5; + if (std::fabs(angle_c - angle) < CV_PI / 5) { + ekf.x[0] += i * 2 * CV_PI / 5; + break; + } + } + } + + // 判断是顺时针还是逆时针旋转 + if (abs(cw_ccw) < 100) { + if (lastangle > angle) + cw_ccw -= 1; + else + cw_ccw += 1; + } + + // 预测下一个状态 + double deltatime = nowtime - lasttime; + A << 1.0; + Eigen::VectorXd B(1); + B << SMALL_W * (cw_ccw > 0 ? 1 : -1); + ekf.predict(A, Q, [&](const Eigen::VectorXd & x) { return A * x + deltatime * B; }); + + // 更新状态,假设测量到的角度为当前状态的第一个元素 + Eigen::VectorXd z(1); + z << angle; + X_best = ekf.update(z, H, R); + + // 更新lasttime lastangle + lasttime = nowtime; + lastangle = angle; + +#ifdef PLOTJUGGLER + nlohmann::json json_obj; + json_obj["angle"] = X_best[0] * 180 / CV_PI; + tools::Plotter().plot(json_obj); +#endif + unsolvable = false; + return; + } + + virtual double predict(double delta_time) override + { + if (unsolvable) + return 0; + else + return (cw_ccw > 0 ? 1 : -1) * SMALL_W * delta_time; + } + + virtual bool is_unsolve() const { return unsolvable; } + + virtual Eigen::VectorXd getX_best() const { return X_best; } +}; + +class Big_Predictor : public Predictor +{ +public: + Big_Predictor() + : Predictor() //, x0(Eigen::VectorXd(5)), P0(5, 5), A(5, 5), Q(5, 5), H(1, 5), R(1, 1) + { + // [angle + // spd + // a 0.78-1.045 + // w 1.884-2.000 + // sita ] + + // 初始状态协方差矩阵 + x0.resize(5); + P0.resize(5, 5); + A.resize(5, 5); + Q.resize(5, 5); + H.resize(1, 5); + R.resize(1, 1); + + // 初始状态 + x0 << 0.0, 1.1775, 0.9125, 1.942, 0.0; // 初始角度为 0,初始角速度为 1,a:0,w:0 + // x0 << 0.0, 1.32, 0.78, 1.884, 0.0; // 初始角度为 0,初始角速度为 1,a:0,w:0 + // x0 << 0.0, 1.045, 1.045, 2.0, 0.0; // 初始角度为 0,初始角速度为 1,a:0,w:0 + // 初始状态协方差矩阵 + P0 << 0.01, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01; + + // 状态转移矩阵 + // A << 1.0, 1.0, + // 0.0, 1.0; + // 过程噪声协方差矩阵 //// 调整 + Q << 0.03, 0.0, 0.0, 0.0, 0.0, // 0.01-0.03 + 0.0, 0.05, 0.0, 0.0, 0.0, // + 0.0, 0.0, 0.01, 0.0, 0.0, // + 0.0, 0.0, 0.0, 0.01, 0.0, // + 0.0, 0.0, 0.0, 0.0, 0.05; // + // + // Q << 0.01, 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.01, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.01, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.01, 0.0, + // 0.0, 0.0, 0.0, 0.0, 0.0, + // 测量方程矩阵 + H << 1.0, 0.0, 0.0, 0.0, 0.0; + // 测量噪声协方差矩阵 //// 调整 + R << 0.05; + // 创建扩展卡尔曼滤波器对象 + ekf = tools::ExtendedKalmanFilter(x0, P0); + } + + virtual void update(double angle, double nowtime) override + { + // 初始化angle + if (first_in) { + first_in = false; + lasttime = nowtime; + lastangle = angle; + ekf.x[0] = angle; + unsolvable = true; + return; + } + + // 处理扇叶跳变 + if (abs(angle - lastangle) > CV_PI / 12) { + for (int i = -5; i <= 5; i++) { + double angle_c = lastangle + i * 2 * CV_PI / 5; + if (std::fabs(angle_c - angle) < CV_PI / 5) { + ekf.x[0] += i * 2 * CV_PI / 5; + lastangle += i * 2 * CV_PI / 5; + break; + } + } + } + + // 判断是顺时针还是逆时针旋转 + if (abs(cw_ccw) < 100) { + if (lastangle > angle) + cw_ccw -= 1; + else + cw_ccw += 1; + } + + // 预测下一个状态 + double deltatime = nowtime - lasttime; + double a = ekf.x[2]; + double w = ekf.x[3]; + double sita = ekf.x[4]; + A << 1.0, 0.0, + (cw_ccw > 0 ? 1 : -1) * (-1 / w * cos(sita + w * deltatime) + 1 / w * cos(sita) - deltatime), + (cw_ccw > 0 ? 1 : -1) * (a / (w * w) * cos(sita + w * deltatime) - a / (w * w) * cos(sita)), + (cw_ccw > 0 ? 1 : -1) * (a / w * sin(sita + w * deltatime) - a / w * sin(sita)), 0.0, 0.0, + sin(sita + w * deltatime) - 1, deltatime * a * cos(sita + w * deltatime), + a * cos(sita + w * deltatime), 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, deltatime, 1.0; + ekf.predict(A, Q, [&](const Eigen::VectorXd & x) { + Eigen::VectorXd m(5); //a w sita + m << x[0] + (cw_ccw > 0 ? 1 : -1) * (-a / w * cos(sita + w * deltatime) + a / w * cos(sita) + + (2.09 - a) * deltatime), + a * sin(sita + w * deltatime) + 2.09 - a, a, w, sita + w * deltatime; + return m; + }); + + // 更新状态 + Eigen::VectorXd z(1); + z << angle; + X_best = ekf.update(z, H, R); + + // 更新lasttime lastangle + lasttime = nowtime; + lastangle = angle; + unsolvable = false; + +#ifndef PLOTJUGGLER + nlohmann::json json_obj; + json_obj["angle"] = X_best[0] * 180 / CV_PI; + json_obj["spd"] = X_best[1] * 180 / CV_PI; + json_obj["a"] = X_best[2]; + json_obj["w"] = X_best[3]; + json_obj["theta"] = X_best[4]; + tools::Plotter().plot(json_obj); +#endif + } + + virtual double predict(double delta_time) override + { + if (unsolvable) return 0; + double a = X_best[2]; + double w = X_best[3]; + double sita = X_best[4]; + return (cw_ccw > 0 ? 1 : -1) * + (-a / w * cos(sita + w * delta_time) + a / w * cos(sita) + (2.09 - a) * delta_time); + } + + virtual bool is_unsolve() const { return unsolvable; } + + virtual Eigen::VectorXd getX_best() const { return X_best; } +}; + +class XYZ_predictor +{ +public: + Eigen::VectorXd x0; + Eigen::MatrixXd P0; + Eigen::MatrixXd A; + Eigen::MatrixXd Q; + Eigen::MatrixXd H; + Eigen::MatrixXd R; + tools::ExtendedKalmanFilter ekf; + Eigen::VectorXd X_best; + XYZ_predictor() + : x0(Eigen::VectorXd(3)), + P0(Eigen::MatrixXd(3, 3)), + A(Eigen::MatrixXd(3, 3)), + Q(Eigen::MatrixXd(3, 3)), + H(Eigen::MatrixXd(3, 3)), + R(Eigen::MatrixXd(3, 3)) + { + // 初始状态 + x0 << 0.0, 0.0, 7.0; // 初始x为 0,初始角y为 0, 初始角z为 7m + // 初始状态协方差矩阵 + P0 << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0; + // 状态转移矩阵 + A << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0; + // 过程噪声协方差矩阵 //// 调整 + Q << 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0; + // 测量方程矩阵 + H << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0; + // 测量噪声协方差矩阵 //// 调整 + R << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0; + // 创建扩展卡尔曼滤波器对象 + ekf = tools::ExtendedKalmanFilter(x0, P0); + } + + void kalman(Eigen::Vector3d & XYZ) + { + if (first_in) { + first_in = false; + ekf.x[0] = XYZ[0]; + ekf.x[1] = XYZ[1]; + ekf.x[2] = XYZ[2]; + return; + } + // 预测下一个状态 + ekf.predict(A, Q); // 预测的角度和角速度 + + // 更新状态 + Eigen::VectorXd z(3); + z << XYZ[0], XYZ[1], XYZ[2]; + X_best = ekf.update(z, H, R); + +#ifdef PLOTJUGGLER + nlohmann::json json_obj; + json_obj["x"] = X_best[0]; + json_obj["y"] = X_best[1]; + json_obj["z"] = X_best[2]; + tools::Plotter().plot(json_obj); +#endif + XYZ = X_best; + } + +private: + bool first_in = true; +}; +#endif // BUFF_PREDICT_HPP diff --git a/tasks/auto_buff/buff_solver.cpp b/tasks/auto_buff/buff_solver.cpp new file mode 100644 index 0000000..13c2268 --- /dev/null +++ b/tasks/auto_buff/buff_solver.cpp @@ -0,0 +1,149 @@ +#include "buff_solver.hpp" +namespace auto_buff +{ +cv::Matx33f Solver::rotation_matrix(double angle) const +{ + return cv::Matx33f( + 1, 0, 0, 0, std::cos(angle), -std::sin(angle), 0, std::sin(angle), std::cos(angle)); +} + +void Solver::compute_rotated_points(std::vector> & object_points) +{ + const std::vector & base_points = object_points[0]; + for (int i = 1; i < 5; ++i) { + double angle = i * THETA; + cv::Matx33f R = rotation_matrix(angle); + std::vector rotated_points; + for (const auto & point : base_points) { + cv::Vec3f vec(point.x, point.y, point.z); + cv::Vec3f rotated_vec = R * vec; + rotated_points.emplace_back(rotated_vec[0], rotated_vec[1], rotated_vec[2]); + } + object_points[i] = rotated_points; + } +} + +Solver::Solver(const std::string & config_path) : R_gimbal2world_(Eigen::Matrix3d::Identity()) +{ + auto yaml = YAML::LoadFile(config_path); + + auto R_gimbal2imubody_data = yaml["R_gimbal2imubody"].as>(); + auto R_camera2gimbal_data = yaml["R_camera2gimbal"].as>(); + auto t_camera2gimbal_data = yaml["t_camera2gimbal"].as>(); + R_gimbal2imubody_ = Eigen::Matrix(R_gimbal2imubody_data.data()); + R_camera2gimbal_ = Eigen::Matrix(R_camera2gimbal_data.data()); + t_camera2gimbal_ = Eigen::Matrix(t_camera2gimbal_data.data()); + + auto camera_matrix_data = yaml["camera_matrix"].as>(); + auto distort_coeffs_data = yaml["distort_coeffs"].as>(); + Eigen::Matrix camera_matrix(camera_matrix_data.data()); + Eigen::Matrix distort_coeffs(distort_coeffs_data.data()); + cv::eigen2cv(camera_matrix, camera_matrix_); + cv::eigen2cv(distort_coeffs, distort_coeffs_); + + // compute_rotated_points(OBJECT_POINTS); +} + +Eigen::Matrix3d Solver::R_gimbal2world() const { return R_gimbal2world_; } + +void Solver::set_R_gimbal2world(const Eigen::Quaterniond & q) +{ + Eigen::Matrix3d R_imubody2imuabs = q.toRotationMatrix(); + R_gimbal2world_ = R_gimbal2imubody_.transpose() * R_imubody2imuabs * R_gimbal2imubody_; +} + +void Solver::solve(std::optional & ps) const +{ + if (!ps.has_value()) return; + PowerRune & p = ps.value(); + // std::vector image_points; + // std::vector object_points; + // int i = 0; + // for (auto & fanblade : p.fanblades) { + // if (fanblade.type != _unlight) { + // image_points.insert(image_points.end(), fanblade.points.begin(), fanblade.points.end()); + // image_points.emplace_back(fanblade.center); + // object_points.insert(object_points.end(), OBJECT_POINTS[i].begin(), OBJECT_POINTS[i].end()); + // } + // ++i; + // } + // image_points.emplace_back(p.r_center); //r_center + // object_points.emplace_back(cv::Point3f(0, 0, 0)); + std::vector image_points = p.target().points; + // image_points.emplace_back(p.target().center); + image_points.emplace_back(p.r_center); + + std::vector image_points_fourth(image_points.begin(), image_points.begin() + 4); + std::vector OBJECT_POINTS_FOURTH(OBJECT_POINTS.begin(), OBJECT_POINTS.begin() + 4); + cv::solvePnP( + OBJECT_POINTS_FOURTH, image_points_fourth, camera_matrix_, distort_coeffs_, rvec_, tvec_, false, + cv::SOLVEPNP_IPPE); + + Eigen::Vector3d t_buff2camera; + cv::cv2eigen(tvec_, t_buff2camera); + cv::Mat rmat; + cv::Rodrigues(rvec_, rmat); + Eigen::Matrix3d R_buff2camera; + cv::cv2eigen(rmat, R_buff2camera); + + Eigen::Vector3d blade_xyz_in_buff{{0, 0, 700e-3}}; + + // buff -> camera + Eigen::Vector3d xyz_in_camera = t_buff2camera; + Eigen::Vector3d blade_xyz_in_camera = R_buff2camera * blade_xyz_in_buff + t_buff2camera; + + // camera -> gimbal + Eigen::Matrix3d R_buff2gimbal = R_camera2gimbal_ * R_buff2camera; + Eigen::Vector3d xyz_in_gimbal = R_camera2gimbal_ * xyz_in_camera + t_camera2gimbal_; + Eigen::Vector3d blade_xyz_in_gimbal = R_camera2gimbal_ * blade_xyz_in_camera + t_camera2gimbal_; + + /// gimbal -> world + Eigen::Matrix3d R_buff2world = R_gimbal2world_ * R_buff2gimbal; + + p.xyz_in_world = R_gimbal2world_ * xyz_in_gimbal; + p.ypd_in_world = tools::xyz2ypd(p.xyz_in_world); + + p.blade_xyz_in_world = R_gimbal2world_ * blade_xyz_in_gimbal; + p.blade_ypd_in_world = tools::xyz2ypd(p.blade_xyz_in_world); + + p.ypr_in_world = tools::eulers(R_buff2world, 2, 1, 0); +} + +// 调试用 +cv::Point2f Solver::point_buff2pixel(cv::Point3f x) +{ + // buff坐标系(单位:m)到像素坐标系 + std::vector world_points; + std::vector image_points; + world_points.push_back(x); + cv::projectPoints(world_points, rvec_, tvec_, camera_matrix_, distort_coeffs_, image_points); + return image_points.back(); +} + +// xyz_in_world2xyz_in_pix +std::vector Solver::reproject_buff( + const Eigen::Vector3d & xyz_in_world, double yaw, double row) const +{ + auto R_buff2world = tools::rotation_matrix(Eigen::Vector3d(yaw, 0.0, row)); + // clang-format on + + // get R_buff2camera t_buff2camera + const Eigen::Vector3d & t_buff2world = xyz_in_world; + Eigen::Matrix3d R_buff2camera = + R_camera2gimbal_.transpose() * R_gimbal2world_.transpose() * R_buff2world; + Eigen::Vector3d t_buff2camera = + R_camera2gimbal_.transpose() * (R_gimbal2world_.transpose() * t_buff2world - t_camera2gimbal_); + + // get rvec tvec + cv::Vec3d rvec; + cv::Mat R_buff2camera_cv; + cv::eigen2cv(R_buff2camera, R_buff2camera_cv); + cv::Rodrigues(R_buff2camera_cv, rvec); + cv::Vec3d tvec(t_buff2camera[0], t_buff2camera[1], t_buff2camera[2]); + + // reproject + std::vector image_points; + cv::projectPoints(OBJECT_POINTS, rvec, tvec, camera_matrix_, distort_coeffs_, image_points); + return image_points; +} +} // namespace auto_buff \ No newline at end of file diff --git a/tasks/auto_buff/buff_solver.hpp b/tasks/auto_buff/buff_solver.hpp new file mode 100644 index 0000000..2555989 --- /dev/null +++ b/tasks/auto_buff/buff_solver.hpp @@ -0,0 +1,67 @@ +#ifndef AUTO_BUFF__SOLVER_HPP +#define AUTO_BUFF__SOLVER_HPP + +#include + +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include + +#include "buff_type.hpp" +#include "tools/math_tools.hpp" +namespace auto_buff +{ +// 旋转角度 +const double THETA = 2.0 * CV_PI / 5.0; // 2/5π + +class Solver +{ +public: + explicit Solver(const std::string & config_path); + + Eigen::Matrix3d R_gimbal2world() const; + + void set_R_gimbal2world(const Eigen::Quaterniond & q); + + void solve(std::optional & ps) const; + + // 调试用 + cv::Point2f point_buff2pixel(cv::Point3f x); + + std::vector reproject_buff( + const Eigen::Vector3d & xyz_in_world, double yaw, double row) const; + +private: + cv::Mat camera_matrix_; + cv::Mat distort_coeffs_; + Eigen::Matrix3d R_gimbal2imubody_; + Eigen::Matrix3d R_camera2gimbal_; + Eigen::Vector3d t_camera2gimbal_; + Eigen::Matrix3d R_gimbal2world_; + + cv::Vec3d rvec_, tvec_; + + // std::vector> OBJECT_POINTS = { + // {cv::Point3f(0, 160e-3, 858.5e-3), cv::Point3f(0, -160e-3, 858.5e-3), + // cv::Point3f(0, -186e-3, 541.5e-3), cv::Point3f(0, 186e-3, 541.5e-3), + // cv::Point3f(0, 0, 700e-3)}, + // {}, + // {}, + // {}, + // {}}; // 单位:米 + + // TODO + const std::vector OBJECT_POINTS = { + cv::Point3f(0, 0, 827e-3), cv::Point3f(0, 127e-3, 700e-3), + cv::Point3f(0, 0, 573e-3), cv::Point3f(0, -127e-3, 700e-3), + cv::Point3f(0, 0, 700e-3), cv::Point3f(0, 0, 220e-3), + cv::Point3f(0, 0, 0)}; // 单位:米 + + // 函数:生成绕x轴旋转的旋转矩阵 + cv::Matx33f rotation_matrix(double angle) const; + + // 函数:旋转点并填充到 OBJECT_POINTS 中 + void compute_rotated_points(std::vector> & object_points); +}; +} // namespace auto_buff +#endif // AUTO_AIM__SOLVER_HPP \ No newline at end of file diff --git a/tasks/auto_buff/buff_target.cpp b/tasks/auto_buff/buff_target.cpp new file mode 100644 index 0000000..9da8230 --- /dev/null +++ b/tasks/auto_buff/buff_target.cpp @@ -0,0 +1,713 @@ +#include "buff_target.hpp" + +namespace auto_buff +{ +///voter + +Voter::Voter() : clockwise_(0) {} + +void Voter::vote(const double angle_last, const double angle_now) +{ + if (std::abs(clockwise_) > 50) return; + if (angle_last > angle_now) + clockwise_--; + else + clockwise_++; +} + +int Voter::clockwise() { return clockwise_ > 0 ? 1 : -1; } + +/// Target + +Target::Target() : first_in_(true), unsolvable_(true) {}; + +Eigen::Vector3d Target::point_buff2world(const Eigen::Vector3d & point_in_buff) const +{ + if (unsolvable_) return Eigen::Vector3d(0, 0, 0); + Eigen::Matrix3d R_buff2world = + tools::rotation_matrix(Eigen::Vector3d(ekf_.x[4], 0.0, ekf_.x[5])); // pitch = 0 + + auto R_yaw = ekf_.x[0]; + auto R_pitch = ekf_.x[2]; + auto R_dis = ekf_.x[3]; + Eigen::Vector3d point_in_world = + R_buff2world * point_in_buff + Eigen::Vector3d( + R_dis * std::cos(R_pitch) * std::cos(R_yaw), + R_dis * std::cos(R_pitch) * std::sin(R_yaw), + R_dis * std::sin(R_pitch)); + return point_in_world; +} + +bool Target::is_unsolve() const { return unsolvable_; } + +Eigen::VectorXd Target::ekf_x() const { return ekf_.x; } + +/// SmallTarget + +SmallTarget::SmallTarget() : Target() {} + +void SmallTarget::get_target( + const std::optional & p, std::chrono::steady_clock::time_point & timestamp) +{ + // 如果没有识别,退出函数 + static int lost_cn = 0; + if (!p.has_value()) { + unsolvable_ = true; + lost_cn++; + return; + } + + static std::chrono::steady_clock::time_point start_timestamp = timestamp; + auto time_gap = tools::delta_time(timestamp, start_timestamp); + + // init + if (first_in_) { + unsolvable_ = true; + init(time_gap, p.value()); + first_in_ = false; + } + + // 处理识别时间间隔过大 + if (lost_cn > 6) { + unsolvable_ = true; + tools::logger()->debug("[Target] 丢失buff"); + lost_cn = 0; + first_in_ = true; + return; + } + + // kalman update + unsolvable_ = false; + update(time_gap, p.value()); + + // 处理发散 + if (std::abs(ekf_.x[6]) > SMALL_W + CV_PI / 18 || std::abs(ekf_.x[6]) < SMALL_W - CV_PI / 18) { + unsolvable_ = true; + tools::logger()->debug("[Target] 小符角度发散spd: {:.2f}", ekf_.x[6] * 180 / CV_PI); + first_in_ = true; + return; + } +} + +void SmallTarget::predict(double dt) +{ + // 预测下一个状态 + // clang-format off + A_ << 1.0, dt, 0.0, 0.0, 0.0, 0.0, 0.0, // R_yaw + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, // R_v_yaw + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, // R_pitch + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, // R_dis + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // yaw + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, dt, // roll + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0; // spd + + // 过程噪声协方差矩阵 //// 调整 + auto v1 = 0.001; // 角加速度方差 + auto a = dt * dt * dt * dt / 4; + auto b = dt * dt * dt / 2; + auto c = dt * dt; + Q_ << a * v1, b * v1, 0.0, 0.0, 0.0, 0.0, 0.0, + b * v1, c * v1, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0; + // clang-format on + auto f = [&](const Eigen::VectorXd & x) -> Eigen::VectorXd { + Eigen::VectorXd x_prior = A_ * x; + x_prior[0] = tools::limit_rad(x_prior[0]); + x_prior[2] = tools::limit_rad(x_prior[2]); + x_prior[4] = tools::limit_rad(x_prior[4]); + x_prior[5] = tools::limit_rad(x_prior[5]); + return x_prior; + }; + ekf_.predict(A_, Q_, f); +} + +void SmallTarget::init(double nowtime, const PowerRune & p) +{ + // 初始化内部变量 + lasttime_ = nowtime; + + // 初始状态协方差矩阵 + x0_.resize(7); + P0_.resize(7, 7); + A_.resize(7, 7); + Q_.resize(7, 7); + H_.resize(7, 7);//z x + R_.resize(7, 7);//z z + // [R_yaw] + // [v_R_yaw] + // [R_pitch] + // [R_dis] + // [yaw] + // [angle/row] + // [spd] w=CV_PI/6 + + // clang-format off + // 初始状态 + x0_ << p.ypd_in_world[0], 0.0, p.ypd_in_world[1], p.ypd_in_world[2], + p.ypr_in_world[0], p.ypr_in_world[2], + SMALL_W * voter.clockwise(); + // 初始状态协方差矩阵 + P0_ << 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-2; + // 状态转移矩阵 + // A_ + // 过程噪声协方差矩阵 //// 调整 + // Q_ + // 测量方程矩阵 + // H_ + // 测量噪声协方差矩阵 //// 调整 + // R_ + + // clang-format on + + // 防止夹角求和出现异常值 + auto x_add = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a + b; + c[0] = tools::limit_rad(c[0]); + c[2] = tools::limit_rad(c[2]); + c[4] = tools::limit_rad(c[4]); + c[5] = tools::limit_rad(c[5]); + return c; + }; + // 创建扩展卡尔曼滤波器对象 + ekf_ = tools::ExtendedKalmanFilter(x0_, P0_, x_add); +} + +void SmallTarget::update(double nowtime, const PowerRune & p) +{ + // [R_yaw] angle0 + // [v_R_yaw] + // [R_pitch] angle2 + // [R_dis] + // [yaw] angle4 + // [angle/row] angle5 + // [spd] w=CV_PI/6 + const Eigen::VectorXd & R_ypd = p.ypd_in_world; // R + const Eigen::VectorXd & ypr = p.ypr_in_world; + const Eigen::VectorXd & B_ypd = p.blade_ypd_in_world; // center of blade + + // 处理扇叶跳变 angle/row + if (abs(ypr[2] - ekf_.x[5]) > CV_PI / 12) { + for (int i = -5; i <= 5; i++) { + double angle_c = ekf_.x[5] + i * 2 * CV_PI / 5; + if (std::fabs(angle_c - ypr[2]) < CV_PI / 5) { + ekf_.x[5] += i * 2 * CV_PI / 5; + break; + } + } + } + + // vote判断是顺时针还是逆时针旋转 + voter.vote(ekf_.x[5], ypr[2]); + if (voter.clockwise() * ekf_.x[6] < 0) ekf_.x[6] *= -1; // spd + + // 预测下一个状态 + predict(nowtime - lasttime_); + + // [R_yaw] angle0 + // [R_pitch] angle1 + // [R_dis] + // [angle/row] angle3 + // [B_yaw] angle4 + // [B_pitch] angle5 + // [B_dis] + + /// 1. + + // [R_yaw] angle0 + // [R_pitch] angle1 + // [R_dis] + // [angle/row] angle3 + + // clang-format off + Eigen::MatrixXd H1{ + {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // R_yaw + {0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0}, // R_pitch + {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, // R_dis + {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0} // roll + }; + + Eigen::MatrixXd R1{ + {0.01, 0.0, 0.0, 0.0}, // R_yaw + {0.0, 0.01, 0.0, 0.0}, // R_pitch + {0.0, 0.0, 0.5, 0.0}, // R_dis + {0.0, 0.0, 0.0, 0.1} // roll + }; + // clang-format on + + // 防止夹角求差出现异常值 + auto z_subtract1 = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a - b; //4 1 + c[0] = tools::limit_rad(c[0]); + c[1] = tools::limit_rad(c[1]); + c[3] = tools::limit_rad(c[3]); + return c; + }; + + Eigen::VectorXd z1{{R_ypd[0], R_ypd[1], R_ypd[2], ypr[2]}}; // R_ypd roll + + ekf_.update(z1, H1, R1, z_subtract1); + + ///2. + + // [B_yaw] angle4 + // [B_pitch] angle5 + // [B_dis] + + // clang-format off + Eigen::MatrixXd H2 = h_jacobian(); // 3*7 + + Eigen::MatrixXd R2{ + {0.01, 0.0, 0.0}, // B_yaw + {0.0, 0.01, 0.0}, // B_pitch + {0.0, 0.0, 0.5} // B_dis + }; + // clang-format on + + // 定义非线性转换函数h: x -> z + auto h2 = [&](const Eigen::VectorXd & x) -> Eigen::Vector3d { + Eigen::VectorXd R_ypd{{x[0], x[2], x[3]}}; + Eigen::VectorXd R_xyz = tools::ypd2xyz(R_ypd); + Eigen::VectorXd R_xyz_and_yr{{R_ypd[0], R_ypd[1], R_ypd[2], x[4], x[5]}}; + Eigen::VectorXd B_xyz = point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + Eigen::VectorXd B_ypd = tools::xyz2ypd(B_xyz); + return B_ypd; + }; + + // 防止夹角求差出现异常值 + auto z_subtract2 = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a - b; //6 1 + c[0] = tools::limit_rad(c[0]); + c[1] = tools::limit_rad(c[1]); + return c; + }; + + Eigen::VectorXd z2{{B_ypd[0], B_ypd[1], B_ypd[2]}}; + + ekf_.update(z2, H2, R2, h2, z_subtract2); + + // 更新lasttime + lasttime_ = nowtime; + return; +} + +Eigen::MatrixXd SmallTarget::h_jacobian() const +{ + /// Z(3,1) = H3(3,3) * H2(3,5) * H1(5,5) * H0(5,7) * x(7,1) + + // clang-format off + Eigen::MatrixXd H0{ + {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0} + };// 5*7 + + Eigen::VectorXd R_ypd{{ekf_.x[0], ekf_.x[2], ekf_.x[3]}}; + Eigen::MatrixXd H_ypd2xyz = tools::ypd2xyz_jacobian(R_ypd); // 3*3 + Eigen::MatrixXd H1{ + {H_ypd2xyz(0, 0), H_ypd2xyz(0, 1), H_ypd2xyz(0, 2), 0.0, 0.0}, + {H_ypd2xyz(1, 0), H_ypd2xyz(1, 1), H_ypd2xyz(1, 2), 0.0, 0.0}, + {H_ypd2xyz(2, 0), H_ypd2xyz(2, 1), H_ypd2xyz(2, 2), 0.0, 0.0}, + { 0.0, 0.0, 0.0, 1.0, 0.0}, + { 0.0, 0.0, 0.0, 0.0, 1.0} + };// 5*5 + + // double pitch = 0; + double yaw = ekf_.x[4]; + double roll = ekf_.x[5]; + double cos_yaw = cos(yaw); + double sin_yaw = sin(yaw); + double cos_roll = cos(roll); + double sin_roll = sin(roll); + Eigen::MatrixXd H2{ + {1.0, 0.0, 0.0, 0.7 * cos_yaw * sin_roll, 0.7 * sin_yaw * cos_roll}, + {0.0, 1.0, 0.0, 0.7 * sin_yaw * sin_roll, -0.7 * cos_yaw * cos_roll}, + {0.0, 0.0, 1.0, 0.0, -0.7 * sin_roll} + };// 3*5 + + Eigen::VectorXd B_xyz = point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + Eigen::MatrixXd H3 = tools::xyz2ypd_jacobian(B_xyz);// 3*3 + // clang-format on + + return H3 * H2 * H1 * H0; // 3*7 + + // auto h2 = [&](const Eigen::VectorXd & x) -> Eigen::Vector3d { + // Eigen::VectorXd R_ypd{{x[0], x[2], x[3]}}; + // Eigen::VectorXd R_xyz = tools::ypd2xyz(R_ypd); + // Eigen::VectorXd R_xyz_and_yr{{R_ypd[0], R_ypd[1], R_ypd[2], x[4], x[5]}}; + // Eigen::VectorXd B_xyz = point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + // Eigen::VectorXd B_ypd = tools::xyz2ypd(B_xyz); + // return B_ypd; + // }; +} + +/// BigTarget + +BigTarget::BigTarget() : Target(), spd_fitter_(100, 0.5, 1.884, 2.000) {} + +void BigTarget::get_target( + const std::optional & p, std::chrono::steady_clock::time_point & timestamp) +{ + // 如果没有识别,退出函数 + static int lost_cn = 0; + if (!p.has_value()) { + unsolvable_ = true; + lost_cn++; + return; + } + + static std::chrono::steady_clock::time_point start_timestamp = timestamp; + auto time_gap = tools::delta_time(timestamp, start_timestamp); + + // init + if (first_in_) { + unsolvable_ = true; + init(time_gap, p.value()); + first_in_ = false; + } + + // 处理识别时间间隔过大 + if (lost_cn > 6) { + unsolvable_ = true; + tools::logger()->debug("[Target] 丢失buff"); + lost_cn = 0; + first_in_ = true; + return; + } + + // kalman update + unsolvable_ = false; + update(time_gap, p.value()); + + // 处理发散 + if ( + ekf_.x[7] > 1.045 * 1.5 || ekf_.x[7] < 0.78 / 1.5 || ekf_.x[8] > 2.0 * 1.5 || + ekf_.x[8] < 1.884 / 1.5) { + tools::logger()->debug("[Target] 大符角度发散a: {:.2f}b:{:.2f}", ekf_.x[7], ekf_.x[8]); + first_in_ = true; + return; + } +} + +void BigTarget::predict(double dt) +{ + // 预测下一个状态 + double spd = fit_spd_; + // double spd = ekf_.x[6]; + double a = ekf_.x[7]; + double w = ekf_.x[8]; + double fi = ekf_.x[9]; + double t = lasttime_ + dt; + // clang-format off + A_ << 1.0, dt, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,//R_yaw + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,//v_R_yaw + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,//R_pitch + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,//R_dis + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,//yaw + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, voter.clockwise() * dt , 0.0, 0.0, 0.0,//row + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, sin(w * t + fi) - 1, t * a * cos(w * t + fi), a * cos(w * t + fi),//spd + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,//a + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,//w + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0;//theta + + // 过程噪声协方差矩阵 //// 调整 + auto v1 = 0.9; // 角加速度方差 + auto a1 = dt * dt * dt * dt / 4; + auto b1 = dt * dt * dt / 2; + auto c1 = dt * dt; + Q_ << a1 * v1, b1 * v1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + b1 * v1, c1 * v1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.0, 0.0, 0.0, 0.0,//row + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0,// spd 0.5 1 + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// a + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// w + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0;// fi + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,// spd 2 + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0; + + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0; + auto f = [&](const Eigen::VectorXd & x) -> Eigen::VectorXd { + Eigen::VectorXd x_prior = x; + x_prior[0] = tools::limit_rad(x_prior[0] + dt * x_prior[1]); + x_prior[2] = tools::limit_rad(x_prior[2]); + x_prior[4] = tools::limit_rad(x_prior[4]); // yaw + x_prior[5] = tools::limit_rad(x_prior[5] + voter.clockwise() * + (-a / w * std::cos(w * t + fi) + a / w * std::cos(w * lasttime_ + fi) + (2.09 - a) * dt)); // roll + x_prior[6] = a * sin(w * t + fi) + 2.09 - a; // spd + return x_prior; + }; + // clang-format on + ekf_.predict(A_, Q_, f); +} + +void BigTarget::init(double nowtime, const PowerRune & p) +{ + // 初始化内部变量 + lasttime_ = nowtime; + unsolvable_ = true; + + // 初始状态协方差矩阵 + x0_.resize(10); + P0_.resize(10, 10); + A_.resize(10, 10); + Q_.resize(10, 10); + H_.resize(7, 10); + R_.resize(7, 7); + + // [R_yaw] + // [v_R_yaw] + // [R_pitch] + // [R_dis] + // [yaw] + // [angle/row] + // [spd] 角速度 a*sin(wt) + 2.09 - a + // [a] 0.78-1.045 + // [w] 1.884-2.000 + // [fi] + + // clang-format off + // 初始状态 + x0_ << p.ypd_in_world[0], 0.0, p.ypd_in_world[1], p.ypd_in_world[2], + p.ypr_in_world[0], p.ypr_in_world[2], + 1.1775, 0.9125, 1.942, 0.0;//std::atan((spd - 2.09) / 0.9125 + 1 + // 初始状态协方差矩阵 + P0_ << 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 400.0; + // 状态转移矩阵 + // A_ + // 过程噪声协方差矩阵 //// 调整 + // Q_ + // 测量方程矩阵 + // H_ + // 测量噪声协方差矩阵 //// 调整 + // R_ + + // clang-format on + + // 防止夹角求和出现异常值 + auto x_add = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a + b; + c[0] = tools::limit_rad(c[0]); + c[2] = tools::limit_rad(c[2]); + c[4] = tools::limit_rad(c[4]); + c[5] = tools::limit_rad(c[5]); + c[9] = tools::limit_rad(c[9]); + return c; + }; + // 创建扩展卡尔曼滤波器对象 + ekf_ = tools::ExtendedKalmanFilter(x0_, P0_, x_add); +} + +void BigTarget::update(double nowtime, const PowerRune & p) +{ + // [R_yaw] + // [v_R_yaw] + // [R_pitch] + // [R_dis] + // [yaw] + // [angle/row] 角度 + // [spd] 角速度 a*sin(wt) + 2.09 - a + // [a] 0.78-1.045 + // [w] 1.884-2.000 + // [fi] + const Eigen::VectorXd & R_ypd = p.ypd_in_world; // R + const Eigen::VectorXd & ypr = p.ypr_in_world; + const Eigen::VectorXd & B_ypd = p.blade_ypd_in_world; // center of blade + + // 处理扇叶跳变 angle/row + if (abs(ypr[2] - ekf_.x[5]) > CV_PI / 12) { + for (int i = -5; i <= 5; i++) { + double angle_c = ekf_.x[5] + i * 2 * CV_PI / 5; + if (std::fabs(angle_c - ypr[2]) < CV_PI / 5) { + ekf_.x[5] += i * 2 * CV_PI / 5; + break; + } + } + } + + // vote判断是顺时针还是逆时针旋转 + voter.vote(ekf_.x[5], ypr[2]); + + auto anglelast = ekf_.x[5]; /// + + // 预测下一个状态 + predict(nowtime - lasttime_); + + // [R_yaw] angle0 + // [R_pitch] angle1 + // [R_dis] + // [angle/row] angle3 + // [B_yaw] angle4 + // [B_pitch] angle5 + // [B_dis] + + /// 1. + + // [R_yaw] angle0 + // [R_pitch] angle1 + // [R_dis] + // [angle/row] angle3 + + // clang-format off + Eigen::MatrixXd H1{ + {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // R_yaw + {0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // R_pitch + {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // R_dis + {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0} // roll + }; + + Eigen::MatrixXd R1{ + {0.01, 0.0, 0.0, 0.0}, // R_yaw + {0.0, 0.01, 0.0, 0.0}, // R_pitch + {0.0, 0.0, 0.5, 0.0}, // R_dis + {0.0, 0.0, 0.0, 0.1} // roll 1: 0.01 2:0.04 + }; + // clang-format on + + // 防止夹角求差出现异常值 + auto z_subtract1 = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a - b; //4 1 + c[0] = tools::limit_rad(c[0]); + c[1] = tools::limit_rad(c[1]); + c[3] = tools::limit_rad(c[3]); + return c; + }; + + Eigen::VectorXd z1{{R_ypd[0], R_ypd[1], R_ypd[2], ypr[2]}}; // R_ypd roll + + ekf_.update(z1, H1, R1, z_subtract1); + + ///2. + + // [B_yaw] angle4 + // [B_pitch] angle5 + // [B_dis] + + // clang-format off + Eigen::MatrixXd H2 = h_jacobian(); // 3*10 + + Eigen::MatrixXd R2{ + {0.01, 0.0, 0.0}, // B_yaw + {0.0, 0.01, 0.0}, // B_pitch + {0.0, 0.0, 0.5} // B_dis + }; + // clang-format on + + // 定义非线性转换函数h: x -> z + auto h2 = [&](const Eigen::VectorXd & x) -> Eigen::Vector3d { + Eigen::VectorXd R_ypd{{x[0], x[2], x[3]}}; + Eigen::VectorXd R_xyz = tools::ypd2xyz(R_ypd); + Eigen::VectorXd R_xyz_and_yr{{R_ypd[0], R_ypd[1], R_ypd[2], x[4], x[5]}}; + Eigen::VectorXd B_xyz = point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + Eigen::VectorXd B_ypd = tools::xyz2ypd(B_xyz); + return B_ypd; + }; + + // 防止夹角求差出现异常值 + auto z_subtract2 = [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) -> Eigen::VectorXd { + Eigen::VectorXd c = a - b; //6 1 + c[0] = tools::limit_rad(c[0]); + c[1] = tools::limit_rad(c[1]); + return c; + }; + + Eigen::VectorXd z2{{B_ypd[0], B_ypd[1], B_ypd[2]}}; + + ekf_.update(z2, H2, R2, h2, z_subtract2); + + // 对ekf速度进行最小二乘拟合 ekf_.x[6] -> fitting_speed -> predict position + if (ekf_.x[6] < 2.1 && ekf_.x[6] >= 0) spd_fitter_.add_data(nowtime, ekf_.x[6]); + spd_fitter_.fit(); + + fit_spd_ = spd_fitter_.sine_function( + nowtime, spd_fitter_.best_result_.A, spd_fitter_.best_result_.omega, + spd_fitter_.best_result_.phi, spd_fitter_.best_result_.C); + + spd = voter.clockwise() * (ekf_.x[5] - anglelast) / (nowtime - lasttime_); // 仅供调试 + spd = fit_spd_; + if (std::abs(spd) > 4) spd = 0; + + // 更新lasttime + lasttime_ = nowtime; + unsolvable_ = false; + return; +} + +Eigen::MatrixXd BigTarget::h_jacobian() const +{ + /// Z(3,1) = H3(3,3) * H2(3,5) * H1(5,5) * H0(5,10) * x(10,1) + + // clang-format off + Eigen::MatrixXd H0{ + {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0} + };// 5*7 + + Eigen::VectorXd R_ypd{{ekf_.x[0], ekf_.x[2], ekf_.x[3]}}; + Eigen::MatrixXd H_ypd2xyz = tools::ypd2xyz_jacobian(R_ypd); // 3*3 + Eigen::MatrixXd H1{ + {H_ypd2xyz(0, 0), H_ypd2xyz(0, 1), H_ypd2xyz(0, 2), 0.0, 0.0}, + {H_ypd2xyz(1, 0), H_ypd2xyz(1, 1), H_ypd2xyz(1, 2), 0.0, 0.0}, + {H_ypd2xyz(2, 0), H_ypd2xyz(2, 1), H_ypd2xyz(2, 2), 0.0, 0.0}, + { 0.0, 0.0, 0.0, 1.0, 0.0}, + { 0.0, 0.0, 0.0, 0.0, 1.0} + };// 5*5 + + // double pitch = 0; + double yaw = ekf_.x[4]; + double roll = ekf_.x[5]; + double cos_yaw = cos(yaw); + double sin_yaw = sin(yaw); + double cos_roll = cos(roll); + double sin_roll = sin(roll); + Eigen::MatrixXd H2{ + {1.0, 0.0, 0.0, 0.7 * cos_yaw * sin_roll, 0.7 * sin_yaw * cos_roll}, + {0.0, 1.0, 0.0, 0.7 * sin_yaw * sin_roll, -0.7 * cos_yaw * cos_roll}, + {0.0, 0.0, 1.0, 0.0, -0.7 * sin_roll} + };// 3*5 + + Eigen::VectorXd B_xyz = point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + Eigen::MatrixXd H3 = tools::xyz2ypd_jacobian(B_xyz);// 3*3 + // clang-format on + + return H3 * H2 * H1 * H0; // 3*7 + + // auto h2 = [&](const Eigen::VectorXd & x) -> Eigen::Vector3d { + // Eigen::VectorXd R_ypd{{x[0], x[2], x[3]}}; + // Eigen::VectorXd R_xyz = tools::ypd2xyz(R_ypd); + // Eigen::VectorXd R_xyz_and_yr{{R_ypd[0], R_ypd[1], R_ypd[2], x[4], x[5]}}; + // Eigen::VectorXd B_xyz = point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.7)); + // Eigen::VectorXd B_ypd = tools::xyz2ypd(B_xyz); + // return B_ypd; + // }; +} +} // namespace auto_buff \ No newline at end of file diff --git a/tasks/auto_buff/buff_target.hpp b/tasks/auto_buff/buff_target.hpp new file mode 100644 index 0000000..eec255a --- /dev/null +++ b/tasks/auto_buff/buff_target.hpp @@ -0,0 +1,117 @@ +#ifndef AUTO_BUFF__TARGET_HPP +#define AUTO_BUFF__TARGET_HPP + +#include +#include +#include +#include +#include + +#include "buff_detector.hpp" +#include "buff_type.hpp" +#include "tools/extended_kalman_filter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/ransac_sine_fitter.hpp" + +namespace auto_buff +{ +class Voter +{ +public: + Voter(); + void vote(const double angle_last, const double angle_now); + int clockwise(); + +private: + int clockwise_; +}; + +/// Target 基类 + +class Target +{ +public: + Target(); + virtual void get_target( + const std::optional & p, + std::chrono::steady_clock::time_point & timestamp) = 0; // 纯虚函数 + + virtual void predict(double dt) = 0; // 纯虚函数 + + Eigen::Vector3d point_buff2world(const Eigen::Vector3d & point_in_buff) const; + + bool is_unsolve() const; + + Eigen::VectorXd ekf_x() const; + + double spd = 0; //调试用 + +protected: + virtual void init(double nowtime, const PowerRune & p) = 0; // 纯虚函数 + + virtual void update(double nowtime, const PowerRune & p) = 0; // 纯虚函数 + + Eigen::VectorXd x0_; + Eigen::MatrixXd P0_; + Eigen::MatrixXd A_; + Eigen::MatrixXd Q_; + Eigen::MatrixXd H_; + Eigen::MatrixXd R_; + tools::ExtendedKalmanFilter ekf_; + double lasttime_ = 0; + Voter voter; // 逆时针-1 顺时针1 + bool first_in_; + bool unsolvable_; +}; + +/// SmallTarget子类 + +class SmallTarget : public Target +{ +public: + SmallTarget(); + + void get_target( + const std::optional & p, std::chrono::steady_clock::time_point & timestamp) override; + + void predict(double dt) override; + +private: + void init(double nowtime, const PowerRune & p) override; + + void update(double nowtime, const PowerRune & p) override; + + Eigen::MatrixXd h_jacobian() const; + + const double SMALL_W = CV_PI / 3; + // const double SMALL_W = 0; +}; + +/// BigTarget子类 + +class BigTarget : public Target +{ +public: + BigTarget(); + + void get_target( + const std::optional & p, std::chrono::steady_clock::time_point & timestamp) override; + + void predict(double dt) override; + +private: + void init(double nowtime, const PowerRune & p) override; + + void update(double nowtime, const PowerRune & p) override; + + Eigen::MatrixXd h_jacobian() const; + + tools::RansacSineFitter spd_fitter_; + + double fit_spd_; +}; + +} // namespace auto_buff +#endif \ No newline at end of file diff --git a/tasks/auto_buff/buff_type.cpp b/tasks/auto_buff/buff_type.cpp new file mode 100644 index 0000000..c5dd270 --- /dev/null +++ b/tasks/auto_buff/buff_type.cpp @@ -0,0 +1,102 @@ +#include "buff_type.hpp" + +#include + +#include "tools/logger.hpp" +namespace auto_buff +{ +FanBlade::FanBlade( + const std::vector & kpt, cv::Point2f keypoints_center, FanBlade_type t) +: center(keypoints_center), type(t) +{ + points.insert(points.end(), kpt.begin(), kpt.end()); +} + +FanBlade::FanBlade(FanBlade_type t) : type(t) +{ + if (t != _unlight) exit(-1); +} + +PowerRune::PowerRune( + std::vector & ts, const cv::Point2f center, std::optional last_powerrune) +: r_center(center), light_num(ts.size()) +{ + /// 找出target + + // 只有一个fanblade,就为target + if (light_num == 1) ts[0].type = _target; + // 没有新亮起来的fanblade + else if (last_powerrune.has_value() && ts.size() == last_powerrune.value().light_num) { + auto last_target_center = last_powerrune.value().fanblades[0].center; + auto target_fanblade_it = ts.begin(); // 初始化为 fanblades 的第一个元素 + float min_distance = norm(ts[0].center - last_target_center); + for (auto it = ts.begin(); it != ts.end(); ++it) { + float distance = norm(it->center - last_target_center); + if (distance < min_distance) { + min_distance = distance; + target_fanblade_it = it; // 更新最近的 fanblade 的迭代器 + } + } + target_fanblade_it->type = _target; // 设置最近的 fanblade 的 type + std::iter_swap(ts.begin(), target_fanblade_it); + } + // 有新亮起来的fanblade + else if (last_powerrune.has_value() && light_num == last_powerrune.value().light_num + 1) { + auto last_fanblades = last_powerrune.value().fanblades; + float max_min_distance = -1.0f; // 初始化最大最小距离为-1 + auto target_fanblade_it = ts.begin(); // 用于存储目标 fanblade 的迭代器 + for (auto it = ts.begin(); it != ts.end(); ++it) { + float min_distance = std::numeric_limits::max(); // 初始化最小距离为最大浮点数 + // 计算当前 fanblade 到 last_fanblades 中每个 fanblade 的最小距离 + for (const auto & last_fanblade : last_fanblades) { + if (last_fanblade.type == _unlight) continue; + float distance = norm(it->center - last_fanblade.center); + if (distance < min_distance) { + min_distance = distance; + } + } + if (min_distance > max_min_distance) { + max_min_distance = min_distance; + target_fanblade_it = it; + } + } + target_fanblade_it->type = _target; + std::iter_swap(ts.begin(), target_fanblade_it); + } + // error + else { + tools::logger()->debug("[PowerRune] 识别出错!"); + unsolvable_ = true; + return; + } + + /// 填充FanBlade.angle + + double angle = atan_angle(ts[0].center); + for (auto & t : ts) { + t.angle = atan_angle(t.center) - angle; + if (t.angle < -1e-3) t.angle += CV_2PI; + } + + /// fanblades调整顺序 + + std::sort(ts.begin(), ts.end(), [](const FanBlade & a, const FanBlade & b) { + return a.angle < b.angle; + }); // 按照 t.angle 从小到大排序 ts + const std::vector target_angles = { + 0, 2.0 * CV_PI / 5.0, 4.0 * CV_PI / 5.0, 6.0 * CV_PI / 5.0, 8.0 * CV_PI / 5.0}; + for (int i = 0, j = 0; i < 5 && j < ts.size(); i++) { + if (std::fabs(ts[j].angle - target_angles[i]) < CV_PI / 5.0) + fanblades.emplace_back(ts[j++]); + else + fanblades.emplace_back(FanBlade(_unlight)); + } +}; + +double PowerRune::atan_angle(cv::Point2f point) const +{ + auto v = point - r_center; + auto angle = std::atan2(v.y, v.x); + return angle >= 0 ? angle : angle + CV_2PI; +} +} // namespace auto_buff diff --git a/tasks/auto_buff/buff_type.hpp b/tasks/auto_buff/buff_type.hpp new file mode 100644 index 0000000..71fb8fa --- /dev/null +++ b/tasks/auto_buff/buff_type.hpp @@ -0,0 +1,70 @@ +#ifndef BUFF__TYPE_HPP +#define BUFF__TYPE_HPP + +#include +#include +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include +#include +#include +#include + +#include "tools/math_tools.hpp" +namespace auto_buff +{ +const int INF = 1000000; +enum PowerRune_type { SMALL, BIG }; +enum FanBlade_type { _target, _unlight, _light }; +enum Track_status { TRACK, TEM_LOSE, LOSE }; + +class FanBlade +{ +public: + cv::Point2f center; // 扇页中心 + std::vector points; // 四个点从左上角开始逆时针 + double angle, width, height; + FanBlade_type type; // 类型 + + explicit FanBlade() = default; + + // explicit FanBlade(const std::vector & kpt, cv::Point2f keypoints_center, FanBlade_type t); + + explicit FanBlade( + const std::vector & kpt, cv::Point2f keypoints_center, FanBlade_type t); + + explicit FanBlade(FanBlade_type t); +}; + +class PowerRune +{ +public: + cv::Point2f r_center; + std::vector fanblades; // 按target开始顺时针 + + int light_num; + + Eigen::Vector3d xyz_in_world; // 单位:m + Eigen::Vector3d ypr_in_world; // 单位:rad + Eigen::Vector3d ypd_in_world; // 球坐标系 + + Eigen::Vector3d blade_xyz_in_world; // 单位:m + Eigen::Vector3d blade_ypd_in_world; // 球坐标系, 单位: m + + explicit PowerRune( + std::vector & ts, const cv::Point2f r_center, + std::optional last_powerrune); + explicit PowerRune() = default; + + FanBlade & target() { return fanblades[0]; }; + + bool is_unsolve() const { return unsolvable_; } + +private: + double target_angle_; + bool unsolvable_ = false; + + double atan_angle(cv::Point2f v) const; // [0, 2CV_PI] +}; +} // namespace auto_buff +#endif // BUFF_TYPE_HPP diff --git a/tasks/auto_buff/yolo11_buff.cpp b/tasks/auto_buff/yolo11_buff.cpp new file mode 100644 index 0000000..40b09a0 --- /dev/null +++ b/tasks/auto_buff/yolo11_buff.cpp @@ -0,0 +1,322 @@ +#include "yolo11_buff.hpp" + +const double ConfidenceThreshold = 0.7f; +const double IouThreshold = 0.4f; +namespace auto_buff +{ +YOLO11_BUFF::YOLO11_BUFF(const std::string & config) +{ + auto yaml = YAML::LoadFile(config); + std::string model_path = yaml["model"].as(); + model = core.read_model(model_path); + // printInputAndOutputsInfo(*model); // 打印模型信息 + /// 载入并编译模型 + compiled_model = core.compile_model(model, "CPU"); + /// 创建推理请求 + infer_request = compiled_model.create_infer_request(); + // 获取模型输入节点 + input_tensor = infer_request.get_input_tensor(); + input_tensor.set_shape({1, 3, 640, 640}); +} + +std::vector YOLO11_BUFF::get_multicandidateboxes(cv::Mat & image) +{ + const int64 start = cv::getTickCount(); // 设置模型输入 + + /// 预处理 + + // const float factor = fill_tensor_data_image(input_tensor, image); // 填充图片到合适的input size + + if (image.empty()) { + tools::logger()->warn("Empty img!, camera drop!"); + return std::vector (); + } + + cv::Mat bgr_img = image; + + auto x_scale = static_cast(640) / bgr_img.rows; + auto y_scale = static_cast(640) / bgr_img.cols; + auto scale = std::min(x_scale, y_scale); + auto h = static_cast(bgr_img.rows * scale); + auto w = static_cast(bgr_img.cols * scale); + + double factor = scale; + + // preproces + auto input = cv::Mat(640, 640, CV_8UC3, cv::Scalar(0, 0, 0)); + auto roi = cv::Rect(0, 0, w, h); + cv::resize(bgr_img, input(roi), {w, h}); + ov::Tensor input_tensor(ov::element::u8, {1, 640, 640, 3}, input.data); + + /// 执行推理计算 + infer_request.infer(); + + /// 处理推理计算结果 + const ov::Tensor output = infer_request.get_output_tensor(); // 获得推理结果 + const ov::Shape output_shape = output.get_shape(); + const float * output_buffer = output.data(); + const int out_rows = output_shape[1]; // 获得"output"节点的rows 15 + const int out_cols = output_shape[2]; // 获得"output"节点的cols 8400 + const cv::Mat det_output( + out_rows, out_cols, CV_32F, (float *)output_buffer); // output_buff类型转换 + std::vector boxes; // 目标框 + std::vector confidences; // 置信度 + std::vector> objects_keypoints; // 关键点 + // 输出格式是[15,8400], 每列代表一个框(即最多有8400个框), 前面4行分别是[cx, cy, ow, oh], 中间score, 最后5*2关键点(3代表每个关键点的信息, 包括[x, y, visibility],如果是2,则没有visibility) + // 15 = 4 + 1 + NUM_POINTS * 2 56 + for (int i = 0; i < det_output.cols; ++i) { + const float score = det_output.at(4, i); + // 如果置信度满足条件则放进vector + if (score > ConfidenceThreshold) { + // 获取目标框 + const float cx = det_output.at(0, i); + const float cy = det_output.at(1, i); + const float ow = det_output.at(2, i); + const float oh = det_output.at(3, i); + cv::Rect box; + box.x = static_cast((cx - 0.5 * ow) * factor); + box.y = static_cast((cy - 0.5 * oh) * factor); + box.width = static_cast(ow * factor); + box.height = static_cast(oh * factor); + boxes.push_back(box); + + // 获取置信度 + confidences.push_back(score); + + // 获取关键点 + std::vector keypoints; + cv::Mat kpts = det_output.col(i).rowRange(NUM_POINTS, 15); + for (int j = 0; j < NUM_POINTS; ++j) { + const float x = kpts.at(j * 2 + 0, 0) * factor; + const float y = kpts.at(j * 2 + 1, 0) * factor; + // const float s = kpts.at(j * 3 + 2, 0); + keypoints.push_back(x); + keypoints.push_back(y); + // keypoints.push_back(s); + } + objects_keypoints.push_back(keypoints); + } + } + + /// NMS,消除具有较低置信度的冗余重叠框,用于处理多个框的情况 + std::vector indexes; + cv::dnn::NMSBoxes(boxes, confidences, ConfidenceThreshold, IouThreshold, indexes); + + std::vector object_result; // 最终得到的object + for (size_t i = 0; i < indexes.size(); ++i) { + Object obj; + const int index = indexes[i]; + obj.rect = boxes[index]; + obj.prob = confidences[index]; + + const std::vector & keypoint = objects_keypoints[index]; + for (int i = 0; i < NUM_POINTS; ++i) { + const float x_coord = keypoint[i * 2]; + const float y_coord = keypoint[i * 2 + 1]; + obj.kpt.push_back(cv::Point2f(x_coord, y_coord)); + } + object_result.push_back(obj); + + /// 绘制关键点和连线 + cv::rectangle(image, obj.rect, cv::Scalar(255, 255, 255), 1, 8); // 绘制矩形框 + const std::string label = "buff:" + std::to_string(obj.prob).substr(0, 4); // 绘制标签 + const cv::Size textSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, nullptr); + const cv::Rect textBox( + obj.rect.tl().x, obj.rect.tl().y - 15, textSize.width, textSize.height + 5); + cv::rectangle(image, textBox, cv::Scalar(0, 255, 255), cv::FILLED); + cv::putText( + image, label, cv::Point(obj.rect.tl().x, obj.rect.tl().y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, + cv::Scalar(0, 0, 0)); + const int radius = 2; // 绘制关键点 + const cv::Size & shape = image.size(); + for (int i = 0; i < NUM_POINTS; ++i) + cv::circle(image, obj.kpt[i], radius, cv::Scalar(255, 0, 0), -1, cv::LINE_AA); + } + /// 计算FPS + const float t = (cv::getTickCount() - start) / static_cast(cv::getTickFrequency()); + cv::putText( + image, cv::format("FPS: %.2f", 1.0 / t), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, + cv::Scalar(255, 0, 0), 2, 8); + + // #ifdef SAVE + // save("save", image); + // #endif + return object_result; +} + +std::vector YOLO11_BUFF::get_onecandidatebox(cv::Mat & image) +{ + const int64 start = cv::getTickCount(); // 设置模型输入 + + /// 预处理 + const float factor = fill_tensor_data_image(input_tensor, image); // 填充图片到合适的input size + + /// 执行推理计算 + + infer_request.infer(); + + /// 处理推理计算结果 output 输出格式是[17,8400], 每列代表一个框(即最多有8400个框), 前面4行分别是[cx, cy, ow, oh], 中间score, 最后6*2关键点 + + const ov::Tensor output = infer_request.get_output_tensor(); // 获得推理结果 + const ov::Shape output_shape = output.get_shape(); + const float * output_buffer = output.data(); + const int out_rows = output_shape[1]; // 获得"output"节点的rows 17 + const int out_cols = output_shape[2]; // 获得"output"节点的cols 8400 + const cv::Mat det_output( + out_rows, out_cols, CV_32F, (float *)output_buffer); // output_buff类型转换 + + /// 寻找置信度最大的框 + + int best_index = -1; + float max_confidence = 0.0f; + for (int i = 0; i < det_output.cols; ++i) { + const float confidence = det_output.at(4, i); + if (confidence > max_confidence) { + max_confidence = confidence; + best_index = i; + } + } + std::vector object_result; // 最终得到的object + if (max_confidence > ConfidenceThreshold) { + Object obj; + // 获取目标框 + const float cx = det_output.at(0, best_index); + const float cy = det_output.at(1, best_index); + const float ow = det_output.at(2, best_index); + const float oh = det_output.at(3, best_index); + obj.rect.x = static_cast((cx - 0.5 * ow) * factor); + obj.rect.y = static_cast((cy - 0.5 * oh) * factor); + obj.rect.width = static_cast(ow * factor); + obj.rect.height = static_cast(oh * factor); + // 获取置信度 + obj.prob = max_confidence; + // 获取关键点 + cv::Mat kpts = det_output.col(best_index).rowRange(5, 5 + NUM_POINTS * 2); + for (int i = 0; i < NUM_POINTS; ++i) { + const float x = kpts.at(i * 2 + 0, 0) * factor; + const float y = kpts.at(i * 2 + 1, 0) * factor; + obj.kpt.push_back(cv::Point2f(x, y)); + } + object_result.push_back(obj); + + /// 0.3-0.7 save + if (max_confidence < 0.7) save(std::to_string(start), image); + + /// 绘制关键点和连线 + cv::rectangle(image, obj.rect, cv::Scalar(255, 255, 255), 1, 8); // 绘制矩形框 + const std::string label = "buff:" + std::to_string(max_confidence).substr(0, 4); // 绘制标签 + const cv::Size textSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, nullptr); + const cv::Rect textBox( + obj.rect.tl().x, obj.rect.tl().y - 15, textSize.width, textSize.height + 5); + cv::rectangle(image, textBox, cv::Scalar(0, 255, 255), cv::FILLED); + cv::putText( + image, label, cv::Point(obj.rect.tl().x, obj.rect.tl().y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, + cv::Scalar(0, 0, 0)); + const int radius = 2; // 绘制关键点 + const cv::Size & shape = image.size(); + for (int i = 0; i < NUM_POINTS; ++i) { + cv::circle(image, obj.kpt[i], radius, cv::Scalar(255, 255, 0), -1, cv::LINE_AA); + cv::putText( + image, std::to_string(i + 1), obj.kpt[i] + cv::Point2f(5, -5), cv::FONT_HERSHEY_SIMPLEX, + 0.5, cv::Scalar(255, 255, 0), 1, cv::LINE_AA); + } + } + + /// 计算FPS + const float t = (cv::getTickCount() - start) / static_cast(cv::getTickFrequency()); + cv::putText( + image, cv::format("FPS: %.2f", 1.0 / t), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, + cv::Scalar(255, 0, 0), 2, 8); + return object_result; +} + +void YOLO11_BUFF::convert( + const cv::Mat & input, cv::Mat & output, const bool normalize, const bool BGR2RGB) const +{ + input.convertTo(output, CV_32F); + if (normalize) output = output / 255.0; // 归一化到[0, 1] + if (BGR2RGB) cv::cvtColor(output, output, cv::COLOR_BGR2RGB); +} + +float YOLO11_BUFF::fill_tensor_data_image(ov::Tensor & input_tensor, const cv::Mat & input_image) const +{ + /// letterbox变换: 不改变宽高比(aspect ratio), 将input_image缩放并放置到blob_image左上角 + const ov::Shape tensor_shape = input_tensor.get_shape(); + const size_t num_channels = tensor_shape[1]; + const size_t height = tensor_shape[2]; + const size_t width = tensor_shape[3]; + // 缩放因子 + const float scale = std::min(height / float(input_image.rows), width / float(input_image.cols)); + const cv::Matx23f matrix{ + scale, 0.0, 0.0, 0.0, scale, 0.0, + }; + cv::Mat blob_image; + // 下面根据scale范围进行数据转换, 这只是为了提高一点速度(主要是提高了交换通道的速度) + // 如果不在意这点速度提升的可以固定一种做法(两个if分支随便一个都可以) + if (scale < 1.0f) { + // 要缩小, 那么先缩小再交换通道 + cv::warpAffine(input_image, blob_image, matrix, cv::Size(width, height)); + convert(blob_image, blob_image, true, true); + } else { + // 要放大, 那么先交换通道再放大 + convert(input_image, blob_image, true, true); + cv::warpAffine(blob_image, blob_image, matrix, cv::Size(width, height)); + } + + /// 将图像数据填入input_tensor + float * const input_tensor_data = input_tensor.data(); + // 原有图片数据为 HWC格式,模型输入节点要求的为 CHW 格式 + for (size_t c = 0; c < num_channels; c++) { + for (size_t h = 0; h < height; h++) { + for (size_t w = 0; w < width; w++) { + input_tensor_data[c * width * height + h * width + w] = + blob_image.at>(h, w)[c]; + } + } + } + return 1 / scale; +} + +void YOLO11_BUFF::printInputAndOutputsInfo(const ov::Model & network) +{ + std::cout << "model name: " << network.get_friendly_name() << std::endl; + + const std::vector> inputs = network.inputs(); + for (const ov::Output & input : inputs) { + std::cout << " inputs" << std::endl; + + const std::string name = input.get_names().empty() ? "NONE" : input.get_any_name(); + std::cout << " input name: " << name << std::endl; + + const ov::element::Type type = input.get_element_type(); + std::cout << " input type: " << type << std::endl; + + const ov::Shape shape = input.get_shape(); + std::cout << " input shape: " << shape << std::endl; + } + + const std::vector> outputs = network.outputs(); + for (const ov::Output & output : outputs) { + std::cout << " outputs" << std::endl; + + const std::string name = output.get_names().empty() ? "NONE" : output.get_any_name(); + std::cout << " output name: " << name << std::endl; + + const ov::element::Type type = output.get_element_type(); + std::cout << " output type: " << type << std::endl; + + const ov::Shape shape = output.get_shape(); + std::cout << " output shape: " << shape << std::endl; + } +} + +void YOLO11_BUFF::save(const std::string & programName, const cv::Mat & image) +{ + const std::filesystem::path saveDir = "../result/"; + if (!std::filesystem::exists(saveDir)) { + std::filesystem::create_directories(saveDir); + } + const std::filesystem::path savePath = saveDir / (programName + ".jpg"); + cv::imwrite(savePath.string(), image); +} +} // namespace auto_buff \ No newline at end of file diff --git a/tasks/auto_buff/yolo11_buff.hpp b/tasks/auto_buff/yolo11_buff.hpp new file mode 100755 index 0000000..3e7fb94 --- /dev/null +++ b/tasks/auto_buff/yolo11_buff.hpp @@ -0,0 +1,56 @@ +#ifndef AUTO_BUFF__YOLO11_BUFF_HPP +#define AUTO_BUFF__YOLO11_BUFF_HPP +#include + +#include +#include +#include + +#include "tools/logger.hpp" + +namespace auto_buff +{ +const std::vector class_names = {"buff", "r"}; + +class YOLO11_BUFF +{ +public: + struct Object + { + cv::Rect_ rect; + int label; + float prob; + std::vector kpt; + }; + + YOLO11_BUFF(const std::string & config); + + // 使用NMS,用来获取多个框 + std::vector get_multicandidateboxes(cv::Mat & image); + + // 寻找置信度最高的框 + std::vector get_onecandidatebox(cv::Mat & image); + +private: + ov::Core core; // 创建OpenVINO Runtime Core对象 + std::shared_ptr model; + ov::CompiledModel compiled_model; + ov::InferRequest infer_request; + ov::Tensor input_tensor; + const int NUM_POINTS = 6; + + // 转换图像数据: 先转换元素类型, (可选)然后归一化到[0, 1], (可选)然后交换RB通道 + void convert( + const cv::Mat & input, cv::Mat & output, const bool normalize, const bool exchangeRB) const; + + // 对网络的输入为图片数据的节点进行赋值,实现图片数据输入网络,return 缩放因子, 该缩放是为了将input_image塞进input_tensor + float fill_tensor_data_image(ov::Tensor & input_tensor, const cv::Mat & input_image) const; + + // 打印模型信息, 这个函数修改自$${OPENVINO_COMMON}/utils/src/args_helper.cpp的同名函数 + void printInputAndOutputsInfo(const ov::Model & network); + + // 将image保存为"../result/$${programName}.jpg" + void save(const std::string & programName, const cv::Mat & image); +}; +} // namespace auto_buff +#endif \ No newline at end of file diff --git a/tasks/omniperception/CMakeLists.txt b/tasks/omniperception/CMakeLists.txt new file mode 100644 index 0000000..b15737d --- /dev/null +++ b/tasks/omniperception/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +set(OpenVINO_DIR "/opt/intel/openvino_2024.6.0/runtime/cmake") + +find_package(OpenVINO REQUIRED COMPONENTS Runtime) + +add_library(omniperception OBJECT + decider.cpp + perceptron.cpp +) +target_link_libraries(omniperception openvino::runtime) \ No newline at end of file diff --git a/tasks/omniperception/decider.cpp b/tasks/omniperception/decider.cpp new file mode 100644 index 0000000..5be2715 --- /dev/null +++ b/tasks/omniperception/decider.cpp @@ -0,0 +1,244 @@ +#include "decider.hpp" + +#include + +#include +#include + +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +namespace omniperception +{ +Decider::Decider(const std::string & config_path) : detector_(config_path), count_(0) +{ + auto yaml = YAML::LoadFile(config_path); + img_width_ = yaml["image_width"].as(); + img_height_ = yaml["image_height"].as(); + fov_h_ = yaml["fov_h"].as(); + fov_v_ = yaml["fov_v"].as(); + new_fov_h_ = yaml["new_fov_h"].as(); + new_fov_v_ = yaml["new_fov_v"].as(); + enemy_color_ = + (yaml["enemy_color"].as() == "red") ? auto_aim::Color::red : auto_aim::Color::blue; + mode_ = yaml["mode"].as(); +} + +io::Command Decider::decide( + auto_aim::YOLO & yolo, const Eigen::Vector3d & gimbal_pos, io::USBCamera & usbcam1, + io::USBCamera & usbcam2, io::Camera & back_camera) +{ + Eigen::Vector2d delta_angle; + io::USBCamera * cams[] = {&usbcam1, &usbcam2}; + + cv::Mat usb_img; + std::chrono::steady_clock::time_point timestamp; + if (count_ < 0 || count_ > 2) { + throw std::runtime_error("count_ out of valid range [0,2]"); + } + if (count_ == 2) { + back_camera.read(usb_img, timestamp); + } else { + cams[count_]->read(usb_img, timestamp); + } + auto armors = yolo.detect(usb_img); + auto empty = armor_filter(armors); + + if (!empty) { + if (count_ == 2) { + delta_angle = this->delta_angle(armors, "back"); + } else { + delta_angle = this->delta_angle(armors, cams[count_]->device_name); + } + + tools::logger()->debug( + "[{} camera] delta yaw:{:.2f},target pitch:{:.2f},armor number:{},armor name:{}", + (count_ == 2 ? "back" : cams[count_]->device_name), delta_angle[0], delta_angle[1], + armors.size(), auto_aim::ARMOR_NAMES[armors.front().name]); + + count_ = (count_ + 1) % 3; + + return io::Command{ + true, false, tools::limit_rad(gimbal_pos[0] + delta_angle[0] / 57.3), + tools::limit_rad(delta_angle[1] / 57.3)}; + } + + count_ = (count_ + 1) % 3; + // 如果没有找到目标,返回默认命令 + return io::Command{false, false, 0, 0}; +} + +io::Command Decider::decide( + auto_aim::YOLO & yolo, const Eigen::Vector3d & gimbal_pos, io::Camera & back_cammera) +{ + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + back_cammera.read(img, timestamp); + auto armors = yolo.detect(img); + auto empty = armor_filter(armors); + + if (!empty) { + auto delta_angle = this->delta_angle(armors, "back"); + tools::logger()->debug( + "[back camera] delta yaw:{:.2f},target pitch:{:.2f},armor number:{},armor name:{}", + delta_angle[0], delta_angle[1], armors.size(), auto_aim::ARMOR_NAMES[armors.front().name]); + + return io::Command{ + true, false, tools::limit_rad(gimbal_pos[0] + delta_angle[0] / 57.3), + tools::limit_rad(delta_angle[1] / 57.3)}; + } + + return io::Command{false, false, 0, 0}; +} + +io::Command Decider::decide(const std::vector & detection_queue) +{ + if (detection_queue.empty()) { + return io::Command{false, false, 0, 0}; + } + + DetectionResult dr = detection_queue.front(); + if (dr.armors.empty()) return io::Command{false, false, 0, 0}; + tools::logger()->info( + "omniperceptron find {},delta yaw is {:.4f}", auto_aim::ARMOR_NAMES[dr.armors.front().name], + dr.delta_yaw * 57.3); + + return io::Command{true, false, dr.delta_yaw, dr.delta_pitch}; +}; + +Eigen::Vector2d Decider::delta_angle( + const std::list & armors, const std::string & camera) +{ + Eigen::Vector2d delta_angle; + if (camera == "left") { + delta_angle[0] = 62 + (new_fov_h_ / 2) - armors.front().center_norm.x * new_fov_h_; + delta_angle[1] = armors.front().center_norm.y * new_fov_v_ - new_fov_v_ / 2; + return delta_angle; + } + + else if (camera == "right") { + delta_angle[0] = -62 + (new_fov_h_ / 2) - armors.front().center_norm.x * new_fov_h_; + delta_angle[1] = armors.front().center_norm.y * new_fov_v_ - new_fov_v_ / 2; + return delta_angle; + } + + else { + delta_angle[0] = 170 + (54.2 / 2) - armors.front().center_norm.x * 54.2; + delta_angle[1] = armors.front().center_norm.y * 44.5 - 44.5 / 2; + return delta_angle; + } +} + +bool Decider::armor_filter(std::list & armors) +{ + if (armors.empty()) return true; + // 过滤非敌方装甲板 + armors.remove_if([&](const auto_aim::Armor & a) { return a.color != enemy_color_; }); + + // 25赛季没有5号装甲板 + armors.remove_if([&](const auto_aim::Armor & a) { return a.name == auto_aim::ArmorName::five; }); + // 不打工程 + // armors.remove_if([&](const auto_aim::Armor & a) { return a.name == auto_aim::ArmorName::two; }); + // 不打前哨站 + armors.remove_if( + [&](const auto_aim::Armor & a) { return a.name == auto_aim::ArmorName::outpost; }); + + // 过滤掉刚复活无敌的装甲板 + armors.remove_if([&](const auto_aim::Armor & a) { + return std::find(invincible_armor_.begin(), invincible_armor_.end(), a.name) != + invincible_armor_.end(); + }); + + return armors.empty(); +} + +void Decider::set_priority(std::list & armors) +{ + if (armors.empty()) return; + + const PriorityMap & priority_map = (mode_ == MODE_ONE) ? mode1 : mode2; + + if (!armors.empty()) { + for (auto & armor : armors) { + armor.priority = priority_map.at(armor.name); + } + } +} + +void Decider::sort(std::vector & detection_queue) +{ + if (detection_queue.empty()) return; + + // 对每个 DetectionResult 调用 armor_filter 和 set_priority + for (auto & dr : detection_queue) { + armor_filter(dr.armors); + set_priority(dr.armors); + + // 对每个 DetectionResult 中的 armors 进行排序 + dr.armors.sort( + [](const auto_aim::Armor & a, const auto_aim::Armor & b) { return a.priority < b.priority; }); + } + + // 根据优先级对 DetectionResult 进行排序 + std::sort( + detection_queue.begin(), detection_queue.end(), + [](const DetectionResult & a, const DetectionResult & b) { + return a.armors.front().priority < b.armors.front().priority; + }); +} + +Eigen::Vector4d Decider::get_target_info( + const std::list & armors, const std::list & targets) +{ + if (armors.empty() || targets.empty()) return Eigen::Vector4d::Zero(); + + auto target = targets.front(); + + for (const auto & armor : armors) { + if (armor.name == target.name) { + return Eigen::Vector4d{ + armor.xyz_in_gimbal[0], armor.xyz_in_gimbal[1], 1, + static_cast(armor.name) + 1}; //避免歧义+1(详见通信协议) + } + } + + return Eigen::Vector4d::Zero(); +} + +void Decider::get_invincible_armor(const std::vector & invincible_enemy_ids) +{ + invincible_armor_.clear(); + + if (invincible_enemy_ids.empty()) return; + + for (const auto & id : invincible_enemy_ids) { + tools::logger()->info("invincible armor id: {}", id); + invincible_armor_.push_back(auto_aim::ArmorName(id - 1)); + } +} + +void Decider::get_auto_aim_target( + std::list & armors, const std::vector & auto_aim_target) +{ + if (auto_aim_target.empty()) return; + + std::vector auto_aim_targets; + + for (const auto & target : auto_aim_target) { + if (target <= 0 || static_cast(target) > auto_aim::ARMOR_NAMES.size()) { + tools::logger()->warn("Received invalid auto_aim target value: {}", int(target)); + continue; + } + auto_aim_targets.push_back(static_cast(target - 1)); + tools::logger()->info("nav send auto_aim target is {}", auto_aim::ARMOR_NAMES[target - 1]); + } + + if (auto_aim_targets.empty()) return; + + armors.remove_if([&](const auto_aim::Armor & a) { + return std::find(auto_aim_targets.begin(), auto_aim_targets.end(), a.name) == + auto_aim_targets.end(); + }); +} + +} // namespace omniperception \ No newline at end of file diff --git a/tasks/omniperception/decider.hpp b/tasks/omniperception/decider.hpp new file mode 100644 index 0000000..9977718 --- /dev/null +++ b/tasks/omniperception/decider.hpp @@ -0,0 +1,96 @@ +#ifndef OMNIPERCEPTION__DECIDER_HPP +#define OMNIPERCEPTION__DECIDER_HPP + +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include +#include + +#include "detection.hpp" +#include "io/camera.hpp" +#include "io/command.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/armor.hpp" +#include "tasks/auto_aim/target.hpp" +#include "tasks/auto_aim/yolo.hpp" + +namespace omniperception +{ +class Decider +{ +public: + Decider(const std::string & config_path); + + io::Command decide( + auto_aim::YOLO & yolo, const Eigen::Vector3d & gimbal_pos, io::USBCamera & usbcam1, + io::USBCamera & usbcam2, io::Camera & back_cammera); + + io::Command decide( + auto_aim::YOLO & yolo, const Eigen::Vector3d & gimbal_pos, io::Camera & back_cammera); + + io::Command decide(const std::vector & detection_queue); + + Eigen::Vector2d delta_angle( + const std::list & armors, const std::string & camera); + + bool armor_filter(std::list & armors); + + void set_priority(std::list & armors); + //对队列中的每一个DetectionResult进行过滤,同时将DetectionResult排序 + void sort(std::vector & detection_queue); + + Eigen::Vector4d get_target_info( + const std::list & armors, const std::list & targets); + + void get_invincible_armor(const std::vector & invincible_enemy_ids); + + void get_auto_aim_target( + std::list & armors, const std::vector & auto_aim_target); + +private: + int img_width_; + int img_height_; + double fov_h_, new_fov_h_; + double fov_v_, new_fov_v_; + int mode_; + int count_; + + auto_aim::Color enemy_color_; + auto_aim::YOLO detector_; + std::vector invincible_armor_; //无敌状态机器人编号,英雄为1,哨兵为6 + + // 定义ArmorName到ArmorPriority的映射类型 + using PriorityMap = std::unordered_map; + + const PriorityMap mode1 = { + {auto_aim::ArmorName::one, auto_aim::ArmorPriority::second}, + {auto_aim::ArmorName::two, auto_aim::ArmorPriority::forth}, + {auto_aim::ArmorName::three, auto_aim::ArmorPriority::first}, + {auto_aim::ArmorName::four, auto_aim::ArmorPriority::first}, + {auto_aim::ArmorName::five, auto_aim::ArmorPriority::third}, + {auto_aim::ArmorName::sentry, auto_aim::ArmorPriority::third}, + {auto_aim::ArmorName::outpost, auto_aim::ArmorPriority::fifth}, + {auto_aim::ArmorName::base, auto_aim::ArmorPriority::fifth}, + {auto_aim::ArmorName::not_armor, auto_aim::ArmorPriority::fifth}}; + + const PriorityMap mode2 = { + {auto_aim::ArmorName::two, auto_aim::ArmorPriority::first}, + {auto_aim::ArmorName::one, auto_aim::ArmorPriority::second}, + {auto_aim::ArmorName::three, auto_aim::ArmorPriority::second}, + {auto_aim::ArmorName::four, auto_aim::ArmorPriority::second}, + {auto_aim::ArmorName::five, auto_aim::ArmorPriority::second}, + {auto_aim::ArmorName::sentry, auto_aim::ArmorPriority::third}, + {auto_aim::ArmorName::outpost, auto_aim::ArmorPriority::third}, + {auto_aim::ArmorName::base, auto_aim::ArmorPriority::third}, + {auto_aim::ArmorName::not_armor, auto_aim::ArmorPriority::third}}; +}; + +enum PriorityMode +{ + MODE_ONE = 1, + MODE_TWO +}; + +} // namespace omniperception + +#endif \ No newline at end of file diff --git a/tasks/omniperception/detection.hpp b/tasks/omniperception/detection.hpp new file mode 100644 index 0000000..b81e969 --- /dev/null +++ b/tasks/omniperception/detection.hpp @@ -0,0 +1,33 @@ +#ifndef OMNIPERCEPTION__DETECTION_HPP +#define OMNIPERCEPTION__DETECTION_HPP + +#include +#include + +#include "tasks/auto_aim/armor.hpp" + +namespace omniperception +{ +//一个识别结果可能包含多个armor,需要排序和过滤。armors, timestamp, delta_yaw, delta_pitch +struct DetectionResult +{ + std::list armors; + std::chrono::steady_clock::time_point timestamp; + double delta_yaw; //rad + double delta_pitch; //rad + + // Assignment operator + DetectionResult & operator=(const DetectionResult & other) + { + if (this != &other) { + armors = other.armors; + timestamp = other.timestamp; + delta_yaw = other.delta_yaw; + delta_pitch = other.delta_pitch; + } + return *this; + } +}; +} // namespace omniperception + +#endif \ No newline at end of file diff --git a/tasks/omniperception/perceptron.cpp b/tasks/omniperception/perceptron.cpp new file mode 100644 index 0000000..60f0d7d --- /dev/null +++ b/tasks/omniperception/perceptron.cpp @@ -0,0 +1,106 @@ +#include "perceptron.hpp" + +#include +#include +#include + +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" + +namespace omniperception +{ +Perceptron::Perceptron( + io::USBCamera * usbcam1, io::USBCamera * usbcam2, io::USBCamera * usbcam3, + io::USBCamera * usbcam4, const std::string & config_path) +: detection_queue_(10), decider_(config_path), stop_flag_(false) +{ + // 初始化 YOLO 模型 + yolo_parallel1_ = std::make_shared(config_path, false); + yolo_parallel2_ = std::make_shared(config_path, false); + yolo_parallel3_ = std::make_shared(config_path, false); + yolo_parallel4_ = std::make_shared(config_path, false); + + std::this_thread::sleep_for(std::chrono::seconds(2)); + // 创建四个线程进行并行推理 + threads_.emplace_back([&] { parallel_infer(usbcam1, yolo_parallel1_); }); + threads_.emplace_back([&] { parallel_infer(usbcam2, yolo_parallel2_); }); + threads_.emplace_back([&] { parallel_infer(usbcam3, yolo_parallel3_); }); + threads_.emplace_back([&] { parallel_infer(usbcam4, yolo_parallel4_); }); + + tools::logger()->info("Perceptron initialized."); +} + +Perceptron::~Perceptron() +{ + { + std::unique_lock lock(mutex_); + stop_flag_ = true; // 设置退出标志 + } + condition_.notify_all(); // 唤醒所有等待的线程 + + // 等待线程结束 + for (auto & t : threads_) { + if (t.joinable()) { + t.join(); + } + } + tools::logger()->info("Perceptron destructed."); +} + +std::vector Perceptron::get_detection_queue() +{ + std::vector result; + DetectionResult temp; + + // 注意:这里的 pop 不阻塞(假设队列为空时会报错或忽略) + while (!detection_queue_.empty()) { + detection_queue_.pop(temp); + result.push_back(std::move(temp)); + } + + return result; +} + +// 将并行推理逻辑移动到类成员函数 +void Perceptron::parallel_infer( + io::USBCamera * cam, std::shared_ptr & yolov8_parallel) +{ + if (!cam) { + tools::logger()->error("Camera pointer is null!"); + return; + } + try { + while (true) { + cv::Mat usb_img; + std::chrono::steady_clock::time_point ts; + + { + std::unique_lock lock(mutex_); + if (stop_flag_) break; // 检查是否需要退出 + } + + cam->read(usb_img, ts); + if (usb_img.empty()) { + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + continue; + } + + auto armors = yolov8_parallel->detect(usb_img); + if (!armors.empty()) { + auto delta_angle = decider_.delta_angle(armors, cam->device_name); + + DetectionResult dr; + dr.armors = std::move(armors); + dr.timestamp = ts; + dr.delta_yaw = delta_angle[0] / 57.3; + dr.delta_pitch = delta_angle[1] / 57.3; + detection_queue_.push(dr); // 推入线程安全队列 + } + } + } catch (const std::exception & e) { + tools::logger()->error("Exception in parallel_infer: {}", e.what()); + } +} + +} // namespace omniperception diff --git a/tasks/omniperception/perceptron.hpp b/tasks/omniperception/perceptron.hpp new file mode 100644 index 0000000..bda95b6 --- /dev/null +++ b/tasks/omniperception/perceptron.hpp @@ -0,0 +1,47 @@ +#ifndef OMNIPERCEPTION__PERCEPTRON_HPP +#define OMNIPERCEPTION__PERCEPTRON_HPP + +#include +#include +#include + +#include "decider.hpp" +#include "detection.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/armor.hpp" +#include "tools/thread_pool.hpp" +#include "tools/thread_safe_queue.hpp" + +namespace omniperception +{ + +class Perceptron +{ +public: + Perceptron( + io::USBCamera * usbcma1, io::USBCamera * usbcam2, io::USBCamera * usbcam3, + io::USBCamera * usbcam4, const std::string & config_path); + + ~Perceptron(); + + std::vector get_detection_queue(); + + void parallel_infer(io::USBCamera * cam, std::shared_ptr & yolo_parallel); + +private: + std::vector threads_; + tools::ThreadSafeQueue detection_queue_; + + std::shared_ptr yolo_parallel1_; + std::shared_ptr yolo_parallel2_; + std::shared_ptr yolo_parallel3_; + std::shared_ptr yolo_parallel4_; + + Decider decider_; + bool stop_flag_; + mutable std::mutex mutex_; + std::condition_variable condition_; +}; + +} // namespace omniperception +#endif \ No newline at end of file diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/auto_aim_test.cpp b/tests/auto_aim_test.cpp new file mode 100644 index 0000000..78b7140 --- /dev/null +++ b/tests/auto_aim_test.cpp @@ -0,0 +1,198 @@ +#include + +#include +#include +#include +#include + +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{config-path c | configs/demo.yaml | yaml配置文件的路径}" + "{start-index s | 0 | 视频起始帧下标 }" + "{end-index e | 0 | 视频结束帧下标 }" + "{@input-path | assets/demo/demo | avi和txt文件的路径}"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto input_path = cli.get(0); + auto config_path = cli.get("config-path"); + auto start_index = cli.get("start-index"); + auto end_index = cli.get("end-index"); + + tools::Plotter plotter; + tools::Exiter exiter; + + auto video_path = fmt::format("{}.avi", input_path); + auto text_path = fmt::format("{}.txt", input_path); + cv::VideoCapture video(video_path); + std::ifstream text(text_path); + + auto_aim::YOLO yolo(config_path); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + + cv::Mat img, drawing; + auto t0 = std::chrono::steady_clock::now(); + + auto_aim::Target last_target; + io::Command last_command; + double last_t = -1; + + video.set(cv::CAP_PROP_POS_FRAMES, start_index); + for (int i = 0; i < start_index; i++) { + double t, w, x, y, z; + text >> t >> w >> x >> y >> z; + } + + for (int frame_count = start_index; !exiter.exit(); frame_count++) { + if (end_index > 0 && frame_count > end_index) break; + + video.read(img); + if (img.empty()) break; + + double t, w, x, y, z; + text >> t >> w >> x >> y >> z; + auto timestamp = t0 + std::chrono::microseconds(int(t * 1e6)); + + /// 自瞄核心逻辑 + + solver.set_R_gimbal2world({w, x, y, z}); + + auto yolo_start = std::chrono::steady_clock::now(); + auto armors = yolo.detect(img, frame_count); + + auto tracker_start = std::chrono::steady_clock::now(); + auto targets = tracker.track(armors, timestamp); + + auto aimer_start = std::chrono::steady_clock::now(); + auto command = aimer.aim(targets, timestamp, 27, false); + + if ( + !targets.empty() && aimer.debug_aim_point.valid && + std::abs(command.yaw - last_command.yaw) * 57.3 < 2) + command.shoot = true; + + if (command.control) last_command = command; + /// 调试输出 + + auto finish = std::chrono::steady_clock::now(); + tools::logger()->info( + "[{}] yolo: {:.1f}ms, tracker: {:.1f}ms, aimer: {:.1f}ms", frame_count, + tools::delta_time(tracker_start, yolo_start) * 1e3, + tools::delta_time(aimer_start, tracker_start) * 1e3, + tools::delta_time(finish, aimer_start) * 1e3); + + tools::draw_text( + img, + fmt::format( + "command is {},{:.2f},{:.2f},shoot:{}", command.control, command.yaw * 57.3, + command.pitch * 57.3, command.shoot), + {10, 60}, {154, 50, 205}); + + Eigen::Quaternion gimbal_q = {w, x, y, z}; + tools::draw_text( + img, + fmt::format( + "gimbal yaw{:.2f}", (tools::eulers(gimbal_q.toRotationMatrix(), 2, 1, 0) * 57.3)[0]), + {10, 90}, {255, 255, 255}); + + nlohmann::json data; + + // 装甲板原始观测数据 + data["armor_num"] = armors.size(); + if (!armors.empty()) { + const auto & armor = armors.front(); + data["armor_x"] = armor.xyz_in_world[0]; + data["armor_y"] = armor.xyz_in_world[1]; + data["armor_yaw"] = armor.ypr_in_world[0] * 57.3; + data["armor_yaw_raw"] = armor.yaw_raw * 57.3; + data["armor_center_x"] = armor.center_norm.x; + data["armor_center_y"] = armor.center_norm.y; + } + + Eigen::Quaternion q{w, x, y, z}; + auto yaw = tools::eulers(q, 2, 1, 0)[0]; + data["gimbal_yaw"] = yaw * 57.3; + data["cmd_yaw"] = command.yaw * 57.3; + data["shoot"] = command.shoot; + + if (!targets.empty()) { + auto target = targets.front(); + + if (last_t == -1) { + last_target = target; + last_t = t; + continue; + } + + std::vector armor_xyza_list; + + // 当前帧target更新后 + armor_xyza_list = target.armor_xyza_list(); + for (const Eigen::Vector4d & xyza : armor_xyza_list) { + auto image_points = + solver.reproject_armor(xyza.head(3), xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 255, 0}); + } + + // aimer瞄准位置 + auto aim_point = aimer.debug_aim_point; + Eigen::Vector4d aim_xyza = aim_point.xyza; + auto image_points = + solver.reproject_armor(aim_xyza.head(3), aim_xyza[3], target.armor_type, target.name); + if (aim_point.valid) tools::draw_points(img, image_points, {0, 0, 255}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["x"] = x[0]; + data["vx"] = x[1]; + data["y"] = x[2]; + data["vy"] = x[3]; + data["z"] = x[4]; + data["vz"] = x[5]; + data["a"] = x[6] * 57.3; + data["w"] = x[7]; + data["r"] = x[8]; + data["l"] = x[9]; + data["h"] = x[10]; + data["last_id"] = target.last_id; + + // 卡方检验数据 + data["residual_yaw"] = target.ekf().data.at("residual_yaw"); + data["residual_pitch"] = target.ekf().data.at("residual_pitch"); + data["residual_distance"] = target.ekf().data.at("residual_distance"); + data["residual_angle"] = target.ekf().data.at("residual_angle"); + data["nis"] = target.ekf().data.at("nis"); + data["nees"] = target.ekf().data.at("nees"); + data["nis_fail"] = target.ekf().data.at("nis_fail"); + data["nees_fail"] = target.ekf().data.at("nees_fail"); + data["recent_nis_failures"] = target.ekf().data.at("recent_nis_failures"); + } + + plotter.plot(data); + + cv::resize(img, img, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("reprojection", img); + auto key = cv::waitKey(30); + if (key == 'q') break; + } + + return 0; +} \ No newline at end of file diff --git a/tests/auto_buff_test.cpp b/tests/auto_buff_test.cpp new file mode 100644 index 0000000..54c9da5 --- /dev/null +++ b/tests/auto_buff_test.cpp @@ -0,0 +1,179 @@ +#include + +#include +#include +#include +#include + +#include "tasks/auto_buff/buff_aimer.hpp" +#include "tasks/auto_buff/buff_detector.hpp" +#include "tasks/auto_buff/buff_solver.hpp" +#include "tasks/auto_buff/buff_target.hpp" +#include "tasks/auto_buff/buff_type.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{config-path c | configs/sentry.yaml | yaml配置文件的路径}" + "{start-index s | 0 | 视频起始帧下标 }" + "{end-index e | 0 | 视频结束帧下标 }" + "{@input-path | | avi和txt文件的路径}"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto input_path = cli.get(0); + auto config_path = cli.get("config-path"); + auto start_index = cli.get("start-index"); + auto end_index = cli.get("end-index"); + + tools::Plotter plotter; + tools::Exiter exiter; + + auto video_path = fmt::format("{}.avi", input_path); + auto text_path = fmt::format("{}.txt", input_path); + cv::VideoCapture video(video_path); + std::ifstream text(text_path); + + auto_buff::Buff_Detector detector(config_path); + auto_buff::Solver solver(config_path); + // auto_buff::SmallTarget target; + auto_buff::BigTarget target; + auto_buff::Aimer aimer(config_path); + + cv::Mat img, drawing; + auto t0 = std::chrono::steady_clock::now(); + + io::Command last_command; + double last_t = -1; + + video.set(cv::CAP_PROP_POS_FRAMES, start_index); + for (int i = 0; i < start_index; i++) { + double t, w, x, y, z; + text >> t >> w >> x >> y >> z; + } + + for (int frame_count = start_index; !exiter.exit(); frame_count++) { + if (end_index > 0 && frame_count > end_index) break; + + video.read(img); + if (img.empty()) break; + + double t, w, x, y, z; + text >> t >> w >> x >> y >> z; + auto timestamp = t0 + std::chrono::microseconds(int(t * 1e6)); + + /// 自瞄核心逻辑 + + solver.set_R_gimbal2world({w, x, y, z}); + + auto power_runes = detector.detect(img); + + solver.solve(power_runes); + + target.get_target(power_runes, timestamp); + + auto target_copy = target; + + auto command = aimer.aim(target_copy, timestamp, 22, false); + + // cboard.send(command); + + // -------------- 调试输出 -------------- + + nlohmann::json data; + + // data["bullet_speed"] = cboard.bullet_speed; + + // buff原始观测数据 + if (power_runes.has_value()) { + const auto & p = power_runes.value(); + data["buff_R_yaw"] = p.ypd_in_world[0]; + data["buff_R_pitch"] = p.ypd_in_world[1]; + data["buff_R_dis"] = p.ypd_in_world[2]; + data["buff_yaw"] = p.ypr_in_world[0] * 57.3; + data["buff_pitch"] = p.ypr_in_world[1] * 57.3; + data["buff_roll"] = p.ypr_in_world[2] * 57.3; + } + + if (!target.is_unsolve()) { + auto & p = power_runes.value(); + + // 显示 + for (int i = 0; i < 4; i++) tools::draw_point(img, p.target().points[i]); + tools::draw_point(img, p.target().center, {0, 0, 255}, 3); + tools::draw_point(img, p.r_center, {0, 0, 255}, 3); + + // 当前帧target更新后buff + auto Rxyz_in_world_now = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.0)); + auto image_points = + solver.reproject_buff(Rxyz_in_world_now, target.ekf_x()[4], target.ekf_x()[5]); + tools::draw_points( + img, std::vector(image_points.begin(), image_points.begin() + 4), {0, 255, 0}); + tools::draw_points( + img, std::vector(image_points.begin() + 4, image_points.end()), {0, 255, 0}); + + // buff瞄准位置(预测) + double dangle = target.ekf_x()[5] - target_copy.ekf_x()[5]; + auto Rxyz_in_world_pre = target.point_buff2world(Eigen::Vector3d(0.0, 0.0, 0.0)); + image_points = + solver.reproject_buff(Rxyz_in_world_pre, target_copy.ekf_x()[4], target_copy.ekf_x()[5]); + tools::draw_points( + img, std::vector(image_points.begin(), image_points.begin() + 4), {255, 0, 0}); + tools::draw_points( + img, std::vector(image_points.begin() + 4, image_points.end()), {255, 0, 0}); + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["R_yaw"] = x[0]; + data["R_V_yaw"] = x[1]; + data["R_pitch"] = x[2]; + data["R_dis"] = x[3]; + data["yaw"] = x[4] * 57.3; + + data["angle"] = x[5] * 57.3; + data["spd"] = x[6] * 57.3; + if (x.size() >= 10) { + data["spd"] = x[6]; + data["a"] = x[7]; + data["w"] = x[8]; + data["fi"] = x[9]; + data["spd0"] = target.spd; + } + } + + // 云台响应情况 + Eigen::Vector3d ypr = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + data["gimbal_yaw"] = ypr[0] * 57.3; + data["gimbal_pitch"] = -ypr[1] * 57.3; + + if (command.control) { + data["cmd_yaw"] = command.yaw * 57.3; + data["cmd_pitch"] = command.pitch * 57.3; + } + + plotter.plot(data); + + cv::imshow("result", img); + + int key = cv::waitKey(1); + if (key == 'q') break; + while (key == ' ') { + int y = cv::waitKey(30); + if (y == 'q') break; + } + } + cv::destroyAllWindows(); + text.close(); // 关闭文件 + + return 0; +} \ No newline at end of file diff --git a/tests/camera_detect_test.cpp b/tests/camera_detect_test.cpp new file mode 100644 index 0000000..4de3854 --- /dev/null +++ b/tests/camera_detect_test.cpp @@ -0,0 +1,61 @@ +#include + +#include +#include + +#include "io/camera.hpp" +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{@config-path | configs/sentry.yaml | yaml配置文件的路径}" + "{tradition t | false | 是否使用传统方法识别}"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + auto use_tradition = cli.get("tradition"); + + tools::Exiter exiter; + + io::Camera camera(config_path); + auto_aim::Detector detector(config_path, true); + auto_aim::YOLO yolo(config_path, true); + + std::chrono::steady_clock::time_point timestamp; + + while (!exiter.exit()) { + cv::Mat img; + std::list armors; + + camera.read(img, timestamp); + + if (img.empty()) break; + + auto last = std::chrono::steady_clock::now(); + + if (use_tradition) + armors = detector.detect(img); + else + armors = yolo.detect(img); + + auto now = std::chrono::steady_clock::now(); + auto dt = tools::delta_time(now, last); + tools::logger()->info("{:.2f} fps", 1 / dt); + + auto key = cv::waitKey(33); + if (key == 'q') break; + } + + return 0; +} \ No newline at end of file diff --git a/tests/camera_test.cpp b/tests/camera_test.cpp new file mode 100644 index 0000000..862eabe --- /dev/null +++ b/tests/camera_test.cpp @@ -0,0 +1,43 @@ +#include "io/camera.hpp" + +#include + +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{config-path c | configs/camera.yaml | yaml配置文件路径 }" + "{d display | | 显示视频流 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + + auto config_path = cli.get("config-path"); + auto display = cli.has("display"); + io::Camera camera(config_path); + + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + auto last_stamp = std::chrono::steady_clock::now(); + while (!exiter.exit()) { + camera.read(img, timestamp); + + auto dt = tools::delta_time(timestamp, last_stamp); + last_stamp = timestamp; + + tools::logger()->info("{:.2f} fps", 1 / dt); + + if (!display) continue; + cv::imshow("img", img); + if (cv::waitKey(1) == 'q') break; + } +} \ No newline at end of file diff --git a/tests/camera_thread_test.cpp b/tests/camera_thread_test.cpp new file mode 100644 index 0000000..1b9ee5f --- /dev/null +++ b/tests/camera_thread_test.cpp @@ -0,0 +1,122 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" +#include "tools/thread_pool.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/ascento.yaml | 位置参数,yaml配置文件路径 }"; + +tools::OrderedQueue frame_queue; + +// 处理detect任务的线程函数 +void detect_frame(tools::Frame && frame, auto_aim::YOLO & yolo) +{ + frame.armors = yolo.detect(frame.img); + frame_queue.enqueue(frame); +} + +int main(int argc, char * argv[]) +{ + tools::Exiter exiter; + tools::Plotter plotter; + // tools::Recorder recorder(100); + + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + // 处理线程函数 + auto process_thread = std::thread([&]() { + tools::Frame process_frame; + while (!exiter.exit()) { + process_frame = frame_queue.dequeue(); + auto img = process_frame.img; + auto armors = process_frame.armors; + auto t = process_frame.t; + + nlohmann::json data; + data["armor_num"] = armors.size(); + + plotter.plot(data); + // cv::resize(img, img, {}, 0.5, 0.5); + // cv::imshow("reprojection", img); + } + }); + + io::Camera camera(config_path); + int num_yolo_thread = 8; + auto yolos = tools::create_yolov8s(config_path, num_yolo_thread, true); + // auto yolos = tools::create_yolo11s(config_path, num_yolo_thread, true); + std::vector yolo_used(num_yolo_thread, false); + tools::ThreadPool thread_pool(num_yolo_thread); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + std::chrono::steady_clock::time_point last_t = std::chrono::steady_clock::now(); + + int frame_id = 0; + + while (!exiter.exit()) { + camera.read(img, t); + auto dt = tools::delta_time(t, last_t); + last_t = t; + + // tools::logger()->info("{:.2f} fps", 1 / dt); + // tools::draw_text(img, fmt::format("{:.2f} fps", 1/dt), {10, 60}, {255, 255, 255}); + nlohmann::json data; + data["fps"] = 1 / dt; + + frame_id++; + + // 将处理任务提交到线程池 + std::mutex yolo_mutex; + thread_pool.enqueue([&, frame_id, t] { + auto_aim::YOLO * yolo = nullptr; + int yolo_id = -1; + for (int i = 0; i < num_yolo_thread; i++) { + if (!yolo_used[i]) { + yolo_used[i] = true; + yolo = &yolos[i]; + yolo_id = i; + break; + } + } + if (yolo) { + tools::Frame frame{frame_id, img.clone(), t}; + detect_frame(std::move(frame), *yolo); + + yolo_used[yolo_id] = false; + } + }); + plotter.plot(data); + + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + return 0; +} diff --git a/tests/cboard_test.cpp b/tests/cboard_test.cpp new file mode 100644 index 0000000..54ea603 --- /dev/null +++ b/tests/cboard_test.cpp @@ -0,0 +1,43 @@ +#include "io/cboard.hpp" + +#include +#include +#include + +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/standard.yaml | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto config_path = cli.get(0); + + tools::Exiter exiter; + + io::CBoard cboard(config_path); + + while (!exiter.exit()) { + auto timestamp = std::chrono::steady_clock::now(); + + std::this_thread::sleep_for(1ms); + + Eigen::Quaterniond q = cboard.imu_at(timestamp); + + Eigen::Vector3d eulers = tools::eulers(q, 2, 1, 0) * 57.3; + tools::logger()->info("z{:.2f} y{:.2f} x{:.2f} degree", eulers[0], eulers[1], eulers[2]); + tools::logger()->info("bullet speed {:.2f} m/s", cboard.bullet_speed); + } + + return 0; +} \ No newline at end of file diff --git a/tests/detector_video_test.cpp b/tests/detector_video_test.cpp new file mode 100644 index 0000000..856fa87 --- /dev/null +++ b/tests/detector_video_test.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#include "tasks/auto_aim/detector.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/plotter.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{config-path c | configs/sentry.yaml | yaml配置文件的路径}" + "{start-index s | 0 | 视频起始帧下标 }" + "{end-index e | 0 | 视频结束帧下标 }" + "{@video_path | | avi路径}" + "{tradition t | false | 是否使用传统方法识别}"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + auto video_path = cli.get(0); + auto config_path = cli.get("config-path"); + auto start_index = cli.get("start-index"); + auto end_index = cli.get("end-index"); + auto use_tradition = cli.get("tradition"); + + tools::Exiter exiter; + tools::Plotter plotter; + + cv::VideoCapture video(video_path); + + auto_aim::Detector detector(config_path); + auto_aim::YOLO yolo(config_path); + + video.set(cv::CAP_PROP_POS_FRAMES, start_index); + + for (int frame_count = start_index; !exiter.exit(); frame_count++) { + if (end_index > 0 && frame_count > end_index) break; + + cv::Mat img; + std::list armors; + video.read(img); + if (img.empty()) break; + // cv::GaussianBlur(img, img, cv::Size(5, 5), 0, 0, cv::BORDER_DEFAULT); + + if (use_tradition) + armors = detector.detect(img, frame_count); + else + armors = yolo.detect(img, frame_count); + + if (!armors.empty()) { + nlohmann::json data; + auto armor = armors.front(); + + data["armor_0_pixel_x"] = armor.points[0].x; + data["armor_0_pixel_y"] = armor.points[0].y; + data["armor_1_pixel_x"] = armor.points[1].x; + data["armor_1_pixel_y"] = armor.points[1].y; + data["armor_2_pixel_x"] = armor.points[2].x; + data["armor_2_pixel_y"] = armor.points[2].y; + data["armor_3_pixel_x"] = armor.points[3].x; + data["armor_3_pixel_y"] = armor.points[3].y; + plotter.plot(data); + } + + auto key = cv::waitKey(33); + if (key == 'q') break; + } + + return 0; +} \ No newline at end of file diff --git a/tests/dm_test.cpp b/tests/dm_test.cpp new file mode 100644 index 0000000..b10b018 --- /dev/null +++ b/tests/dm_test.cpp @@ -0,0 +1,28 @@ +#include +#include + +#include "io/dm_imu/dm_imu.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +using namespace std::chrono_literals; + +int main() +{ + tools::Exiter exiter; + io::DM_IMU imu; + + while (!exiter.exit()) { + auto timestamp = std::chrono::steady_clock::now(); + + std::this_thread::sleep_for(1ms); + + Eigen::Quaterniond q = imu.imu_at(timestamp); + + Eigen::Vector3d eulers = tools::eulers(q, 2, 1, 0) * 57.3; + tools::logger()->info("z{:.2f} y{:.2f} x{:.2f} degree", eulers[0], eulers[1], eulers[2]); + } + + return 0; +} \ No newline at end of file diff --git a/tests/fire_test.cpp b/tests/fire_test.cpp new file mode 100644 index 0000000..0046b53 --- /dev/null +++ b/tests/fire_test.cpp @@ -0,0 +1,70 @@ +#include + +#include "io/gimbal/gimbal.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" +#include "tools/recorder.hpp" +#include "tools/trajectory.hpp" + +// 定义命令行参数 +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + // 读取命令行参数 + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + // 初始化绘图器、录制器、退出器 + tools::Plotter plotter; + tools::Recorder recorder; + tools::Exiter exiter; + + // 初始化云台 + io::Gimbal gimbal(config_path); + io::VisionToGimbal plan; + auto last_t = std::chrono::steady_clock::now(); + plan.yaw = 0; + plan.yaw_vel = 0; + plan.yaw_acc = 0; + plan.pitch = 0; + plan.pitch_vel = 0; + plan.pitch_acc = 0; + + while (!exiter.exit()) { + auto now = std::chrono::steady_clock::now(); + auto gs = gimbal.state(); + if(tools::delta_time(now, last_t) > 1.600) { + plan.mode = 2; + tools::logger()->debug("fire!"); + last_t = now; + } else plan.mode = 1; + + + gimbal.send(plan); + + // -------------- 调试输出 -------------- + + nlohmann::json data; + + if (plan.mode != 0) { + data["shoot"] = plan.mode == 2 ? 1 : 0; + } + + plotter.plot(data); + + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + return 0; +} diff --git a/tests/gimbal_response_test.cpp b/tests/gimbal_response_test.cpp new file mode 100644 index 0000000..98fb887 --- /dev/null +++ b/tests/gimbal_response_test.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#include + +#include "io/cboard.hpp" +#include "io/command.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{delta-angle a | 8 | yaw轴delta角}" + "{circle c | 0.2 | delta_angle的切片数}" + "{signal-mode m | triangle_wave | 发送信号的模式}" + "{axis x | yaw | 发送信号的轴}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }"; + +double yaw_cal(double t) +{ + double A = 7; + double T = 4; // s + + return A * std::sin(2 * M_PI * t / T); // 31是云台yaw初始角度,单位为度 +} + +double pitch_cal(double t) +{ + double A = 7; + double T = 4; // s + + return A * std::sin(2 * M_PI * t / T + M_PI / 2) + 18; // 18是云台pitch初始角度,单位为度 +} + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get(0); + auto delta_angle = cli.get("delta-angle"); + auto circle = cli.get("circle"); + auto signal_mode = cli.get("signal-mode"); + auto axis = cli.get("axis"); + if (cli.has("help") || config_path.empty()) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + + io::CBoard cboard(config_path); + + auto init_angle = 0; + double slice = circle * 100; //切片数=周期*帧率 + auto dangle = delta_angle / slice; + double cmd_angle = init_angle; + + int axis_index = axis == "yaw" ? 0 : 1; // 0 for yaw, 1 for pitch + + double error = 0; + int count = 0; + + io::Command init_command{1, 0, 0, 0}; + cboard.send(init_command); + std::this_thread::sleep_for(5s); //等待云台归零 + + io::Command command{0}; + io::Command last_command{0}; + + double t = 0; + auto last_t = t; + double dt = 0.005; // 5ms, 模拟200fps + + auto t0 = std::chrono::steady_clock::now(); + + while (!exiter.exit()) { + nlohmann::json data; + auto timestamp = std::chrono::steady_clock::now(); + + std::this_thread::sleep_for(1ms); + + Eigen::Quaterniond q = cboard.imu_at(timestamp); + + Eigen::Vector3d eulers = tools::eulers(q, 2, 1, 0); + + if (signal_mode == "triangle_wave") { + if (count == slice) { + cmd_angle = init_angle; + command = {1, 0, 0, 0}; + if (axis_index == 0) + command.yaw = cmd_angle / 57.3; + else + command.pitch = cmd_angle / 57.3; + count = 0; + + } else { + cmd_angle += dangle; + if (axis_index == 0) + command.yaw = cmd_angle / 57.3; + else + command.pitch = cmd_angle / 57.3; + count++; + } + + cboard.send(command); + if (axis_index == 0) { + data["cmd_yaw"] = command.yaw * 57.3; + data["last_cmd_yaw"] = last_command.yaw * 57.3; + data["gimbal_yaw"] = eulers[0] * 57.3; + } else { + data["cmd_pitch"] = command.pitch * 57.3; + data["last_cmd_pitch"] = last_command.pitch * 57.3; + data["gimbal_pitch"] = eulers[1] * 57.3; + } + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + last_command = command; + plotter.plot(data); + std::this_thread::sleep_for(8ms); //模拟自瞄100fps + } + + else if (signal_mode == "step") { + if (count == 300) { + cmd_angle += delta_angle; + count = 0; + } + command = {1, 0, tools::limit_rad(cmd_angle / 57.3), 0}; + count++; + + cboard.send(command); + data["cmd_yaw"] = command.yaw * 57.3; + data["last_cmd_yaw"] = last_command.yaw * 57.3; + data["gimbal_yaw"] = eulers[0] * 57.3; + last_command = command; + plotter.plot(data); + std::this_thread::sleep_for(8ms); //模拟自瞄100fps + } + + else if (signal_mode == "circle") { + std::cout << "t: " << t << std::endl; + command.yaw = yaw_cal(t) / 57.3; + command.pitch = pitch_cal(t) / 57.3; + command.control = 1; + command.shoot = 0; + t += dt; + if (t - last_t > 2) { + t += 2.4; + last_t = t; + } + cboard.send(command); + + data["t"] = t; + data["cmd_yaw"] = command.yaw * 57.3; + data["cmd_pitch"] = command.pitch * 57.3; + data["gimbal_yaw"] = eulers[0] * 57.3; + data["gimbal_pitch"] = eulers[1] * 57.3; + plotter.plot(data); + std::this_thread::sleep_for(9ms); + } + } + return 0; +} \ No newline at end of file diff --git a/tests/gimbal_test.cpp b/tests/gimbal_test.cpp new file mode 100644 index 0000000..387620a --- /dev/null +++ b/tests/gimbal_test.cpp @@ -0,0 +1,99 @@ +#include "io/gimbal/gimbal.hpp" + +#include +#include +#include + +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{f | | 是否开火}" + "{@config-path | | yaml配置文件路径 }"; + +using namespace std::chrono_literals; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto test_fire = cli.get("f"); + auto config_path = cli.get("@config-path"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + + io::Gimbal gimbal(config_path); + + auto t0 = std::chrono::steady_clock::now(); + auto last_mode = gimbal.mode(); + uint16_t last_bullet_count = 0; + + auto fire = false; + auto fire_count = 0; + auto fire_stamp = std::chrono::steady_clock::now(); + auto first_fired = false; + + while (!exiter.exit()) { + auto mode = gimbal.mode(); + + if (mode != last_mode) { + tools::logger()->info("Gimbal mode changed: {}", gimbal.str(mode)); + last_mode = mode; + } + + auto t = std::chrono::steady_clock::now(); + auto state = gimbal.state(); + auto q = gimbal.q(t); + auto ypr = tools::eulers(q, 2, 1, 0); + + auto fired = state.bullet_count > last_bullet_count; + last_bullet_count = state.bullet_count; + + if (!first_fired && fired) { + first_fired = true; + tools::logger()->info("Gimbal first fired after: {:.3f}s", tools::delta_time(t, fire_stamp)); + } + + if (fire && fire_count > 20) { + // 0.2 s + fire = false; + fire_count = 0; + } else if (!fire && fire_count > 100) { + // 1s + fire = true; + fire_count = 0; + fire_stamp = t; + first_fired = false; + } + fire_count++; + + gimbal.send(true, test_fire && fire, 1, 0, 0, 0, 0, 0); + + nlohmann::json data; + data["q_yaw"] = ypr[0]; + data["q_pitch"] = ypr[1]; + data["yaw"] = state.yaw; + data["vyaw"] = state.yaw_vel; + data["pitch"] = state.pitch; + data["vpitch"] = state.pitch_vel; + data["bullet_speed"] = state.bullet_speed; + data["bullet_count"] = state.bullet_count; + data["fired"] = fired ? 1 : 0; + data["fire"] = test_fire && fire ? 1 : 0; + data["t"] = tools::delta_time(t, t0); + plotter.plot(data); + + std::this_thread::sleep_for(9ms); + } + + gimbal.send(false, false, 0, 0, 0, 0, 0, 0); + + return 0; +} \ No newline at end of file diff --git a/tests/handeye_test.cpp b/tests/handeye_test.cpp new file mode 100644 index 0000000..9fd9f4d --- /dev/null +++ b/tests/handeye_test.cpp @@ -0,0 +1,75 @@ + +#include +#include + +#include // 必须在opencv2/core/eigen.hpp上面 +#include +#include +#include +#include + +#include "io/camera.hpp" +#include "io/cboard.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{config-path c | configs/handeye.yaml | yaml配置文件路径 }" + "{d display | | 显示视频流 }"; + +// 世界坐标到像素坐标的转换 + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + + auto config_path = cli.get("config-path"); + auto display = cli.has("display"); + auto yaml = YAML::LoadFile(config_path); + auto height = yaml["height"].as(); + auto grid_num = yaml["grid_num"].as(); + auto grid_size = yaml["grid_size"].as(); + auto delay = yaml["delay"].as(); + io::CBoard cboard(config_path); + io::Camera camera(config_path); + auto_aim::Solver solver(config_path); + + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point t; + std::vector points; + for (int x = 0; x < grid_num; x++) { + for (int y = 0; y < grid_num; y++) { + points.emplace_back(x * grid_size, y * grid_size - grid_num * grid_size / 2, -height); + points.emplace_back(-x * grid_size, y * grid_size - grid_num * grid_size / 2, -height); + } + } + while (!exiter.exit()) { + camera.read(img, t); + q = cboard.imu_at(t - 1ms * delay); + solver.set_R_gimbal2world(q); + cv::Mat result = img.clone(); + std::vector projectedPoints = solver.world2pixel(points); + for (const auto & point : projectedPoints) { + if (point.x >= 0 && point.x < result.cols && point.y >= 0 && point.y < result.rows) { + cv::circle(result, point, 3, cv::Scalar(255, 255, 255), -1); + } + } + Eigen::Vector3d euler = solver.R_gimbal2world().eulerAngles(2, 1, 0) * 180.0 / M_PI; + tools::draw_text(result, fmt::format("yaw {:.2f}", euler[0]), {40, 40}, {0, 0, 255}); + tools::draw_text(result, fmt::format("pitch {:.2f}", euler[1]), {40, 80}, {0, 0, 255}); + tools::draw_text(result, fmt::format("roll {:.2f}", euler[2]), {40, 120}, {0, 0, 255}); + if (!display) continue; + cv::imshow("result", result); + if (cv::waitKey(1) == 'q') break; + } +} \ No newline at end of file diff --git a/tests/minimum_vision_system.cpp b/tests/minimum_vision_system.cpp new file mode 100644 index 0000000..35512f4 --- /dev/null +++ b/tests/minimum_vision_system.cpp @@ -0,0 +1,153 @@ +#include +#include +#include + +#include "io/camera.hpp" +#include "io/dm_imu/dm_imu.hpp" +#include "tasks/auto_aim/aimer.hpp" +#include "tasks/auto_aim/multithread/mt_detector.hpp" +#include "tasks/auto_aim/shooter.hpp" +#include "tasks/auto_aim/solver.hpp" +#include "tasks/auto_aim/tracker.hpp" +#include "tools/exiter.hpp" +#include "tools/img_tools.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + auto config_path = cli.get("@config-path"); + + tools::Exiter exiter; + tools::Plotter plotter; + io::Camera camera(config_path); + io::DM_IMU dm_imu; + + auto_aim::multithread::MultiThreadDetector detector(config_path); + auto_aim::Solver solver(config_path); + auto_aim::Tracker tracker(config_path, solver); + auto_aim::Aimer aimer(config_path); + auto_aim::Shooter shooter(config_path); + + auto detect_thread = std::thread([&]() { + cv::Mat img; + std::chrono::steady_clock::time_point t; + + while (!exiter.exit()) { + camera.read(img, t); + detector.push(img, t); + } + }); + + auto last_t = std::chrono::steady_clock::now(); + nlohmann::json data; + + while (!exiter.exit()) { + auto [img, armors, t] = detector.debug_pop(); + + Eigen::Quaterniond q = dm_imu.imu_at(t); + + solver.set_R_gimbal2world(q); + + Eigen::Vector3d gimbal_pos = tools::eulers(solver.R_gimbal2world(), 2, 1, 0); + + auto targets = tracker.track(armors, t); + + auto command = aimer.aim(targets, t, 22); + + shooter.shoot(command, aimer, targets, gimbal_pos); + + auto dt = tools::delta_time(t, last_t); + last_t = t; + + data["dt"] = dt; + data["fps"] = 1 / dt; + plotter.plot(data); + // 装甲板原始观测数据 + data["armor_num"] = armors.size(); + if (!armors.empty()) { + auto min_x = 1e10; + auto & armor = armors.front(); + for (auto & a : armors) { + if (a.center.x < min_x) { + min_x = a.center.x; + armor = a; + } + } //always left + solver.solve(armor); + data["armor_x"] = armor.xyz_in_world[0]; + data["armor_y"] = armor.xyz_in_world[1]; + data["armor_yaw"] = armor.ypr_in_world[0] * 57.3; + data["armor_yaw_raw"] = armor.yaw_raw * 57.3; + } + + if (!targets.empty()) { + auto target = targets.front(); + tools::draw_text(img, fmt::format("[{}]", tracker.state()), {10, 30}, {255, 255, 255}); + + // 当前帧target更新后 + std::vector armor_xyza_list = target.armor_xyza_list(); + for (const Eigen::Vector4d & xyza : armor_xyza_list) { + auto image_points = + solver.reproject_armor(xyza.head(3), xyza[3], target.armor_type, target.name); + tools::draw_points(img, image_points, {0, 255, 0}); + } + + // aimer瞄准位置 + auto aim_point = aimer.debug_aim_point; + Eigen::Vector4d aim_xyza = aim_point.xyza; + auto image_points = + solver.reproject_armor(aim_xyza.head(3), aim_xyza[3], target.armor_type, target.name); + if (aim_point.valid) + tools::draw_points(img, image_points, {0, 0, 255}); // red + else + tools::draw_points(img, image_points, {255, 0, 0}); // blue + + // 观测器内部数据 + Eigen::VectorXd x = target.ekf_x(); + data["x"] = x[0]; + data["vx"] = x[1]; + data["y"] = x[2]; + data["vy"] = x[3]; + data["z"] = x[4]; + data["vz"] = x[5]; + data["a"] = x[6] * 57.3; + data["w"] = x[7]; + data["r"] = x[8]; + data["l"] = x[9]; + data["h"] = x[10]; + data["last_id"] = target.last_id; + data["distance"] = std::sqrt(x[0] * x[0] + x[2] * x[2] + x[4] * x[4]); + + // 卡方检验数据 + data["residual_yaw"] = target.ekf().data.at("residual_yaw"); + data["residual_pitch"] = target.ekf().data.at("residual_pitch"); + data["residual_distance"] = target.ekf().data.at("residual_distance"); + data["residual_angle"] = target.ekf().data.at("residual_angle"); + data["nis"] = target.ekf().data.at("nis"); + data["nees"] = target.ekf().data.at("nees"); + data["nis_fail"] = target.ekf().data.at("nis_fail"); + data["nees_fail"] = target.ekf().data.at("nees_fail"); + data["recent_nis_failures"] = target.ekf().data.at("recent_nis_failures"); + } + cv::resize(img, img, {}, 0.5, 0.5); // 显示时缩小图片尺寸 + cv::imshow("reprojection", img); + auto key = cv::waitKey(1); + if (key == 'q') break; + } + + detect_thread.join(); + + return 0; +} \ No newline at end of file diff --git a/tests/multi_usbcamera_test.cpp b/tests/multi_usbcamera_test.cpp new file mode 100644 index 0000000..6476f68 --- /dev/null +++ b/tests/multi_usbcamera_test.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include "io/camera.hpp" +#include "io/usbcamera/usbcamera.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }" + "{d display | | 显示视频流 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + tools::Exiter exiter; + + auto config_path = cli.get(0); + auto display = cli.has("display"); + + io::USBCamera usbcam1("video0", config_path); + io::USBCamera usbcam2("video2", config_path); + io::Camera camera("configs/camera.yaml"); + + cv::Mat img1, img2, img3; + std::chrono::steady_clock::time_point timestamp; + auto last_stamp = std::chrono::steady_clock::now(); + while (!exiter.exit()) { + usbcam1.read(img1, timestamp); + usbcam2.read(img2, timestamp); + camera.read(img3, timestamp); + + auto dt = tools::delta_time(timestamp, last_stamp); + last_stamp = timestamp; + + tools::logger()->info("{:.2f} fps", 1 / dt); + + if (!display) continue; + cv::imshow("img1", img1); + cv::imshow("img2", img2); + cv::imshow("img3", img3); + + if (cv::waitKey(1) == 'q') break; + } +} \ No newline at end of file diff --git a/tests/planner_test.cpp b/tests/planner_test.cpp new file mode 100644 index 0000000..95e142f --- /dev/null +++ b/tests/planner_test.cpp @@ -0,0 +1,79 @@ +#include "tasks/auto_aim/planner/planner.hpp" + +#include +#include +#include +#include + +#include "io/gimbal/gimbal.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{d | 3.0 | Target距离(m) }" + "{w | 5.0 | Target角速度(rad/s) }" + "{@config-path | | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get("@config-path"); + auto d = cli.get("d"); + auto w = cli.get("w"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + + io::Gimbal gimbal(config_path); + auto_aim::Planner planner(config_path); + auto_aim::Target target(d, w, 0.2, 0.1); + + auto t0 = std::chrono::steady_clock::now(); + + while (!exiter.exit()) { + target.predict(0.01); + + auto gs = gimbal.state(); + auto plan = planner.plan(target, gs.bullet_speed); + + gimbal.send( + plan.control, plan.fire, plan.yaw, plan.yaw_vel, plan.yaw_acc, plan.pitch, plan.pitch_vel, + plan.pitch_acc); + + nlohmann::json data; + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + + data["gimbal_yaw"] = gs.yaw; + data["gimbal_yaw_vel"] = gs.yaw_vel; + data["gimbal_pitch"] = gs.pitch; + data["gimbal_pitch_vel"] = gs.pitch_vel; + + data["target_yaw"] = plan.target_yaw; + data["target_pitch"] = plan.target_pitch; + + data["plan_yaw"] = plan.yaw; + data["plan_yaw_vel"] = plan.yaw_vel; + data["plan_yaw_acc"] = plan.yaw_acc; + + data["plan_pitch"] = plan.pitch; + data["plan_pitch_vel"] = plan.pitch_vel; + data["plan_pitch_acc"] = plan.pitch_acc; + + plotter.plot(data); + + std::this_thread::sleep_for(10ms); + } + + gimbal.send(false, false, 0, 0, 0, 0, 0, 0); + + return 0; +} \ No newline at end of file diff --git a/tests/planner_test_offline.cpp b/tests/planner_test_offline.cpp new file mode 100644 index 0000000..ae11bfb --- /dev/null +++ b/tests/planner_test_offline.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "tasks/auto_aim/planner/planner.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" +#include "tools/plotter.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明 }" + "{d | 3.0 | Target距离(m) }" + "{w | 5.0 | Target角速度(rad/s) }" + "{@config-path | | yaml配置文件路径 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + auto config_path = cli.get("@config-path"); + auto d = cli.get("d"); + auto w = cli.get("w"); + if (cli.has("help") || !cli.has("@config-path")) { + cli.printMessage(); + return 0; + } + + tools::Exiter exiter; + tools::Plotter plotter; + + auto_aim::Planner planner(config_path); + auto_aim::Target target(d, w, 0.2, 0.1); + + auto t0 = std::chrono::steady_clock::now(); + + while (!exiter.exit()) { + target.predict(0.01); + + auto plan = planner.plan(target, 22); + + nlohmann::json data; + data["t"] = tools::delta_time(std::chrono::steady_clock::now(), t0); + + data["target_yaw"] = plan.target_yaw; + data["target_pitch"] = plan.target_pitch; + + data["plan_yaw"] = plan.yaw; + data["plan_yaw_vel"] = plan.yaw_vel; + data["plan_yaw_acc"] = plan.yaw_acc; + + data["plan_pitch"] = plan.pitch; + data["plan_pitch_vel"] = plan.pitch_vel; + data["plan_pitch_acc"] = plan.pitch_acc; + + plotter.plot(data); + + std::this_thread::sleep_for(10ms); + } + + return 0; +} \ No newline at end of file diff --git a/tests/publish_test.cpp b/tests/publish_test.cpp new file mode 100644 index 0000000..c13e634 --- /dev/null +++ b/tests/publish_test.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include "io/ros2/ros2.hpp" +#include "tasks/auto_aim/armor.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" + +int main(int argc, char ** argv) +{ + tools::Exiter exiter; + io::ROS2 ros2; + + double i = 0; + while (!exiter.exit()) { + Eigen::Vector4d data{i, i + 1, 1, auto_aim::ArmorName::sentry + 1}; + ros2.publish(data); + i++; + + std::this_thread::sleep_for(std::chrono::seconds(1)); + if (i > 1000) break; + } + return 0; +} diff --git a/tests/subscribe_test.cpp b/tests/subscribe_test.cpp new file mode 100644 index 0000000..70a8335 --- /dev/null +++ b/tests/subscribe_test.cpp @@ -0,0 +1,26 @@ +#include +#include + +#include "io/ros2/ros2.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" + +int main(int argc, char ** argv) +{ + tools::Exiter exiter; + io::ROS2 ros2; + + int i = 0; + while (!exiter.exit()) { + auto x = ros2.subscribe_enemy_status(); + // tools::logger()->info("invincible enemy ids size is{}", x.size()); + for (const auto & id : x) { + tools::logger()->info("id:{}", id); + } + // i++; + + std::this_thread::sleep_for(std::chrono::microseconds(500)); + // if (i > 1000) break; + } + return 0; +} diff --git a/tests/topic_loop_test.cpp b/tests/topic_loop_test.cpp new file mode 100644 index 0000000..59ea7fa --- /dev/null +++ b/tests/topic_loop_test.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include "io/ros2/ros2.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" + +int main(int argc, char ** argv) +{ + tools::Exiter exiter; + io::ROS2 ros2; + rclcpp::Clock clock; + auto string_publisher = + ros2.create_publisher("temp_node", "enemy_status", 10); + + int i = 0; + while (!exiter.exit()) { + sp_msgs::msg::EnemyStatusMsg msg; + msg.invincible_enemy_ids = {1, 2, 3}; + msg.timestamp = clock.now(); + string_publisher->publish(msg); + RCLCPP_INFO( + rclcpp::get_logger("msg send timestamp is"), "msg.timestamp: %d.%09u", msg.timestamp.sec, + msg.timestamp.nanosec); + + i++; + std::this_thread::sleep_for(std::chrono::microseconds(5)); + + if (i % 3 == 0) { + auto x = ros2.subscribe_enemy_status(); + // tools::logger()->info("invincible enemy ids size is{}", x.size()); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + if (i > 1000) break; + } + + return 0; +} diff --git a/tests/usbcamera_detect_test.cpp b/tests/usbcamera_detect_test.cpp new file mode 100644 index 0000000..f24497f --- /dev/null +++ b/tests/usbcamera_detect_test.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "io/usbcamera/usbcamera.hpp" +#include "tasks/auto_aim/yolo.hpp" +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{name n | video0 | 端口名称 }" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }" + "{d display | | 显示视频流 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + tools::Exiter exiter; + + auto config_path = cli.get(0); + auto device_name = cli.get("name"); + auto display = cli.has("display"); + + io::USBCamera usbcam(device_name, config_path); + + auto_aim::YOLO yolo(config_path, true); + + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + auto last_stamp = std::chrono::steady_clock::now(); + while (!exiter.exit()) { + usbcam.read(img, timestamp); + yolo.detect(img); + + auto dt = tools::delta_time(timestamp, last_stamp); + last_stamp = timestamp; + + tools::logger()->info("{:.2f} fps", 1 / dt); + std::this_thread::sleep_for(10ms); + + if (!display) continue; + cv::imshow("img", img); + if (cv::waitKey(1) == 'q') break; + } +} \ No newline at end of file diff --git a/tests/usbcamera_test.cpp b/tests/usbcamera_test.cpp new file mode 100644 index 0000000..5afbd34 --- /dev/null +++ b/tests/usbcamera_test.cpp @@ -0,0 +1,49 @@ +#include "io/usbcamera/usbcamera.hpp" + +#include +#include + +#include "tools/exiter.hpp" +#include "tools/logger.hpp" +#include "tools/math_tools.hpp" + +using namespace std::chrono_literals; + +const std::string keys = + "{help h usage ? | | 输出命令行参数说明}" + "{name n | video0 | 端口名称 }" + "{@config-path | configs/sentry.yaml | 位置参数,yaml配置文件路径 }" + "{d display | | 显示视频流 }"; + +int main(int argc, char * argv[]) +{ + cv::CommandLineParser cli(argc, argv, keys); + if (cli.has("help")) { + cli.printMessage(); + return 0; + } + tools::Exiter exiter; + + auto config_path = cli.get(0); + auto device_name = cli.get("name"); + auto display = cli.has("display"); + + io::USBCamera usbcam(device_name, config_path); + + cv::Mat img; + std::chrono::steady_clock::time_point timestamp; + auto last_stamp = std::chrono::steady_clock::now(); + while (!exiter.exit()) { + usbcam.read(img, timestamp); + + auto dt = tools::delta_time(timestamp, last_stamp); + last_stamp = timestamp; + + tools::logger()->info("{:.2f} fps", 1 / dt); + std::this_thread::sleep_for(10ms); + + if (!display) continue; + cv::imshow("img", img); + if (cv::waitKey(1) == 'q') break; + } +} \ No newline at end of file diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..eab3782 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) + +add_library(tools OBJECT + exiter.cpp + extended_kalman_filter.cpp + ransac_sine_fitter.cpp + img_tools.cpp + math_tools.cpp + plotter.cpp + trajectory.cpp + recorder.cpp + logger.cpp + pid.cpp + crc.cpp +) \ No newline at end of file diff --git a/tools/crc.cpp b/tools/crc.cpp new file mode 100644 index 0000000..bbea7ef --- /dev/null +++ b/tools/crc.cpp @@ -0,0 +1,91 @@ +#include "crc.hpp" + +constexpr uint8_t CRC8_INIT = 0xff; +const uint8_t CRC8_TABLE[256] = { + 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, + 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, + 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62, + 0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, + 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07, + 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, + 0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, + 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, + 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, + 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, + 0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, + 0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, + 0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, + 0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, + 0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, + 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35, +}; + +constexpr uint16_t CRC16_INIT = 0xffff; +const uint16_t CRC16_TABLE[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, + 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, + 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, + 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, + 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, + 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, + 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, + 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, + 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, + 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, + 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, + 0x3de3, 0x2c6a, 0x1ef1, 0x0f78}; + +namespace tools +{ +uint8_t get_crc8(const uint8_t * data, uint16_t len) +{ + uint8_t crc8 = CRC8_INIT; + uint8_t byte; + uint8_t i; + + while (len--) { + byte = *data++; + i = crc8 ^ byte; + crc8 = CRC8_TABLE[i]; + } + + return crc8; +} + +bool check_crc8(const uint8_t * data, uint16_t len) +{ + return get_crc8(data, len - 1) == data[len - 1]; +} + +uint16_t get_crc16(const uint8_t * data, uint32_t len) +{ + uint16_t crc16 = CRC16_INIT; + uint8_t byte; + uint8_t i; + + while (len--) { + byte = *data++; + i = (crc16 ^ byte) & 0x00ff; + crc16 = (crc16 >> 8) ^ CRC16_TABLE[i]; + } + + return crc16; +} + +bool check_crc16(const uint8_t * data, uint32_t len) +{ + uint16_t crc16 = (data[len - 1] << 8) | data[len - 2]; + return get_crc16(data, len - 2) == crc16; +} + +} // namespace tools \ No newline at end of file diff --git a/tools/crc.hpp b/tools/crc.hpp new file mode 100644 index 0000000..2d71cf8 --- /dev/null +++ b/tools/crc.hpp @@ -0,0 +1,22 @@ +#ifndef TOOLS__CRC_HPP +#define TOOLS__CRC_HPP + +#include + +namespace tools +{ +// len不包括crc8 +uint8_t get_crc8(const uint8_t * data, uint16_t len); + +// len包括crc8 +bool check_crc8(const uint8_t * data, uint16_t len); + +// len不包括crc16 +uint16_t get_crc16(const uint8_t * data, uint32_t len); + +// len包括crc16 +bool check_crc16(const uint8_t * data, uint32_t len); + +} // namespace tools + +#endif // TOOLS__CRC_HPP diff --git a/tools/exiter.cpp b/tools/exiter.cpp new file mode 100644 index 0000000..a4d1d5d --- /dev/null +++ b/tools/exiter.cpp @@ -0,0 +1,20 @@ +#include "exiter.hpp" + +#include +#include + +namespace tools +{ +bool exit_ = false; +bool exiter_inited_ = false; + +Exiter::Exiter() +{ + if (exiter_inited_) throw std::runtime_error("Multiple Exiter instances!"); + std::signal(SIGINT, [](int) { exit_ = true; }); + exiter_inited_ = true; +} + +bool Exiter::exit() const { return exit_; } + +} // namespace tools \ No newline at end of file diff --git a/tools/exiter.hpp b/tools/exiter.hpp new file mode 100644 index 0000000..1b0de77 --- /dev/null +++ b/tools/exiter.hpp @@ -0,0 +1,16 @@ +#ifndef TOOLS__EXITER_HPP +#define TOOLS__EXITER_HPP + +namespace tools +{ +class Exiter +{ +public: + Exiter(); + + bool exit() const; +}; + +} // namespace tools + +#endif // TOOLS__EXITER_HPP \ No newline at end of file diff --git a/tools/extended_kalman_filter.cpp b/tools/extended_kalman_filter.cpp new file mode 100644 index 0000000..b73f40f --- /dev/null +++ b/tools/extended_kalman_filter.cpp @@ -0,0 +1,94 @@ +#include "extended_kalman_filter.hpp" + +#include + +namespace tools +{ +ExtendedKalmanFilter::ExtendedKalmanFilter( + const Eigen::VectorXd & x0, const Eigen::MatrixXd & P0, + std::function x_add) +: x(x0), P(P0), I(Eigen::MatrixXd::Identity(x0.rows(), x0.rows())), x_add(x_add) +{ + data["residual_yaw"] = 0.0; + data["residual_pitch"] = 0.0; + data["residual_distance"] = 0.0; + data["residual_angle"] = 0.0; + data["nis"] = 0.0; + data["nees"] = 0.0; + data["nis_fail"] = 0.0; + data["nees_fail"] = 0.0; + data["recent_nis_failures"] = 0.0; +} + +Eigen::VectorXd ExtendedKalmanFilter::predict(const Eigen::MatrixXd & F, const Eigen::MatrixXd & Q) +{ + return predict(F, Q, [&](const Eigen::VectorXd & x) { return F * x; }); +} + +Eigen::VectorXd ExtendedKalmanFilter::predict( + const Eigen::MatrixXd & F, const Eigen::MatrixXd & Q, + std::function f) +{ + P = F * P * F.transpose() + Q; + x = f(x); + return x; +} + +Eigen::VectorXd ExtendedKalmanFilter::update( + const Eigen::VectorXd & z, const Eigen::MatrixXd & H, const Eigen::MatrixXd & R, + std::function z_subtract) +{ + return update(z, H, R, [&](const Eigen::VectorXd & x) { return H * x; }, z_subtract); +} + +Eigen::VectorXd ExtendedKalmanFilter::update( + const Eigen::VectorXd & z, const Eigen::MatrixXd & H, const Eigen::MatrixXd & R, + std::function h, + std::function z_subtract) +{ + Eigen::VectorXd x_prior = x; + Eigen::MatrixXd K = P * H.transpose() * (H * P * H.transpose() + R).inverse(); + + // Stable Compution of the Posterior Covariance + // https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python/blob/master/07-Kalman-Filter-Math.ipynb + P = (I - K * H) * P * (I - K * H).transpose() + K * R * K.transpose(); + + x = x_add(x, K * z_subtract(z, h(x))); + + /// 卡方检验 + Eigen::VectorXd residual = z_subtract(z, h(x)); + // 新增检验 + Eigen::MatrixXd S = H * P * H.transpose() + R; + double nis = residual.transpose() * S.inverse() * residual; + double nees = (x - x_prior).transpose() * P.inverse() * (x - x_prior); + + // 卡方检验阈值(自由度=4,取置信水平95%) + constexpr double nis_threshold = 0.711; + constexpr double nees_threshold = 0.711; + + if (nis > nis_threshold) nis_count_++, data["nis_fail"] = 1; + if (nees > nees_threshold) nees_count_++, data["nees_fail"] = 1; + total_count_++; + last_nis = nis; + + recent_nis_failures.push_back(nis > nis_threshold ? 1 : 0); + + if (recent_nis_failures.size() > window_size) { + recent_nis_failures.pop_front(); + } + + int recent_failures = std::accumulate(recent_nis_failures.begin(), recent_nis_failures.end(), 0); + double recent_rate = static_cast(recent_failures) / recent_nis_failures.size(); + + data["residual_yaw"] = residual[0]; + data["residual_pitch"] = residual[1]; + data["residual_distance"] = residual[2]; + data["residual_angle"] = residual[3]; + data["nis"] = nis; + data["nees"] = nees; + data["recent_nis_failures"] = recent_rate; + + return x; +} + +} // namespace tools \ No newline at end of file diff --git a/tools/extended_kalman_filter.hpp b/tools/extended_kalman_filter.hpp new file mode 100644 index 0000000..2fed923 --- /dev/null +++ b/tools/extended_kalman_filter.hpp @@ -0,0 +1,57 @@ +#ifndef TOOLS__EXTENDED_KALMAN_FILTER_HPP +#define TOOLS__EXTENDED_KALMAN_FILTER_HPP + +#include +#include +#include +#include + +namespace tools +{ +class ExtendedKalmanFilter +{ +public: + Eigen::VectorXd x; + Eigen::MatrixXd P; + + ExtendedKalmanFilter() = default; + + ExtendedKalmanFilter( + const Eigen::VectorXd & x0, const Eigen::MatrixXd & P0, + std::function x_add = + [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) { return a + b; }); + + Eigen::VectorXd predict(const Eigen::MatrixXd & F, const Eigen::MatrixXd & Q); + + Eigen::VectorXd predict( + const Eigen::MatrixXd & F, const Eigen::MatrixXd & Q, + std::function f); + + Eigen::VectorXd update( + const Eigen::VectorXd & z, const Eigen::MatrixXd & H, const Eigen::MatrixXd & R, + std::function z_subtract = + [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) { return a - b; }); + + Eigen::VectorXd update( + const Eigen::VectorXd & z, const Eigen::MatrixXd & H, const Eigen::MatrixXd & R, + std::function h, + std::function z_subtract = + [](const Eigen::VectorXd & a, const Eigen::VectorXd & b) { return a - b; }); + + std::map data; //卡方检验数据 + std::deque recent_nis_failures{0}; + size_t window_size = 100; + double last_nis; + +private: + Eigen::MatrixXd I; + std::function x_add; + + int nees_count_ = 0; + int nis_count_ = 0; + int total_count_ = 0; +}; + +} // namespace tools + +#endif // TOOLS__EXTENDED_KALMAN_FILTER_HPP \ No newline at end of file diff --git a/tools/img_tools.cpp b/tools/img_tools.cpp new file mode 100644 index 0000000..81545f9 --- /dev/null +++ b/tools/img_tools.cpp @@ -0,0 +1,31 @@ +#include "img_tools.hpp" + +namespace tools +{ +void draw_point(cv::Mat & img, const cv::Point & point, const cv::Scalar & color, int radius) +{ + cv::circle(img, point, radius, color, -1); +} + +void draw_points( + cv::Mat & img, const std::vector & points, const cv::Scalar & color, int thickness) +{ + std::vector> contours = {points}; + cv::drawContours(img, contours, -1, color, thickness); +} + +void draw_points( + cv::Mat & img, const std::vector & points, const cv::Scalar & color, int thickness) +{ + std::vector int_points(points.begin(), points.end()); + draw_points(img, int_points, color, thickness); +} + +void draw_text( + cv::Mat & img, const std::string & text, const cv::Point & point, const cv::Scalar & color, + double font_scale, int thickness) +{ + cv::putText(img, text, point, cv::FONT_HERSHEY_SIMPLEX, font_scale, color, thickness); +} + +} // namespace tools \ No newline at end of file diff --git a/tools/img_tools.hpp b/tools/img_tools.hpp new file mode 100644 index 0000000..13a3e8d --- /dev/null +++ b/tools/img_tools.hpp @@ -0,0 +1,27 @@ +#ifndef TOOLS__IMG_TOOLS_HPP +#define TOOLS__IMG_TOOLS_HPP + +#include +#include +#include + +namespace tools +{ +void draw_point( + cv::Mat & img, const cv::Point & point, const cv::Scalar & color = {0, 0, 255}, int radius = 3); + +void draw_points( + cv::Mat & img, const std::vector & points, const cv::Scalar & color = {0, 0, 255}, + int thickness = 2); + +void draw_points( + cv::Mat & img, const std::vector & points, const cv::Scalar & color = {0, 0, 255}, + int thickness = 2); + +void draw_text( + cv::Mat & img, const std::string & text, const cv::Point & point, + const cv::Scalar & color = {0, 255, 255}, double font_scale = 1.0, int thickness = 2); + +} // namespace tools + +#endif // TOOLS__IMG_TOOLS_HPP \ No newline at end of file diff --git a/tools/logger.cpp b/tools/logger.cpp new file mode 100644 index 0000000..2cb9c79 --- /dev/null +++ b/tools/logger.cpp @@ -0,0 +1,35 @@ +#include "logger.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace tools +{ +std::shared_ptr logger_ = nullptr; + +void set_logger() +{ + auto file_name = fmt::format("logs/{:%Y-%m-%d_%H-%M-%S}.log", std::chrono::system_clock::now()); + auto file_sink = std::make_shared(file_name, true); + file_sink->set_level(spdlog::level::debug); + + auto console_sink = std::make_shared(); + console_sink->set_level(spdlog::level::debug); + + logger_ = std::make_shared("", spdlog::sinks_init_list{file_sink, console_sink}); + logger_->set_level(spdlog::level::debug); + logger_->flush_on(spdlog::level::info); +} + +std::shared_ptr logger() +{ + if (!logger_) set_logger(); + return logger_; +} + +} // namespace tools diff --git a/tools/logger.hpp b/tools/logger.hpp new file mode 100644 index 0000000..5c87aee --- /dev/null +++ b/tools/logger.hpp @@ -0,0 +1,12 @@ +#ifndef TOOLS__LOGGER_HPP +#define TOOLS__LOGGER_HPP + +#include + +namespace tools +{ +std::shared_ptr logger(); + +} // namespace tools + +#endif // TOOLS__LOGGER_HPP \ No newline at end of file diff --git a/tools/math_tools.cpp b/tools/math_tools.cpp new file mode 100644 index 0000000..992c0aa --- /dev/null +++ b/tools/math_tools.cpp @@ -0,0 +1,202 @@ +#include "math_tools.hpp" + +#include +#include // CV_PI + +namespace tools +{ +double limit_rad(double angle) +{ + while (angle > CV_PI) angle -= 2 * CV_PI; + while (angle <= -CV_PI) angle += 2 * CV_PI; + return angle; +} + +Eigen::Vector3d eulers(Eigen::Quaterniond q, int axis0, int axis1, int axis2, bool extrinsic) +{ + if (!extrinsic) std::swap(axis0, axis2); + + auto i = axis0, j = axis1, k = axis2; + auto is_proper = (i == k); + if (is_proper) k = 3 - i - j; + auto sign = (i - j) * (j - k) * (k - i) / 2; + + double a, b, c, d; + Eigen::Vector4d xyzw = q.coeffs(); + if (is_proper) { + a = xyzw[3]; + b = xyzw[i]; + c = xyzw[j]; + d = xyzw[k] * sign; + } else { + a = xyzw[3] - xyzw[j]; + b = xyzw[i] + xyzw[k] * sign; + c = xyzw[j] + xyzw[3]; + d = xyzw[k] * sign - xyzw[i]; + } + + Eigen::Vector3d eulers; + auto n2 = a * a + b * b + c * c + d * d; + eulers[1] = std::acos(2 * (a * a + b * b) / n2 - 1); + + auto half_sum = std::atan2(b, a); + auto half_diff = std::atan2(-d, c); + + auto eps = 1e-7; + auto safe1 = std::abs(eulers[1]) >= eps; + auto safe2 = std::abs(eulers[1] - CV_PI) >= eps; + auto safe = safe1 && safe2; + if (safe) { + eulers[0] = half_sum + half_diff; + eulers[2] = half_sum - half_diff; + } else { + if (!extrinsic) { + eulers[0] = 0; + if (!safe1) eulers[2] = 2 * half_sum; + if (!safe2) eulers[2] = -2 * half_diff; + } else { + eulers[2] = 0; + if (!safe1) eulers[0] = 2 * half_sum; + if (!safe2) eulers[0] = 2 * half_diff; + } + } + + for (int i = 0; i < 3; i++) eulers[i] = limit_rad(eulers[i]); + + if (!is_proper) { + eulers[2] *= sign; + eulers[1] -= CV_PI / 2; + } + + if (!extrinsic) std::swap(eulers[0], eulers[2]); + + return eulers; +} + +Eigen::Vector3d eulers(Eigen::Matrix3d R, int axis0, int axis1, int axis2, bool extrinsic) +{ + Eigen::Quaterniond q(R); + return eulers(q, axis0, axis1, axis2, extrinsic); +} + +Eigen::Matrix3d rotation_matrix(const Eigen::Vector3d & ypr) +{ + double roll = ypr[2]; + double pitch = ypr[1]; + double yaw = ypr[0]; + double cos_yaw = cos(yaw); + double sin_yaw = sin(yaw); + double cos_pitch = cos(pitch); + double sin_pitch = sin(pitch); + double cos_roll = cos(roll); + double sin_roll = sin(roll); + // clang-format off + Eigen::Matrix3d R{ + {cos_yaw * cos_pitch, cos_yaw * sin_pitch * sin_roll - sin_yaw * cos_roll, cos_yaw * sin_pitch * cos_roll + sin_yaw * sin_roll}, + {sin_yaw * cos_pitch, sin_yaw * sin_pitch * sin_roll + cos_yaw * cos_roll, sin_yaw * sin_pitch * cos_roll - cos_yaw * sin_roll}, + { -sin_pitch, cos_pitch * sin_roll, cos_pitch * cos_roll} + }; + // clang-format on + return R; +} + +Eigen::Vector3d xyz2ypd(const Eigen::Vector3d & xyz) +{ + auto x = xyz[0], y = xyz[1], z = xyz[2]; + auto yaw = std::atan2(y, x); + auto pitch = std::atan2(z, std::sqrt(x * x + y * y)); + auto distance = std::sqrt(x * x + y * y + z * z); + return {yaw, pitch, distance}; +} + +Eigen::MatrixXd xyz2ypd_jacobian(const Eigen::Vector3d & xyz) +{ + auto x = xyz[0], y = xyz[1], z = xyz[2]; + + auto dyaw_dx = -y / (x * x + y * y); + auto dyaw_dy = x / (x * x + y * y); + auto dyaw_dz = 0.0; + + auto dpitch_dx = -(x * z) / ((z * z / (x * x + y * y) + 1) * std::pow((x * x + y * y), 1.5)); + auto dpitch_dy = -(y * z) / ((z * z / (x * x + y * y) + 1) * std::pow((x * x + y * y), 1.5)); + auto dpitch_dz = 1 / ((z * z / (x * x + y * y) + 1) * std::pow((x * x + y * y), 0.5)); + + auto ddistance_dx = x / std::pow((x * x + y * y + z * z), 0.5); + auto ddistance_dy = y / std::pow((x * x + y * y + z * z), 0.5); + auto ddistance_dz = z / std::pow((x * x + y * y + z * z), 0.5); + + // clang-format off + Eigen::MatrixXd J{ + {dyaw_dx, dyaw_dy, dyaw_dz}, + {dpitch_dx, dpitch_dy, dpitch_dz}, + {ddistance_dx, ddistance_dy, ddistance_dz} + }; + // clang-format on + + return J; +} + +Eigen::Vector3d ypd2xyz(const Eigen::Vector3d & ypd) +{ + auto yaw = ypd[0], pitch = ypd[1], distance = ypd[2]; + auto x = distance * std::cos(pitch) * std::cos(yaw); + auto y = distance * std::cos(pitch) * std::sin(yaw); + auto z = distance * std::sin(pitch); + return {x, y, z}; +} + +Eigen::MatrixXd ypd2xyz_jacobian(const Eigen::Vector3d & ypd) +{ + auto yaw = ypd[0], pitch = ypd[1], distance = ypd[2]; + double cos_yaw = std::cos(yaw); + double sin_yaw = std::sin(yaw); + double cos_pitch = std::cos(pitch); + double sin_pitch = std::sin(pitch); + + auto dx_dyaw = distance * cos_pitch * -sin_yaw; + auto dy_dyaw = distance * cos_pitch * cos_yaw; + auto dz_dyaw = 0.0; + + auto dx_dpitch = distance * -sin_pitch * cos_yaw; + auto dy_dpitch = distance * -sin_pitch * sin_yaw; + auto dz_dpitch = distance * cos_pitch; + + auto dx_ddistance = cos_pitch * cos_yaw; + auto dy_ddistance = cos_pitch * sin_yaw; + auto dz_ddistance = sin_pitch; + + // clang-format off + Eigen::MatrixXd J{ + {dx_dyaw, dx_dpitch, dx_ddistance}, + {dy_dyaw, dy_dpitch, dy_ddistance}, + {dz_dyaw, dz_dpitch, dz_ddistance} + }; + // clang-format on + + return J; +} + +double delta_time( + const std::chrono::steady_clock::time_point & a, const std::chrono::steady_clock::time_point & b) +{ + std::chrono::duration c = a - b; + return c.count(); +} + +double get_abs_angle(const Eigen::Vector2d & vec1, const Eigen::Vector2d & vec2) +{ + if (vec1.norm() == 0. || vec2.norm() == 0.) { + return 0.; + } + return std::acos(vec1.dot(vec2) / (vec1.norm() * vec2.norm())); +} + +double limit_min_max(double input, double min, double max) +{ + if (input > max) + return max; + else if (input < min) + return min; + return input; +} +} // namespace tools \ No newline at end of file diff --git a/tools/math_tools.hpp b/tools/math_tools.hpp new file mode 100644 index 0000000..ffb9b9f --- /dev/null +++ b/tools/math_tools.hpp @@ -0,0 +1,58 @@ +#ifndef TOOLS__MATH_TOOLS_HPP +#define TOOLS__MATH_TOOLS_HPP + +#include +#include + +namespace tools +{ +// 将弧度值限制在(-pi, pi] +double limit_rad(double angle); + +// 四元数转欧拉角 +// x = 0, y = 1, z = 2 +// e.g. 先绕z轴旋转,再绕y轴旋转,最后绕x轴旋转:axis0=2, axis1=1, axis2=0 +// 参考:https://github.com/evbernardes/quaternion_to_euler +Eigen::Vector3d eulers( + Eigen::Quaterniond q, int axis0, int axis1, int axis2, bool extrinsic = false); + +// 旋转矩阵转欧拉角 +// x = 0, y = 1, z = 2 +// e.g. 先绕z轴旋转,再绕y轴旋转,最后绕x轴旋转:axis0=2, axis1=1, axis2=0 +Eigen::Vector3d eulers(Eigen::Matrix3d R, int axis0, int axis1, int axis2, bool extrinsic = false); + +// 欧拉角转旋转矩阵 +// zyx:先绕z轴旋转,再绕y轴旋转,最后绕x轴旋转 +Eigen::Matrix3d rotation_matrix(const Eigen::Vector3d & ypr); + +// 直角坐标系转球坐标系 +// ypd为yaw、pitch、distance的缩写 +Eigen::Vector3d xyz2ypd(const Eigen::Vector3d & xyz); + +// 直角坐标系转球坐标系转换函数对xyz的雅可比矩阵 +Eigen::MatrixXd xyz2ypd_jacobian(const Eigen::Vector3d & xyz); + +// 球坐标系转直角坐标系 +Eigen::Vector3d ypd2xyz(const Eigen::Vector3d & ypd); + +// 球坐标系转直角坐标系转换函数对xyz的雅可比矩阵 +Eigen::MatrixXd ypd2xyz_jacobian(const Eigen::Vector3d & ypd); + +// 计算时间差a - b,单位:s +double delta_time( + const std::chrono::steady_clock::time_point & a, const std::chrono::steady_clock::time_point & b); + +// 向量夹角 总是返回 0 ~ pi 来自SJTU +double get_abs_angle(const Eigen::Vector2d & vec1, const Eigen::Vector2d & vec2); + +// 返回输入值的平方 +template +T square(T const & a) +{ + return a * a; +}; + +double limit_min_max(double input, double min, double max); +} // namespace tools + +#endif // TOOLS__MATH_TOOLS_HPP \ No newline at end of file diff --git a/tools/pid.cpp b/tools/pid.cpp new file mode 100644 index 0000000..7ad4ddf --- /dev/null +++ b/tools/pid.cpp @@ -0,0 +1,27 @@ +#include "pid.hpp" + +#include "math_tools.hpp" + +float clip(float value, float min, float max) { return std::max(min, std::min(max, value)); } + +namespace tools +{ +PID::PID(float dt, float kp, float ki, float kd, float max_out, float max_iout, bool angular) +: dt_(dt), kp_(kp), ki_(ki), kd_(kd), max_out_(max_out), max_iout_(max_iout), angular_(angular) +{ +} + +float PID::calc(float set, float fdb) +{ + float e = angular_ ? limit_rad(set - fdb) : (set - fdb); + float de = angular_ ? limit_rad(last_fdb_ - fdb) : (last_fdb_ - fdb); + last_fdb_ = fdb; + + this->pout = e * kp_; + this->iout = clip(this->iout + e * dt_ * ki_, -max_iout_, max_iout_); + this->dout = de / dt_ * kd_; + + return clip(this->pout + this->iout + this->dout, -max_out_, max_out_); +} + +} // namespace tools diff --git a/tools/pid.hpp b/tools/pid.hpp new file mode 100644 index 0000000..a534e52 --- /dev/null +++ b/tools/pid.hpp @@ -0,0 +1,37 @@ +#ifndef TOOLS__PID_HPP +#define TOOLS__PID_HPP + +namespace tools +{ +class PID +{ +public: + // dt: 控制周期, 单位: s + // kp: P项系数 + // ki: I项系数 + // kd: D项系数 + // max_out: PID最大输出值 + // max_iout I项最大输出值 + PID(float dt, float kp, float ki, float kd, float max_out, float max_iout, bool angular = false); + + float pout = 0.0f; // P项输出, 用于调试 + float iout = 0.0f; // I项输出, 用于调试 + float dout = 0.0f; // D项输出, 用于调试 + + // 计算PID输出值 + // set: 目标值 + // fdb: 反馈值(feedback) + float calc(float set, float fdb); + +private: + const float dt_; + const float kp_, ki_, kd_; + const float max_out_, max_iout_; + const bool angular_; + + float last_fdb_ = 0.0f; // 上次反馈值 +}; + +} // namespace tools + +#endif // TOOLS__PID_HPP \ No newline at end of file diff --git a/tools/plotter.cpp b/tools/plotter.cpp new file mode 100644 index 0000000..ce85468 --- /dev/null +++ b/tools/plotter.cpp @@ -0,0 +1,29 @@ +#include "plotter.hpp" + +#include // htons, inet_addr +#include // socket, sendto +#include // close + +namespace tools +{ +Plotter::Plotter(std::string host, uint16_t port) +{ + socket_ = ::socket(AF_INET, SOCK_DGRAM, 0); + + destination_.sin_family = AF_INET; + destination_.sin_port = ::htons(port); + destination_.sin_addr.s_addr = ::inet_addr(host.c_str()); +} + +Plotter::~Plotter() { ::close(socket_); } + +void Plotter::plot(const nlohmann::json & json) +{ + std::lock_guard lock(mutex_); + auto data = json.dump(); + ::sendto( + socket_, data.c_str(), data.length(), 0, reinterpret_cast(&destination_), + sizeof(destination_)); +} + +} // namespace tools \ No newline at end of file diff --git a/tools/plotter.hpp b/tools/plotter.hpp new file mode 100644 index 0000000..9e3ca50 --- /dev/null +++ b/tools/plotter.hpp @@ -0,0 +1,29 @@ +#ifndef TOOLS__PLOTTER_HPP +#define TOOLS__PLOTTER_HPP + +#include // sockaddr_in + +#include +#include +#include + +namespace tools +{ +class Plotter +{ +public: + Plotter(std::string host = "127.0.0.1", uint16_t port = 9870); + + ~Plotter(); + + void plot(const nlohmann::json & json); + +private: + int socket_; + sockaddr_in destination_; + std::mutex mutex_; +}; + +} // namespace tools + +#endif // TOOLS__PLOTTER_HPP \ No newline at end of file diff --git a/tools/ransac_sine_fitter.cpp b/tools/ransac_sine_fitter.cpp new file mode 100644 index 0000000..00f1cb7 --- /dev/null +++ b/tools/ransac_sine_fitter.cpp @@ -0,0 +1,105 @@ +#include "ransac_sine_fitter.hpp" + +#include +#include +#include +#include + +namespace tools +{ + +RansacSineFitter::RansacSineFitter( + int max_iterations, double threshold, double min_omega, double max_omega) +: max_iterations_(max_iterations), + threshold_(threshold), + min_omega_(min_omega), + max_omega_(max_omega), + gen_(std::random_device{}()) +{ +} + +void RansacSineFitter::add_data(double t, double v) +{ + if (fit_data_.size() > 0 && (t - fit_data_.back().first > 5)) fit_data_.clear(); + fit_data_.emplace_back(std::make_pair(t, v)); +} + +void RansacSineFitter::fit() +{ + if (fit_data_.size() < 3) return; + + std::uniform_real_distribution omega_dist(min_omega_, max_omega_); + std::vector indices(fit_data_.size()); + std::iota(indices.begin(), indices.end(), 0); + + for (int iter = 0; iter < max_iterations_; ++iter) { + std::shuffle(indices.begin(), indices.end(), gen_); + + std::vector> sample; + for (int i = 0; i < 3; ++i) { + sample.push_back(fit_data_[indices[i]]); + } + + double omega = omega_dist(gen_); + Eigen::Vector3d params; + if (!fit_partial_model(sample, omega, params)) continue; + + double A1 = params(0); + double A2 = params(1); + double C = params(2); + + double A = std::sqrt(A1 * A1 + A2 * A2); + double phi = std::atan2(A2, A1); + + int inlier_count = evaluate_inliers(A, omega, phi, C); + + if (inlier_count > best_result_.inliers) { + best_result_.A = A; + best_result_.omega = omega; + best_result_.phi = phi; + best_result_.C = C; + best_result_.inliers = inlier_count; + } + } + + if (fit_data_.size() > 150) fit_data_.pop_front(); +} + +bool RansacSineFitter::fit_partial_model( + const std::vector> & sample, double omega, Eigen::Vector3d & params) +{ + Eigen::MatrixXd X(sample.size(), 3); + Eigen::VectorXd Y(sample.size()); + + for (size_t i = 0; i < sample.size(); ++i) { + double t = sample[i].first; + double y = sample[i].second; + X(i, 0) = std::sin(omega * t); + X(i, 1) = std::cos(omega * t); + X(i, 2) = 1.0; + Y(i) = y; + } + + try { + params = X.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(Y); + return true; + } catch (...) { + return false; + } +} + +int RansacSineFitter::evaluate_inliers(double A, double omega, double phi, double C) +{ + int count = 0; + for (const auto & p : fit_data_) { + double t = p.first; + double y = p.second; + double pred = A * std::sin(omega * t + phi) + C; + if (std::abs(y - pred) < threshold_) { + ++count; + } + } + return count; +} + +} // namespace tools diff --git a/tools/ransac_sine_fitter.hpp b/tools/ransac_sine_fitter.hpp new file mode 100644 index 0000000..c7ced21 --- /dev/null +++ b/tools/ransac_sine_fitter.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace tools +{ + +class RansacSineFitter +{ +public: + struct Result + { + double A = 0.0; + double omega = 0.0; + double phi = 0.0; + double C = 0.0; + int inliers = 0; + }; + Result best_result_; + + RansacSineFitter(int max_iterations, double threshold, double min_omega, double max_omega); + + void add_data(double t, double v); + + void fit(); + + double sine_function(double t, double A, double omega, double phi, double C) + { + return A * std::sin(omega * t + phi) + C; + } + +private: + int max_iterations_; + double threshold_; + double min_omega_; + double max_omega_; + std::mt19937 gen_; + std::deque> fit_data_; + + bool fit_partial_model( + const std::vector> & sample, double omega, Eigen::Vector3d & params); + + int evaluate_inliers(double A, double omega, double phi, double C); +}; + +} // namespace tools diff --git a/tools/recorder.cpp b/tools/recorder.cpp new file mode 100644 index 0000000..c95083c --- /dev/null +++ b/tools/recorder.cpp @@ -0,0 +1,81 @@ +#include "recorder.hpp" + +#include + +#include +#include + +#include "math_tools.hpp" +#include "tools/logger.hpp" + +namespace tools +{ +Recorder::Recorder(double fps) : init_(false), fps_(fps), queue_(1), stop_thread_(false) +{ + start_time_ = std::chrono::steady_clock::now(); + last_time_ = start_time_; + + auto folder_path = "records"; + auto file_name = fmt::format("{:%Y-%m-%d_%H-%M-%S}", std::chrono::system_clock::now()); + text_path_ = fmt::format("{}/{}.txt", folder_path, file_name); + video_path_ = fmt::format("{}/{}.avi", folder_path, file_name); + + std::filesystem::create_directory(folder_path); +} + +Recorder::~Recorder() +{ + stop_thread_ = true; + // 退出时给队列中额外推入一个空帧,避免pop一直等待 + queue_.push({cv::Mat::zeros(0, 0, 0), {0, 0, 0, 0}, std::chrono::steady_clock::now()}); + if (saving_thread_.joinable()) saving_thread_.join(); // 等待视频保存线程结束 + + if (!init_) return; + text_writer_.close(); + video_writer_.release(); +} + +void Recorder::save_to_file() +{ + while (!stop_thread_) { + FrameData frame; + queue_.pop(frame); // 从队列中取出帧数据 + if (frame.img.empty()) { + tools::logger()->debug("Recorder received empty img. Skip this frame."); + continue; + } + // 写入视频文件 + video_writer_.write(frame.img); + + // 写入文本文件(输出顺序为wxyz) + Eigen::Vector4d xyzw = frame.q.coeffs(); + auto since_begin = tools::delta_time(frame.timestamp, start_time_); + text_writer_ << fmt::format( + "{} {} {} {} {}\n", since_begin, xyzw[3], xyzw[0], xyzw[1], xyzw[2]); + } +} + +void Recorder::record( + const cv::Mat & img, const Eigen::Quaterniond & q, + const std::chrono::steady_clock::time_point & timestamp) +{ + if (img.empty()) return; + if (!init_) init(img); + + auto since_last = tools::delta_time(timestamp, last_time_); + if (since_last < 1.0 / fps_) return; + + last_time_ = timestamp; + queue_.push({img, q, timestamp}); +} + +void Recorder::init(const cv::Mat & img) +{ + text_writer_.open(text_path_); + auto fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G'); + video_writer_ = cv::VideoWriter(video_path_, fourcc, fps_, img.size()); + saving_thread_ = std::thread(&Recorder::save_to_file, this); // 启动保存线程 + init_ = true; +} + +} // namespace tools diff --git a/tools/recorder.hpp b/tools/recorder.hpp new file mode 100644 index 0000000..f4224e8 --- /dev/null +++ b/tools/recorder.hpp @@ -0,0 +1,46 @@ +#ifndef TOOLS__RECORDER_HPP +#define TOOLS__RECORDER_HPP + +#include +#include +#include +#include +#include + +#include "tools/thread_safe_queue.hpp" +namespace tools +{ +class Recorder +{ +public: + Recorder(double fps = 30); + ~Recorder(); + void record( + const cv::Mat & img, const Eigen::Quaterniond & q, + const std::chrono::steady_clock::time_point & timestamp); + +private: + struct FrameData + { + cv::Mat img; + Eigen::Quaterniond q; + std::chrono::steady_clock::time_point timestamp; + }; + bool init_; + std::atomic stop_thread_; + double fps_; + std::string text_path_; + std::string video_path_; + std::ofstream text_writer_; + cv::VideoWriter video_writer_; + std::chrono::steady_clock::time_point start_time_; + std::chrono::steady_clock::time_point last_time_; + tools::ThreadSafeQueue queue_; + std::thread saving_thread_; // 负责保存帧数据的线程 + void init(const cv::Mat & img); + void save_to_file(); +}; + +} // namespace tools + +#endif // TOOLS__RECORDER_HPP \ No newline at end of file diff --git a/tools/thread_pool.hpp b/tools/thread_pool.hpp new file mode 100644 index 0000000..a8561af --- /dev/null +++ b/tools/thread_pool.hpp @@ -0,0 +1,185 @@ +#ifndef TOOLS__THREAD_POOL_HPP +#define TOOLS__THREAD_POOL_HPP + +#include +#include +#include +#include +#include +#include + +#include "tasks/auto_aim/yolo.hpp" +#include "tools/logger.hpp" + +namespace tools +{ +struct Frame +{ + int id; + cv::Mat img; + std::chrono::steady_clock::time_point t; + Eigen::Quaterniond q; + std::list armors; +}; + +inline std::vector create_yolo11s( + const std::string & config_path, int numebr, bool debug) +{ + std::vector yolo11s; + for (int i = 0; i < numebr; i++) { + yolo11s.push_back(auto_aim::YOLO(config_path, debug)); + } + return yolo11s; +} + +inline std::vector create_yolov8s( + const std::string & config_path, int numebr, bool debug) +{ + std::vector yolov8s; + for (int i = 0; i < numebr; i++) { + yolov8s.push_back(auto_aim::YOLO(config_path, debug)); + } + return yolov8s; +} + +class OrderedQueue +{ +public: + OrderedQueue() : current_id_(1) {} + ~OrderedQueue() + { + { + std::lock_guard lock(mutex_); + + main_queue_ = std::queue(); + buffer_.clear(); + current_id_ = 0; + } + tools::logger()->info("OrderedQueue destroyed, queue and buffer cleared."); + } + + void enqueue(const tools::Frame & item) + { + std::lock_guard lock(mutex_); + + if (item.id < current_id_) { + tools::logger()->warn("small id"); + return; + } + + if (item.id == current_id_) { + main_queue_.push(item); + current_id_++; + + auto it = buffer_.find(current_id_); + while (it != buffer_.end()) { + main_queue_.push(it->second); + buffer_.erase(it); + current_id_++; + it = buffer_.find(current_id_); + } + + if (main_queue_.size() >= 1) { + cond_var_.notify_one(); + } + } else { + buffer_[item.id] = item; + } + } + + tools::Frame dequeue() + { + std::unique_lock lock(mutex_); + + cond_var_.wait(lock, [this]() { return !main_queue_.empty(); }); + + tools::Frame item = main_queue_.front(); + main_queue_.pop(); + return item; + } + + // 不会阻塞队列 + bool try_dequeue(tools::Frame & item) + { + std::lock_guard lock(mutex_); + if (main_queue_.empty()) { + return false; + } + item = main_queue_.front(); + main_queue_.pop(); + return true; + } + + size_t get_size() { return main_queue_.size() + buffer_.size(); } + +private: + std::queue main_queue_; + std::unordered_map buffer_; + int current_id_; + std::mutex mutex_; + std::condition_variable cond_var_; +}; + +class ThreadPool +{ +public: + ThreadPool(size_t num_threads) : stop(false) + { + for (size_t i = 0; i < num_threads; ++i) { + workers.emplace_back([this] { + while (true) { + std::function task; + { + std::unique_lock lock(queue_mutex); + condition.wait(lock, [this] { return stop || !tasks.empty(); }); + if (stop && tasks.empty()) { + return; + } + task = std::move(tasks.front()); + tasks.pop(); + } + task(); + } + }); + } + } + + ~ThreadPool() + { + { + std::unique_lock lock(queue_mutex); + stop = true; + tasks = std::queue>(); + } + condition.notify_all(); + for (std::thread & worker : workers) { + if (worker.joinable()) { + worker.join(); + } + } + } + + // 添加任务到任务队列 + template + void enqueue(F && f) + { + { + std::unique_lock lock(queue_mutex); + if (stop) { + throw std::runtime_error("enqueue on stopped ThreadPool"); + } + tasks.emplace(std::forward(f)); + } + condition.notify_one(); + } + +private: + std::vector workers; // 工作线程 + std::queue> tasks; // 任务队列 + std::mutex queue_mutex; // 任务队列互斥锁 + std::condition_variable condition; // 条件变量,用于等待任务 + bool stop; // 是否停止线程池 +}; +} // namespace tools + +#endif // TOOLS__THREAD_POOL_HPP diff --git a/tools/thread_safe_queue.hpp b/tools/thread_safe_queue.hpp new file mode 100644 index 0000000..e9bb50b --- /dev/null +++ b/tools/thread_safe_queue.hpp @@ -0,0 +1,111 @@ +#ifndef TOOLS__THREAD_SAFE_QUEUE_HPP +#define TOOLS__THREAD_SAFE_QUEUE_HPP + +#include +#include +#include +#include +#include + +namespace tools +{ +template +class ThreadSafeQueue +{ +public: + ThreadSafeQueue( + size_t max_size, std::function full_handler = [] {}) + : max_size_(max_size), full_handler_(full_handler) + { + } + + void push(const T & value) + { + std::unique_lock lock(mutex_); + + if (queue_.size() >= max_size_) { + if (PopWhenFull) { + queue_.pop(); + } else { + full_handler_(); + return; + } + } + + queue_.push(value); + not_empty_condition_.notify_all(); + } + + void pop(T & value) + { + std::unique_lock lock(mutex_); + + not_empty_condition_.wait(lock, [this] { return !queue_.empty(); }); + + if (queue_.empty()) { + std::cerr << "Error: Attempt to pop from an empty queue." << std::endl; + return; + } + + value = queue_.front(); + queue_.pop(); + } + + T pop() + { + std::unique_lock lock(mutex_); + + not_empty_condition_.wait(lock, [this] { return !queue_.empty(); }); + + T value = std::move(queue_.front()); + queue_.pop(); + return std::move(value); + } + + T front() + { + std::unique_lock lock(mutex_); + + not_empty_condition_.wait(lock, [this] { return !queue_.empty(); }); + + return queue_.front(); + } + + void back(T & value) + { + std::unique_lock lock(mutex_); + + if (queue_.empty()) { + std::cerr << "Error: Attempt to access the back of an empty queue." << std::endl; + return; + } + + value = queue_.back(); + } + + bool empty() + { + std::unique_lock lock(mutex_); + return queue_.empty(); + } + + void clear() + { + std::unique_lock lock(mutex_); + while (!queue_.empty()) { + queue_.pop(); + } + not_empty_condition_.notify_all(); // 如果其他线程正在等待队列不为空,这样可以唤醒它们 + } + +private: + std::queue queue_; + size_t max_size_; + mutable std::mutex mutex_; + std::condition_variable not_empty_condition_; + std::function full_handler_; +}; + +} // namespace tools + +#endif // TOOLS__THREAD_SAFE_QUEUE_HPP \ No newline at end of file diff --git a/tools/trajectory.cpp b/tools/trajectory.cpp new file mode 100644 index 0000000..2e28956 --- /dev/null +++ b/tools/trajectory.cpp @@ -0,0 +1,33 @@ +#include "trajectory.hpp" + +#include + +namespace tools +{ +constexpr double g = 9.7833; + +Trajectory::Trajectory(const double v0, const double d, const double h) +{ + auto a = g * d * d / (2 * v0 * v0); + auto b = -d; + auto c = a + h; + auto delta = b * b - 4 * a * c; + + if (delta < 0) { + unsolvable = true; + return; + } + + unsolvable = false; + auto tan_pitch_1 = (-b + std::sqrt(delta)) / (2 * a); + auto tan_pitch_2 = (-b - std::sqrt(delta)) / (2 * a); + auto pitch_1 = std::atan(tan_pitch_1); + auto pitch_2 = std::atan(tan_pitch_2); + auto t_1 = d / (v0 * std::cos(pitch_1)); + auto t_2 = d / (v0 * std::cos(pitch_2)); + + pitch = (t_1 < t_2) ? pitch_1 : pitch_2; + fly_time = (t_1 < t_2) ? t_1 : t_2; +} + +} // namespace tools \ No newline at end of file diff --git a/tools/trajectory.hpp b/tools/trajectory.hpp new file mode 100644 index 0000000..ed817cc --- /dev/null +++ b/tools/trajectory.hpp @@ -0,0 +1,21 @@ +#ifndef TOOLS__TRAJECTORY_HPP +#define TOOLS__TRAJECTORY_HPP + +namespace tools +{ +struct Trajectory +{ + bool unsolvable; + double fly_time; + double pitch; // 抬头为正 + + // 不考虑空气阻力 + // v0 子弹初速度大小,单位:m/s + // d 目标水平距离,单位:m + // h 目标竖直高度,单位:m + Trajectory(const double v0, const double d, const double h); +}; + +} // namespace tools + +#endif // TOOLS__TRAJECTORY_HPP \ No newline at end of file diff --git a/tools/yaml.hpp b/tools/yaml.hpp new file mode 100644 index 0000000..149aaaf --- /dev/null +++ b/tools/yaml.hpp @@ -0,0 +1,33 @@ +#ifndef TOOLS__YAML_HPP +#define TOOLS__YAML_HPP + +#include + +#include "tools/logger.hpp" + +namespace tools +{ +inline YAML::Node load(const std::string & path) +{ + try { + return YAML::LoadFile(path); + } catch (const YAML::BadFile & e) { + logger()->error("[YAML] Failed to load file: {}", e.what()); + exit(1); + } catch (const YAML::ParserException & e) { + logger()->error("[YAML] Parser error: {}", e.what()); + exit(1); + } +} + +template +inline T read(const YAML::Node & yaml, const std::string & key) +{ + if (yaml[key]) return yaml[key].as(); + logger()->error("[YAML] {} not found!", key); + exit(1); +} + +} // namespace tools + +#endif // TOOLS__YAML_HPP \ No newline at end of file