Bläddra i källkod

Implemented MVS camera control.

jcsyshc 2 år sedan
förälder
incheckning
096c97b01f
5 ändrade filer med 206 tillägg och 1 borttagningar
  1. 2 1
      CMakeLists.txt
  2. 13 0
      src/config.h
  3. 15 0
      src/main.cpp
  4. 136 0
      src/mvs_camera.cpp
  5. 40 0
      src/mvs_camera.h

+ 2 - 1
CMakeLists.txt

@@ -59,7 +59,7 @@ target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
 
 # MVS config
 if (WIN32)
-    set(MVS_DIR "C:/Program Files (x86) /MVS/Development")
+    set(MVS_DIR "C:/BuildEssentials/Library/MVS/Development")
     set(MVS_INCLUDE_DIR ${MVS_DIR}/Includes)
     set(MVS_LIB_DIR ${MVS_DIR}/Libraries/win64)
 else ()
@@ -70,6 +70,7 @@ endif ()
 find_library(MVS_LIB MvCameraControl HINTS ${MVS_LIB_DIR})
 target_include_directories(${PROJECT_NAME} PRIVATE ${MVS_INCLUDE_DIR})
 target_link_libraries(${PROJECT_NAME} ${MVS_LIB})
+target_sources(${PROJECT_NAME} PRIVATE src/mvs_camera.cpp)
 
 # Boost config
 find_package(Boost REQUIRED)

+ 13 - 0
src/config.h

@@ -1,7 +1,20 @@
 #ifndef REMOTEAR2_CONFIG_H
 #define REMOTEAR2_CONFIG_H
 
+#include <chrono>
+
 static constexpr auto main_window_width = 800;
 static constexpr auto main_window_height = 600;
 
+static constexpr auto camera_image_width = 2448;
+static constexpr auto camera_image_height = 2048;
+static constexpr auto camera_image_size = camera_image_width * camera_image_height * 1; // 1 byte per elem
+
+static constexpr auto default_time_out = std::chrono::milliseconds(50); // 50ms
+
+#define RET_ERROR \
+    assert(false); \
+    return false; \
+    (void) 0
+
 #endif //REMOTEAR2_CONFIG_H

+ 15 - 0
src/main.cpp

