mvs_camera.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #include "mvs_camera.h"
  2. #include "image_ringbuffer.hpp"
  3. #ifdef _MSC_VER
  4. #pragma warning(disable: 4828)
  5. #endif
  6. #include <MvCameraControl.h>
  7. #include <spdlog/spdlog.h>
  8. #include <opencv2/core/mat.hpp>
  9. #include <boost/predef.h>
  10. #ifdef BOOST_OS_WINDOWS_AVAILABLE
  11. #include <format>
  12. #define fmt std
  13. #else
  14. #include <fmt/chrono.h>
  15. #include <fmt/format.h>
  16. #endif
  17. bool check_mvs_api_call(int api_ret, unsigned int line_number,
  18. const char *file_name, const char *api_call_str) {
  19. if (api_ret == MV_OK) [[likely]] return true;
  20. SPDLOG_ERROR("MVS api call {} failed at {}:{} with error 0x{:x}",
  21. api_call_str, file_name, line_number, api_ret);
  22. RET_ERROR;
  23. }
  24. #define MVS_API_CHECK(api_call) \
  25. if (!check_mvs_api_call( \
  26. api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
  27. return false
  28. struct mvs_camera::impl {
  29. static constexpr auto image_width = 1224;
  30. static constexpr auto image_height = 1024;
  31. static constexpr auto image_size = image_width * sizeof(uint8_t) * image_height;
  32. void *handle = nullptr;
  33. const char *cam_name = nullptr;
  34. image_ringbuffer *ring_buf = nullptr;
  35. bool is_capturing = false;
  36. explicit impl(const char *_cam_name)
  37. : cam_name(_cam_name) {
  38. CALL_ASSERT(open());
  39. }
  40. ~impl() {
  41. CALL_ASSERT(close());
  42. }
  43. bool open() {
  44. MV_CC_DEVICE_INFO_LIST dev_info_list;
  45. MVS_API_CHECK(MV_CC_EnumDevices(MV_USB_DEVICE, &dev_info_list));
  46. MV_CC_DEVICE_INFO *dev_info = nullptr;
  47. for (int i = 0; i < dev_info_list.nDeviceNum; ++i) {
  48. auto tmp_dev_info = dev_info_list.pDeviceInfo[i];
  49. auto tmp_dev_name = (char *) tmp_dev_info->SpecialInfo.stUsb3VInfo.chUserDefinedName;
  50. if (strcmp(cam_name, tmp_dev_name) == 0) {
  51. dev_info = tmp_dev_info;
  52. }
  53. }
  54. if (dev_info == nullptr) {
  55. SPDLOG_ERROR("No camera named {}.", cam_name);
  56. RET_ERROR;
  57. }
  58. // MVS_API_CHECK(MV_CC_IsDeviceAccessible(dev_info, MV_ACCESS_Control));
  59. MVS_API_CHECK(MV_CC_CreateHandle(&handle, dev_info));
  60. MVS_API_CHECK(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
  61. // close and open again to fix some bug
  62. MVS_API_CHECK(MV_CC_CloseDevice(handle));
  63. MVS_API_CHECK(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
  64. // register callbacks
  65. MVS_API_CHECK(MV_CC_RegisterExceptionCallBack(handle, impl::on_error, this));
  66. MVS_API_CHECK(MV_CC_RegisterImageCallBackEx(handle, impl::on_image, this));
  67. SPDLOG_INFO("Camera {} opened successfully.", cam_name);
  68. return true;
  69. }
  70. bool start() {
  71. // make sure ring buffer is assigned properly
  72. assert(ring_buf != nullptr);
  73. // config camera
  74. assert(handle != nullptr);
  75. MVS_API_CHECK(MV_CC_SetEnumValue(handle, "PixelFormat", PixelType_Gvsp_BayerRG8));
  76. MVS_API_CHECK(MV_CC_SetEnumValue(handle, "BinningHorizontal", 2));
  77. MVS_API_CHECK(MV_CC_SetEnumValue(handle, "BinningVertical", 2));
  78. MVS_API_CHECK(MV_CC_SetEnumValue(handle, "AcquisitionMode",
  79. MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS));
  80. MVS_API_CHECK(MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_ON));
  81. MVS_API_CHECK(MV_CC_SetEnumValue(handle, "TriggerSource",
  82. MV_CAM_TRIGGER_SOURCE::MV_TRIGGER_SOURCE_SOFTWARE));
  83. // start capture
  84. MVS_API_CHECK(MV_CC_StartGrabbing(handle));
  85. is_capturing = true;
  86. SPDLOG_INFO("Camera {} is capturing.", cam_name);
  87. return true;
  88. }
  89. bool trigger() {
  90. assert(handle != nullptr);
  91. MVS_API_CHECK(MV_CC_TriggerSoftwareExecute(handle));
  92. return true;
  93. }
  94. bool stop() {
  95. assert(handle != nullptr);
  96. if (!is_capturing) return true;
  97. MVS_API_CHECK(MV_CC_StopGrabbing(handle));
  98. is_capturing = false;
  99. SPDLOG_INFO("Camera {} stopped capturing.", cam_name);
  100. return true;
  101. }
  102. bool close() {
  103. assert(handle != nullptr);
  104. stop();
  105. MVS_API_CHECK(MV_CC_CloseDevice(handle));
  106. MVS_API_CHECK(MV_CC_DestroyHandle(handle));
  107. SPDLOG_INFO("Camera {} closed.", cam_name);
  108. return true;
  109. }
  110. static void on_error(unsigned int msg_type, void *user_data) {
  111. auto pimpl = (impl *) user_data;
  112. SPDLOG_ERROR("MVS camera {} exception 0x{:x}.", pimpl->cam_name, msg_type);
  113. if (msg_type == 0x8003) return; // stop capture
  114. assert(false);
  115. }
  116. static void on_image(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info, void *user_data) {
  117. ((impl *) user_data)->on_image_impl(data, frame_info);
  118. }
  119. void on_image_impl(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info) {
  120. assert(frame_info->nFrameLen == image_size);
  121. auto host_img = cv::Mat{image_height, image_width, CV_8UC1, data};
  122. ring_buf->push_image(host_img);
  123. }
  124. };
  125. mvs_camera::mvs_camera(const char *camera_name)
  126. : pimpl(std::make_unique<impl>(camera_name)) {
  127. }
  128. mvs_camera::~mvs_camera() = default;
  129. cv::Size mvs_camera::get_output_size() {
  130. return {impl::image_width, impl::image_height};
  131. }
  132. void mvs_camera::set_ring_buffer(image_ringbuffer *ring_buf) {
  133. pimpl->ring_buf = ring_buf;
  134. }
  135. bool mvs_camera::start() {
  136. return pimpl->start();
  137. }
  138. bool mvs_camera::trigger() {
  139. return pimpl->trigger();
  140. }
  141. void mvs_camera::stop() {
  142. pimpl->stop();
  143. }
  144. bool mvs_camera::set_capture_config(const capture_config &config) {
  145. MVS_API_CHECK(MV_CC_SetFloatValue(pimpl->handle, "ExposureTime", config.exposure_time_ms * 1000)); // ms -> us
  146. MVS_API_CHECK(MV_CC_SetFloatValue(pimpl->handle, "Gain", config.analog_gain_db));
  147. return true;
  148. }
  149. bool mvs_camera::is_capturing() const {
  150. return pimpl->is_capturing;
  151. }