Pārlūkot izejas kodu

Implemented camera control GUI.

jcsyshc 2 gadi atpakaļ
vecāks
revīzija
dfb33817de
5 mainītis faili ar 202 papildinājumiem un 2 dzēšanām
  1. 24 0
      src/config.h
  2. 50 1
      src/main.cpp
  3. 15 1
      src/mvs_camera.cpp
  4. 4 0
      src/mvs_camera.h
  5. 109 0
      src/stereo_camera.hpp

+ 24 - 0
src/config.h

@@ -2,6 +2,9 @@
 #define REMOTEAR2_CONFIG_H
 
 #include <chrono>
+#include <string_view>
+
+#include <spdlog/spdlog.h>
 
 static constexpr auto main_window_width = 800;
 static constexpr auto main_window_height = 600;
@@ -10,11 +13,32 @@ 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 left_camera_name = "LeftEye";
+static constexpr auto right_camera_name = "RightEye";
+
+static constexpr auto default_camera_exposure_time_ms = 5; // 5ms
+static constexpr auto default_camera_analog_gain = 20; // 20dB
+static constexpr auto default_camera_capture_interval_ms = 33; // 33ms
+
 static constexpr auto default_time_out = std::chrono::milliseconds(50); // 50ms
+static constexpr auto default_spin_time = std::chrono::milliseconds(100); // 100us
 
 #define RET_ERROR \
     assert(false); \
     return false; \
     (void) 0
 