@@ -49,6 +49,21 @@ int main() {
 
         ImGui::ShowDemoWindow();
 
+        if (ImGui::Begin("Remote AR Control")) {
+
+            // camera control
+            if (ImGui::CollapsingHeader("Camera")) {
+
+                // camera actions
+                ImGui::SeparatorText("Actions");
+                if (ImGui::Button("Open")) {
+
+                }
+            }
+
+        }
+        ImGui::End();
+
         ImGui::Render();
         int frame_width, frame_height;
         glfwGetFramebufferSize(main_window, &frame_width, &frame_height);

+ 136 - 0
src/mvs_camera.cpp

@@ -0,0 +1,136 @@
+#include "mvs_camera.h"
+#include "config.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4828)
+#endif
+
+#include <MvCameraControl.h>
+
+#include <spdlog/spdlog.h>
+
+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)) \
+        return false
+
+struct mvs_camera::impl {
+    void *handle = nullptr;
+    std::string_view cam_name;
+    bool is_capturing = false;
+
+    cv::cuda::GpuMat *inner_img = nullptr;
+    std::atomic<cv::cuda::GpuMat *> next_img;
+
+    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);
+        assert(false);
+    }
+
+    static void on_image(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info, void *user_data) {
+        assert(frame_info->nFrameLen == camera_image_size);
+        auto pimpl = (impl *) user_data;
+        auto host_img = cv::Mat{camera_image_height, camera_image_width, CV_8UC1, data};
+
+        // upload image to gpu
+        if (pimpl->inner_img == nullptr) [[unlikely]] {
+            pimpl->inner_img = new cv::cuda::GpuMat{};
+        }
+        pimpl->inner_img->upload(host_img);
+
+        // commit new image
+        pimpl->inner_img = pimpl->next_img.exchange(pimpl->inner_img);
+        pimpl->next_img.notify_all();
+    }
+};
+
+mvs_camera::mvs_camera()
+        : pimpl(std::make_unique<impl>()) {}
+
+mvs_camera::~mvs_camera() {
+    close();
+}
+
+bool mvs_camera::open(std::string_view camera_name) {
+    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 (camera_name == tmp_dev_name) {
+            dev_info = tmp_dev_info;
+        }
+    }
+    if (dev_info == nullptr) {
+        SPDLOG_ERROR("No camera named {}.", camera_name);
+        RET_ERROR;
+    }
+    pimpl->cam_name = camera_name;
+
+    MVS_API_CHECK(MV_CC_IsDeviceAccessible(dev_info, MV_ACCESS_Control));
+    MVS_API_CHECK(MV_CC_CreateHandle(&pimpl->handle, dev_info));
+    MVS_API_CHECK(MV_CC_OpenDevice(pimpl->handle, MV_ACCESS_Control));
+
+    // close and open again to fix some bug
+    MVS_API_CHECK(MV_CC_CloseDevice(pimpl->handle));
+    MVS_API_CHECK(MV_CC_OpenDevice(pimpl->handle, MV_ACCESS_Control));
+
+    // register callbacks
+    MVS_API_CHECK(MV_CC_RegisterExceptionCallBack(pimpl->handle, impl::on_error, pimpl.get()));
+    MVS_API_CHECK(MV_CC_RegisterImageCallBackEx(pimpl->handle, impl::on_image, pimpl.get()));
+
+    return true;
+}
+
+void mvs_camera::close() {
+    if (pimpl->handle == nullptr) return;
+    stop_capture();
+    MV_CC_CloseDevice(pimpl->handle);
+    MV_CC_DestroyHandle(pimpl->handle);
+    pimpl->handle = nullptr;
+}
+
+bool mvs_camera::start_capture(const capture_config *config) {
+    assert(pimpl->handle != nullptr);
+
+    // config camera
+    MVS_API_CHECK(MV_CC_SetEnumValue(pimpl->handle, "PixelFormat", PixelType_Gvsp_BayerRG8));
+    MVS_API_CHECK(MV_CC_SetEnumValue(pimpl->handle, "AcquisitionMode",
+                                     MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS));
+    MVS_API_CHECK(MV_CC_SetEnumValue(pimpl->handle, "TriggerMode", MV_TRIGGER_MODE_ON));
+    MVS_API_CHECK(MV_CC_SetEnumValue(pimpl->handle, "TriggerSource",
+                                     MV_CAM_TRIGGER_SOURCE::MV_TRIGGER_SOURCE_SOFTWARE));
+    MVS_API_CHECK(MV_CC_SetFloatValue(pimpl->handle, "ExposureTime", config->exposure_time));
+    MVS_API_CHECK(MV_CC_SetFloatValue(pimpl->handle, "Gain", config->analog_gain));
+
+    MVS_API_CHECK(MV_CC_StartGrabbing(pimpl->handle));
+    pimpl->is_capturing = true;
+    return true;
+}
+
+void mvs_camera::stop_capture() {
+    if (pimpl->handle == nullptr || !pimpl->is_capturing) return;
+    MV_CC_StopGrabbing(pimpl->handle);
+}
+
+bool mvs_camera::software_trigger() {
+    assert(pimpl->handle != nullptr);
+    MVS_API_CHECK(MV_CC_TriggerSoftwareExecute(pimpl->handle));
+    return true;
+}
+
+void mvs_camera::retrieve_image(cv::cuda::GpuMat **image_ptr) {
+    pimpl->next_img.wait(nullptr);
+    *image_ptr = pimpl->next_img.exchange(*image_ptr);
+}

+ 40 - 0
src/mvs_camera.h

@@ -0,0 +1,40 @@
+#ifndef REMOTEAR2_MVS_CAMERA_H
+#define REMOTEAR2_MVS_CAMERA_H
+
+#include <opencv2/core/cuda.hpp>
+
+#include <atomic>
+#include <memory>
+#include <string_view>
+
+class mvs_camera {
+public:
+
+    mvs_camera();
+
+    ~mvs_camera();
+
+    bool open(std::string_view camera_name);
+
+    void close();
+
+    struct capture_config {
+        float exposure_time;
+        float analog_gain;
+    };
+
+    bool start_capture(const capture_config *config);
+
+    void stop_capture();
+
+    bool software_trigger();
+
+    void retrieve_image(cv::cuda::GpuMat **image_ptr);
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+
+#endif //REMOTEAR2_MVS_CAMERA_H