|
|
@@ -1,205 +1,205 @@
|
|
|
-#include "mvs_camera.h"
|
|
|
-#include "simple_mq.h"
|
|
|
-#include "third_party/scope_guard.hpp"
|
|
|
-#include "utility.hpp"
|
|
|
-
|
|
|
-#include <MvCameraControl.h>
|
|
|
-
|
|
|
-#include <opencv2/core/mat.hpp>
|
|
|
-
|
|
|
-#include <spdlog/spdlog.h>
|
|
|
-
|
|
|
-using namespace simple_mq_singleton;
|
|
|
-
|
|
|
-namespace mvs {
|
|
|
-
|
|
|
- bool check_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);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
-#define API_CHECK(api_call) \
|
|
|
- check_api_call( \
|
|
|
- api_call, __LINE__, __FILE__, #api_call)
|
|
|
-
|
|
|
-#define API_CHECK_P(api_call) \
|
|
|
- if (!check_api_call( \
|
|
|
- api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
|
|
|
- return nullptr
|
|
|
-
|
|
|
-#define API_CHECK_B(api_call) \
|
|
|
- if (!check_api_call( \
|
|
|
- api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
|
|
|
- return false
|
|
|
-
|
|
|
- struct camera::impl {
|
|
|
- void *handle = nullptr;
|
|
|
- std::string name;
|
|
|
- pixel_type type = RG_8;
|
|
|
- int image_out_index = -1;
|
|
|
- int frame_width = -1, frame_height = -1;
|
|
|
- bool is_capture = false;
|
|
|
-
|
|
|
- ~impl() {
|
|
|
- if (is_capture) {
|
|
|
- API_CHECK(MV_CC_StopGrabbing(handle));
|
|
|
- }
|
|
|
- API_CHECK(MV_CC_CloseDevice(handle));
|
|
|
- API_CHECK(MV_CC_DestroyHandle(handle));
|
|
|
- SPDLOG_INFO("MVS camera {} closed.", name);
|
|
|
- };
|
|
|
-
|
|
|
- void on_error_impl(unsigned int msg_type) const {
|
|
|
- if (msg_type == 0x8003) return; // stop capture event, not an error
|
|
|
- SPDLOG_ERROR("MVS camera {} exception 0x{:x}.", name, msg_type);
|
|
|
- }
|
|
|
-
|
|
|
- static void on_error(unsigned int msg_type, void *user_data) {
|
|
|
- ((impl *) user_data)->on_error_impl(msg_type);
|
|
|
- }
|
|
|
-
|
|
|
- size_t calc_frame_size() const {
|
|
|
- switch (type) {
|
|
|
- case RG_8:
|
|
|
- return frame_width * frame_height * sizeof(uint8_t);
|
|
|
- }
|
|
|
- unreachable();
|
|
|
- }
|
|
|
-
|
|
|
- cv::Mat create_mat_view(unsigned char *data) {
|
|
|
- switch (type) {
|
|
|
- case RG_8:
|
|
|
- return cv::Mat{frame_height, frame_width, CV_8UC1, data};
|
|
|
- }
|
|
|
- unreachable();
|
|
|
- }
|
|
|
-
|
|
|
- void on_image_impl(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info) {
|
|
|
- assert(frame_info->nFrameLen == calc_frame_size());
|
|
|
- auto img_view = create_mat_view(data);
|
|
|
- mq().update_variable(image_out_index, img_view.clone());
|
|
|
- }
|
|
|
-
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- static MvGvspPixelType convert_pixel_type(pixel_type type) {
|
|
|
- switch (type) {
|
|
|
- case RG_8:
|
|
|
- return PixelType_Gvsp_BayerRG8;
|
|
|
- }
|
|
|
- unreachable();
|
|
|
- }
|
|
|
-
|
|
|
- static impl *create(const create_config &conf) {
|
|
|
- // find
|
|
|
- MV_CC_DEVICE_INFO_LIST dev_info_list;
|
|
|
- API_CHECK_P(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 cur_dev_info = dev_info_list.pDeviceInfo[i];
|
|
|
- auto cur_dev_name = (char *) cur_dev_info->SpecialInfo.stUsb3VInfo.chUserDefinedName;
|
|
|
- if (cur_dev_name == conf.name) {
|
|
|
- dev_info = cur_dev_info;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (dev_info == nullptr) {
|
|
|
- SPDLOG_ERROR("MVS camera with name {} not found.", conf.name);
|
|
|
- return nullptr;
|
|
|
- }
|
|
|
- void *handle = nullptr;
|
|
|
- API_CHECK_P(MV_CC_CreateHandle(&handle, dev_info));
|
|
|
- assert(handle != nullptr);
|
|
|
-
|
|
|
- // open
|
|
|
- API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
|
|
|
- API_CHECK_P(MV_CC_CloseDevice(handle)); // close and open again to fix some bug
|
|
|
- API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
|
|
|
- SPDLOG_INFO("MVS camera {} opened.", conf.name);
|
|
|
-
|
|
|
- // config
|
|
|
- API_CHECK_P(MV_CC_SetEnumValue(handle, "PixelFormat",
|
|
|
- convert_pixel_type(conf.pixel)));
|
|
|
- API_CHECK_P(MV_CC_SetEnumValue(handle, "AcquisitionMode",
|
|
|
- MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS));
|
|
|
- API_CHECK_P(MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF));
|
|
|
- API_CHECK_P(MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", true));
|
|
|
-
|
|
|
- // create impl
|
|
|
- auto ret = new impl;
|
|
|
- auto closer = sg::make_scope_guard([&] { delete ret; });
|
|
|
- ret->handle = handle;
|
|
|
- ret->name = conf.name;
|
|
|
- ret->type = conf.pixel;
|
|
|
- ret->image_out_index = conf.image_out_index;
|
|
|
- MVCC_INTVALUE int_val;
|
|
|
- API_CHECK_P(MV_CC_GetIntValue(handle, "Width", &int_val));
|
|
|
- ret->frame_width = int_val.nCurValue;
|
|
|
- API_CHECK_P(MV_CC_GetIntValue(handle, "Height", &int_val));
|
|
|
- ret->frame_height = int_val.nCurValue;
|
|
|
-
|
|
|
- // register callbacks
|
|
|
- API_CHECK_P(MV_CC_RegisterExceptionCallBack(handle, impl::on_error, ret));
|
|
|
- API_CHECK_P(MV_CC_RegisterImageCallBackEx(handle, impl::on_image, ret));
|
|
|
-
|
|
|
- closer.dismiss();
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- bool set_capture_config(const capture_config &conf) {
|
|
|
- API_CHECK_B(MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", conf.frame_rate));
|
|
|
- API_CHECK_B(MV_CC_SetFloatValue(handle, "ExposureTime", conf.expo_time_ms * 1000)); // ms -> us
|
|
|
- API_CHECK_B(MV_CC_SetFloatValue(handle, "Gain", conf.gain_db));
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- bool start_capture() {
|
|
|
- assert(!is_capture);
|
|
|
- API_CHECK_B(MV_CC_StartGrabbing(handle));
|
|
|
- is_capture = true;
|
|
|
- SPDLOG_INFO("MVS camera {} started capturing.", name);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- bool stop_capture() {
|
|
|
- assert(is_capture);
|
|
|
- API_CHECK_B(MV_CC_StopGrabbing(handle));
|
|
|
- is_capture = false;
|
|
|
- SPDLOG_INFO("MVS camera {} stopped capturing.", name);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
- camera::~camera() = default;
|
|
|
-
|
|
|
- bool camera::set_capture_config(const mvs::capture_config &conf) {
|
|
|
- return pimpl->set_capture_config(conf);
|
|
|
- }
|
|
|
-
|
|
|
- bool camera::start_capture() {
|
|
|
- return pimpl->start_capture();
|
|
|
- }
|
|
|
-
|
|
|
- bool camera::stop_capture() {
|
|
|
- return pimpl->stop_capture();
|
|
|
- }
|
|
|
-
|
|
|
- bool camera::is_capture() const {
|
|
|
- return pimpl->is_capture;
|
|
|
- }
|
|
|
-
|
|
|
- camera *camera::create(const mvs::create_config &conf) {
|
|
|
- auto pimpl = impl::create(conf);
|
|
|
- if (pimpl == nullptr) return nullptr;
|
|
|
- auto ret = new camera;
|
|
|
- ret->pimpl.reset(pimpl);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
+#include "mvs_camera.h"
|
|
|
+#include "simple_mq.h"
|
|
|
+#include "third_party/scope_guard.hpp"
|
|
|
+#include "utility.hpp"
|
|
|
+
|
|
|
+#include <MvCameraControl.h>
|
|
|
+
|
|
|
+#include <opencv2/core/mat.hpp>
|
|
|
+
|
|
|
+#include <spdlog/spdlog.h>
|
|
|
+
|
|
|
+using namespace simple_mq_singleton;
|
|
|
+
|
|
|
+namespace mvs {
|
|
|
+
|
|
|
+ bool check_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);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+#define API_CHECK(api_call) \
|
|
|
+ check_api_call( \
|
|
|
+ api_call, __LINE__, __FILE__, #api_call)
|
|
|
+
|
|
|
+#define API_CHECK_P(api_call) \
|
|
|
+ if (!check_api_call( \
|
|
|
+ api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
|
|
|
+ return nullptr
|
|
|
+
|
|
|
+#define API_CHECK_B(api_call) \
|
|
|
+ if (!check_api_call( \
|
|
|
+ api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
|
|
|
+ return false
|
|
|
+
|
|
|
+ struct camera::impl {
|
|
|
+ void *handle = nullptr;
|
|
|
+ std::string name;
|
|
|
+ pixel_type type = RG_8;
|
|
|
+ int image_out_index = -1;
|
|
|
+ int frame_width = -1, frame_height = -1;
|
|
|
+ bool is_capture = false;
|
|
|
+
|
|
|
+ ~impl() {
|
|
|
+ if (is_capture) {
|
|
|
+ API_CHECK(MV_CC_StopGrabbing(handle));
|
|
|
+ }
|
|
|
+ API_CHECK(MV_CC_CloseDevice(handle));
|
|
|
+ API_CHECK(MV_CC_DestroyHandle(handle));
|
|
|
+ SPDLOG_INFO("MVS camera {} closed.", name);
|
|
|
+ };
|
|
|
+
|
|
|
+ void on_error_impl(unsigned int msg_type) const {
|
|
|
+ if (msg_type == 0x8003) return; // stop capture event, not an error
|
|
|
+ SPDLOG_ERROR("MVS camera {} exception 0x{:x}.", name, msg_type);
|
|
|
+ }
|
|
|
+
|
|
|
+ static void on_error(unsigned int msg_type, void *user_data) {
|
|
|
+ ((impl *) user_data)->on_error_impl(msg_type);
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t calc_frame_size() const {
|
|
|
+ switch (type) {
|
|
|
+ case RG_8:
|
|
|
+ return frame_width * frame_height * sizeof(uint8_t);
|
|
|
+ }
|
|
|
+ unreachable();
|
|
|
+ }
|
|
|
+
|
|
|
+ cv::Mat create_mat_view(unsigned char *data) {
|
|
|
+ switch (type) {
|
|
|
+ case RG_8:
|
|
|
+ return cv::Mat{frame_height, frame_width, CV_8UC1, data};
|
|
|
+ }
|
|
|
+ unreachable();
|
|
|
+ }
|
|
|
+
|
|
|
+ void on_image_impl(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info) {
|
|
|
+ assert(frame_info->nFrameLen == calc_frame_size());
|
|
|
+ auto img_view = create_mat_view(data);
|
|
|
+ mq().update_variable(image_out_index, img_view.clone());
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ static MvGvspPixelType convert_pixel_type(pixel_type type) {
|
|
|
+ switch (type) {
|
|
|
+ case RG_8:
|
|
|
+ return PixelType_Gvsp_BayerRG8;
|
|
|
+ }
|
|
|
+ unreachable();
|
|
|
+ }
|
|
|
+
|
|
|
+ static impl *create(const create_config &conf) {
|
|
|
+ // find
|
|
|
+ MV_CC_DEVICE_INFO_LIST dev_info_list;
|
|
|
+ API_CHECK_P(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 cur_dev_info = dev_info_list.pDeviceInfo[i];
|
|
|
+ auto cur_dev_name = (char *) cur_dev_info->SpecialInfo.stUsb3VInfo.chUserDefinedName;
|
|
|
+ if (cur_dev_name == conf.name) {
|
|
|
+ dev_info = cur_dev_info;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (dev_info == nullptr) {
|
|
|
+ SPDLOG_ERROR("MVS camera with name {} not found.", conf.name);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ void *handle = nullptr;
|
|
|
+ API_CHECK_P(MV_CC_CreateHandle(&handle, dev_info));
|
|
|
+ assert(handle != nullptr);
|
|
|
+
|
|
|
+ // open
|
|
|
+ API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
|
|
|
+ API_CHECK_P(MV_CC_CloseDevice(handle)); // close and open again to fix some bug
|
|
|
+ API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
|
|
|
+ SPDLOG_INFO("MVS camera {} opened.", conf.name);
|
|
|
+
|
|
|
+ // config
|
|
|
+ API_CHECK_P(MV_CC_SetEnumValue(handle, "PixelFormat",
|
|
|
+ convert_pixel_type(conf.pixel)));
|
|
|
+ API_CHECK_P(MV_CC_SetEnumValue(handle, "AcquisitionMode",
|
|
|
+ MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS));
|
|
|
+ API_CHECK_P(MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF));
|
|
|
+ API_CHECK_P(MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", true));
|
|
|
+
|
|
|
+ // create impl
|
|
|
+ auto ret = new impl;
|
|
|
+ auto closer = sg::make_scope_guard([&] { delete ret; });
|
|
|
+ ret->handle = handle;
|
|
|
+ ret->name = conf.name;
|
|
|
+ ret->type = conf.pixel;
|
|
|
+ ret->image_out_index = conf.image_out_index;
|
|
|
+ MVCC_INTVALUE int_val;
|
|
|
+ API_CHECK_P(MV_CC_GetIntValue(handle, "Width", &int_val));
|
|
|
+ ret->frame_width = int_val.nCurValue;
|
|
|
+ API_CHECK_P(MV_CC_GetIntValue(handle, "Height", &int_val));
|
|
|
+ ret->frame_height = int_val.nCurValue;
|
|
|
+
|
|
|
+ // register callbacks
|
|
|
+ API_CHECK_P(MV_CC_RegisterExceptionCallBack(handle, impl::on_error, ret));
|
|
|
+ API_CHECK_P(MV_CC_RegisterImageCallBackEx(handle, impl::on_image, ret));
|
|
|
+
|
|
|
+ closer.dismiss();
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool set_capture_config(const capture_config &conf) {
|
|
|
+ API_CHECK_B(MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", conf.frame_rate));
|
|
|
+ API_CHECK_B(MV_CC_SetFloatValue(handle, "ExposureTime", conf.expo_time_ms * 1000)); // ms -> us
|
|
|
+ API_CHECK_B(MV_CC_SetFloatValue(handle, "Gain", conf.gain_db));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool start_capture() {
|
|
|
+ assert(!is_capture);
|
|
|
+ API_CHECK_B(MV_CC_StartGrabbing(handle));
|
|
|
+ is_capture = true;
|
|
|
+ SPDLOG_INFO("MVS camera {} started capturing.", name);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool stop_capture() {
|
|
|
+ assert(is_capture);
|
|
|
+ API_CHECK_B(MV_CC_StopGrabbing(handle));
|
|
|
+ is_capture = false;
|
|
|
+ SPDLOG_INFO("MVS camera {} stopped capturing.", name);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ camera::~camera() = default;
|
|
|
+
|
|
|
+ bool camera::set_capture_config(const mvs::capture_config &conf) {
|
|
|
+ return pimpl->set_capture_config(conf);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool camera::start_capture() {
|
|
|
+ return pimpl->start_capture();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool camera::stop_capture() {
|
|
|
+ return pimpl->stop_capture();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool camera::is_capture() const {
|
|
|
+ return pimpl->is_capture;
|
|
|
+ }
|
|
|
+
|
|
|
+ camera *camera::create(const mvs::create_config &conf) {
|
|
|
+ auto pimpl = impl::create(conf);
|
|
|
+ if (pimpl == nullptr) return nullptr;
|
|
|
+ auto ret = new camera;
|
|
|
+ ret->pimpl.reset(pimpl);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
}
|