+inline bool check_function_call(bool function_ret, unsigned int line_number,
+                                const char *file_name, const char *function_call_str) {
+    if (function_ret) [[likely]] return true;
+    SPDLOG_ERROR("Function call {} failed at {}:{}.",
+                 function_call_str, file_name, line_number);
+    RET_ERROR;
+}
+
+#define CALL_CHECK(function_call) \
+    if (!check_function_call( \
+        function_call, __LINE__, __FILE__, #function_call)) \
+        return false
+
 #endif //REMOTEAR2_CONFIG_H

+ 50 - 1
src/main.cpp

@@ -1,4 +1,5 @@
 #include "config.h"
+#include "stereo_camera.hpp"
 
 #include <imgui.h>
 #include <imgui_impl_glfw.h>
@@ -14,6 +15,10 @@
 int main() {
 
     // setup glfw and main window
+    glfwSetErrorCallback([](int error, const char *desc) {
+        SPDLOG_ERROR("GLFW error: code = {}, description = {}", error, desc);
+        assert(false);
+    });
     auto ret = glfwInit();
     assert(ret == GLFW_TRUE);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
@@ -38,6 +43,9 @@ int main() {
     ImGui_ImplGlfw_InitForOpenGL(main_window, true);
     ImGui_ImplOpenGL3_Init();
 
+    // working staffs
+    stereo_camera camera;
+
     // main loop
     while (!glfwWindowShouldClose(main_window)) {
 
@@ -54,10 +62,51 @@ int main() {
             // camera control
             if (ImGui::CollapsingHeader("Camera")) {
 
+                static int trigger_interval_ms = default_camera_capture_interval_ms;
+                static float exposure_time_ms = default_camera_exposure_time_ms;
+                static float analog_gain = default_camera_analog_gain;
+
                 // camera actions
                 ImGui::SeparatorText("Actions");
-                if (ImGui::Button("Open")) {
+                if (!camera.is_opened()) {
+                    if (ImGui::Button("Open")) {
+                        camera.open();
+                    }
+                } else {
+                    if (ImGui::Button("Close")) {
+                        camera.close();
+                    }
+                    ImGui::SameLine();
+                    if (!camera.is_capturing()) {
+                        if (ImGui::Button("Start")) {
+                            camera.start_capture(1000 * exposure_time_ms, analog_gain, trigger_interval_ms);
+                        }
+                    } else {
+                        if (ImGui::Button("Stop")) {
+                            camera.stop_capture();
+                        }
+                    }
+                }
 
+                // camera configs
+                if (camera.is_opened()) {
+                    ImGui::SeparatorText("Configs");
+
+                    // don't allow config change wile camera is capturing
+                    if (camera.is_capturing()) {
+                        ImGui::BeginDisabled();
+                    }
+
+                    ImGui::PushItemWidth(200);
+                    ImGui::SliderInt("Trigger Interval (ms)", &trigger_interval_ms, 17, 100);
+                    ImGui::DragFloat("Exposure Time (ms)", &exposure_time_ms,
+                                     0.1, 1, (float) trigger_interval_ms, "%.1f");
+                    ImGui::DragFloat("Analog Gain (dB)", &analog_gain, 0.1, 0, 24, "%.1f");
+                    ImGui::PopItemWidth();
+
+                    if (camera.is_capturing()) {
+                        ImGui::EndDisabled();
+                    }
                 }
             }
 

+ 15 - 1
src/mvs_camera.cpp

@@ -33,6 +33,7 @@ struct mvs_camera::impl {
     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);
     }
 
@@ -78,7 +79,7 @@ bool mvs_camera::open(std::string_view camera_name) {
     }
     pimpl->cam_name = camera_name;
 
-    MVS_API_CHECK(MV_CC_IsDeviceAccessible(dev_info, MV_ACCESS_Control));
+//    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));
 
@@ -90,6 +91,7 @@ bool mvs_camera::open(std::string_view camera_name) {
     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()));
 
+    SPDLOG_INFO("Camera {} opened successfully.", pimpl->cam_name);
     return true;
 }
 
@@ -99,6 +101,7 @@ void mvs_camera::close() {
     MV_CC_CloseDevice(pimpl->handle);
     MV_CC_DestroyHandle(pimpl->handle);
     pimpl->handle = nullptr;
+    SPDLOG_INFO("Camera {} closed.", pimpl->cam_name);
 }
 
 bool mvs_camera::start_capture(const capture_config *config) {
@@ -116,12 +119,15 @@ bool mvs_camera::start_capture(const capture_config *config) {
 
     MVS_API_CHECK(MV_CC_StartGrabbing(pimpl->handle));
     pimpl->is_capturing = true;
+    SPDLOG_INFO("Camera {} is capturing.", pimpl->cam_name);
     return true;
 }
 
 void mvs_camera::stop_capture() {
     if (pimpl->handle == nullptr || !pimpl->is_capturing) return;
     MV_CC_StopGrabbing(pimpl->handle);
+    pimpl->is_capturing = false;
+    SPDLOG_INFO("Camera {} stopped capturing.", pimpl->cam_name);
 }
 
 bool mvs_camera::software_trigger() {
@@ -133,4 +139,12 @@ bool mvs_camera::software_trigger() {
 void mvs_camera::retrieve_image(cv::cuda::GpuMat **image_ptr) {
     pimpl->next_img.wait(nullptr);
     *image_ptr = pimpl->next_img.exchange(*image_ptr);
+}
+
+bool mvs_camera::is_opened() const {
+    return pimpl->handle != nullptr;
+}
+
+bool mvs_camera::is_capturing() const {
+    return pimpl->is_capturing;
 }

+ 4 - 0
src/mvs_camera.h

@@ -31,6 +31,10 @@ public:
 
     void retrieve_image(cv::cuda::GpuMat **image_ptr);
 
+    bool is_opened() const;
+
+    bool is_capturing() const;
+
 private:
     struct impl;
     std::unique_ptr<impl> pimpl;

+ 109 - 0
src/stereo_camera.hpp

@@ -0,0 +1,109 @@
+#ifndef REMOTEAR2_STEREO_CAMERA_HPP
+#define REMOTEAR2_STEREO_CAMERA_HPP
+
+#include "config.h"
+#include "mvs_camera.h"
+
+#include <atomic>
+#include <thread>
+
+struct stereo_camera {
+
+    mvs_camera left_camera, right_camera;
+
+    bool open() {
+        if (!open_impl()) { // keep consistency
+            close();
+            return false;
+        }
+        return true;
+    }
+
+    void close() {
+        left_camera.close();
+        right_camera.close();
+    }
+
+    bool start_capture(float exposure_time_us, float analog_gain, int trigger_interval_ms) {
+        if (!start_capture_impl(exposure_time_us, analog_gain)) {
+            stop_capture();
+            return false;
+        }
+        start_trigger_thread(trigger_interval_ms);
+        return true;
+    }
+
+    void stop_capture() {
+        left_camera.stop_capture();
+        right_camera.stop_capture();
+
+        // stop trigger thread
+        if (trigger_thread != nullptr) {
+
+            // let thread exit by itself
+            should_stop.test_and_set();
+            trigger_thread->join();
+
+            // cleanup
+            should_stop.clear();
+            trigger_thread = nullptr;
+        }
+    }
+
+    bool software_trigger() {
+        return left_camera.software_trigger() &&
+               right_camera.software_trigger();
+    }
+
+    bool is_opened() {
+        assert(left_camera.is_opened() == right_camera.is_opened());
+        return left_camera.is_opened();
+    }
+
+    bool is_capturing() {
+        assert(left_camera.is_capturing() == right_camera.is_capturing());
+        return left_camera.is_capturing();
+    }
+
+private:
+
+    std::thread *trigger_thread = nullptr;
+    std::atomic_flag should_stop = false;
+
+    bool open_impl() {
+        CALL_CHECK(left_camera.open(left_camera_name));
+        CALL_CHECK(right_camera.open(right_camera_name));
+        return true;
+    }
+
+    bool start_capture_impl(float exposure_time, float analog_gain) {
+        auto config = mvs_camera::capture_config{};
+        config.exposure_time = exposure_time;
+        config.analog_gain = analog_gain;
+
+        CALL_CHECK(left_camera.start_capture(&config));
+        CALL_CHECK(right_camera.start_capture(&config));
+        return true;
+    }
+
+    void start_trigger_thread(int trigger_interval_ms) {
+        assert(trigger_thread == nullptr);
+        trigger_thread = new std::thread{[=, this]() {
+            auto trigger_interval = std::chrono::milliseconds{trigger_interval_ms};
+            auto next_trigger_time = std::chrono::high_resolution_clock::now();
+            while (true) {
+                if (should_stop.test()) break;
+                software_trigger();
+
+                // resume at (almost) exact time
+                next_trigger_time += trigger_interval;
+                std::this_thread::sleep_until(next_trigger_time - default_spin_time);
+                while (std::chrono::high_resolution_clock::now() < next_trigger_time)
+                    std::this_thread::yield();
+            }
+        }};
+    }
+
+};
+
+#endif //REMOTEAR2_STEREO_CAMERA_HPP