diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2019-03-18 15:20:09 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2019-03-18 15:20:09 +0100 |
commit | 5023b54ba76325bb0b5598d59714bdad2d55d81e (patch) | |
tree | 15cc639eff7dbfa12eeccaa52d7fd251f18969e6 | |
parent | 3aababf6fd53a7b0312c2c1492bab6b43584b613 (diff) |
video: add support for camera modules
Issue: #910
28 files changed, 497 insertions, 143 deletions
diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp index df1344bf..65b8e10d 100644 --- a/api/plugin-support.hpp +++ b/api/plugin-support.hpp @@ -35,10 +35,11 @@ struct dylib final Tracker = 0xcafebeef, Protocol = 0xdeadf00d, Extension = 0xcafebabe, + Video = 0xbadf00d, Invalid = (unsigned)-1, }; - dylib(const QString& filename_, Type t) : + dylib(const QString& filename_, Type t, bool load = true) : full_filename(filename_), module_name(trim_filename(filename_)) { @@ -57,19 +58,25 @@ struct dylib final if (check(!handle.load())) return; - if (check((Dialog = (module_ctor_t) handle.resolve("GetDialog"), !Dialog))) - return; + if (load) + { + if (check((Dialog = (module_ctor_t) handle.resolve("GetDialog"), !Dialog))) + return; - if (check((Constructor = (module_ctor_t) handle.resolve("GetConstructor"), !Constructor))) - return; + if (check((Constructor = (module_ctor_t) handle.resolve("GetConstructor"), !Constructor))) + return; - if (check((Meta = (module_metadata_t) handle.resolve("GetMetadata"), !Meta))) - return; + if (check((Meta = (module_metadata_t) handle.resolve("GetMetadata"), !Meta))) + return; + + std::unique_ptr<Metadata_> m{Meta()}; - std::unique_ptr<Metadata_> m{Meta()}; + if (check(!m)) + return; - icon = m->icon(); - name = m->name(); + icon = m->icon(); + name = m->name(); + } type = t; #ifdef __clang__ @@ -88,18 +95,20 @@ struct dylib final const struct filter_ { Type type{Invalid}; QString glob; + bool load = true; } filters[] = { { Filter, QStringLiteral(OPENTRACK_LIBRARY_PREFIX "opentrack-filter-*." OPENTRACK_LIBRARY_EXTENSION), }, { Tracker, QStringLiteral(OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-*." OPENTRACK_LIBRARY_EXTENSION), }, { Protocol, QStringLiteral(OPENTRACK_LIBRARY_PREFIX "opentrack-proto-*." OPENTRACK_LIBRARY_EXTENSION), }, { Extension, QStringLiteral(OPENTRACK_LIBRARY_PREFIX "opentrack-ext-*." OPENTRACK_LIBRARY_EXTENSION), }, + { Video, QStringLiteral(OPENTRACK_LIBRARY_PREFIX "opentrack-video-*." OPENTRACK_LIBRARY_EXTENSION), false, }, }; for (const filter_& filter : filters) { for (const QString& filename : module_directory.entryList({ filter.glob }, QDir::Files, QDir::Name)) { - auto lib = std::make_shared<dylib>(QStringLiteral("%1/%2").arg(library_path, filename), filter.type); + auto lib = std::make_shared<dylib>(QStringLiteral("%1/%2").arg(library_path, filename), filter.type, filter.load); if (lib->type == Invalid) continue; @@ -159,6 +168,7 @@ private: OPENTRACK_LIBRARY_PREFIX "opentrack-proto-", OPENTRACK_LIBRARY_PREFIX "opentrack-filter-", OPENTRACK_LIBRARY_PREFIX "opentrack-ext-", + OPENTRACK_LIBRARY_PREFIX "opentrack-video-", }; for (auto name : names) @@ -198,7 +208,8 @@ struct Modules final filter_modules(filter(dylib::Filter)), tracker_modules(filter(dylib::Tracker)), protocol_modules(filter(dylib::Protocol)), - extension_modules(filter(dylib::Extension)) + extension_modules(filter(dylib::Extension)), + video_modules(filter(dylib::Video)) {} dylib_list& filters() { return filter_modules; } dylib_list& trackers() { return tracker_modules; } @@ -211,6 +222,7 @@ private: dylib_list tracker_modules; dylib_list protocol_modules; dylib_list extension_modules; + dylib_list video_modules; static dylib_list& sorted(dylib_list& xs) { diff --git a/tracker-aruco/ftnoir_tracker_aruco.cpp b/tracker-aruco/ftnoir_tracker_aruco.cpp index 8928566f..d9674755 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.cpp +++ b/tracker-aruco/ftnoir_tracker_aruco.cpp @@ -6,8 +6,6 @@ */ #include "ftnoir_tracker_aruco.h" -#include "cv/video-property-page.hpp" -#include "compat/camera-names.hpp" #include "compat/sleep.hpp" #include "compat/math-imports.hpp" @@ -75,7 +73,6 @@ aruco_tracker::~aruco_tracker() wait(); // fast start/stop causes breakage portable::sleep(1000); - camera.release(); } module_status aruco_tracker::start_tracker(QFrame* videoframe) @@ -166,16 +163,18 @@ bool aruco_tracker::open_camera() QMutexLocker l(&camera_mtx); - camera = cv::VideoCapture(camera_name_to_index(s.camera_name)); + camera = video::make_camera(s.camera_name); + video::impl::camera::info args {}; + if (res.width) { - camera.set(cv::CAP_PROP_FRAME_WIDTH, res.width); - camera.set(cv::CAP_PROP_FRAME_HEIGHT, res.height); + args.width = res.width; + args.height = res.height; } if (fps) - camera.set(cv::CAP_PROP_FPS, fps); + args.fps = fps; - if (!camera.isOpened()) + if (!camera->start(args)) { qDebug() << "aruco tracker: can't open camera"; return false; @@ -372,14 +371,28 @@ void aruco_tracker::run() { QMutexLocker l(&camera_mtx); - if (!camera.read(color)) + auto [ img, res ] = camera->get_frame(); + + if (!res) { portable::sleep(100); continue; } - } - cv::cvtColor(color, grayscale, cv::COLOR_BGR2GRAY); + color = cv::Mat(img.height, img.width, CV_8UC(img.channels), (void*)img.data, img.stride); + + switch (img.channels) + { + case 1: + grayscale.setTo(color); break; + case 3: + cv::cvtColor(color, grayscale, cv::COLOR_BGR2GRAY); + break; + default: + qDebug() << "aruco: can't handle" << img.channels << "color channels"; + return; + } + } #ifdef DEBUG_UNSHARP_MASKING { @@ -496,7 +509,9 @@ aruco_dialog::aruco_dialog() : tracker = nullptr; calib_timer.setInterval(100); - ui.cameraName->addItems(get_camera_names()); + + for (const auto& str : video::camera_names()) + ui.cameraName->addItem(str); tie_setting(s.camera_name, ui.cameraName); tie_setting(s.resolution, ui.resolution); @@ -572,10 +587,10 @@ void aruco_dialog::camera_settings() if (tracker) { QMutexLocker l(&tracker->camera_mtx); - video_property_page::show_from_capture(tracker->camera, camera_name_to_index(s.camera_name)); + (void)tracker->camera->show_dialog(); } else - video_property_page::show(camera_name_to_index(s.camera_name)); + (void)video::show_dialog(s.camera_name); } void aruco_dialog::update_camera_settings_state(const QString& name) diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h index 1d6fd107..0a33f02b 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.h +++ b/tracker-aruco/ftnoir_tracker_aruco.h @@ -13,6 +13,7 @@ #include "api/plugin-api.hpp" #include "cv/video-widget.hpp" #include "compat/timer.hpp" +#include "video/camera.hpp" #include "aruco/markerdetector.h" @@ -27,7 +28,6 @@ #include <cinttypes> #include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> // value 0->1 //#define DEBUG_UNSHARP_MASKING .75 @@ -77,7 +77,7 @@ public: void getRT(cv::Matx33d &r, cv::Vec3d &t); QMutex camera_mtx; - cv::VideoCapture camera; + std::unique_ptr<video::impl::camera> camera; private: bool detect_with_roi(); diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt index f12f530b..304a6b3d 100644 --- a/tracker-pt/CMakeLists.txt +++ b/tracker-pt/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(OpenCV QUIET) if(OpenCV_FOUND) otr_module(tracker-pt-base STATIC) target_include_directories(${self} SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) - target_link_libraries(${self} opencv_imgproc opentrack-cv opencv_core) + target_link_libraries(${self} opencv_imgproc opentrack-cv opencv_core opentrack-video) set_property(TARGET ${self} PROPERTY OUTPUT_NAME "pt-base") endif() add_subdirectory(module) diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp index 3854e531..4b796af7 100644 --- a/tracker-pt/ftnoir_tracker_pt.cpp +++ b/tracker-pt/ftnoir_tracker_pt.cpp @@ -8,7 +8,6 @@ #include "ftnoir_tracker_pt.h" #include "video/video-widget.hpp" -#include "compat/camera-names.hpp" #include "compat/math-imports.hpp" #include "pt-api.hpp" @@ -121,7 +120,7 @@ bool Tracker_PT::maybe_reopen_camera() { QMutexLocker l(&camera_mtx); - return camera->start(camera_name_to_index(s.camera_name), + return camera->start(s.camera_name, s.cam_fps, s.cam_res_x, s.cam_res_y); } diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp index 2b06c823..edf689a9 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp +++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp @@ -7,10 +7,9 @@ */ #include "ftnoir_tracker_pt_dialog.h" - #include "compat/math.hpp" -#include "compat/camera-names.hpp" -#include "cv/video-property-page.hpp" +#include "video/camera.hpp" + #include <opencv2/core.hpp> #include <QString> @@ -33,7 +32,8 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) : ui.setupUi(this); - ui.camdevice_combo->addItems(get_camera_names()); + for (const QString& str : video::camera_names()) + ui.camdevice_combo->addItem(str); tie_setting(s.camera_name, ui.camdevice_combo); tie_setting(s.cam_res_x, ui.res_x_spin); @@ -231,10 +231,7 @@ void TrackerDialog_PT::show_camera_settings() tracker->camera->show_camera_settings(); } else - { - const int idx = camera_name_to_index(s.camera_name); - video_property_page::show(idx); - } + (void)video::show_dialog(s.camera_name); } void TrackerDialog_PT::trans_calib_step() diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp index 1afecc92..687f5bff 100644 --- a/tracker-pt/module/camera.cpp +++ b/tracker-pt/module/camera.cpp @@ -8,15 +8,9 @@ #include "camera.h" #include "frame.hpp" -#include "compat/sleep.hpp" -#include "compat/camera-names.hpp" #include "compat/math-imports.hpp" -#include <opencv2/imgproc.hpp> - -#include "cv/video-property-page.hpp" - -#include <cstdlib> +#include <opencv2/core.hpp> namespace pt_module { @@ -26,22 +20,18 @@ Camera::Camera(const QString& module_name) : s { module_name } QString Camera::get_desired_name() const { - return desired_name; + return cam_desired.name; } QString Camera::get_active_name() const { - return active_name; + return cam_info.name; } void Camera::show_camera_settings() { - const int idx = camera_name_to_index(s.camera_name); - - if (cap && cap->isOpened()) - video_property_page::show_from_capture(*cap, idx); - else - video_property_page::show(idx); + if (cap) + (void)cap->show_dialog(); } Camera::result Camera::get_info() const @@ -83,59 +73,53 @@ Camera::result Camera::get_frame(pt_frame& frame_) return { false, {} }; } -bool Camera::start(int idx, int fps, int res_x, int res_y) +bool Camera::start(const QString& name, int fps, int res_x, int res_y) { - if (idx >= 0 && fps >= 0 && res_x >= 0 && res_y >= 0) + if (fps >= 0 && res_x >= 0 && res_y >= 0) { - if (cam_desired.idx != idx || + if (cam_desired.name != name || (int)cam_desired.fps != fps || cam_desired.res_x != res_x || cam_desired.res_y != res_y || - !cap || !cap->isOpened() || !cap->grab()) + !cap || !cap->is_open()) { stop(); - desired_name = get_camera_names().value(idx); - cam_desired.idx = idx; + cam_desired.name = name; cam_desired.fps = fps; cam_desired.res_x = res_x; cam_desired.res_y = res_y; cam_desired.fov = fov; - cap = camera_ptr(new cv::VideoCapture(idx)); + cap = video::make_camera(name); - if (cam_desired.res_x > 0 && cam_desired.res_y > 0) - { - cap->set(cv::CAP_PROP_FRAME_WIDTH, res_x); - cap->set(cv::CAP_PROP_FRAME_HEIGHT, res_y); - } + if (!cap) + goto fail; - if (fps > 0) - cap->set(cv::CAP_PROP_FPS, fps); + camera::info info {}; + info.fps = fps; + info.width = res_x; + info.height = res_y; - if (cap->isOpened()) - { - cam_info = pt_camera_info(); - cam_info.idx = idx; - dt_mean = 0; - active_name = desired_name; + if (!cap->start(info)) + goto fail; - cv::Mat tmp; + cam_info = pt_camera_info(); + cam_info.name = name; + dt_mean = 0; - if (get_frame_(tmp)) - { - t.start(); - return true; - } - } + cv::Mat tmp; - cap = nullptr; - return false; - } + if (!get_frame_(tmp)) + goto fail; - return true; + t.start(); + } } + return true; + +fail: stop(); return false; } @@ -143,34 +127,23 @@ bool Camera::start(int idx, int fps, int res_x, int res_y) void Camera::stop() { cap = nullptr; - desired_name = QString{}; - active_name = QString{}; cam_info = {}; cam_desired = {}; } -bool Camera::get_frame_(cv::Mat& frame) +bool Camera::get_frame_(cv::Mat& img) { - if (cap && cap->isOpened()) + if (cap && cap->is_open()) { - for (unsigned i = 0; i < 10; i++) + auto [ frame, ret ] = cap->get_frame(); + if (ret) { - if (cap->read(frame)) - return true; - portable::sleep(50); + img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, frame.stride); + return true; } } - return false; -} -void Camera::camera_deleter::operator()(cv::VideoCapture* cap) -{ - if (cap) - { - if (cap->isOpened()) - cap->release(); - delete cap; - } + return false; } } // ns pt_module diff --git a/tracker-pt/module/camera.h b/tracker-pt/module/camera.h index 2ea633d0..02e2fe4d 100644 --- a/tracker-pt/module/camera.h +++ b/tracker-pt/module/camera.h @@ -9,11 +9,11 @@ #include "pt-api.hpp" #include "compat/timer.hpp" +#include "video/camera.hpp" #include <memory> #include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> #include <QString> @@ -23,7 +23,7 @@ struct Camera final : pt_camera { Camera(const QString& module_name); - bool start(int idx, int fps, int res_x, int res_y) override; + bool start(const QString& name, int fps, int res_x, int res_y) override; void stop() override; result get_frame(pt_frame& Frame) override; @@ -37,23 +37,16 @@ struct Camera final : pt_camera void show_camera_settings() override; private: + using camera = typename video::impl::camera; + [[nodiscard]] bool get_frame_(cv::Mat& frame); f dt_mean = 0, fov = 30; Timer t; pt_camera_info cam_info; pt_camera_info cam_desired; - QString desired_name, active_name; - - struct camera_deleter final - { - void operator()(cv::VideoCapture* cap); - }; - - using camera_ptr = std::unique_ptr<cv::VideoCapture, camera_deleter>; - - camera_ptr cap; + std::unique_ptr<camera> cap; pt_settings s; static constexpr f dt_eps = f{1}/256; diff --git a/tracker-pt/module/point_extractor.cpp b/tracker-pt/module/point_extractor.cpp index 298d8752..2cb5db97 100644 --- a/tracker-pt/module/point_extractor.cpp +++ b/tracker-pt/module/point_extractor.cpp @@ -13,8 +13,6 @@ #include "cv/numeric.hpp" #include "compat/math.hpp" -#include <opencv2/videoio.hpp> - #undef PREVIEW //#define PREVIEW diff --git a/tracker-pt/pt-api.hpp b/tracker-pt/pt-api.hpp index b44cfea2..741576a1 100644 --- a/tracker-pt/pt-api.hpp +++ b/tracker-pt/pt-api.hpp @@ -12,6 +12,7 @@ #include <opencv2/core.hpp> #include <QImage> +#include <QString> #ifdef __clang__ # pragma clang diagnostic push @@ -30,7 +31,7 @@ struct pt_camera_info final int res_x = 0; int res_y = 0; - int idx = -1; + QString name; }; struct pt_pixel_pos_mixin @@ -74,7 +75,7 @@ struct pt_camera pt_camera(); virtual ~pt_camera(); - [[nodiscard]] virtual bool start(int idx, int fps, int res_x, int res_y) = 0; + [[nodiscard]] virtual bool start(const QString& name, int fps, int res_x, int res_y) = 0; virtual void stop() = 0; virtual result get_frame(pt_frame& frame) = 0; diff --git a/tracker-wii/wii_camera.cpp b/tracker-wii/wii_camera.cpp index 90ad6385..97a32b9f 100644 --- a/tracker-wii/wii_camera.cpp +++ b/tracker-wii/wii_camera.cpp @@ -16,13 +16,10 @@ #include "wii_frame.hpp" #include "compat/sleep.hpp" -#include "compat/camera-names.hpp" #include "compat/math-imports.hpp" #include <opencv2/imgproc.hpp> -#include "cv/video-property-page.hpp" - #include <bluetoothapis.h> using namespace pt_module; @@ -33,7 +30,7 @@ WIICamera::WIICamera(const QString& module_name) : s { module_name } cam_info.res_x = 1024; cam_info.res_y = 768; cam_info.fov = 42.0f; - cam_info.idx = 0; + cam_info.name = "Wii"; } WIICamera::~WIICamera() @@ -86,7 +83,7 @@ WIICamera::result WIICamera::get_frame(pt_frame& frame_) return result(true, cam_info); } -bool WIICamera::start(int idx, int fps, int res_x, int res_y) +bool WIICamera::start(const QString& name, int fps, int res_x, int res_y) { m_pDev = std::make_unique<wiimote>(); m_pDev->ChangedCallback = on_state_change; diff --git a/tracker-wii/wii_camera.h b/tracker-wii/wii_camera.h index 05f5436c..7bc74559 100644 --- a/tracker-wii/wii_camera.h +++ b/tracker-wii/wii_camera.h @@ -17,7 +17,6 @@ #include <tuple> #include <opencv2/core.hpp> -#include <opencv2/videoio.hpp> #include <QString> @@ -31,7 +30,7 @@ struct WIICamera final : pt_camera WIICamera(const QString& module_name); ~WIICamera() override; - bool start(int idx, int fps, int res_x, int res_y) override; + bool start(const QString& name, int fps, int res_x, int res_y) override; void stop() override; result get_frame(pt_frame& Frame) override; diff --git a/tracker-wii/wii_point_extractor.cpp b/tracker-wii/wii_point_extractor.cpp index a23e0e5b..89e4b41b 100644 --- a/tracker-wii/wii_point_extractor.cpp +++ b/tracker-wii/wii_point_extractor.cpp @@ -14,8 +14,6 @@ #include "cv/numeric.hpp" #include "compat/math.hpp" -#include <opencv2/videoio.hpp> - #undef PREVIEW //#define PREVIEW diff --git a/variant/default/_variant.cmake b/variant/default/_variant.cmake index 161fefda..7501b0a4 100644 --- a/variant/default/_variant.cmake +++ b/variant/default/_variant.cmake @@ -24,6 +24,7 @@ function(otr_init_variant) "migration" "main-window" "video" + "video-*" ) set_property(GLOBAL PROPERTY opentrack-subprojects "${subprojects}") diff --git a/variant/default/main-window.cpp b/variant/default/main-window.cpp index a223c32f..334695cb 100644 --- a/variant/default/main-window.cpp +++ b/variant/default/main-window.cpp @@ -448,7 +448,7 @@ void main_window::stop_tracker_() with_tracker_teardown sentinel; pose_update_timer.stop(); - ui.pose_display->rotate_sync(0,0,0, 0,0,0); + ui.pose_display->present(0,0,0, 0,0,0); if (pTrackerDialog) pTrackerDialog->unregister_tracker(); @@ -473,8 +473,8 @@ void main_window::stop_tracker_() void main_window::show_pose_(const double* mapped, const double* raw) { - ui.pose_display->rotate_async(mapped[Yaw], mapped[Pitch], -mapped[Roll], - mapped[TX], mapped[TY], mapped[TZ]); + ui.pose_display->present(mapped[Yaw], mapped[Pitch], -mapped[Roll], + mapped[TX], mapped[TY], mapped[TZ]); QLCDNumber* raw_[] = { ui.raw_x, ui.raw_y, ui.raw_z, diff --git a/video-opencv/CMakeLists.txt b/video-opencv/CMakeLists.txt new file mode 100644 index 00000000..d8b9b896 --- /dev/null +++ b/video-opencv/CMakeLists.txt @@ -0,0 +1,6 @@ +find_package(OpenCV QUIET) + +if(OpenCV_FOUND) + otr_module(video-opencv) + target_link_libraries(${self} opencv_core opencv_videoio opentrack-video) +endif() diff --git a/video-opencv/camera-impl.cpp b/video-opencv/camera-impl.cpp new file mode 100644 index 00000000..ca18fd4b --- /dev/null +++ b/video-opencv/camera-impl.cpp @@ -0,0 +1,173 @@ +/* Copyright (c) 2019 Stanislaw Halik <sthalik@misaki.pl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#include "compat/sleep.hpp" +#include "video/camera.hpp" + +#include "camera-names.hpp" +#include "video-property-page.hpp" + +#include <optional> + +#include <opencv2/core.hpp> +#include <opencv2/videoio.hpp> + +using namespace video::impl; + +struct cam; + +struct metadata : camera_ +{ + metadata(); + std::vector<QString> camera_names() const override; + std::unique_ptr<camera> make_camera(const QString& name) override; + bool can_show_dialog(const QString& camera_name) override; + bool show_dialog(const QString& camera_name) override; +}; + +struct cam final : camera +{ + cam(int idx); + ~cam() override; + + bool start(const info& args) override; + void stop() override; + bool is_open() override; + std::tuple<const frame&, bool> get_frame() override; + bool show_dialog() override; + + bool get_frame_(); + + std::optional<cv::VideoCapture> cap; + cv::Mat mat; + frame frame_; + int idx = -1; +}; + +metadata::metadata() = default; + +std::unique_ptr<camera> metadata::make_camera(const QString& name) +{ + int idx = camera_name_to_index(name); + if (idx != -1) + return std::make_unique<cam>(idx); + else + return nullptr; +} + +std::vector<QString> metadata::camera_names() const +{ + return get_camera_names(); +} + +bool metadata::can_show_dialog(const QString& camera_name) +{ + return camera_name_to_index(camera_name) != -1; +} + +bool metadata::show_dialog(const QString& camera_name) +{ + int idx = camera_name_to_index(camera_name); + if (idx != -1) + { + video_property_page::show(idx); + return true; + } + else + return false; +} + +cam::cam(int idx) : idx(idx) +{ +} + +cam::~cam() +{ + stop(); +} + +void cam::stop() +{ + if (cap) + { + if (cap->isOpened()) + cap->release(); + cap = std::nullopt; + } + mat = cv::Mat(); + frame_ = { {}, false }; +} + +bool cam::is_open() +{ + return !!cap; +} + +bool cam::start(const info& args) +{ + stop(); + cap.emplace(idx); + + if (args.width > 0 && args.height > 0) + { + cap->set(cv::CAP_PROP_FRAME_WIDTH, args.width); + cap->set(cv::CAP_PROP_FRAME_HEIGHT, args.height); + } + if (args.fps > 0) + cap->set(cv::CAP_PROP_FPS, args.fps); + + if (!cap->isOpened()) + goto fail; + + if (!get_frame_()) + goto fail; + + return true; + +fail: + stop(); + return false; +} + +bool cam::get_frame_() +{ + if (!is_open()) + return false; + + for (unsigned i = 0; i < 10; i++) + { + if (cap->read(mat)) + { + frame_.data = mat.data; + frame_.width = mat.cols; + frame_.height = mat.rows; + frame_.stride = mat.step.p[0]; + frame_.channels = mat.channels(); + + return true; + } + portable::sleep(50); + } + + return false; +} + +std::tuple<const frame&, bool> cam::get_frame() +{ + bool ret = get_frame_(); + return { frame_, ret }; +} + +bool cam::show_dialog() +{ + if (is_open()) + return video_property_page::show_from_capture(*cap, idx); + else + return video_property_page::show(idx); +} + +OTR_REGISTER_CAMERA(metadata) diff --git a/compat/camera-names.cpp b/video-opencv/camera-names.cpp index 246d76ee..69926e5a 100644 --- a/compat/camera-names.cpp +++ b/video-opencv/camera-names.cpp @@ -1,5 +1,8 @@ #include "camera-names.hpp" +#include <algorithm> +#include <iterator> + #ifdef _WIN32 # include <cwchar> # define NO_DSHOW_STRSAFE @@ -21,15 +24,16 @@ int camera_name_to_index(const QString &name) { auto list = get_camera_names(); - int ret = list.indexOf(name); - if (ret < 0) - ret = 0; - return ret; + auto it = std::find(list.cbegin(), list.cend(), name); + if (it != list.cend()) + return std::distance(list.cbegin(), it); + + return -1; } -QList<QString> get_camera_names() +std::vector<QString> get_camera_names() { - QList<QString> ret; + std::vector<QString> ret; #ifdef _WIN32 // Create the System Device Enumerator. HRESULT hr; @@ -62,7 +66,7 @@ QList<QString> get_camera_names() { // Display the name in your UI somehow. QString str((QChar*)var.bstrVal, int(std::wcslen(var.bstrVal))); - ret.append(str); + ret.push_back(str); } VariantClear(&var); pPropBag->Release(); @@ -93,7 +97,7 @@ QList<QString> get_camera_names() close(fd); continue; } - ret.append(QString{(const char*)video_cap.card}); + ret.push_back(QString((const char*)video_cap.card)); close(fd); } } diff --git a/compat/camera-names.hpp b/video-opencv/camera-names.hpp index 97184c8c..9f0883f5 100644 --- a/compat/camera-names.hpp +++ b/video-opencv/camera-names.hpp @@ -8,11 +8,11 @@ #pragma once -#include <QList> +#include <vector> #include <QString> #include "export.hpp" -OTR_COMPAT_EXPORT QList<QString> get_camera_names(); -OTR_COMPAT_EXPORT int camera_name_to_index(const QString &name); +std::vector<QString> get_camera_names(); +int camera_name_to_index(const QString &name); diff --git a/video-opencv/export.hpp b/video-opencv/export.hpp new file mode 100644 index 00000000..1d43a9f1 --- /dev/null +++ b/video-opencv/export.hpp @@ -0,0 +1,11 @@ +// generates export.hpp for each module from compat/linkage.hpp + +#pragma once + +#include "compat/linkage-macros.hpp" + +#ifdef BUILD_VIDEO_OPENCV +# define OTR_VIDEO_OPENCV_EXPORT OTR_GENERIC_EXPORT +#else +# define OTR_VIDEO_OPENCV_EXPORT OTR_GENERIC_IMPORT +#endif diff --git a/video-opencv/lang/nl_NL.ts b/video-opencv/lang/nl_NL.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/nl_NL.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video-opencv/lang/ru_RU.ts b/video-opencv/lang/ru_RU.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/ru_RU.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video-opencv/lang/stub.ts b/video-opencv/lang/stub.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/stub.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video-opencv/lang/zh_CN.ts b/video-opencv/lang/zh_CN.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/zh_CN.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/cv/video-property-page.cpp b/video-opencv/video-property-page.cpp index bae5e8b3..92abd887 100644 --- a/cv/video-property-page.cpp +++ b/video-opencv/video-property-page.cpp @@ -9,7 +9,7 @@ #ifdef _WIN32 -#include "compat/camera-names.hpp" +#include "camera-names.hpp" #include "compat/sleep.hpp" #include "compat/run-in-thread.hpp" #include "compat/library-path.hpp" diff --git a/cv/video-property-page.hpp b/video-opencv/video-property-page.hpp index c2b9525d..c2b9525d 100644 --- a/cv/video-property-page.hpp +++ b/video-opencv/video-property-page.hpp diff --git a/video/camera.cpp b/video/camera.cpp new file mode 100644 index 00000000..c33ab13a --- /dev/null +++ b/video/camera.cpp @@ -0,0 +1,66 @@ +#include "camera.hpp" + +#include <algorithm> +#include <utility> +#include <QMutex> + +static std::vector<std::unique_ptr<video::impl::camera_>> metadata; +static QMutex mtx; + +namespace video::impl { + +camera_::camera_() = default; +camera_::~camera_() = default; + +camera::camera() = default; +camera::~camera() = default; + +void register_camera(std::unique_ptr<impl::camera_> camera) +{ + QMutexLocker l(&mtx); + metadata.push_back(std::move(camera)); +} + +} // ns video::impl + +namespace video { + +bool show_dialog(const QString& camera_name) +{ + QMutexLocker l(&mtx); + + for (auto& camera : metadata) + for (const QString& name : camera->camera_names()) + if (name == camera_name) + return camera->show_dialog(camera_name); + + return false; +} + +std::unique_ptr<camera_impl> make_camera(const QString& name) +{ + QMutexLocker l(&mtx); + + for (auto& camera : metadata) + for (const QString& name_ : camera->camera_names()) + if (name_ == name) + return camera->make_camera(name); + + return nullptr; +} + +std::vector<QString> camera_names() +{ + QMutexLocker l(&mtx); + std::vector<QString> names; names.reserve(32); + + for (auto& camera : metadata) + for (const QString& name : camera->camera_names()) + if (std::find(names.cbegin(), names.cend(), name) == names.cend()) + names.push_back(name); + + std::sort(names.begin(), names.end()); + return names; +} + +} // ns video diff --git a/video/camera.hpp b/video/camera.hpp new file mode 100644 index 00000000..c9577933 --- /dev/null +++ b/video/camera.hpp @@ -0,0 +1,95 @@ +/* Copyright (c) 2019 Stanislaw Halik <sthalik@misaki.pl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#pragma once + +#include "export.hpp" + +#include <memory> +#include <vector> + +#include <QString> + +namespace video +{ + +struct frame final +{ + unsigned char* data = nullptr; + int width = 0, height = 0, stride = 0, channels = 0; +}; + +} // ns video + +namespace video::impl { + +using namespace video; + +struct camera; + +struct OTR_VIDEO_EXPORT camera_ +{ + camera_(); + virtual ~camera_(); + + virtual std::vector<QString> camera_names() const = 0; + virtual std::unique_ptr<camera> make_camera(const QString& name) = 0; + [[nodiscard]] virtual bool show_dialog(const QString& camera_name) = 0; + virtual bool can_show_dialog(const QString& camera_name) = 0; +}; + +struct OTR_VIDEO_EXPORT camera +{ + struct info final + { + int width = 0, height = 0, fps = 0; + }; + + camera(); + virtual ~camera(); + + [[nodiscard]] virtual bool start(const info& args) = 0; + virtual void stop() = 0; + virtual bool is_open() = 0; + + virtual std::tuple<const frame&, bool> get_frame() = 0; + [[nodiscard]] virtual bool show_dialog() = 0; +}; + +OTR_VIDEO_EXPORT +void register_camera(std::unique_ptr<impl::camera_> metadata); + +} // ns video::impl + +#define OTR_REGISTER_CAMERA2(type, ctr) \ + namespace { \ + struct init_##ctr \ + { \ + static char fuzz; \ + }; \ + char init_##ctr :: fuzz = \ + (::video::impl::register_camera(std::make_unique<type>()), 0); \ + } // anon ns + +#define OTR_REGISTER_CAMERA(type) \ + OTR_REGISTER_CAMERA2(type, __COUNTER__) + +namespace video +{ +using camera_impl = typename impl::camera; + +OTR_VIDEO_EXPORT +std::unique_ptr<camera_impl> make_camera(const QString& name); + +OTR_VIDEO_EXPORT +std::vector<QString> camera_names(); + +[[nodiscard]] +OTR_VIDEO_EXPORT +bool show_dialog(const QString& camera_name); + +} // ns video |