summaryrefslogtreecommitdiffhomepage
path: root/tracker-pt
diff options
context:
space:
mode:
Diffstat (limited to 'tracker-pt')
-rw-r--r--tracker-pt/CMakeLists.txt13
-rw-r--r--tracker-pt/FTNoIR_PT_Controls.ui789
-rw-r--r--tracker-pt/export.hpp15
-rw-r--r--tracker-pt/ftnoir_tracker_pt.cpp223
-rw-r--r--tracker-pt/ftnoir_tracker_pt.h57
-rw-r--r--tracker-pt/ftnoir_tracker_pt_dialog.cpp149
-rw-r--r--tracker-pt/ftnoir_tracker_pt_dialog.h17
-rw-r--r--tracker-pt/lang/de_DE.ts378
-rw-r--r--tracker-pt/lang/nl_NL.ts205
-rw-r--r--tracker-pt/lang/ru_RU.ts205
-rw-r--r--tracker-pt/lang/stub.ts205
-rw-r--r--tracker-pt/lang/zh_CN.ts207
-rw-r--r--tracker-pt/module/CMakeLists.txt10
-rw-r--r--tracker-pt/module/camera.cpp149
-rw-r--r--tracker-pt/module/camera.h30
-rw-r--r--tracker-pt/module/export.hpp11
-rw-r--r--tracker-pt/module/frame.cpp66
-rw-r--r--tracker-pt/module/frame.hpp20
-rw-r--r--tracker-pt/module/lang/de_DE.ts11
-rw-r--r--tracker-pt/module/lang/nl_NL.ts7
-rw-r--r--tracker-pt/module/lang/ru_RU.ts7
-rw-r--r--tracker-pt/module/lang/stub.ts7
-rw-r--r--tracker-pt/module/lang/zh_CN.ts9
-rw-r--r--tracker-pt/module/module.cpp21
-rw-r--r--tracker-pt/module/module.hpp20
-rw-r--r--tracker-pt/module/point_extractor.cpp307
-rw-r--r--tracker-pt/module/point_extractor.h26
-rw-r--r--tracker-pt/point-filter.cpp72
-rw-r--r--tracker-pt/point-filter.hpp32
-rw-r--r--tracker-pt/point_tracker.cpp210
-rw-r--r--tracker-pt/point_tracker.h41
-rw-r--r--tracker-pt/pt-api.cpp76
-rw-r--r--tracker-pt/pt-api.hpp92
-rw-r--r--tracker-pt/pt-settings.hpp48
34 files changed, 2575 insertions, 1160 deletions
diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt
index ae923d3d..0c2e9ce3 100644
--- a/tracker-pt/CMakeLists.txt
+++ b/tracker-pt/CMakeLists.txt
@@ -1,9 +1,12 @@
-find_package(OpenCV 3.0 QUIET)
-set(modules opencv_core opencv_videoio opencv_imgproc)
+include(opentrack-opencv)
+find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ foreach(k core)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
otr_module(tracker-pt-base STATIC)
- target_include_directories(opentrack-tracker-pt-base SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS})
- target_link_libraries(opentrack-tracker-pt-base opentrack-cv ${modules})
+ target_include_directories(${self} SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS})
+ target_link_libraries(${self} opentrack-cv opencv_core)
+ set_property(TARGET ${self} PROPERTY OUTPUT_NAME "pt-base")
endif()
add_subdirectory(module)
-
diff --git a/tracker-pt/FTNoIR_PT_Controls.ui b/tracker-pt/FTNoIR_PT_Controls.ui
index 366f219c..53a29c1a 100644
--- a/tracker-pt/FTNoIR_PT_Controls.ui
+++ b/tracker-pt/FTNoIR_PT_Controls.ui
@@ -9,12 +9,12 @@
<rect>
<x>0</x>
<y>0</y>
- <width>418</width>
- <height>724</height>
+ <width>413</width>
+ <height>630</height>
</rect>
</property>
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -32,85 +32,24 @@
<property name="autoFillBackground">
<bool>false</bool>
</property>
- <layout class="QGridLayout" name="gridLayout_9">
- <property name="sizeConstraint">
- <enum>QLayout::SetFixedSize</enum>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
</property>
- <item row="1" column="0" alignment="Qt::AlignVCenter">
- <widget class="QGroupBox" name="groupBox_5">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Status</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_10">
- <item row="1" column="0">
- <widget class="QLabel" name="label_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Extracted Points:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_38">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Camera Info:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="pointinfo_label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="caminfo_label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item row="0" column="0">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
<widget class="QTabWidget" name="tabWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
@@ -118,6 +57,12 @@
<number>0</number>
</property>
<widget class="QWidget" name="tab_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<attribute name="title">
<string>Camera</string>
</attribute>
@@ -134,64 +79,100 @@
<string>Camera settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="1">
- <widget class="QComboBox" name="camdevice_combo">
+ <item row="11" column="1">
+ <widget class="QCheckBox" name="chroma_key_overexposed">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_4">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumContentsLength">
- <number>10</number>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It's only neccessary to get position correspond to real-world values.</string>
+ </property>
+ <property name="text">
+ <string>Diagonal field of view</string>
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="QSpinBox" name="fov">
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_5">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="suffix">
- <string>°</string>
+ <property name="text">
+ <string>Dynamic pose (for caps only, never clips)</string>
</property>
- <property name="prefix">
- <string/>
+ </widget>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="minimum">
- <number>10</number>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
</property>
- <property name="maximum">
- <number>90</number>
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Color channels used</string>
</property>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QLabel" name="label_4">
+ <item row="8" column="1">
+ <widget class="QPushButton" name="camera_settings">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Diagonal field of view</string>
+ <string>Open</string>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_36">
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="use_mjpeg">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_13">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</string>
+ </property>
<property name="text">
- <string>Width</string>
+ <string>MJPEG compression</string>
</property>
</widget>
</item>
@@ -211,30 +192,28 @@
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QSpinBox" name="res_y_spin">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_36">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="toolTip">
- <string>Desired capture height</string>
- </property>
- <property name="suffix">
- <string> px</string>
- </property>
- <property name="maximum">
- <number>2000</number>
+ <property name="text">
+ <string>Width</string>
</property>
- <property name="singleStep">
- <number>10</number>
+ </widget>
+ </item>
+ <item row="11" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Chroma key includes overexposed pixels</string>
</property>
</widget>
</item>
- <item row="6" column="0">
- <widget class="QLabel" name="label_6">
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -242,30 +221,30 @@
</sizepolicy>
</property>
<property name="text">
- <string>Dynamic pose timeout</string>
+ <string>Camera settings (when available)</string>
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QSpinBox" name="fps_spin">
+ <item row="7" column="1">
+ <widget class="QSpinBox" name="init_phase_timeout">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="toolTip">
- <string>Desired capture framerate</string>
- </property>
<property name="suffix">
- <string> Hz</string>
+ <string> ms</string>
+ </property>
+ <property name="minimum">
+ <number>50</number>
</property>
<property name="maximum">
- <number>2000</number>
+ <number>5000</number>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="6" column="1">
<widget class="QCheckBox" name="dynamic_pose">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -300,8 +279,8 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_41">
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -309,26 +288,20 @@
</sizepolicy>
</property>
<property name="text">
- <string>Height</string>
+ <string>Dynamic pose timeout</string>
</property>
</widget>
</item>
- <item row="6" column="1">
- <widget class="QSpinBox" name="init_phase_timeout">
+ <item row="0" column="1">
+ <widget class="QComboBox" name="camdevice_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="suffix">
- <string> ms</string>
- </property>
- <property name="minimum">
- <number>50</number>
- </property>
- <property name="maximum">
- <number>5000</number>
+ <property name="minimumContentsLength">
+ <number>10</number>
</property>
</widget>
</item>
@@ -348,46 +321,39 @@
</property>
</widget>
</item>
- <item row="7" column="1">
- <widget class="QPushButton" name="camera_settings">
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_41">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Open</string>
+ <string>Height</string>
</property>
</widget>
</item>
- <item row="7" column="0">
- <widget class="QLabel" name="label_9">
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="fps_spin">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Camera settings (when available)</string>
+ <property name="toolTip">
+ <string>Desired capture framerate</string>
</property>
- </widget>
- </item>
- <item row="8" column="0">
- <widget class="QLabel" name="label_12">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <property name="suffix">
+ <string> Hz</string>
</property>
- <property name="text">
- <string>Color channels used</string>
+ <property name="maximum">
+ <number>2000</number>
</property>
</widget>
</item>
- <item row="8" column="1">
+ <item row="9" column="1">
<widget class="QComboBox" name="blob_color">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -397,12 +363,12 @@
</property>
<item>
<property name="text">
- <string>Average</string>
+ <string>Grayscale BT.709</string>
</property>
</item>
<item>
<property name="text">
- <string>Natural</string>
+ <string>Grayscale (from hardware)</string>
</property>
</item>
<item>
@@ -412,24 +378,133 @@
</item>
<item>
<property name="text">
+ <string>Green only</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string>Blue only</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>Red chroma key</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Green chroma key</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Blue chroma key</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Cyan chroma key</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yellow chroma key</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Magenta chroma key</string>
+ </property>
+ </item>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_5">
+ <item row="5" column="1">
+ <widget class="QSpinBox" name="fov">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string>°</string>
+ </property>
+ <property name="prefix">
+ <string/>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>90</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="res_y_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="toolTip">
+ <string>Desired capture height</string>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="maximum">
+ <number>2000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="0">
+ <widget class="QLabel" name="label_17">
<property name="text">
- <string>Dynamic pose (for caps only, never clips)</string>
+ <string>Chroma key strength</string>
</property>
</widget>
</item>
+ <item row="10" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QSlider" name="chroma_key_strength_slider">
+ <property name="minimum">
+ <number>5</number>
+ </property>
+ <property name="maximum">
+ <number>40</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="chroma_key_strength_label">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="minimum">
+ <double>0.500000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>4.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
</layout>
</widget>
</item>
@@ -469,6 +544,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Set minimum size to avoid small stray lights from being treated as points.</string>
+ </property>
<property name="text">
<string>Min size</string>
</property>
@@ -545,6 +626,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</string>
+ </property>
<property name="text">
<string>Automatic threshold</string>
</property>
@@ -572,6 +659,26 @@
</property>
</widget>
</item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="threshold_value_display">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Value</string>
+ </property>
+ </widget>
+ </item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="mindiam_spin">
<property name="sizePolicy">
@@ -594,29 +701,22 @@
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QLabel" name="threshold_value_display">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_14">
- <property name="text">
- <string>Value</string>
- </property>
- </widget>
- </item>
</layout>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
@@ -1318,6 +1418,231 @@ Don't roll or change position.</string>
</layout>
</widget>
</item>
+ <item row="2" column="0">
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Filter</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_9">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Point filter</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_12">
+ <item row="1" column="1">
+ <widget class="QSlider" name="point_filter_limit_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>99</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <double>999.990000000000009</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Limit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_deadzone_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSlider" name="point_filter_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>400</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSlider" name="point_filter_deadzone_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>3</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_30">
+ <property name="text">
+ <string>Deadzone</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDoubleSpinBox" name="point_filter_limit_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <double>1.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="enable_point_filter">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Filter point centers prior to pose estimation.</string>
+ </property>
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
@@ -1355,7 +1680,74 @@ Don't roll or change position.</string>
</widget>
</widget>
</item>
- <item row="2" column="0">
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Status</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_10">
+ <item row="1" column="1">
+ <widget class="QLabel" name="pointinfo_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="caminfo_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Extracted Points:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_38">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Camera Info:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -1376,11 +1768,14 @@ Don't roll or change position.</string>
<tabstop>res_x_spin</tabstop>
<tabstop>res_y_spin</tabstop>
<tabstop>fps_spin</tabstop>
+ <tabstop>use_mjpeg</tabstop>
<tabstop>fov</tabstop>
<tabstop>dynamic_pose</tabstop>
<tabstop>init_phase_timeout</tabstop>
<tabstop>camera_settings</tabstop>
<tabstop>blob_color</tabstop>
+ <tabstop>chroma_key_strength_slider</tabstop>
+ <tabstop>chroma_key_overexposed</tabstop>
<tabstop>auto_threshold</tabstop>
<tabstop>threshold_slider</tabstop>
<tabstop>mindiam_spin</tabstop>
@@ -1391,8 +1786,8 @@ Don't roll or change position.</string>
<tabstop>clip_bheight_spin</tabstop>
<tabstop>clip_blength_spin</tabstop>
<tabstop>cap_length_spin</tabstop>
- <tabstop>cap_height_spin</tabstop>
<tabstop>cap_width_spin</tabstop>
+ <tabstop>cap_height_spin</tabstop>
<tabstop>m1x_spin</tabstop>
<tabstop>m1y_spin</tabstop>
<tabstop>m1z_spin</tabstop>
@@ -1403,9 +1798,25 @@ Don't roll or change position.</string>
<tabstop>ty_spin</tabstop>
<tabstop>tz_spin</tabstop>
<tabstop>tcalib_button</tabstop>
+ <tabstop>enable_point_filter</tabstop>
+ <tabstop>point_filter_slider</tabstop>
+ <tabstop>point_filter_limit_slider</tabstop>
+ <tabstop>point_filter_deadzone_slider</tabstop>
+ <tabstop>maxdiam_spin</tabstop>
+ <tabstop>mindiam_spin</tabstop>
+ <tabstop>auto_threshold</tabstop>
+ <tabstop>threshold_slider</tabstop>
</tabstops>
<resources>
<include location="module/tracker_pt.qrc"/>
+ <include location="module/tracker_pt.qrc"/>
+ <include location="module/tracker_pt.qrc"/>
+ <include location="module/tracker_pt.qrc"/>
+ <include location="module/tracker_pt.qrc"/>
+ <include location="tracker_pt_base.qrc"/>
+ <include location="tracker_pt_base.qrc"/>
+ <include location="tracker_pt_base.qrc"/>
+ <include location="tracker_pt_base.qrc"/>
<include location="tracker_pt_base.qrc"/>
</resources>
<connections/>
diff --git a/tracker-pt/export.hpp b/tracker-pt/export.hpp
deleted file mode 100644
index cef63f83..00000000
--- a/tracker-pt/export.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// generates export.hpp for each module from compat/linkage.hpp
-
-#pragma once
-
-#if 0
-# include "compat/linkage-macros.hpp"
-# ifdef BUILD_TRACKER_PT
-# define OTR_PT_EXPORT OTR_GENERIC_EXPORT
-# else
-# define OTR_PT_EXPORT OTR_GENERIC_IMPORT
-# endif
-#else
-// static link
-# define OTR_PT_EXPORT
-#endif
diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp
index 243fbd60..0f8495d9 100644
--- a/tracker-pt/ftnoir_tracker_pt.cpp
+++ b/tracker-pt/ftnoir_tracker_pt.cpp
@@ -6,36 +6,35 @@
* copyright notice and this permission notice appear in all copies.
*/
+#undef NDEBUG
#include "ftnoir_tracker_pt.h"
-#include "compat/camera-names.hpp"
-#include "compat/math-imports.hpp"
-
#include "pt-api.hpp"
+#include "cv/init.hpp"
+#include "video/video-widget.hpp"
+#include "compat/math-imports.hpp"
+#include "compat/check-visible.hpp"
+#include "compat/thread-name.hpp"
+#include "compat/qt-dpi.hpp"
-#include <cmath>
-
-#include <opencv2/imgproc.hpp>
-
+#include <cassert>
#include <QHBoxLayout>
#include <QDebug>
#include <QFile>
#include <QCoreApplication>
-using namespace types;
+using namespace options;
+
+namespace pt_impl {
-Tracker_PT::Tracker_PT(pointer<pt_runtime_traits> traits) :
+Tracker_PT::Tracker_PT(pointer<pt_runtime_traits> const& traits) :
traits { traits },
s { traits->get_module_name() },
point_extractor { traits->make_point_extractor() },
- camera { traits->make_camera() },
- frame { traits->make_frame() },
- preview_frame { traits->make_preview(preview_width, preview_height) }
+ frame { traits->make_frame() }
{
- cv::setBreakOnError(true);
+ opencv_init();
- connect(s.b.get(), SIGNAL(saving()), this, SLOT(maybe_reopen_camera()), Qt::DirectConnection);
- connect(&s.fov, SIGNAL(valueChanged(int)), this, SLOT(set_fov(int)), Qt::DirectConnection);
- set_fov(s.fov);
+ connect(&*s.b, &bundle_::saving, this, [this]{ reopen_camera_flag = true; }, Qt::DirectConnection);
}
Tracker_PT::~Tracker_PT()
@@ -43,111 +42,108 @@ Tracker_PT::~Tracker_PT()
requestInterruption();
wait();
- QMutexLocker l(&camera_mtx);
- camera->stop();
+ if (camera)
+ camera->stop();
}
-void Tracker_PT::run()
+bool Tracker_PT::check_camera()
{
- cv::setNumThreads(1);
+ if (reopen_camera_flag)
+ {
+ reopen_camera_flag = false;
+
+ camera = nullptr;
+ camera = traits->make_camera();
+ if (!camera || !camera->start(s))
+ return false;
+ }
+ assert(camera);
+ if (progn(bool x = true; return open_camera_dialog_flag.compare_exchange_strong(x, false);))
+ run_in_thread_sync(qApp->thread(), [this] { camera->show_camera_settings(); });
+ return true;
+}
-#ifdef PT_PERF_LOG
- QFile log_file(OPENTRACK_BASE_PATH + "/PointTrackerPerformance.txt");
- if (!log_file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
- QTextStream log_stream(&log_file);
-#endif
+void Tracker_PT::run()
+{
+ portable::set_curthread_name("tracker/pt");
while(!isInterruptionRequested())
{
+ if (!check_camera())
+ break;
+
pt_camera_info info;
bool new_frame = false;
{
- QMutexLocker l(&camera_mtx);
-
- if (camera)
- std::tie(new_frame, info) = camera->get_frame(*frame);
+ camera->set_fov(s.fov);
+ std::tie(new_frame, info) = camera->get_frame(*frame);
}
if (new_frame)
{
- *preview_frame = *frame;
+ const bool preview_visible = check_is_visible();
- point_extractor->extract_points(*frame, *preview_frame, points);
- point_count = points.size();
+ if (preview_visible && !widget->fresh())
+ preview_frame->set_last_frame(*frame);
- const double fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
+ point_extractor->extract_points(*frame, *preview_frame, preview_visible && !widget->fresh(), points);
+ point_count.store(points.size(), std::memory_order_relaxed);
const bool success = points.size() >= PointModel::N_POINTS;
- if (success)
- {
- point_tracker.track(points,
- PointModel(s),
- info,
- s.dynamic_pose ? s.init_phase_timeout : 0);
- ever_success = true;
- }
+ Affine X_CM;
{
- Affine X_CM;
+ QMutexLocker l(&center_lock);
+
+ if (success)
{
- QMutexLocker l(&data_mtx);
- X_CM = point_tracker.pose();
+ int dynamic_pose_ms = s.dynamic_pose ? s.init_phase_timeout : 0;
+
+ point_tracker.track(points, PointModel(s), info, dynamic_pose_ms, filter, camera->deadzone_amount());
+ ever_success.store(true, std::memory_order_relaxed);
}
- // just copy pasted these lines from below
+ QMutexLocker l2(&data_lock);
+ X_CM = point_tracker.pose();
+ }
+
+ if (preview_visible && !widget->fresh())
+ {
+ const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z));
Affine X_GH = X_CM * X_MH;
vec3 p = X_GH.t; // head (center?) position in global space
- preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]);
- }
-
- video_widget->update_image(preview_frame->get_bitmap());
+ if (p[2] > f(.1))
+ preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]);
- {
- int w = -1, h = -1;
- video_widget->get_preview_size(w, h);
- if (w != preview_width || h != preview_height)
- {
- preview_width = w, preview_height = h;
- preview_frame = traits->make_preview(w, h);
- }
+ widget->update_image(preview_frame->get_bitmap());
}
}
}
- qDebug() << "pt: thread stopped";
-}
-
-bool Tracker_PT::maybe_reopen_camera()
-{
- QMutexLocker l(&camera_mtx);
-
- return camera->start(camera_name_to_index(s.camera_name),
- s.cam_fps, s.cam_res_x, s.cam_res_y);
-}
-
-void Tracker_PT::set_fov(int value)
-{
- QMutexLocker l(&camera_mtx);
- camera->set_fov(value);
}
module_status Tracker_PT::start_tracker(QFrame* video_frame)
{
- //video_frame->setAttribute(Qt::WA_NativeWindow);
+ {
+ auto camera = traits->make_camera();
+ if (!camera || !camera->start(s))
+ return error(tr("Failed to open camera '%1'").arg(s.camera_name));
+ }
- video_widget = std::make_unique<cv_video_widget>(video_frame);
+ widget = std::make_unique<video_widget>(video_frame);
layout = std::make_unique<QHBoxLayout>(video_frame);
layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(video_widget.get());
- video_frame->setLayout(layout.get());
+ layout->addWidget(&*widget);
+ video_frame->setLayout(&*layout);
//video_widget->resize(video_frame->width(), video_frame->height());
video_frame->show();
- if (!maybe_reopen_camera())
- return { tr("Can't open camera") };
+ double dpi = screen_dpi(video_frame);
+ preview_frame = traits->make_preview(iround(preview_width * dpi),
+ iround(preview_height * dpi));
start(QThread::HighPriority);
@@ -156,76 +152,75 @@ module_status Tracker_PT::start_tracker(QFrame* video_frame)
void Tracker_PT::data(double *data)
{
- if (ever_success)
+ if (ever_success.load(std::memory_order_relaxed))
{
- Affine X_CM = pose();
+ Affine X_CM;
+ {
+ QMutexLocker l(&data_lock);
+ X_CM = point_tracker.pose();
+ }
Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z));
- Affine X_GH = X_CM * X_MH;
+ Affine X_GH(X_CM * X_MH);
// translate rotation matrix from opengl (G) to roll-pitch-yaw (E) frame
// -z -> x, y -> z, x -> -y
mat33 R_EG(0, 0,-1,
-1, 0, 0,
0, 1, 0);
- mat33 R = R_EG * X_GH.R * R_EG.t();
+ mat33 R(R_EG * X_GH.R * R_EG.t());
// get translation(s)
const vec3& t = X_GH.t;
// extract rotation angles
- {
- f alpha, beta, gamma;
- beta = atan2( -R(2,0), sqrt(R(2,1)*R(2,1) + R(2,2)*R(2,2)) );
- alpha = atan2( R(1,0), R(0,0));
- gamma = atan2( R(2,1), R(2,2));
+ auto r00 = (double)R(0, 0);
+ auto r10 = (double)R(1,0), r20 = (double)R(2,0);
+ auto r21 = (double)R(2,1), r22 = (double)R(2,2);
-#if 0
- if (t[2] > 1e-4)
- {
- alpha += copysign(atan(t[0] / t[2]), t[0]);
- // pitch is skewed anyway due to only one focal length value
- //beta -= copysign(atan(t[1] / t[2]), t[1]);
- }
-#endif
+ double beta = atan2(-r20, sqrt(r21*r21 + r22*r22));
+ double alpha = atan2(r10, r00);
+ double gamma = atan2(r21, r22);
- data[Yaw] = rad2deg * alpha;
- data[Pitch] = -rad2deg * beta;
- data[Roll] = rad2deg * gamma;
- }
+ constexpr double rad2deg = 180/M_PI;
+
+ data[Yaw] = rad2deg * alpha;
+ data[Pitch] = -rad2deg * beta;
+ data[Roll] = rad2deg * gamma;
// convert to cm
- data[TX] = t[0] / 10;
- data[TY] = t[1] / 10;
- data[TZ] = t[2] / 10;
+ data[TX] = (double)t[0] / 10;
+ data[TY] = (double)t[1] / 10;
+ data[TZ] = (double)t[2] / 10;
}
}
bool Tracker_PT::center()
{
+ QMutexLocker l(&center_lock);
+
point_tracker.reset_state();
return false;
}
-Affine Tracker_PT::pose()
-{
- QMutexLocker l(&data_mtx);
-
- return point_tracker.pose();
-}
-
int Tracker_PT::get_n_points()
{
- return int(point_count);
+ return (int)point_count.load(std::memory_order_relaxed);
}
-bool Tracker_PT::get_cam_info(pt_camera_info* info)
+bool Tracker_PT::get_cam_info(pt_camera_info& info)
{
- QMutexLocker lock(&camera_mtx);
- bool ret;
+ bool ret = false;
- std::tie(ret, *info) = camera->get_info();
+ if (camera)
+ std::tie(ret, info) = camera->get_info();
return ret;
}
+Affine Tracker_PT::pose() const
+{
+ QMutexLocker l(&data_lock);
+ return point_tracker.pose();
+}
+} // ns pt_impl
diff --git a/tracker-pt/ftnoir_tracker_pt.h b/tracker-pt/ftnoir_tracker_pt.h
index 03812092..a793f94b 100644
--- a/tracker-pt/ftnoir_tracker_pt.h
+++ b/tracker-pt/ftnoir_tracker_pt.h
@@ -9,59 +9,47 @@
#pragma once
#include "api/plugin-api.hpp"
-
-#include "cv/numeric.hpp"
-
#include "pt-api.hpp"
#include "point_tracker.h"
-#include "cv/video-widget.hpp"
+#include "cv/numeric.hpp"
+#include "video/video-widget.hpp"
+#include "point-filter.hpp"
#include <atomic>
#include <memory>
#include <vector>
-#include <opencv2/core.hpp>
-
#include <QThread>
#include <QMutex>
#include <QLayout>
-#include <QTimer>
-class TrackerDialog_PT;
+namespace pt_impl {
-namespace pt_module {
+class TrackerDialog_PT;
-using namespace types;
+using namespace numeric_types;
-class Tracker_PT : public QThread, public ITracker
+struct Tracker_PT : QThread, ITracker
{
- Q_OBJECT
+ friend class TrackerDialog_PT;
- friend class ::TrackerDialog_PT;
+ template<typename t> using pointer = pt_pointer<t>;
- template<typename t>
- using pointer = typename pt_runtime_traits::pointer<t>;
-
-public:
- Tracker_PT(pointer<pt_runtime_traits> pt_runtime_traits);
+ explicit Tracker_PT(pointer<pt_runtime_traits> const& pt_runtime_traits);
~Tracker_PT() override;
module_status start_tracker(QFrame* parent_window) override;
void data(double* data) override;
bool center() override;
- Affine pose();
int get_n_points();
- bool get_cam_info(pt_camera_info* info);
-public slots:
- bool maybe_reopen_camera();
- void set_fov(int value);
-protected:
- void run() override;
+ [[nodiscard]] bool get_cam_info(pt_camera_info& info);
+ Affine pose() const;
+
private:
- pointer<pt_runtime_traits> traits;
+ void run() override;
+ [[nodiscard]] bool check_camera();
- QMutex camera_mtx;
- QMutex data_mtx;
+ pointer<pt_runtime_traits> traits;
PointTracker point_tracker;
@@ -74,17 +62,18 @@ private:
pointer<pt_point_extractor> point_extractor;
pointer<pt_camera> camera;
- pointer<cv_video_widget> video_widget;
+ pointer<video_widget> widget;
pointer<pt_frame> frame;
pointer<pt_preview> preview_frame;
- std::atomic<unsigned> point_count = 0;
+ std::atomic<unsigned> point_count { 0 };
std::atomic<bool> ever_success = false;
-
- static constexpr inline f rad2deg = f(180/M_PI);
- //static constexpr float deg2rad = float(M_PI/180);
+ std::atomic<bool> reopen_camera_flag = true;
+ std::atomic<bool> open_camera_dialog_flag = false;
+ mutable QMutex center_lock, data_lock;
+ point_filter filter{s};
};
} // ns pt_impl
-using pt_module::Tracker_PT;
+using Tracker_PT = pt_impl::Tracker_PT;
diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
index 5bd1a4c8..d67f79a7 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
@@ -7,11 +7,8 @@
*/
#include "ftnoir_tracker_pt_dialog.h"
-
#include "compat/math.hpp"
-#include "compat/camera-names.hpp"
-#include "cv/video-property-page.hpp"
-#include <opencv2/core.hpp>
+#include "video/camera.hpp"
#include <QString>
#include <QtGlobal>
@@ -19,22 +16,28 @@
using namespace options;
+static void init_resources() { Q_INIT_RESOURCE(tracker_pt_base); }
+
+namespace pt_impl {
+
TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) :
s(module_name),
tracker(nullptr),
timer(this),
- trans_calib(1, 2, 0)
+ trans_calib(1, 2)
{
- Q_INIT_RESOURCE(tracker_pt_base);
+ init_resources();
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);
tie_setting(s.cam_res_y, ui.res_y_spin);
tie_setting(s.cam_fps, ui.fps_spin);
+ tie_setting(s.use_mjpeg, ui.use_mjpeg);
tie_setting(s.threshold_slider, ui.threshold_slider);
@@ -88,13 +91,18 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) :
poll_tracker_info_impl();
- connect(this, &TrackerDialog_PT::poll_tracker_info, this, &TrackerDialog_PT::poll_tracker_info_impl, Qt::DirectConnection);
-
constexpr pt_color_type color_types[] = {
- pt_color_average,
- pt_color_natural,
+ pt_color_bt709,
+ pt_color_hardware,
pt_color_red_only,
+ pt_color_green_only,
pt_color_blue_only,
+ pt_color_red_chromakey,
+ pt_color_green_chromakey,
+ pt_color_blue_chromakey,
+ pt_color_cyan_chromakey,
+ pt_color_yellow_chromakey,
+ pt_color_magenta_chromakey,
};
for (unsigned k = 0; k < std::size(color_types); k++)
@@ -102,14 +110,38 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) :
tie_setting(s.blob_color, ui.blob_color);
+ tie_setting(s.chroma_key_strength, ui.chroma_key_strength_slider);
+ connect(&s.chroma_key_strength, value_::value_changed<slider_value>(), ui.chroma_key_strength_label,
+ [this] { ui.chroma_key_strength_label->setValue(*s.chroma_key_strength); });
+ ui.chroma_key_strength_label->setValue(*s.chroma_key_strength);
+
+ tie_setting(s.chroma_key_overexposed, ui.chroma_key_overexposed);
+ connect(ui.blob_color, &QComboBox::currentTextChanged, this, &TrackerDialog_PT::chroma_key_controls_enable);
+
+ chroma_key_controls_enable("");
+
tie_setting(s.threshold_slider, ui.threshold_value_display, [this](const slider_value& val) {
return threshold_display_text(int(val));
});
// refresh threshold display on auto-threshold checkbox state change
- tie_setting(s.auto_threshold,
- this,
- [this](bool) { s.threshold_slider.notify(); });
+ tie_setting(s.auto_threshold, this, [this](bool) { s.threshold_slider.notify_(); });
+
+ tie_setting(s.enable_point_filter, ui.enable_point_filter);
+ tie_setting(s.point_filter_coefficient, ui.point_filter_slider);
+ tie_setting(s.point_filter_limit, ui.point_filter_limit_slider);
+ connect(&s.point_filter_coefficient, value_::value_changed<slider_value>(),
+ ui.point_filter_label, [this] { ui.point_filter_label->setValue(*s.point_filter_coefficient); } );
+ connect(&s.point_filter_limit, value_::value_changed<slider_value>(), ui.point_filter_limit_label,
+ [this] { ui.point_filter_limit_label->setValue(*s.point_filter_limit); }, Qt::QueuedConnection);
+ ui.point_filter_label->setValue(*s.point_filter_coefficient);
+ ui.point_filter_limit_label->setValue(*s.point_filter_limit);
+
+ tie_setting(s.point_filter_deadzone, ui.point_filter_deadzone_slider);
+ ui.point_filter_deadzone_label->setValue(*s.point_filter_deadzone);
+
+ connect(&s.point_filter_deadzone, value_::value_changed<slider_value>(), ui.point_filter_deadzone_label,
+ [this] { ui.point_filter_deadzone_label->setValue(*s.point_filter_deadzone); }, Qt::QueuedConnection);
}
QString TrackerDialog_PT::threshold_display_text(int threshold_value)
@@ -122,12 +154,18 @@ QString TrackerDialog_PT::threshold_display_text(int threshold_value)
int w = s.cam_res_x, h = s.cam_res_y;
if (w * h <= 0)
- w = 640, h = 480;
+ {
+ w = 640;
+ h = 480;
+ }
- if (tracker && tracker->get_cam_info(&info) && info.res_x * info.res_y != 0)
- w = info.res_x, h = info.res_y;
+ if (tracker && tracker->get_cam_info(info) && info.res_x * info.res_y != 0)
+ {
+ w = info.res_x;
+ h = info.res_y;
+ }
- double value = pt_point_extractor::threshold_radius_value(w, h, threshold_value);
+ double value = (double)pt_point_extractor::threshold_radius_value(w, h, threshold_value);
return tr("LED radius %1 pixels").arg(value, 0, 'f', 2);
}
@@ -153,9 +191,7 @@ void TrackerDialog_PT::startstop_trans_calib(bool start)
calib_timer.stop();
qDebug() << "pt: stopping translation calibration";
{
- cv::Vec3f tmp;
- cv::Vec3i nsamples;
- std::tie(tmp, nsamples) = trans_calib.get_estimate();
+ auto [tmp, nsamples] = trans_calib.get_estimate();
s.t_MH_x = int(tmp[0]);
s.t_MH_y = int(tmp[1]);
s.t_MH_z = int(tmp[2]);
@@ -167,18 +203,16 @@ void TrackerDialog_PT::startstop_trans_calib(bool start)
// Don't bother counting roll samples. Roll calibration is hard enough
// that it's a hidden unsupported feature anyway.
- const QString sample_feedback = progn(
- if (nsamples[0] < min_yaw_samples)
- return tr("%1 yaw samples. Yaw more to %2 samples for stable calibration.")
- .arg(nsamples[0]).arg(min_yaw_samples);
- if (nsamples[1] < min_pitch_samples)
- return tr("%1 pitch samples. Pitch more to %2 samples for stable calibration.")
- .arg(nsamples[1]).arg(min_pitch_samples);
-
+ QString sample_feedback;
+ if (nsamples[0] < min_yaw_samples)
+ sample_feedback = tr("%1 yaw samples. Yaw more to %2 samples for stable calibration.").arg(nsamples[0]).arg(min_yaw_samples);
+ else if (nsamples[1] < min_pitch_samples)
+ sample_feedback = tr("%1 pitch samples. Pitch more to %2 samples for stable calibration.").arg(nsamples[1]).arg(min_pitch_samples);
+ else
+ {
const int nsamples_total = nsamples[0] + nsamples[1];
-
- return tr("%1 samples. Over %2, good!").arg(nsamples_total).arg(min_samples);
- );
+ sample_feedback = tr("%1 samples. Over %2, good!").arg(nsamples_total).arg(min_samples);
+ }
ui.sample_count_display->setText(sample_feedback);
}
@@ -186,18 +220,17 @@ void TrackerDialog_PT::startstop_trans_calib(bool start)
ui.tx_spin->setEnabled(!start);
ui.ty_spin->setEnabled(!start);
ui.tz_spin->setEnabled(!start);
- ui.tcalib_button->setText(progn(
- if (start)
- return tr("Stop calibration");
- else
- return tr("Start calibration");
- ));
+
+ if (start)
+ ui.tcalib_button->setText(tr("Stop calibration"));
+ else
+ ui.tcalib_button->setText(tr("Start calibration"));
}
void TrackerDialog_PT::poll_tracker_info_impl()
{
pt_camera_info info;
- if (tracker && tracker->get_cam_info(&info))
+ if (tracker && tracker->get_cam_info(info))
{
ui.caminfo_label->setText(tr("%1x%2 @ %3 FPS").arg(info.res_x).arg(info.res_y).arg(iround(info.fps)));
@@ -220,15 +253,23 @@ void TrackerDialog_PT::set_camera_settings_available(const QString& /* camera_na
void TrackerDialog_PT::show_camera_settings()
{
if (tracker)
- {
- QMutexLocker l(&tracker->camera_mtx);
- tracker->camera->show_camera_settings();
- }
+ tracker->open_camera_dialog_flag = true;
else
+ (void)video::show_dialog(s.camera_name);
+}
+
+void TrackerDialog_PT::chroma_key_controls_enable(const QString&)
+{
+ bool enabled = false;
+ QVariant data = ui.blob_color->currentData();
+ if (data.isValid())
{
- const int idx = camera_name_to_index(s.camera_name);
- video_property_page::show(idx);
+ pt_color_type blob_color = pt_color_type(data.toInt());
+ enabled = blob_color >= pt_color_red_chromakey && blob_color <= pt_color_magenta_chromakey;
}
+ ui.chroma_key_strength_slider->setEnabled(enabled);
+ ui.chroma_key_strength_label->setEnabled(enabled);
+ ui.chroma_key_overexposed->setEnabled(enabled);
}
void TrackerDialog_PT::trans_calib_step()
@@ -264,14 +305,26 @@ void TrackerDialog_PT::register_tracker(ITracker *t)
{
tracker = static_cast<Tracker_PT*>(t);
ui.tcalib_button->setEnabled(true);
- poll_tracker_info();
+ poll_tracker_info_impl();
timer.start();
}
void TrackerDialog_PT::unregister_tracker()
{
- tracker = NULL;
+ tracker = nullptr;
ui.tcalib_button->setEnabled(false);
- poll_tracker_info();
+ poll_tracker_info_impl();
timer.stop();
}
+
+void TrackerDialog_PT::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+}
+
+void TrackerDialog_PT::reload()
+{
+ s.b->reload();
+}
+
+} // ns pt_impl
diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.h b/tracker-pt/ftnoir_tracker_pt_dialog.h
index f36fe7b2..79cd91bd 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.h
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.h
@@ -12,11 +12,13 @@
#include "ftnoir_tracker_pt.h"
#include "tracker-pt/ui_FTNoIR_PT_Controls.h"
#include "cv/translation-calibrator.hpp"
-#include "cv/video-widget.hpp"
+#include "video/video-widget.hpp"
#include <QTimer>
#include <QMutex>
+namespace pt_impl {
+
class TrackerDialog_PT : public ITrackerDialog
{
Q_OBJECT
@@ -24,7 +26,10 @@ public:
TrackerDialog_PT(const QString& module_name);
void register_tracker(ITracker *tracker) override;
void unregister_tracker() override;
- void save();
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
+ void save() override;
+ void reload() override;
public slots:
void doOK();
void doCancel();
@@ -34,8 +39,8 @@ public slots:
void poll_tracker_info_impl();
void set_camera_settings_available(const QString& camera_name);
void show_camera_settings();
-signals:
- void poll_tracker_info();
+ void chroma_key_controls_enable(const QString&);
+
protected:
QString threshold_display_text(int threshold_value);
@@ -47,3 +52,7 @@ protected:
Ui::UICPTClientControls ui;
};
+
+} // ns pt_impl
+
+using TrackerDialog_PT = pt_impl::TrackerDialog_PT;
diff --git a/tracker-pt/lang/de_DE.ts b/tracker-pt/lang/de_DE.ts
new file mode 100644
index 00000000..30ab42c2
--- /dev/null
+++ b/tracker-pt/lang/de_DE.ts
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICPTClientControls</name>
+ <message>
+ <source>PointTracker Settings</source>
+ <translation>PointTracker-Einstellungen</translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation>Kamera</translation>
+ </message>
+ <message>
+ <source>Camera settings</source>
+ <translation>Kamera-Einstellungen</translation>
+ </message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation>Für die PS3 Eye sollte dies 56° oder 76° sein, abhängig von der Einstellung der physikalischen Linse. Dies wird nur benutzt, um die Position aus den Welt-Koordinaten zur ermitteln.</translation>
+ </message>
+ <message>
+ <source>Diagonal field of view</source>
+ <translation>Diagonales Sichtfeld</translation>
+ </message>
+ <message>
+ <source>Dynamic pose (for caps only, never clips)</source>
+ <translation>Dynamische Pose (nur für Hüte, niemals Sticker)</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;‚Natürlich‘ ist, dank des optimierten SIMD-Codes, die schnellste Methode für LEDs. Mit dem Farbschlüssel kann man normale farbige Papierstücke tracken.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Color channels used</source>
+ <translation>Benutzte Farbkanäle</translation>
+ </message>
+ <message>
+ <source>Open</source>
+ <translation>Öffnen</translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation>Aktiviert MJPEG-Kompression für Hochgeschwindigkeitskameras, PS3 Eye ausgenommen. Nur für Windows.</translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation>MJPEG-Kompression</translation>
+ </message>
+ <message>
+ <source>FPS</source>
+ <translation>FPS</translation>
+ </message>
+ <message>
+ <source>Width</source>
+ <translation>Breite</translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation>Chroma-Key enthält überbelichtete Pixel</translation>
+ </message>
+ <message>
+ <source>Camera settings (when available)</source>
+ <translation>Kamera-Einstellungen (falls verfügbar)</translation>
+ </message>
+ <message>
+ <source> ms</source>
+ <translation> ms</translation>
+ </message>
+ <message>
+ <source>Desired capture width</source>
+ <translation>Angestrebte Aufnahmebreite</translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation> px</translation>
+ </message>
+ <message>
+ <source>Dynamic pose timeout</source>
+ <translation>Dynamisches Pose-Timeout</translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation>Gerät</translation>
+ </message>
+ <message>
+ <source>Height</source>
+ <translation>Höhe</translation>
+ </message>
+ <message>
+ <source>Desired capture framerate</source>
+ <translation>Angestrebte Aufnahme-Bildrate</translation>
+ </message>
+ <message>
+ <source> Hz</source>
+ <translation> Hz</translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation>Graustufen BT.709</translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation>Graustufen (gemäß Hardware)</translation>
+ </message>
+ <message>
+ <source>Red only</source>
+ <translation>nur Rot</translation>
+ </message>
+ <message>
+ <source>Green only</source>
+ <translation>nur Grün</translation>
+ </message>
+ <message>
+ <source>Blue only</source>
+ <translation>nur Blau</translation>
+ </message>
+ <message>
+ <source>Red chroma key</source>
+ <translation>Roter Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Green chroma key</source>
+ <translation>Grüner Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Blue chroma key</source>
+ <translation>Blauer Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Cyan chroma key</source>
+ <translation>Cyan-Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Yellow chroma key</source>
+ <translation>Gelber Chroma-Key</translation>
+ </message>
+ <message>
+ <source>Magenta chroma key</source>
+ <translation>Magenta-Chroma-Key</translation>
+ </message>
+ <message>
+ <source>°</source>
+ <translation>°</translation>
+ </message>
+ <message>
+ <source>Desired capture height</source>
+ <translation>Angestrebte Aufnahmehöhe</translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation>Chroma-Key-Stärke</translation>
+ </message>
+ <message>
+ <source>Point extraction</source>
+ <translation>Punktextraktion</translation>
+ </message>
+ <message>
+ <source>Threshold</source>
+ <translation>Schwelle</translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation>Setzt die Mindestgröße, um zu verhindern, dass kleine Streulichter als Punkte erkannt werden.</translation>
+ </message>
+ <message>
+ <source>Min size</source>
+ <translation>Mindestgröße</translation>
+ </message>
+ <message>
+ <source>Max size</source>
+ <translation>Maximale Größe</translation>
+ </message>
+ <message>
+ <source>Intensity threshold for point extraction</source>
+ <translation>Intensitätsschwelle für die Punktextraktion</translation>
+ </message>
+ <message>
+ <source>Enable, slider sets point size</source>
+ <translation>Aktivieren, Regler setzt die Punktgröße</translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation>Tracking abhängig von der Punktgröße anstelle der absoluten Helligkeit durchführen. Dies kann stabileres Tracking ermöglichen.</translation>
+ </message>
+ <message>
+ <source>Automatic threshold</source>
+ <translation>Automatische Schwelle</translation>
+ </message>
+ <message>
+ <source>Maximum point diameter</source>
+ <translation>Maximaler Punktdurchmesser</translation>
+ </message>
+ <message>
+ <source>Value</source>
+ <translation>Wert</translation>
+ </message>
+ <message>
+ <source>Minimum point diameter</source>
+ <translation>Minimaler Punktdurchmesser</translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation>Modell</translation>
+ </message>
+ <message>
+ <source>Clip</source>
+ <translation>Sticker</translation>
+ </message>
+ <message>
+ <source>Model Dimensions</source>
+ <translation>Modellabmessungen</translation>
+ </message>
+ <message>
+ <source> mm</source>
+ <translation> mm</translation>
+ </message>
+ <message>
+ <source>Side</source>
+ <translation>Seitlich</translation>
+ </message>
+ <message>
+ <source>Front</source>
+ <translation>Vorne</translation>
+ </message>
+ <message>
+ <source>Cap</source>
+ <translation>Hut</translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation>Benutzerdefiniert</translation>
+ </message>
+ <message>
+ <source>z:</source>
+ <translation>z:</translation>
+ </message>
+ <message>
+ <source>x:</source>
+ <translation>x:</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Location of the two remaining model points&lt;br/&gt;with respect to the reference point in default pose&lt;/p&gt;&lt;p&gt;Use any units you want, not necessarily centimeters.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ort der verbleibenden zwei Modellpunkte&lt;br/&gt;unter Berücksichtigung der Referenzpunkte der Standard-Pose&lt;/p&gt;&lt;p&gt;Dies können beliebige Einheiten sein, nicht zwingend Zentimeter.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>y:</source>
+ <translation>y:</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;3&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;3&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;2&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:16pt;&quot;&gt;P&lt;/span&gt;&lt;span style=&quot; font-size:16pt; vertical-align:sub;&quot;&gt;2&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Model position</source>
+ <translation>Modell-Position</translation>
+ </message>
+ <message>
+ <source>Use only yaw and pitch while calibrating.
+Don&apos;t roll or change position.</source>
+ <translation>Bitte nur gieren oder nicken während der Kalibrierung.
+Bitte nicht rollen oder die Position ändern.</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>Kalibrierung starten</translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation>Filter</translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation>Punktfilter</translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation>Grenze</translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation>Totbereich</translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation>Punktmitten vor der Posen-Abschätzung filtern.</translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation>Einschalten</translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation>Über</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;FTNoIR PointTracker Plugin&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;by Patrick Ruoff&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://ftnoirpt.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#0000ff;&quot;&gt;Manual (external)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;FTNoIR PointTracker Plugin&lt;br/&gt;Version 1.1&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;von Patrick Ruoff&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://ftnoirpt.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline; color:#0000ff;&quot;&gt;Anleitung (extern)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <source>Extracted Points:</source>
+ <translation>Extrahierte Punkte:</translation>
+ </message>
+ <message>
+ <source>Camera Info:</source>
+ <translation>Kamera-Info:</translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::TrackerDialog_PT</name>
+ <message>
+ <source>Brightness %1/255</source>
+ <translation>Helligkeit %1/255</translation>
+ </message>
+ <message>
+ <source>LED radius %1 pixels</source>
+ <translation>LED-Radius %1 Pixel</translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation>%1 Gieren-Proben. Weiterhin gieren bis %2 Proben für eine stabile Kalibrierung.</translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation>%1 Nicken-Proben. Weiterhin nicken bis %2 Proben für eine stabile Kalibrierung.</translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation>%1 Proben. Mehr als %2, gut!</translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation>Kalibrierung stoppen</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>Kalibrierung starten</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS</source>
+ <translation>%1x%2 @ %3 FPS</translation>
+ </message>
+ <message>
+ <source>%1 OK!</source>
+ <translation>%1 OKAY!</translation>
+ </message>
+ <message>
+ <source>%1 BAD!</source>
+ <translation>%1 SCHLECHT!</translation>
+ </message>
+ <message>
+ <source>Tracker offline</source>
+ <translation>Tracker offline</translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation>Öffnen der Kamera ‚%1‘ fehlgeschlagen</translation>
+ </message>
+</context>
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation>PointTracker 1.1</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-pt/lang/nl_NL.ts b/tracker-pt/lang/nl_NL.ts
index 34f88c88..fc44b0f1 100644
--- a/tracker-pt/lang/nl_NL.ts
+++ b/tracker-pt/lang/nl_NL.ts
@@ -2,53 +2,6 @@
<!DOCTYPE TS>
<TS version="2.1" language="nl_NL">
<context>
- <name>TrackerDialog_PT</name>
- <message>
- <source>Brightness %1/255</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>LED radius %1 pixels</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 samples. Over %2, good!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Stop calibration</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Start calibration</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1x%2 @ %3 FPS</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 OK!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 BAD!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Tracker offline</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
<source>PointTracker Settings</source>
@@ -155,14 +108,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -275,11 +220,157 @@ Don&apos;t roll or change position.</source>
<source>Camera Info:</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Green only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Green chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyan chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yellow chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Magenta chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::TrackerDialog_PT</name>
+ <message>
+ <source>Brightness %1/255</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED radius %1 pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 OK!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracker offline</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
- <name>pt_module::Tracker_PT</name>
+ <name>pt_module::metadata_pt</name>
<message>
- <source>Can&apos;t open camera</source>
+ <source>PointTracker 1.1</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-pt/lang/ru_RU.ts b/tracker-pt/lang/ru_RU.ts
index 63b4847a..7ff4657e 100644
--- a/tracker-pt/lang/ru_RU.ts
+++ b/tracker-pt/lang/ru_RU.ts
@@ -2,53 +2,6 @@
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
- <name>TrackerDialog_PT</name>
- <message>
- <source>%1 samples. Over %2, good!</source>
- <translation>Получено %1 образца(-ов). Больше %2, отлично!!</translation>
- </message>
- <message>
- <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
- <translation>По оси YAW выполнено: %1 замер(а/ов). Для стабильного результата необходимо не меньше %2</translation>
- </message>
- <message>
- <source>Brightness %1/255</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>LED radius %1 pixels</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
- <translation>По оси Pitch выполнено: %1 замер(а/ов). Для стабильного результата необходимо не меньше %2</translation>
- </message>
- <message>
- <source>Stop calibration</source>
- <translation>Остановить калибровку</translation>
- </message>
- <message>
- <source>Start calibration</source>
- <translation>Начать калибровку</translation>
- </message>
- <message>
- <source>%1x%2 @ %3 FPS</source>
- <translation></translation>
- </message>
- <message>
- <source>%1 OK!</source>
- <translation></translation>
- </message>
- <message>
- <source>%1 BAD!</source>
- <translation></translation>
- </message>
- <message>
- <source>Tracker offline</source>
- <translation>Отслеживание отключено</translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
<source>PointTracker Settings</source>
@@ -159,14 +112,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -280,11 +225,157 @@ ROLL или X/Y-смещения.</translation>
<source>Camera Info:</source>
<translation>Параметры камеры:</translation>
</message>
+ <message>
+ <source>Green only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Green chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyan chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yellow chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Magenta chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::TrackerDialog_PT</name>
+ <message>
+ <source>Brightness %1/255</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED radius %1 pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation type="unfinished">По оси YAW выполнено: %1 замер(а/ов). Для стабильного результата необходимо не меньше %2</translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation type="unfinished">По оси Pitch выполнено: %1 замер(а/ов). Для стабильного результата необходимо не меньше %2</translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation type="unfinished">Получено %1 образца(-ов). Больше %2, отлично!!</translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation type="unfinished">Остановить калибровку</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation type="unfinished">Начать калибровку</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 OK!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracker offline</source>
+ <translation type="unfinished">Отслеживание отключено</translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
- <name>pt_module::Tracker_PT</name>
+ <name>pt_module::metadata_pt</name>
<message>
- <source>Can&apos;t open camera</source>
+ <source>PointTracker 1.1</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-pt/lang/stub.ts b/tracker-pt/lang/stub.ts
index e83487a9..3dbe208d 100644
--- a/tracker-pt/lang/stub.ts
+++ b/tracker-pt/lang/stub.ts
@@ -2,53 +2,6 @@
<!DOCTYPE TS>
<TS version="2.1">
<context>
- <name>TrackerDialog_PT</name>
- <message>
- <source>Brightness %1/255</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>LED radius %1 pixels</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 samples. Over %2, good!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Stop calibration</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Start calibration</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1x%2 @ %3 FPS</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 OK!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 BAD!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Tracker offline</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
<source>PointTracker Settings</source>
@@ -155,14 +108,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -275,11 +220,157 @@ Don&apos;t roll or change position.</source>
<source>Camera Info:</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Green only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Green chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyan chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yellow chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Magenta chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::TrackerDialog_PT</name>
+ <message>
+ <source>Brightness %1/255</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>LED radius %1 pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 OK!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 BAD!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Tracker offline</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::Tracker_PT</name>
+ <message>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
- <name>pt_module::Tracker_PT</name>
+ <name>pt_module::metadata_pt</name>
<message>
- <source>Can&apos;t open camera</source>
+ <source>PointTracker 1.1</source>
<translation type="unfinished"></translation>
</message>
</context>
diff --git a/tracker-pt/lang/zh_CN.ts b/tracker-pt/lang/zh_CN.ts
index d6ce141e..3519d719 100644
--- a/tracker-pt/lang/zh_CN.ts
+++ b/tracker-pt/lang/zh_CN.ts
@@ -2,53 +2,6 @@
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
- <name>TrackerDialog_PT</name>
- <message>
- <source>Brightness %1/255</source>
- <translation>亮度 %1/255</translation>
- </message>
- <message>
- <source>LED radius %1 pixels</source>
- <translation>光源半径 %1 像素</translation>
- </message>
- <message>
- <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>%1 samples. Over %2, good!</source>
- <translation>%1 样本。%2 正常</translation>
- </message>
- <message>
- <source>Stop calibration</source>
- <translation>停止校准</translation>
- </message>
- <message>
- <source>Start calibration</source>
- <translation>开始校准</translation>
- </message>
- <message>
- <source>%1x%2 @ %3 FPS</source>
- <translation>%1x%2 @ %3 帧</translation>
- </message>
- <message>
- <source>%1 OK!</source>
- <translation>%1 正常</translation>
- </message>
- <message>
- <source>%1 BAD!</source>
- <translation>%1 异常</translation>
- </message>
- <message>
- <source>Tracker offline</source>
- <translation>跟踪器脱机</translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
<source>PointTracker Settings</source>
@@ -247,14 +200,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <source>Average</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Natural</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<source>Red only</source>
<translation type="unfinished"></translation>
</message>
@@ -275,12 +220,158 @@
Don&apos;t roll or change position.</source>
<translation>用pitch和yaw校准。不要roll或者变换位置</translation>
</message>
+ <message>
+ <source>Green only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Green chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Cyan chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yellow chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Magenta chroma key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It&apos;s only neccessary to get position correspond to real-world values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Set minimum size to avoid small stray lights from being treated as points.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For LEDs, &apos;Natural&apos; is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>MJPEG compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Point filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter point centers prior to pose estimation.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Limit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale BT.709</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Grayscale (from hardware)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Deadzone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key includes overexposed pixels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Chroma key strength</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_impl::TrackerDialog_PT</name>
+ <message>
+ <source>Brightness %1/255</source>
+ <translation type="unfinished">亮度 %1/255</translation>
+ </message>
+ <message>
+ <source>LED radius %1 pixels</source>
+ <translation type="unfinished">光源半径 %1 像素</translation>
+ </message>
+ <message>
+ <source>%1 yaw samples. Yaw more to %2 samples for stable calibration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 pitch samples. Pitch more to %2 samples for stable calibration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1 samples. Over %2, good!</source>
+ <translation type="unfinished">%1 样本。%2 正常</translation>
+ </message>
+ <message>
+ <source>Stop calibration</source>
+ <translation type="unfinished">停止校准</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation type="unfinished">开始校准</translation>
+ </message>
+ <message>
+ <source>%1x%2 @ %3 FPS</source>
+ <translation type="unfinished">%1x%2 @ %3 帧</translation>
+ </message>
+ <message>
+ <source>%1 OK!</source>
+ <translation type="unfinished">%1 正常</translation>
+ </message>
+ <message>
+ <source>%1 BAD!</source>
+ <translation type="unfinished">%1 异常</translation>
+ </message>
+ <message>
+ <source>Tracker offline</source>
+ <translation type="unfinished">跟踪器脱机</translation>
+ </message>
</context>
<context>
- <name>pt_module::Tracker_PT</name>
+ <name>pt_impl::Tracker_PT</name>
<message>
- <source>Can&apos;t open camera</source>
- <translation type="unfinished">无法打开摄像头</translation>
+ <source>Failed to open camera &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
</message>
</context>
</TS>
diff --git a/tracker-pt/module/CMakeLists.txt b/tracker-pt/module/CMakeLists.txt
index 1d1b4458..b7fc974f 100644
--- a/tracker-pt/module/CMakeLists.txt
+++ b/tracker-pt/module/CMakeLists.txt
@@ -1,6 +1,10 @@
-find_package(OpenCV 3.0 QUIET)
+include(opentrack-opencv)
+find_package(OpenCV QUIET)
if(OpenCV_FOUND)
+ foreach(k core imgproc)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
otr_module(tracker-pt)
- target_link_libraries(opentrack-tracker-pt opentrack-tracker-pt-base)
- target_include_directories(opentrack-tracker-pt PRIVATE "${CMAKE_SOURCE_DIR}/tracker-pt")
+ target_link_libraries(${self} opentrack-video opencv_imgproc opentrack-tracker-pt-base)
+ target_include_directories(${self} PUBLIC "${CMAKE_SOURCE_DIR}/tracker-pt")
endif()
diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp
index 9c62e8a3..1beba474 100644
--- a/tracker-pt/module/camera.cpp
+++ b/tracker-pt/module/camera.cpp
@@ -7,16 +7,9 @@
#include "camera.h"
#include "frame.hpp"
+#include <opencv2/core/mat.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"
-
-using namespace pt_module;
+namespace pt_module {
Camera::Camera(const QString& module_name) : s { module_name }
{
@@ -24,22 +17,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
@@ -54,16 +43,16 @@ Camera::result Camera::get_frame(pt_frame& frame_)
{
cv::Mat& frame = frame_.as<Frame>()->mat;
- const bool new_frame = _get_frame(frame);
+ const bool new_frame = get_frame_(frame);
if (new_frame)
{
- const double dt = t.elapsed_seconds();
+ const f dt = (f)t.elapsed_seconds();
t.start();
// measure fps of valid frames
- constexpr double RC = .1; // seconds
- const double alpha = dt/(dt + RC);
+ constexpr f RC = f{1}/10; // seconds
+ const f alpha = dt/(dt + RC);
if (dt_mean < dt_eps)
dt_mean = dt;
@@ -75,64 +64,69 @@ Camera::result Camera::get_frame(pt_frame& frame_)
cam_info.res_y = frame.rows;
cam_info.fov = fov;
- return result(true, cam_info);
+ return { true, cam_info };
}
else
- return result(false, pt_camera_info());
+ return { false, {} };
}
-bool Camera::start(int idx, int fps, int res_x, int res_y)
+bool Camera::start(const pt_settings& s)
{
- if (idx >= 0 && fps >= 0 && res_x >= 0 && res_y >= 0)
+ int fps = s.cam_fps, res_x = s.cam_res_x, res_y = s.cam_res_y;
+ QString name = s.camera_name;
+ bool use_mjpeg = s.use_mjpeg;
+
+ if (fps >= 0 && res_x >= 0 && res_y >= 0)
{
- if (cam_desired.idx != idx ||
- cam_desired.fps != fps ||
+ 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())
+ cam_desired.use_mjpeg != use_mjpeg ||
+ !cap || !cap->is_open())
{
stop();
- desired_name = get_camera_names().value(idx);
- cam_desired.idx = idx;
- cam_desired.fps = fps;
+ cam_desired.name = name;
+ cam_desired.fps = (f)fps;
cam_desired.res_x = res_x;
cam_desired.res_y = res_y;
cam_desired.fov = fov;
+ cam_desired.use_mjpeg = use_mjpeg;
- cap = camera_ptr(new cv::VideoCapture(cam_desired.idx));
-
- if (cam_desired.res_x)
- cap->set(cv::CAP_PROP_FRAME_WIDTH, cam_desired.res_x);
- if (cam_desired.res_y)
- cap->set(cv::CAP_PROP_FRAME_HEIGHT, cam_desired.res_y);
- if (cam_desired.fps)
- cap->set(cv::CAP_PROP_FPS, cam_desired.fps);
-
- if (cap->isOpened())
- {
- cam_info = pt_camera_info();
- active_name = QString();
- cam_info.idx = idx;
- dt_mean = 0;
- active_name = desired_name;
-
- cv::Mat tmp;
-
- if (_get_frame(tmp))
- {
- t.start();
- return true;
- }
- }
-
- cap = nullptr;
- return false;
- }
+ cap = video::make_camera(name);
+
+ if (!cap)
+ goto fail;
+
+ camera::info info {};
+ info.fps = fps;
+ info.width = res_x;
+ info.height = res_y;
+ info.use_mjpeg = use_mjpeg;
+ info.num_channels = s.blob_color == pt_color_hardware ? 1 : 3;
+
+ if (!cap->start(info))
+ goto fail;
- return true;
+ cam_info = pt_camera_info();
+ cam_info.name = name;
+ cam_info.use_mjpeg = use_mjpeg;
+ cam_info.fov = (f)s.fov;
+ dt_mean = 0;
+
+ cv::Mat tmp;
+
+ if (!get_frame_(tmp))
+ goto fail;
+
+ t.start();
+ }
}
+ return true;
+
+fail:
stop();
return false;
}
@@ -140,33 +134,26 @@ 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 = pt_camera_info();
- cam_desired = pt_camera_info();
+ 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 (int i = 0; i < 5; i++)
+ auto [ frame, ret ] = cap->get_frame();
+ if (ret)
{
- if (cap->read(frame))
- return true;
- portable::sleep(1);
+ int stride = frame.stride;
+ if (stride == 0)
+ stride = cv::Mat::AUTO_STEP;
+ img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, (size_t)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 79e3dca0..e4772178 100644
--- a/tracker-pt/module/camera.h
+++ b/tracker-pt/module/camera.h
@@ -8,15 +8,10 @@
#pragma once
#include "pt-api.hpp"
-
#include "compat/timer.hpp"
+#include "video/camera.hpp"
-#include <functional>
#include <memory>
-#include <tuple>
-
-#include <opencv2/core.hpp>
-#include <opencv2/videoio.hpp>
#include <QString>
@@ -26,7 +21,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 pt_settings& s) override;
void stop() override;
result get_frame(pt_frame& Frame) override;
@@ -36,30 +31,23 @@ struct Camera final : pt_camera
QString get_desired_name() const override;
QString get_active_name() const override;
- void set_fov(double value) override { fov = value; }
+ void set_fov(f value) override { fov = value; }
void show_camera_settings() override;
private:
- warn_result_unused bool _get_frame(cv::Mat& Frame);
+ using camera = video::impl::camera;
+
+ [[nodiscard]] bool get_frame_(cv::Mat& frame);
- double dt_mean = 0, fov = 30;
+ 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 inline double dt_eps = 1./384;
+ static constexpr f dt_eps = f{1}/256;
};
} // ns pt_module
diff --git a/tracker-pt/module/export.hpp b/tracker-pt/module/export.hpp
new file mode 100644
index 00000000..a733c9fe
--- /dev/null
+++ b/tracker-pt/module/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_TRACKER_PT
+# define OTR_PT_EXPORT OTR_GENERIC_EXPORT
+#else
+# define OTR_PT_EXPORT OTR_GENERIC_IMPORT
+#endif
diff --git a/tracker-pt/module/frame.cpp b/tracker-pt/module/frame.cpp
index e403af07..1a276f16 100644
--- a/tracker-pt/module/frame.cpp
+++ b/tracker-pt/module/frame.cpp
@@ -1,48 +1,54 @@
#include "frame.hpp"
-
#include "compat/math.hpp"
-
-#include <cstring>
-#include <tuple>
-
#include <opencv2/imgproc.hpp>
-using namespace pt_module;
+namespace pt_module {
-Preview& Preview::operator=(const pt_frame& frame_)
+void Preview::set_last_frame(const pt_frame& frame_)
{
const cv::Mat& frame = frame_.as_const<const Frame>()->mat;
- ensure_size(frame_copy, frame_out.cols, frame_out.rows, CV_8UC3);
+ const bool need_resize = frame.size != frame_copy.size;
- if (frame.channels() != 3)
+ if (frame.channels() == 1)
{
- once_only(qDebug() << "tracker/pt: camera frame depth: 3 !=" << frame.channels());
- return *this;
+ if (need_resize)
+ {
+ frame_tmp.create(frame.size(), CV_8UC3);
+ cv::cvtColor(frame, frame_tmp, cv::COLOR_GRAY2BGR);
+ cv::resize(frame_tmp, frame_copy, frame_copy.size(), 0, 0, cv::INTER_NEAREST);
+ }
+ else
+ cv::cvtColor(frame, frame_copy, cv::COLOR_GRAY2BGR);
+ }
+ else if (frame.channels() == 3)
+ {
+ if (need_resize)
+ cv::resize(frame, frame_copy, frame_copy.size(), 0, 0, cv::INTER_NEAREST);
+ else
+ frame.copyTo(frame_copy);
}
-
- const bool need_resize = frame.cols != frame_out.cols || frame.rows != frame_out.rows;
- if (need_resize)
- cv::resize(frame, frame_copy, cv::Size(frame_out.cols, frame_out.rows), 0, 0, cv::INTER_NEAREST);
else
- frame.copyTo(frame_copy);
-
- return *this;
+ {
+ eval_once(qDebug() << "tracker/pt: camera frame depth" << frame.channels() << "!= 3");
+ frame_copy.create(frame_copy.size(), CV_8UC3);
+ frame_copy.setTo({0});
+ }
}
Preview::Preview(int w, int h)
{
- ensure_size(frame_out, w, h, CV_8UC4);
-
- frame_out.setTo(cv::Scalar(0, 0, 0, 0));
+ frame_out.create(h, w, CV_8UC4);
+ frame_copy.create(h, w, CV_8UC3);
+ frame_copy.setTo({0});
}
QImage Preview::get_bitmap()
{
- int stride = frame_out.step.p[0];
+ int stride = (int)frame_out.step.p[0];
- if (stride < 64 || stride < frame_out.cols * 4)
+ if (stride < frame_out.cols * 4)
{
- once_only(qDebug() << "bad stride" << stride
+ eval_once(qDebug() << "bad stride" << stride
<< "for bitmap size" << frame_copy.cols << frame_copy.rows);
return QImage();
}
@@ -55,11 +61,9 @@ QImage Preview::get_bitmap()
QImage::Format_ARGB32);
}
-void Preview::draw_head_center(double x, double y)
+void Preview::draw_head_center(f x, f y)
{
- double px_, py_;
-
- std::tie(px_, py_) = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows);
+ auto [px_, py_] = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows);
int px = iround(px_), py = iround(py_);
@@ -76,8 +80,4 @@ void Preview::draw_head_center(double x, double y)
color, 1);
}
-void Preview::ensure_size(cv::Mat& frame, int w, int h, int type)
-{
- if (frame.cols != w || frame.rows != h)
- frame = cv::Mat(h, w, type);
-}
+} // ns pt_module
diff --git a/tracker-pt/module/frame.hpp b/tracker-pt/module/frame.hpp
index 9e4f809a..0569a323 100644
--- a/tracker-pt/module/frame.hpp
+++ b/tracker-pt/module/frame.hpp
@@ -2,9 +2,14 @@
#include "pt-api.hpp"
-#include <opencv2/core.hpp>
+#include <opencv2/core/mat.hpp>
#include <QImage>
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
namespace pt_module {
struct Frame final : pt_frame
@@ -19,18 +24,19 @@ struct Preview final : pt_preview
{
Preview(int w, int h);
- Preview& operator=(const pt_frame& frame) override;
+ void set_last_frame(const pt_frame& frame) override;
QImage get_bitmap() override;
- void draw_head_center(double x, double y) override;
+ void draw_head_center(f x, f y) override;
operator cv::Mat&() { return frame_copy; }
operator cv::Mat const&() const { return frame_copy; }
private:
- static void ensure_size(cv::Mat& frame, int w, int h, int type);
-
- bool fresh = true;
- cv::Mat frame_copy, frame_color, frame_out;
+ cv::Mat frame_copy, frame_out, frame_tmp;
};
} // ns pt_module
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
diff --git a/tracker-pt/module/lang/de_DE.ts b/tracker-pt/module/lang/de_DE.ts
new file mode 100644
index 00000000..6c548aba
--- /dev/null
+++ b/tracker-pt/module/lang/de_DE.ts
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation>PointTracker 1.1</translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-pt/module/lang/nl_NL.ts b/tracker-pt/module/lang/nl_NL.ts
index 9e739505..4679971e 100644
--- a/tracker-pt/module/lang/nl_NL.ts
+++ b/tracker-pt/module/lang/nl_NL.ts
@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="nl_NL">
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/tracker-pt/module/lang/ru_RU.ts b/tracker-pt/module/lang/ru_RU.ts
index f62cf2e1..c3611ef0 100644
--- a/tracker-pt/module/lang/ru_RU.ts
+++ b/tracker-pt/module/lang/ru_RU.ts
@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/tracker-pt/module/lang/stub.ts b/tracker-pt/module/lang/stub.ts
index 6401616d..03d19f4e 100644
--- a/tracker-pt/module/lang/stub.ts
+++ b/tracker-pt/module/lang/stub.ts
@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/tracker-pt/module/lang/zh_CN.ts b/tracker-pt/module/lang/zh_CN.ts
index 6401616d..c39728a1 100644
--- a/tracker-pt/module/lang/zh_CN.ts
+++ b/tracker-pt/module/lang/zh_CN.ts
@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1">
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>pt_module::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
</TS>
diff --git a/tracker-pt/module/module.cpp b/tracker-pt/module/module.cpp
index 5c298ca5..f665face 100644
--- a/tracker-pt/module/module.cpp
+++ b/tracker-pt/module/module.cpp
@@ -1,6 +1,6 @@
#include "ftnoir_tracker_pt.h"
-#include "api/plugin-api.hpp"
+#include "module.hpp"
#include "camera.h"
#include "frame.hpp"
#include "point_extractor.h"
@@ -12,7 +12,11 @@
static const QString module_name = "tracker-pt";
-using namespace pt_module;
+#ifdef __clang__
+# pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+namespace pt_module {
struct pt_module_traits final : pt_runtime_traits
{
@@ -54,16 +58,15 @@ struct dialog_pt : TrackerDialog_PT
dialog_pt();
};
-class metadata_pt : public Metadata
-{
- QString name() { return _("PointTracker 1.1"); }
- QIcon icon() { return QIcon(":/Resources/Logo_IR.png"); }
-};
+dialog_pt::dialog_pt() : TrackerDialog_PT(module_name) {}
+
+QString metadata_pt::name() { return tr("PointTracker 1.1"); }
+QIcon metadata_pt::icon() { return QIcon(":/Resources/Logo_IR.png"); }
+
+}
// ns pt_module
using namespace pt_module;
-dialog_pt::dialog_pt() : TrackerDialog_PT(module_name) {}
-
OPENTRACK_DECLARE_TRACKER(tracker_pt, dialog_pt, metadata_pt)
diff --git a/tracker-pt/module/module.hpp b/tracker-pt/module/module.hpp
new file mode 100644
index 00000000..0b3f12cf
--- /dev/null
+++ b/tracker-pt/module/module.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "api/plugin-api.hpp"
+#include <QIcon>
+#include <QString>
+
+#include "compat/linkage-macros.hpp"
+
+namespace pt_module
+{
+
+class OTR_GENERIC_EXPORT metadata_pt : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override;
+ QIcon icon() override;
+};
+
+} // ns pt_module
diff --git a/tracker-pt/module/point_extractor.cpp b/tracker-pt/module/point_extractor.cpp
index b67c4036..3329fafc 100644
--- a/tracker-pt/module/point_extractor.cpp
+++ b/tracker-pt/module/point_extractor.cpp
@@ -9,11 +9,11 @@
#include "point_extractor.h"
#include "point_tracker.h"
#include "frame.hpp"
-
#include "cv/numeric.hpp"
#include "compat/math.hpp"
+#include "compat/math-imports.hpp"
-#include <opencv2/videoio.hpp>
+#include <opencv2/imgproc.hpp>
#undef PREVIEW
//#define PREVIEW
@@ -29,12 +29,13 @@
#include <QDebug>
-using namespace types;
-using namespace pt_module;
+using namespace numeric_types;
+
+// meanshift code written by Michael Welter
/*
http://en.wikipedia.org/wiki/Mean-shift
-In this application the idea, is to eliminate any bias of the point estimate
+In this application the idea, is to eliminate any bias of the point estimate
which is introduced by the rather arbitrary thresholded area. One must recognize
that the thresholded area can only move in one pixel increments since it is
binary. Thus, its center of mass might make "jumps" as pixels are added/removed
@@ -43,31 +44,28 @@ With mean-shift, a moving "window" or kernel is multiplied with the gray-scale
image, and the COM is calculated of the result. This is iterated where the
kernel center is set the previously computed COM. Thus, peaks in the image intensity
distribution "pull" the kernel towards themselves. Eventually it stops moving, i.e.
-then the computed COM coincides with the kernel center. We hope that the
+then the computed COM coincides with the kernel center. We hope that the
corresponding location is a good candidate for the extracted point.
-The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free
+The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free
algorithm for tracking single particles with variable size and shape." (2008).
*/
-static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 &current_center, f filter_width)
+static vec2 MeanShiftIteration(const cv::Mat1b &frame_gray, const vec2 &current_center, f filter_width)
{
- // Most amazingly this function runs faster with doubles than with floats.
- const f s = 1.0 / filter_width;
+ const f s = 1 / filter_width;
f m = 0;
- vec2 com { 0, 0 };
+ vec2 com { 0, 0 };
for (int i = 0; i < frame_gray.rows; i++)
{
- auto frame_ptr = (uint8_t const* restrict_ptr)frame_gray.ptr(i);
+ uint8_t const* const __restrict frame_ptr = frame_gray.ptr(i);
for (int j = 0; j < frame_gray.cols; j++)
{
f val = frame_ptr[j];
- val = val * val; // taking the square wights brighter parts of the image stronger.
- {
- f dx = (j - current_center[0])*s;
- f dy = (i - current_center[1])*s;
- f f = std::fmax(0, 1 - dx*dx - dy*dy);
- val *= f;
- }
+ val = val * val; // taking the square weighs brighter parts of the image stronger.
+ f dx = (j - current_center[0])*s;
+ f dy = (i - current_center[1])*s;
+ f max = std::fmax(f(0), 1 - dx*dx - dy*dy);
+ val *= max;
m += val;
com[0] += j * val;
com[1] += i * val;
@@ -75,13 +73,15 @@ static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 &curre
}
if (m > f(.1))
{
- com *= f(1) / m;
+ com *= 1 / m;
return com;
}
else
return current_center;
}
+namespace pt_module {
+
PointExtractor::PointExtractor(const QString& module_name) : s(module_name)
{
blobs.reserve(max_blobs);
@@ -89,24 +89,19 @@ PointExtractor::PointExtractor(const QString& module_name) : s(module_name)
void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame)
{
- if (ch[0].rows != orig_frame.rows || ch[0].cols != orig_frame.cols)
- for (unsigned k = 0; k < 3; k++)
- ch[k] = cv::Mat1b(orig_frame.rows, orig_frame.cols);
+ for (cv::Mat1b& x : ch)
+ x.create(orig_frame.rows, orig_frame.cols);
}
void PointExtractor::ensure_buffers(const cv::Mat& frame)
{
const int W = frame.cols, H = frame.rows;
- if (frame_gray.rows != W || frame_gray.cols != H)
- {
- frame_gray = cv::Mat1b(H, W);
- frame_bin = cv::Mat1b(H, W);
- frame_blobs = cv::Mat1b(H, W);
- }
+ frame_gray.create(H, W);
+ frame_bin.create(H, W);
}
-void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest)
+void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest)
{
ensure_channel_buffers(orig_frame);
@@ -117,17 +112,50 @@ void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx,
cv::mixChannels(&orig_frame, 1, &dest, 1, from_to, 1);
}
-void PointExtractor::extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs)
+void PointExtractor::filter_single_channel(const cv::Mat& orig_frame, float r, float g, float b, bool overexp, cv::Mat1b& dest)
{
ensure_channel_buffers(orig_frame);
- cv::mixChannels(&orig_frame, 1, (cv::Mat*) ch, order_npairs, order, order_npairs);
+ // just filter for colour or also include overexposed regions?
+ if (!overexp)
+ cv::transform(orig_frame, dest, cv::Mat(cv::Matx13f(b, g, r)));
+ else
+ {
+ for (int i = 0; i < orig_frame.rows; i++)
+ {
+ cv::Vec3b const* const __restrict orig_ptr = orig_frame.ptr<cv::Vec3b>(i);
+ uint8_t* const __restrict dest_ptr = dest.ptr(i);
+ for (int j = 0; j < orig_frame.cols; j++)
+ {
+ // get the intensity of the key color (i.e. +ve coefficients)
+ uchar blue = orig_ptr[j][0], green = orig_ptr[j][1], red = orig_ptr[j][2];
+ float key = std::max(b, 0.0f) * blue + std::max(g, 0.0f) * green + std::max(r, 0.0f) * red;
+ // get the intensity of the non-key color (i.e. -ve coefficients)
+ float nonkey = std::max(-b, 0.0f) * blue + std::max(-g, 0.0f) * green + std::max(-r, 0.0f) * red;
+ // the result is key color minus non-key color inversely weighted by key colour intensity
+ dest_ptr[j] = std::max(0.0f, std::min(255.0f, key - (255.0f - key) / 255.0f * nonkey));
+ }
+ }
+ }
}
void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output)
{
+ if (frame.channels() == 1)
+ {
+ output.create(frame.rows, frame.cols);
+ frame.copyTo(output);
+ return;
+ }
+
+ const float half_chr_key_str = *s.chroma_key_strength * 0.5;
switch (s.blob_color)
{
+ case pt_color_green_only:
+ {
+ extract_single_channel(frame, 1, output);
+ break;
+ }
case pt_color_blue_only:
{
extract_single_channel(frame, 0, output);
@@ -138,18 +166,44 @@ void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output)
extract_single_channel(frame, 2, output);
break;
}
- case pt_color_average:
+ case pt_color_red_chromakey:
+ {
+ filter_single_channel(frame, 1, -half_chr_key_str, -half_chr_key_str, s.chroma_key_overexposed, output);
+ break;
+ }
+ case pt_color_green_chromakey:
{
- const int W = frame.cols, H = frame.rows;
- const cv::Mat tmp = frame.reshape(1, W * H);
- cv::Mat output_ = output.reshape(1, W * H);
- cv::reduce(tmp, output_, 1, cv::REDUCE_AVG);
+ filter_single_channel(frame, -half_chr_key_str, 1, -half_chr_key_str, s.chroma_key_overexposed, output);
break;
}
+ case pt_color_blue_chromakey:
+ {
+ filter_single_channel(frame, -half_chr_key_str, -half_chr_key_str, 1, s.chroma_key_overexposed, output);
+ break;
+ }
+ case pt_color_cyan_chromakey:
+ {
+ filter_single_channel(frame, -*s.chroma_key_strength, 0.5, 0.5, s.chroma_key_overexposed, output);
+ break;
+ }
+ case pt_color_yellow_chromakey:
+ {
+ filter_single_channel(frame, 0.5, 0.5, -*s.chroma_key_strength, s.chroma_key_overexposed, output);
+ break;
+ }
+ case pt_color_magenta_chromakey:
+ {
+ filter_single_channel(frame, 0.5, -*s.chroma_key_strength, 0.5, s.chroma_key_overexposed, output);
+ break;
+ }
+ case pt_color_hardware:
+ eval_once(qDebug() << "camera driver doesn't support grayscale");
+ goto do_grayscale;
default:
- once_only(qDebug() << "wrong pt_color_type enum value" << int(s.blob_color));
- /*FALLTHROUGH*/
- case pt_color_natural:
+ eval_once(qDebug() << "wrong pt_color_type enum value" << int(s.blob_color));
+ [[fallthrough]];
+ case pt_color_bt709:
+do_grayscale:
cv::cvtColor(frame, output, cv::COLOR_BGR2GRAY);
break;
}
@@ -175,31 +229,97 @@ void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& outpu
cv::noArray(),
hist,
1,
- (int const*) &hist_size,
+ &hist_size,
&ranges);
- const f radius = (f) threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value);
+ const f radius = threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value);
- auto ptr = (float const* const restrict_ptr) hist.ptr(0);
- const unsigned area = uround(3 * M_PI * radius*radius);
+ float const* const __restrict ptr = hist.ptr<float>(0);
+ const unsigned area = unsigned(iround(3 * pi * radius*radius));
const unsigned sz = unsigned(hist.cols * hist.rows);
- unsigned thres = 32;
- for (unsigned i = sz-1, cnt = 0; i > 32; i--)
+ unsigned thres = 1;
+ for (unsigned i = sz-1, cnt = 0; i > 1; i--)
{
- cnt += ptr[i];
+ cnt += (unsigned)ptr[i];
if (cnt >= area)
break;
thres = i;
}
- cv::threshold(frame_gray, output, thres, 255, CV_THRESH_BINARY);
+ cv::threshold(frame_gray, output, thres, 255, cv::THRESH_BINARY);
}
}
-void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector<vec2>& points)
+static void draw_blobs(cv::Mat& preview_frame, const blob* blobs, unsigned nblobs, const cv::Size& size)
+{
+ for (unsigned k = 0; k < nblobs; k++)
+ {
+ const blob& b = blobs[k];
+
+ if (b.radius < 0)
+ continue;
+
+ const f dpi = preview_frame.cols / f(320);
+ const f offx = 10 * dpi, offy = f(7.5) * dpi;
+
+ const f cx = preview_frame.cols / f(size.width),
+ cy = preview_frame.rows / f(size.height),
+ c = std::fmax(f(1), cx+cy)/2;
+
+ cv::Point p(iround(b.pos[0] * cx), iround(b.pos[1] * cy));
+
+ auto outline_color = k >= PointModel::N_POINTS
+ ? cv::Scalar(192, 192, 192)
+ : cv::Scalar(255, 255, 0);
+
+ cv::ellipse(preview_frame, p,
+ {iround(b.rect.width/(f)2+2*c), iround(b.rect.height/(f)2+2*c)},
+ 0, 0, 360, outline_color, iround(dpi), cv::LINE_AA);
+
+ char buf[16];
+ std::snprintf(buf, sizeof(buf), "%.2fpx", (double)b.radius);
+
+ auto text_color = k >= PointModel::N_POINTS
+ ? cv::Scalar(160, 160, 160)
+ : cv::Scalar(0, 0, 255);
+
+ cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy));
+ cv::putText(preview_frame, buf, pos,
+ cv::FONT_HERSHEY_PLAIN, iround(dpi), text_color,
+ 1);
+ }
+}
+
+static vec2 meanshift_initial_guess(const cv::Rect rect, cv::Mat& frame_roi)
+{
+ vec2 ret = {rect.width/(f)2, rect.height/(f)2};
+
+ // compute center initial guess
+ double ynorm = 0, xnorm = 0, y = 0, x = 0;
+ for (int j = 0; j < rect.height; j++)
+ {
+ const unsigned char* __restrict ptr = frame_roi.ptr<unsigned char>(j);
+ for (int i = 0; i < rect.width; i++)
+ {
+ double val = ptr[i] * 1./255;
+ x += i * val;
+ y += j * val;
+ xnorm += val;
+ ynorm += val;
+ }
+ }
+ constexpr double eps = 1e-4;
+ if (xnorm > eps && ynorm > eps)
+ ret = { (f)(x / xnorm), (f)(y / ynorm) };
+ return ret;
+}
+
+void PointExtractor::extract_points(const pt_frame& frame_,
+ pt_preview& preview_frame_,
+ bool preview_visible,
+ std::vector<vec2>& points)
{
const cv::Mat& frame = frame_.as_const<Frame>()->mat;
- cv::Mat& preview_frame = *preview_frame_.as<Preview>();
ensure_buffers(frame);
color_to_grayscale(frame, frame_gray);
@@ -211,24 +331,24 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_
threshold_image(frame_gray, frame_bin);
- blobs.clear();
- frame_bin.copyTo(frame_blobs);
-
- const f region_size_min = s.min_point_size;
- const f region_size_max = s.max_point_size;
+ const f region_size_min = (f)s.min_point_size;
+ const f region_size_max = (f)s.max_point_size;
unsigned idx = 0;
- for (int y=0; y < frame_blobs.rows; y++)
+
+ blobs.clear();
+
+ for (int y=0; y < frame_bin.rows; y++)
{
- const unsigned char* ptr_bin = frame_blobs.ptr(y);
- for (int x=0; x < frame_blobs.cols; x++)
+ const unsigned char* __restrict ptr_bin = frame_bin.ptr(y);
+ for (int x=0; x < frame_bin.cols; x++)
{
if (ptr_bin[x] != 255)
continue;
idx = blobs.size() + 1;
cv::Rect rect;
- cv::floodFill(frame_blobs,
+ cv::floodFill(frame_bin,
cv::Point(x,y),
cv::Scalar(idx),
&rect,
@@ -244,8 +364,8 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_
for (int i=rect.y; i < ymax; i++)
{
- unsigned char const* const restrict_ptr ptr_blobs = frame_blobs.ptr(i);
- unsigned char const* const restrict_ptr ptr_gray = frame_gray.ptr(i);
+ unsigned char const* const __restrict ptr_blobs = frame_bin.ptr(i);
+ unsigned char const* const __restrict ptr_gray = frame_gray.ptr(i);
for (int j=rect.x; j < xmax; j++)
{
if (ptr_blobs[j] != idx)
@@ -257,12 +377,12 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_
}
}
- const double radius = std::sqrt(cnt / M_PI);
+ const f radius = std::sqrt((f)cnt) / std::sqrt(pi);
if (radius > region_size_max || radius < region_size_min)
continue;
blobs.emplace_back(radius,
- vec2(rect.width/2., rect.height/2.),
+ vec2(rect.width/f(2), rect.height/f(2)),
std::pow(f(norm), f(1.1))/cnt,
rect);
@@ -272,9 +392,7 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_
// XXX we could go to the next scanline unless the points are really small.
// i'd expect each point being present on at least one unique scanline
// but it turns out some people are using 2px points -sh 20180110
-#if BROKEN && 0
- break;
-#endif
+ //break;
}
}
end:
@@ -288,29 +406,22 @@ end:
for (idx = 0; idx < sz; ++idx)
{
- blob &b = blobs[idx];
- cv::Rect rect = b.rect;
-
- rect.x -= rect.width / 2;
- rect.y -= rect.height / 2;
- rect.width *= 2;
- rect.height *= 2;
- rect &= cv::Rect(0, 0, W, H); // crop at frame boundaries
-
+ blob& b = blobs[idx];
+ cv::Rect rect = b.rect & cv::Rect(0, 0, W, H); // crop at frame boundaries
cv::Mat frame_roi = frame_gray(rect);
// smaller values mean more changes. 1 makes too many changes while 1.5 makes about .1
static constexpr f radius_c = f(1.75);
const f kernel_radius = b.radius * radius_c;
- vec2 pos(rect.width/2., rect.height/2.); // position relative to ROI.
+ vec2 pos = meanshift_initial_guess(rect, frame_roi); // position relative to ROI.
for (int iter = 0; iter < 10; ++iter)
{
vec2 com_new = MeanShiftIteration(frame_roi, pos, kernel_radius);
vec2 delta = com_new - pos;
pos = com_new;
- if (delta.dot(delta) < 1e-2)
+ if (delta.dot(delta) < f(1e-3))
break;
}
@@ -318,43 +429,11 @@ end:
b.pos[1] = pos[1] + rect.y;
}
- for (unsigned k = 0; k < blobs.size(); k++)
- {
- blob& b = blobs[k];
-
- const f dpi = preview_frame.cols / f(320);
- const f offx = 10 * dpi, offy = 7.5 * dpi;
-
- const f cx = preview_frame.cols / f(frame.cols),
- cy = preview_frame.rows / f(frame.rows),
- c_ = (cx+cy)/2;
-
- static constexpr unsigned fract_bits = 16;
- static constexpr double c_fract(1 << fract_bits);
-
- cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract));
+ if (preview_visible)
+ draw_blobs(preview_frame_.as<Frame>()->mat,
+ blobs.data(), blobs.size(),
+ frame_gray.size());
- auto circle_color = k >= PointModel::N_POINTS
- ? cv::Scalar(192, 192, 192)
- : cv::Scalar(255, 255, 0);
-
- const f overlay_size = dpi > 1.5 ? 2 : 1;
-
- cv::circle(preview_frame, p, iround((b.radius + 3.3) * c_ * c_fract), circle_color, overlay_size, cv::LINE_AA, fract_bits);
-
- char buf[16];
- buf[sizeof(buf)-1] = '\0';
- std::snprintf(buf, sizeof(buf) - 1, "%.2fpx", b.radius);
-
- auto text_color = k >= PointModel::N_POINTS
- ? cv::Scalar(160, 160, 160)
- : cv::Scalar(0, 0, 255);
-
- cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy));
- cv::putText(preview_frame, buf, pos,
- cv::FONT_HERSHEY_PLAIN, overlay_size, text_color,
- 1);
- }
// End of mean shift code. At this point, blob positions are updated with hopefully less noisy less biased values.
points.reserve(max_blobs);
@@ -375,3 +454,5 @@ blob::blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect) :
{
//qDebug() << "radius" << radius << "pos" << pos[0] << pos[1];
}
+
+} // ns pt_module
diff --git a/tracker-pt/module/point_extractor.h b/tracker-pt/module/point_extractor.h
index eac2268c..fbfdbb0b 100644
--- a/tracker-pt/module/point_extractor.h
+++ b/tracker-pt/module/point_extractor.h
@@ -9,17 +9,15 @@
#pragma once
#include "pt-api.hpp"
-
+#include <opencv2/core/mat.hpp>
+#include <opencv2/core/types.hpp>
#include <vector>
-#include <opencv2/core.hpp>
-#include <opencv2/imgproc.hpp>
-
namespace pt_module {
-using namespace types;
+using namespace numeric_types;
-struct blob
+struct blob final
{
f radius, brightness;
vec2 pos;
@@ -33,14 +31,18 @@ class PointExtractor final : public pt_point_extractor
public:
// extracts points from frame and draws some processing info into frame, if draw_output is set
// dt: time since last call in seconds
- void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector<vec2>& points) override;
- PointExtractor(const QString& module_name);
+ void extract_points(const pt_frame& frame,
+ pt_preview& preview_frame, bool preview_visible,
+ std::vector<vec2>& points) override;
+
+ explicit PointExtractor(const QString& module_name);
+
private:
- static constexpr inline int max_blobs = 16;
+ static constexpr int max_blobs = 16;
pt_settings s;
- cv::Mat1b frame_gray, frame_bin, frame_blobs;
+ cv::Mat1b frame_bin, frame_gray;
cv::Mat1f hist;
std::vector<blob> blobs;
cv::Mat1b ch[3];
@@ -48,8 +50,8 @@ private:
void ensure_channel_buffers(const cv::Mat& orig_frame);
void ensure_buffers(const cv::Mat& frame);
- void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest);
- void extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs);
+ void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest);
+ void filter_single_channel(const cv::Mat& orig_frame, float r, float g, float b, bool overexp, cv::Mat1b& dest);
void color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output);
void threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output);
diff --git a/tracker-pt/point-filter.cpp b/tracker-pt/point-filter.cpp
new file mode 100644
index 00000000..10448fe2
--- /dev/null
+++ b/tracker-pt/point-filter.cpp
@@ -0,0 +1,72 @@
+#include "point-filter.hpp"
+#include <algorithm>
+#include <cmath>
+#include <QDebug>
+
+namespace pt_point_filter_impl {
+
+void point_filter::reset()
+{
+ t = std::nullopt;
+}
+
+const PointOrder& point_filter::operator()(const PointOrder& input, f deadzone_amount)
+{
+ using std::fmod;
+ using std::sqrt;
+ using std::pow;
+ using std::clamp;
+
+ if (!s.enable_point_filter)
+ {
+ t = std::nullopt;
+ state_ = input;
+ return state_;
+ }
+
+ if (!t)
+ {
+ t.emplace();
+ state_ = input;
+ return state_;
+ }
+
+ constexpr auto E = (f)1.75;
+ const f limit = (f)*s.point_filter_limit;
+ const f C = progn(
+ constexpr int A = 1'000'000;
+ double K = *s.point_filter_coefficient;
+ f log10_pos = -2 + (int)K, rest = (f)(.999-fmod(K, 1.)*.9);
+ return A * pow((f)10, (f)-log10_pos) * rest;
+ );
+
+ f dist = 0, dz = deadzone_amount * (f)s.point_filter_deadzone / 800; // sqrt(640^2 + 480^2)
+
+ for (unsigned i = 0; i < 3; i++)
+ {
+ vec2 tmp = input[i] - state_[i];
+ f x = sqrt(tmp.dot(tmp));
+ x = std::max((f)0, x - dz);
+ dist = std::max(dist, x);
+ }
+
+ if (dist < (f)1e-6)
+ return state_;
+
+ f dt = (f)t->elapsed_seconds(); t->start();
+ f delta = pow(dist, E) * C * dt; // gain
+
+ //qDebug() << "gain" << std::min((f)1, delta);
+
+ for (unsigned i = 0; i < 3; i++)
+ {
+ f x = clamp(delta, (f)0, limit);
+ state_[i] += x*(input[i] - state_[i]);
+ }
+
+ return state_;
+}
+
+point_filter::point_filter(const pt_settings& s) : s{s} {}
+
+} // ns pt_point_filter_impl
diff --git a/tracker-pt/point-filter.hpp b/tracker-pt/point-filter.hpp
new file mode 100644
index 00000000..c3c045dd
--- /dev/null
+++ b/tracker-pt/point-filter.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "cv/numeric.hpp"
+#include "compat/timer.hpp"
+#include "pt-settings.hpp"
+#include <optional>
+#include <array>
+
+namespace pt_point_filter_impl {
+
+using namespace numeric_types;
+using PointOrder = std::array<numeric_types::vec2, 3>;
+
+class point_filter final
+{
+ PointOrder state_;
+ std::optional<Timer> t;
+ const pt_settings& s;
+
+public:
+ void reset();
+ const PointOrder& operator()(const PointOrder& input, f deadzone_amount);
+
+ explicit point_filter(const pt_settings& s);
+ ~point_filter() = default;
+
+ OTR_DISABLE_MOVE_COPY(point_filter);
+};
+
+} // ns pt_point_filter_impl
+
+using point_filter = pt_point_filter_impl::point_filter;
diff --git a/tracker-pt/point_tracker.cpp b/tracker-pt/point_tracker.cpp
index 6116bec5..39e96038 100644
--- a/tracker-pt/point_tracker.cpp
+++ b/tracker-pt/point_tracker.cpp
@@ -8,14 +8,16 @@
#include "point_tracker.h"
#include "compat/math-imports.hpp"
-using namespace types;
-
#include <vector>
#include <algorithm>
#include <cmath>
#include <QDebug>
+namespace pt_impl {
+
+using namespace numeric_types;
+
static void get_row(const mat33& m, int i, vec3& v)
{
v[0] = m(i,0);
@@ -48,13 +50,16 @@ void PointModel::set_model(const pt_settings& s)
{
switch (s.active_model_panel)
{
+ default:
+ eval_once(qDebug() << "pt: wrong model type selected");
+ [[fallthrough]];
case Clip:
- M01 = vec3(0, static_cast<f>(s.clip_ty), -static_cast<f>(s.clip_tz));
- M02 = vec3(0, -static_cast<f>(s.clip_by), -static_cast<f>(s.clip_bz));
+ M01 = vec3(0, s.clip_ty, -s.clip_tz);
+ M02 = vec3(0, -s.clip_by, -s.clip_bz);
break;
case Cap:
- M01 = vec3(-static_cast<f>(s.cap_x), -static_cast<f>(s.cap_y), -static_cast<f>(s.cap_z));
- M02 = vec3(static_cast<f>(s.cap_x), -static_cast<f>(s.cap_y), -static_cast<f>(s.cap_z));
+ M01 = vec3(-s.cap_x, -s.cap_y, -s.cap_z);
+ M02 = vec3(s.cap_x, -s.cap_y, -s.cap_z);
break;
case Custom:
M01 = vec3(s.m01_x, s.m01_y, s.m01_z);
@@ -63,69 +68,62 @@ void PointModel::set_model(const pt_settings& s)
}
}
-void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const
+void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d)
{
+ constexpr unsigned cnt = PointModel::N_POINTS;
// fit line to orthographically projected points
using t = std::pair<f,unsigned>;
- t d_vals[3];
+ t d_vals[cnt];
// get sort indices with respect to d scalar product
- for (unsigned i = 0; i < PointModel::N_POINTS; ++i)
- d_vals[i] = t(d.dot(points[i]), i);
+ for (unsigned i = 0; i < cnt; ++i)
+ d_vals[i] = {d.dot(points[i]), i};
- std::sort(d_vals,
- d_vals + 3u,
- [](const t& a, const t& b) { return a.first < b.first; });
+ std::sort(std::begin(d_vals), std::end(d_vals),
+ [](t a, t b) { return a.first < b.first; });
- for (unsigned i = 0; i < PointModel::N_POINTS; ++i)
+ for (unsigned i = 0; i < cnt; ++i)
d_order[i] = d_vals[i].second;
}
-PointTracker::PointTracker()
-{
-}
+PointTracker::PointTracker() = default;
PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2* points,
const PointModel& model,
const pt_camera_info& info)
{
- const double fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
- PointTracker::PointOrder p;
- p[0] = project(vec3(0,0,0), fx);
- p[1] = project(model.M01, fx);
- p[2] = project(model.M02, fx);
+ const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
+ PointTracker::PointOrder p {
+ project(vec3(0,0,0), fx),
+ project(model.M01, fx),
+ project(model.M02, fx)
+ };
- const int diagonal = int(std::sqrt(f(info.res_x*info.res_x + info.res_y*info.res_y)));
- constexpr int div = 100;
- const int max_dist = diagonal / div; // 8 pixels for 640x480
+ constexpr unsigned sz = PointModel::N_POINTS;
// set correspondences by minimum distance to projected model point
- bool point_taken[PointModel::N_POINTS];
- for (unsigned i=0; i<PointModel::N_POINTS; ++i)
- point_taken[i] = false;
+ bool point_taken[sz] {};
- for (unsigned i=0; i<PointModel::N_POINTS; ++i)
+ for (unsigned i=0; i < sz; ++i)
{
f min_sdist = 0;
unsigned min_idx = 0;
// find closest point to projected model point i
- for (unsigned j=0; j<PointModel::N_POINTS; ++j)
+ for (unsigned j=0; j < sz; ++j)
{
vec2 d = p[i]-points[j];
f sdist = d.dot(d);
- if (sdist < min_sdist || j==0)
+ if (sdist < min_sdist || j == 0)
{
min_idx = j;
min_sdist = sdist;
}
}
- if (min_sdist > max_dist)
- return find_correspondences(points, model);
// if one point is closest to more than one model point, fallback
if (point_taken[min_idx])
{
- init_phase = true;
+ reset_state();
return find_correspondences(points, model);
}
point_taken[min_idx] = true;
@@ -135,119 +133,63 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2*
return p;
}
-bool PointTracker::maybe_use_old_point_order(const PointOrder& order, const pt_camera_info& info)
-{
- constexpr f std_width = 640, std_height = 480;
-
- PointOrder scaled_order;
-
- const f cx = std_width / info.res_x;
- const f cy = std_height / info.res_y;
-
- for (unsigned k = 0; k < 3; k++)
- {
- // note, the .y component is actually scaled by width
- scaled_order[k][0] = std_width * cx * order[k][0];
- scaled_order[k][1] = std_width * cy * order[k][1];
- }
-
- f sum = 0;
-
- for (unsigned k = 0; k < 3; k++)
- {
- vec2 tmp = prev_scaled_order[k] - scaled_order[k];
- sum += std::sqrt(tmp.dot(tmp));
- }
-
- // CAVEAT don't increase too much, it visibly loses precision
- constexpr f max_dist = f(.13);
-
- const bool validp = sum < max_dist;
-
- prev_order_valid &= validp;
-
- if (!prev_order_valid)
- {
- prev_order = order;
- prev_scaled_order = scaled_order;
- }
-
-#if 0
- {
- static Timer tt;
- static int cnt1 = 0, cnt2 = 0;
- if (tt.elapsed_ms() >= 1000)
- {
- tt.start();
- if (cnt1 + cnt2)
- {
- qDebug() << "old-order" << ((cnt1 * 100) / f(cnt1 + cnt2)) << "nsamples" << (cnt1 + cnt2);
- cnt1 = 0, cnt2 = 0;
- }
- }
- if (validp)
- cnt1++;
- else
- cnt2++;
- }
-#endif
-
- prev_order_valid = validp;
-
- return validp;
-}
-
void PointTracker::track(const std::vector<vec2>& points,
const PointModel& model,
const pt_camera_info& info,
- int init_phase_timeout)
+ int init_phase_timeout,
+ point_filter& filter,
+ f deadzone_amount)
{
- const double fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
+ const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y);
PointOrder order;
- if (init_phase_timeout > 0 && t.elapsed_ms() > init_phase_timeout)
+ if (init_phase || init_phase_timeout <= 0 || t.elapsed_ms() > init_phase_timeout)
{
- t.start();
- init_phase = true;
- }
-
- if (!(init_phase_timeout > 0 && !init_phase))
+ reset_state();
order = find_correspondences(points.data(), model);
+ }
else
order = find_correspondences_previous(points.data(), model, info);
- if (maybe_use_old_point_order(order, info) ||
- POSIT(model, order, fx) != -1)
+ if (POSIT(model, filter(order, deadzone_amount), fx) != -1)
{
init_phase = false;
t.start();
}
+ else
+ reset_state();
}
PointTracker::PointOrder PointTracker::find_correspondences(const vec2* points, const PointModel& model)
{
- static const Affine a(mat33::eye(), vec3(0, 0, 1));
+ constexpr unsigned cnt = PointModel::N_POINTS;
// We do a simple freetrack-like sorting in the init phase...
- unsigned point_d_order[PointModel::N_POINTS];
- unsigned model_d_order[PointModel::N_POINTS];
- // sort points
+ unsigned point_d_order[cnt];
+ unsigned model_d_order[cnt];
+ // calculate d and d_order for simple freetrack-like point correspondence
vec2 d(model.M01[0]-model.M02[0], model.M01[1]-model.M02[1]);
+ // sort points
model.get_d_order(points, point_d_order, d);
- // calculate d and d_order for simple freetrack-like point correspondence
- vec2 pts[3] = {
- vec2(0, 0),
- vec2(model.M01[0], model.M01[1]),
- vec2(model.M02[0], model.M02[1])
+ vec2 pts[cnt] {
+ { 0, 0 },
+ { model.M01[0], model.M01[1] },
+ { model.M02[0], model.M02[1] },
};
model.get_d_order(pts, model_d_order, d);
+
// set correspondences
PointOrder p;
- for (unsigned i = 0; i < PointModel::N_POINTS; ++i)
+ for (unsigned i = 0; i < cnt; ++i)
p[model_d_order[i]] = points[point_d_order[i]];
return p;
}
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wfloat-equal"
+#endif
+
int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f focal_length)
{
// POSIT algorithm for coplanar points as presented in
@@ -256,7 +198,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
// The expected rotation used for resolving the ambiguity in POSIT:
// In every iteration step the rotation closer to R_expected is taken
- static const mat33 R_expected(X_CM.R);
+ const mat33& R_expected{X_CM_expected.R};
// initial pose = last (predicted) pose
vec3 k;
@@ -265,8 +207,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
f old_epsilon_1 = 0;
f old_epsilon_2 = 0;
- f epsilon_1 = 1;
- f epsilon_2 = 1;
+ f epsilon_1, epsilon_2;
vec3 I0, J0;
vec2 I0_coeff, J0_coeff;
@@ -277,8 +218,8 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
constexpr int max_iter = 100;
- int i=1;
- for (; i<max_iter; ++i)
+ int i;
+ for (i = 1; i < max_iter; ++i)
{
epsilon_1 = k.dot(model.M01)/Z0;
epsilon_2 = k.dot(model.M02)/Z0;
@@ -303,14 +244,14 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
// CAVEAT don't change to comparison with an epsilon -sh 20160423
if (JJ0 == II0) {
rho = sqrt(fabs(2*IJ0));
- theta = -M_PI/4;
+ theta = -pi/4;
if (IJ0<0) theta *= -1;
}
else {
rho = sqrt(sqrt( (JJ0-II0)*(JJ0-II0) + 4*IJ0*IJ0 ));
theta = atan( -2*IJ0 / (JJ0-II0) );
// avoid branch misprediction
- theta += (JJ0 - II0 < 0) * M_PI;
+ theta += (JJ0 - II0 < 0) * pi;
theta *= f(.5);
}
@@ -321,7 +262,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
J_1 = J0 + rho*sin(theta)*model.u;
J_2 = J0 - rho*sin(theta)*model.u;
- f norm_const = 1/cv::norm(I_1); // all have the same norm
+ f norm_const = (f)(1/cv::norm(I_1)); // all have the same norm
// create rotation matrices
I_1 *= norm_const; J_1 *= norm_const;
@@ -340,8 +281,8 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
// pick the rotation solution closer to the expected one
// in simple metric d(A,B) = || I - A * B^T ||
- f R_1_deviation = cv::norm(mat33::eye() - R_expected * R_1.t());
- f R_2_deviation = cv::norm(mat33::eye() - R_expected * R_2.t());
+ f R_1_deviation = (f)(cv::norm(mat33::eye() - R_expected * R_1.t()));
+ f R_2_deviation = (f)(cv::norm(mat33::eye() - R_expected * R_2.t()));
if (R_1_deviation < R_2_deviation)
R_current = &R_1;
@@ -353,7 +294,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
// check for convergence condition
const f delta = fabs(epsilon_1 - old_epsilon_1) + fabs(epsilon_2 - old_epsilon_2);
- if (!(delta > constants::eps))
+ if (delta < eps)
break;
old_epsilon_1 = epsilon_1;
@@ -373,17 +314,17 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
int ret = std::fpclassify(r(i, j));
if (ret == FP_NAN || ret == FP_INFINITE)
{
- qDebug() << "posit nan -- R";
+ qDebug() << "posit nan R";
return -1;
}
}
- for (unsigned i = 0; i < 3; i++)
+ for (unsigned i = 0; i < 3; i++) // NOLINT(modernize-loop-convert)
{
int ret = std::fpclassify(t[i]);
if (ret == FP_NAN || ret == FP_INFINITE)
{
- qDebug() << "posit nan -- T";
+ qDebug() << "posit nan T";
return -1;
}
}
@@ -394,11 +335,17 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
X_CM.t[1] = t[1];
X_CM.t[2] = t[2];
+ X_CM_expected = X_CM;
+
//qDebug() << "iter:" << i;
return i;
}
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
vec2 PointTracker::project(const vec3& v_M, f focal_length)
{
return project(v_M, focal_length, X_CM);
@@ -412,7 +359,8 @@ vec2 PointTracker::project(const vec3& v_M, f focal_length, const Affine& X_CM)
void PointTracker::reset_state()
{
- prev_order_valid = false;
init_phase = true;
+ X_CM_expected = {};
}
+} // ns pt_impl
diff --git a/tracker-pt/point_tracker.h b/tracker-pt/point_tracker.h
index 5e741c75..7492b4eb 100644
--- a/tracker-pt/point_tracker.h
+++ b/tracker-pt/point_tracker.h
@@ -11,28 +11,25 @@
#include "cv/affine.hpp"
#include "cv/numeric.hpp"
#include "pt-api.hpp"
+#include "point-filter.hpp"
-#include <cstddef>
-#include <memory>
#include <vector>
#include <array>
-#include <opencv2/core.hpp>
-
#include <QObject>
-namespace pt_module {
+namespace pt_impl {
// ----------------------------------------------------------------------------
// Describes a 3-point model
// nomenclature as in
// [Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis: "Iterative Pose Estimation Using Coplanar Feature Points"]
-using namespace types;
+using namespace numeric_types;
struct PointModel final
{
- static constexpr inline unsigned N_POINTS = 3;
+ static constexpr unsigned N_POINTS = 3;
vec3 M01; // M01 in model frame
vec3 M02; // M02 in model frame
@@ -43,10 +40,10 @@ struct PointModel final
enum Model { Clip, Cap, Custom };
- PointModel(const pt_settings& s);
+ explicit PointModel(const pt_settings& s);
void set_model(const pt_settings& s);
- void get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const;
+ static void get_d_order(const vec2* points, unsigned* d_order, const vec2& d);
};
// ----------------------------------------------------------------------------
@@ -60,8 +57,13 @@ public:
// track the pose using the set of normalized point coordinates (x pos in range -0.5:0.5)
// f : (focal length)/(sensor width)
// dt : time since last call
- void track(const std::vector<vec2>& projected_points, const PointModel& model, const pt_camera_info& info, int init_phase_timeout);
- Affine pose() { return X_CM; }
+ void track(const std::vector<vec2>& projected_points,
+ const PointModel& model,
+ const pt_camera_info& info,
+ int init_phase_timeout,
+ point_filter& filter,
+ f deadzone_amount);
+ Affine pose() const { return X_CM; }
vec2 project(const vec3& v_M, f focal_length);
vec2 project(const vec3& v_M, f focal_length, const Affine& X_CM);
void reset_state();
@@ -70,19 +72,18 @@ private:
// the points in model order
using PointOrder = std::array<vec2, 3>;
- bool maybe_use_old_point_order(const PointOrder& order, const pt_camera_info& info);
-
- PointOrder find_correspondences(const vec2* projected_points, const PointModel &model);
+ static PointOrder find_correspondences(const vec2* projected_points, const PointModel &model);
PointOrder find_correspondences_previous(const vec2* points, const PointModel &model, const pt_camera_info& info);
- int POSIT(const PointModel& point_model, const PointOrder& order, f focal_length); // The POSIT algorithm, returns the number of iterations
+ // The POSIT algorithm, returns the number of iterations
+ int POSIT(const PointModel& point_model, const PointOrder& order, f focal_length);
- Affine X_CM; // transform from model to camera
- PointOrder prev_order, prev_scaled_order;
+ Affine X_CM; // transform from model to camera
+ Affine X_CM_expected;
Timer t;
- bool init_phase = true, prev_order_valid = false;
+ bool init_phase = true;
};
} // ns pt_impl
-using pt_module::PointTracker;
-using pt_module::PointModel;
+using PointTracker = pt_impl::PointTracker;
+using PointModel = pt_impl::PointModel;
diff --git a/tracker-pt/pt-api.cpp b/tracker-pt/pt-api.cpp
index 596590dc..d71c6e13 100644
--- a/tracker-pt/pt-api.cpp
+++ b/tracker-pt/pt-api.cpp
@@ -1,77 +1,53 @@
#include "pt-api.hpp"
#include "cv/numeric.hpp"
-using namespace types;
+using namespace numeric_types;
-pt_camera_info::pt_camera_info()
-{
-}
+pt_camera_info::pt_camera_info() = default;
-double pt_camera_info::get_focal_length(f fov, int res_x, int res_y)
+f pt_camera_info::get_focal_length(f fov, int res_x, int res_y)
{
- const double diag_len = std::sqrt(double(res_x*res_x + res_y*res_y));
- const double aspect_x = res_x / diag_len;
+ const f diag_len = std::sqrt(f(res_x*res_x + res_y*res_y));
+ const f aspect_x = res_x / diag_len;
//const double aspect_y = res_y / diag_len;
- const double diag_fov = fov * M_PI/180;
- const double fov_x = 2*std::atan(std::tan(diag_fov*.5) * aspect_x);
+ const f diag_fov = fov * pi/180;
+ const f fov_x = 2*std::atan(std::tan(diag_fov*f{.5}) * aspect_x);
//const double fov_y = 2*atan(tan(diag_fov*.5) * aspect_y);
- const double fx = .5 / std::tan(fov_x * .5);
+ const f fx = f{.5} / std::tan(fov_x * f{.5});
return fx;
//fy = .5 / tan(fov_y * .5);
//static bool once = false; if (!once) { once = true; qDebug() << "f" << ret << "fov" << (fov * 180/M_PI); }
}
-pt_camera::pt_camera()
-{
-}
-
-pt_camera::~pt_camera()
-{
-}
-
-pt_runtime_traits::pt_runtime_traits()
-{
-}
-
-pt_runtime_traits::~pt_runtime_traits()
-{
-}
-
-pt_point_extractor::pt_point_extractor()
-{
-}
-
-pt_point_extractor::~pt_point_extractor()
-{
-}
+pt_camera::pt_camera() = default;
+pt_camera::~pt_camera() = default;
+pt_runtime_traits::pt_runtime_traits() = default;
+pt_runtime_traits::~pt_runtime_traits() = default;
+pt_point_extractor::pt_point_extractor() = default;
+pt_point_extractor::~pt_point_extractor() = default;
-double pt_point_extractor::threshold_radius_value(int w, int h, int threshold)
+f pt_point_extractor::threshold_radius_value(int w, int h, int threshold)
{
- double cx = w / 640., cy = h / 480.;
+ f cx = w / f{640}, cy = h / f{480};
- const double min_radius = 1.75 * cx;
- const double max_radius = 15 * cy;
+ const f min_radius = f{1.75} * cx;
+ const f max_radius = f{30} * cy;
- const double radius = std::fmax(0., (max_radius-min_radius) * threshold / f(255) + min_radius);
+ const f radius = std::fmax(f{0}, (max_radius-min_radius) * threshold / f(255) + min_radius);
return radius;
}
-std::tuple<double, double> pt_pixel_pos_mixin::to_pixel_pos(double x, double y, int w, int h)
-{
- return std::make_tuple(w*(x+.5), .5*(h - 2*y*w));
-}
-
-std::tuple<double, double> pt_pixel_pos_mixin::to_screen_pos(double px, double py, int w, int h)
+std::tuple<f, f> pt_pixel_pos_mixin::to_pixel_pos(f x, f y, int w, int h)
{
- px *= w/(w-1.), py *= h/(h-1.);
- return std::make_tuple((px - w/2.)/w, -(py - h/2.)/w);
+ return { w*(x+f{.5}), f{.5}*(h - 2*y*w) };
}
-pt_frame::pt_frame()
+std::tuple<f, f> pt_pixel_pos_mixin::to_screen_pos(f px, f py, int w, int h)
{
+ px *= w/(w-f{1}); py *= h/(h-f{1});
+ return { (px - w/f{2})/w, -(py - h/f{2})/w };
}
-pt_frame::~pt_frame()
-{
-}
+pt_frame::pt_frame() = default;
+pt_frame::~pt_frame() = default;
diff --git a/tracker-pt/pt-api.hpp b/tracker-pt/pt-api.hpp
index de097a04..15021ff3 100644
--- a/tracker-pt/pt-api.hpp
+++ b/tracker-pt/pt-api.hpp
@@ -1,42 +1,47 @@
#pragma once
-#include "export.hpp"
-
#include "pt-settings.hpp"
#include "cv/numeric.hpp"
#include "options/options.hpp"
#include <tuple>
-#include <type_traits>
+#include <vector>
#include <memory>
-#include <opencv2/core.hpp>
-
#include <QImage>
+#include <QString>
-struct OTR_PT_EXPORT pt_camera_info final
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+struct pt_camera_info final
{
- typedef typename types::f f;
+ using f = numeric_types::f;
pt_camera_info();
- static double get_focal_length(f fov, int res_x, int res_y);
+ static f get_focal_length(f fov, int res_x, int res_y);
- double fov = 0;
- double fps = 0;
+ f fov = 0;
+ f fps = 0;
int res_x = 0;
int res_y = 0;
- int idx = -1;
+ QString name;
+ bool use_mjpeg = false;
};
-struct OTR_PT_EXPORT pt_pixel_pos_mixin
+struct pt_pixel_pos_mixin
{
- static std::tuple<double, double> to_pixel_pos(double x, double y, int w, int h);
- static std::tuple<double, double> to_screen_pos(double px, double py, int w, int h);
+ using f = numeric_types::f;
+
+ static std::tuple<f, f> to_pixel_pos(f x, f y, int w, int h);
+ static std::tuple<f, f> to_screen_pos(f px, f py, int w, int h);
};
-struct OTR_PT_EXPORT pt_frame : pt_pixel_pos_mixin
+struct pt_frame : pt_pixel_pos_mixin
{
pt_frame();
virtual ~pt_frame();
@@ -44,62 +49,78 @@ struct OTR_PT_EXPORT pt_frame : pt_pixel_pos_mixin
template<typename t>
t* as() &
{
- using u = std::decay_t<t>;
- static_assert(std::is_convertible_v<u*, pt_frame*>, "must be derived from pt_image");
-
return static_cast<t*>(this);
}
template<typename t>
t const* as_const() const&
{
- return const_cast<pt_frame*>(this)->as<const t>();
+ return static_cast<t const*>(this);
}
+
+protected:
+ pt_frame(const pt_frame&) = default;
+ pt_frame(pt_frame&&) = default;
+ pt_frame& operator=(const pt_frame&) = default;
+ pt_frame& operator=(pt_frame&&) = default;
};
-struct OTR_PT_EXPORT pt_preview : pt_frame
+struct pt_preview : pt_frame
{
- virtual pt_preview& operator=(const pt_frame&) = 0;
+ pt_preview() = default;
+
+ OTR_DISABLE_MOVE_COPY(pt_preview);
+
+ virtual void set_last_frame(const pt_frame&) = 0;
virtual QImage get_bitmap() = 0;
- virtual void draw_head_center(double x, double y) = 0;
+ virtual void draw_head_center(f x, f y) = 0;
};
-struct OTR_PT_EXPORT pt_camera
+struct pt_camera
{
using result = std::tuple<bool, pt_camera_info>;
+ using f = numeric_types::f;
pt_camera();
virtual ~pt_camera();
- virtual warn_result_unused bool start(int idx, int fps, int res_x, int res_y) = 0;
+ OTR_DISABLE_MOVE_COPY(pt_camera);
+
+ [[nodiscard]] virtual bool start(const pt_settings& s) = 0;
virtual void stop() = 0;
- virtual warn_result_unused result get_frame(pt_frame& frame) = 0;
- virtual warn_result_unused result get_info() const = 0;
+ virtual result get_frame(pt_frame& frame) = 0;
+ virtual result get_info() const = 0;
virtual pt_camera_info get_desired() const = 0;
virtual QString get_desired_name() const = 0;
virtual QString get_active_name() const = 0;
- virtual void set_fov(double value) = 0;
+ virtual void set_fov(f value) = 0;
virtual void show_camera_settings() = 0;
+ virtual f deadzone_amount() const { return 1; }
};
-struct OTR_PT_EXPORT pt_point_extractor : pt_pixel_pos_mixin
+struct pt_point_extractor : pt_pixel_pos_mixin
{
- using vec2 = types::vec2;
+ using vec2 = numeric_types::vec2;
+ using f = numeric_types::f;
+
+ OTR_DISABLE_MOVE_COPY(pt_point_extractor);
pt_point_extractor();
virtual ~pt_point_extractor();
- virtual void extract_points(const pt_frame& image, pt_preview& preview_frame, std::vector<vec2>& points) = 0;
+ virtual void extract_points(const pt_frame& image, pt_preview& preview_frame, bool preview_visible, std::vector<vec2>& points) = 0;
- static double threshold_radius_value(int w, int h, int threshold);
+ static f threshold_radius_value(int w, int h, int threshold);
};
-struct OTR_PT_EXPORT pt_runtime_traits
+struct pt_runtime_traits
{
template<typename t> using pointer = std::shared_ptr<t>;
+ OTR_DISABLE_MOVE_COPY(pt_runtime_traits);
+
pt_runtime_traits();
virtual ~pt_runtime_traits();
@@ -109,3 +130,10 @@ struct OTR_PT_EXPORT pt_runtime_traits
virtual pointer<pt_preview> make_preview(int w, int h) const = 0;
virtual QString get_module_name() const = 0;
};
+
+template<typename t>
+using pt_pointer = typename pt_runtime_traits::pointer<t>;
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
diff --git a/tracker-pt/pt-settings.hpp b/tracker-pt/pt-settings.hpp
index 471bb941..5d16d973 100644
--- a/tracker-pt/pt-settings.hpp
+++ b/tracker-pt/pt-settings.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include "export.hpp"
#include "options/options.hpp"
#include <QString>
@@ -9,19 +8,32 @@ enum pt_color_type
{
// explicit values, gotta preserve the numbering in .ini
// don't reuse when removing some of the modes
- pt_color_natural = 2,
+ pt_color_bt709 = 2,
+ pt_color_hardware = 14,
pt_color_red_only = 3,
- pt_color_average = 5,
pt_color_blue_only = 6,
+ pt_color_green_only = 7,
+ pt_color_red_chromakey = 8,
+ pt_color_green_chromakey = 9,
+ pt_color_blue_chromakey = 10,
+ pt_color_cyan_chromakey = 11,
+ pt_color_yellow_chromakey = 12,
+ pt_color_magenta_chromakey = 13,
};
+namespace pt_impl {
+
+using namespace options;
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
struct pt_settings final : options::opts
{
using slider_value = options::slider_value;
- pt_settings(const QString& name) : opts(name) {}
- ~pt_settings() {}
-
value<QString> camera_name { b, "camera-name", "" };
value<int> cam_res_x { b, "camera-res-width", 640 },
cam_res_y { b, "camera-res-height", 480 },
@@ -48,10 +60,28 @@ struct pt_settings final : options::opts
value<int> fov { b, "camera-fov", 56 };
- value<bool> dynamic_pose { b, "dynamic-pose-resolution", true };
+ value<bool> dynamic_pose { b, "dynamic-pose-resolution", false };
value<int> init_phase_timeout { b, "init-phase-timeout", 250 };
value<bool> auto_threshold { b, "automatic-threshold", true };
- value<pt_color_type> blob_color { b, "blob-color", pt_color_natural };
+ value<pt_color_type> blob_color { b, "blob-color", pt_color_bt709 };
+ value<bool> use_mjpeg { b, "use-mjpeg", false };
+ value<slider_value> chroma_key_strength{ b, "chroma-key-strength", { 1.0, 0.5, 4. } };
+ value<bool> chroma_key_overexposed{ b, "chroma-key-overexposed", false };
+
+ value<slider_value> threshold_slider { b, "threshold-slider", { 128, 0, 255 } };
- value<slider_value> threshold_slider { b, "threshold-slider", slider_value(128, 0, 255) };
+ value<bool> enable_point_filter{ b, "enable-point-filter", false };
+ value<slider_value> point_filter_coefficient { b, "point-filter-coefficient", { 1.0, 0, 4 } };
+ value<slider_value> point_filter_limit { b, "point-filter-limit", { 0.1, 0.01, 1 }};
+ value<slider_value> point_filter_deadzone { b, "point-filter-deadzone", {0, 0, 1} };
+
+ explicit pt_settings(const QString& name) : opts(name) {}
};
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+} // ns pt_impl
+
+using pt_settings = pt_impl::pt_settings;