#include "mvs_camera.h" #include "image_ringbuffer.hpp" #ifdef _MSC_VER #pragma warning(disable: 4828) #endif #include #include #include #include #ifdef BOOST_OS_WINDOWS_AVAILABLE #include #define fmt std #else #include #include #endif bool check_mvs_api_call(int api_ret, unsigned int line_number, const char *file_name, const char *api_call_str) { if (api_ret == MV_OK) [[likely]] return true; SPDLOG_ERROR("MVS api call {} failed at {}:{} with error 0x{:x}", api_call_str, file_name, line_number, api_ret); RET_ERROR; } #define MVS_API_CHECK(api_call) \ if (!check_mvs_api_call( \ api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \ return false struct mvs_camera::impl { static constexpr auto image_width = 1224; static constexpr auto image_height = 1024; static constexpr auto image_size = image_width * sizeof(uint8_t) * image_height; void *handle = nullptr; const char *cam_name = nullptr; image_ringbuffer *ring_buf = nullptr; bool is_capturing = false; explicit impl(const char *_cam_name) : cam_name(_cam_name) { CALL_ASSERT(open()); } ~impl() { CALL_ASSERT(close()); } bool open() { MV_CC_DEVICE_INFO_LIST dev_info_list; MVS_API_CHECK(MV_CC_EnumDevices(MV_USB_DEVICE, &dev_info_list)); MV_CC_DEVICE_INFO *dev_info = nullptr; for (int i = 0; i < dev_info_list.nDeviceNum; ++i) { auto tmp_dev_info = dev_info_list.pDeviceInfo[i]; auto tmp_dev_name = (char *) tmp_dev_info->SpecialInfo.stUsb3VInfo.chUserDefinedName; if (strcmp(cam_name, tmp_dev_name) == 0) { dev_info = tmp_dev_info; } } if (dev_info == nullptr) { SPDLOG_ERROR("No camera named {}.", cam_name); RET_ERROR; } // MVS_API_CHECK(MV_CC_IsDeviceAccessible(dev_info, MV_ACCESS_Control)); MVS_API_CHECK(MV_CC_CreateHandle(&handle, dev_info)); MVS_API_CHECK(MV_CC_OpenDevice(handle, MV_ACCESS_Control)); // close and open again to fix some bug MVS_API_CHECK(MV_CC_CloseDevice(handle)); MVS_API_CHECK(MV_CC_OpenDevice(handle, MV_ACCESS_Control)); // register callbacks MVS_API_CHECK(MV_CC_RegisterExceptionCallBack(handle, impl::on_error, this)); MVS_API_CHECK(MV_CC_RegisterImageCallBackEx(handle, impl::on_image, this)); SPDLOG_INFO("Camera {} opened successfully.", cam_name); return true; } bool start() { // make sure ring buffer is assigned properly assert(ring_buf != nullptr); // config camera assert(handle != nullptr); MVS_API_CHECK(MV_CC_SetEnumValue(handle, "PixelFormat", PixelType_Gvsp_BayerRG8)); MVS_API_CHECK(MV_CC_SetEnumValue(handle, "BinningHorizontal", 2)); MVS_API_CHECK(MV_CC_SetEnumValue(handle, "BinningVertical", 2)); MVS_API_CHECK(MV_CC_SetEnumValue(handle, "AcquisitionMode", MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS)); MVS_API_CHECK(MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_ON)); MVS_API_CHECK(MV_CC_SetEnumValue(handle, "TriggerSource", MV_CAM_TRIGGER_SOURCE::MV_TRIGGER_SOURCE_SOFTWARE)); // start capture MVS_API_CHECK(MV_CC_StartGrabbing(handle)); is_capturing = true; SPDLOG_INFO("Camera {} is capturing.", cam_name); return true; } bool trigger() { assert(handle != nullptr); MVS_API_CHECK(MV_CC_TriggerSoftwareExecute(handle)); return true; } bool stop() { assert(handle != nullptr); if (!is_capturing) return true; MVS_API_CHECK(MV_CC_StopGrabbing(handle)); is_capturing = false; SPDLOG_INFO("Camera {} stopped capturing.", cam_name); return true; } bool close() { assert(handle != nullptr); stop(); MVS_API_CHECK(MV_CC_CloseDevice(handle)); MVS_API_CHECK(MV_CC_DestroyHandle(handle)); SPDLOG_INFO("Camera {} closed.", cam_name); return true; } static void on_error(unsigned int msg_type, void *user_data) { auto pimpl = (impl *) user_data; SPDLOG_ERROR("MVS camera {} exception 0x{:x}.", pimpl->cam_name, msg_type); if (msg_type == 0x8003) return; // stop capture assert(false); } static void on_image(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info, void *user_data) { ((impl *) user_data)->on_image_impl(data, frame_info); } void on_image_impl(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info) { assert(frame_info->nFrameLen == image_size); auto host_img = cv::Mat{image_height, image_width, CV_8UC1, data}; ring_buf->push_image(host_img); } }; mvs_camera::mvs_camera(const char *camera_name) : pimpl(std::make_unique(camera_name)) { } mvs_camera::~mvs_camera() = default; cv::Size mvs_camera::get_output_size() { return {impl::image_width, impl::image_height}; } void mvs_camera::set_ring_buffer(image_ringbuffer *ring_buf) { pimpl->ring_buf = ring_buf; } bool mvs_camera::start() { return pimpl->start(); } bool mvs_camera::trigger() { return pimpl->trigger(); } void mvs_camera::stop() { pimpl->stop(); } bool mvs_camera::set_capture_config(const capture_config &config) { MVS_API_CHECK(MV_CC_SetFloatValue(pimpl->handle, "ExposureTime", config.exposure_time_ms * 1000)); // ms -> us MVS_API_CHECK(MV_CC_SetFloatValue(pimpl->handle, "Gain", config.analog_gain_db)); return true; } bool mvs_camera::is_capturing() const { return pimpl->is_capturing; }