summaryrefslogtreecommitdiffhomepage
path: root/tracker-pt
diff options
context:
space:
mode:
Diffstat (limited to 'tracker-pt')
-rw-r--r--tracker-pt/CMakeLists.txt14
-rw-r--r--tracker-pt/FTNoIR_PT_Controls.ui889
-rw-r--r--tracker-pt/affine.cpp32
-rw-r--r--tracker-pt/affine.hpp29
-rw-r--r--tracker-pt/camera.cpp161
-rw-r--r--tracker-pt/camera.h86
-rw-r--r--tracker-pt/ftnoir_tracker_pt.cpp273
-rw-r--r--tracker-pt/ftnoir_tracker_pt.h111
-rw-r--r--tracker-pt/ftnoir_tracker_pt_dialog.cpp208
-rw-r--r--tracker-pt/ftnoir_tracker_pt_dialog.h36
-rw-r--r--tracker-pt/ftnoir_tracker_pt_settings.cpp7
-rw-r--r--tracker-pt/ftnoir_tracker_pt_settings.h68
-rw-r--r--tracker-pt/lang/de_DE.ts378
-rw-r--r--tracker-pt/lang/nl_NL.ts266
-rw-r--r--tracker-pt/lang/ru_RU.ts268
-rw-r--r--tracker-pt/lang/stub.ts266
-rw-r--r--tracker-pt/lang/zh_CN.ts377
-rw-r--r--tracker-pt/module/CMakeLists.txt10
-rw-r--r--tracker-pt/module/Resources/Logo_IR.pngbin0 -> 10386 bytes
-rw-r--r--tracker-pt/module/camera.cpp159
-rw-r--r--tracker-pt/module/camera.h53
-rw-r--r--tracker-pt/module/export.hpp11
-rw-r--r--tracker-pt/module/frame.cpp83
-rw-r--r--tracker-pt/module/frame.hpp42
-rw-r--r--tracker-pt/module/lang/de_DE.ts11
-rw-r--r--tracker-pt/module/lang/nl_NL.ts11
-rw-r--r--tracker-pt/module/lang/ru_RU.ts11
-rw-r--r--tracker-pt/module/lang/stub.ts11
-rw-r--r--tracker-pt/module/lang/zh_CN.ts11
-rw-r--r--tracker-pt/module/module.cpp72
-rw-r--r--tracker-pt/module/module.hpp20
-rw-r--r--tracker-pt/module/point_extractor.cpp458
-rw-r--r--tracker-pt/module/point_extractor.h61
-rw-r--r--tracker-pt/module/tracker_pt.qrc5
-rw-r--r--tracker-pt/numeric.hpp22
-rw-r--r--tracker-pt/point-filter.cpp72
-rw-r--r--tracker-pt/point-filter.hpp32
-rw-r--r--tracker-pt/point_extractor.cpp291
-rw-r--r--tracker-pt/point_extractor.h55
-rw-r--r--tracker-pt/point_tracker.cpp185
-rw-r--r--tracker-pt/point_tracker.h54
-rw-r--r--tracker-pt/pt-api.cpp53
-rw-r--r--tracker-pt/pt-api.hpp139
-rw-r--r--tracker-pt/pt-settings.hpp87
-rw-r--r--tracker-pt/tracker_pt_base.qrc (renamed from tracker-pt/ftnoir_tracker_pt.qrc)1
45 files changed, 3913 insertions, 1576 deletions
diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt
index abc7fb33..0c2e9ce3 100644
--- a/tracker-pt/CMakeLists.txt
+++ b/tracker-pt/CMakeLists.txt
@@ -1,6 +1,12 @@
-find_package(OpenCV 3.0 QUIET COMPONENTS ${opencv-modules})
+include(opentrack-opencv)
+find_package(OpenCV QUIET)
if(OpenCV_FOUND)
- otr_module(tracker-pt)
- target_link_libraries(opentrack-tracker-pt opentrack-cv ${OpenCV_LIBS})
- target_include_directories(opentrack-tracker-pt SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS})
+ foreach(k core)
+ otr_install_lib("opencv_${k}" "${opentrack-libexec}")
+ endforeach()
+ otr_module(tracker-pt-base STATIC)
+ 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 718c7df9..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>424</width>
- <height>584</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>
@@ -23,7 +23,7 @@
<string>PointTracker Settings</string>
</property>
<property name="windowIcon">
- <iconset resource="ftnoir_tracker_pt.qrc">
+ <iconset resource="module/tracker_pt.qrc">
<normaloff>:/Resources/Logo_IR.png</normaloff>:/Resources/Logo_IR.png</iconset>
</property>
<property name="layoutDirection">
@@ -32,24 +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="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="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
@@ -57,75 +57,129 @@
<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>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QGroupBox" name="groupBox">
+ <widget class="QGroupBox" name="camera_settings_groupbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="title">
<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="Fixed">
+ <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">
- <property name="suffix">
- <string>°</string>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="prefix">
- <string/>
+ <property name="text">
+ <string>Dynamic pose (for caps only, never clips)</string>
</property>
- <property name="minimum">
- <number>10</number>
+ </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="maximum">
- <number>90</number>
+ <property name="cursor">
+ <cursorShape>WhatsThisCursor</cursorShape>
+ </property>
+ <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="Expanding" vsizetype="Preferred">
+ <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="Expanding" vsizetype="Preferred">
+ <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>
<item row="3" column="0">
<widget class="QLabel" name="label_37">
<property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -138,56 +192,66 @@
</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="Fixed">
+ <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>
- <property name="singleStep">
- <number>10</number>
+ <property name="text">
+ <string>Width</string>
</property>
</widget>
</item>
- <item row="6" column="0">
- <widget class="QLabel" name="label_6">
+ <item row="11" column="0">
+ <widget class="QLabel" name="label_16">
<property name="text">
- <string>Dynamic pose timeout</string>
+ <string>Chroma key includes overexposed pixels</string>
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QSpinBox" name="fps_spin">
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_9">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="toolTip">
- <string>Desired capture framerate</string>
+ <property name="text">
+ <string>Camera settings (when available)</string>
+ </property>
+ </widget>
+ </item>
+ <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="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">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
<string/>
</property>
@@ -196,7 +260,7 @@
<item row="1" column="1">
<widget class="QSpinBox" name="res_x_spin">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -215,10 +279,52 @@
</property>
</widget>
</item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Dynamic pose timeout</string>
+ </property>
+ </widget>
+ </item>
+ <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="minimumContentsLength">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Device</string>
+ </property>
+ <property name="buddy">
+ <cstring>camdevice_combo</cstring>
+ </property>
+ </widget>
+ </item>
<item row="2" column="0">
<widget class="QLabel" name="label_41">
<property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -228,89 +334,200 @@
</property>
</widget>
</item>
- <item row="6" column="1">
- <widget class="QSpinBox" name="init_phase_timeout">
- <property name="suffix">
- <string> ms</string>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="fps_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="minimum">
- <number>50</number>
+ <property name="toolTip">
+ <string>Desired capture framerate</string>
+ </property>
+ <property name="suffix">
+ <string> Hz</string>
</property>
<property name="maximum">
- <number>5000</number>
+ <number>2000</number>
</property>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_5">
+ <item row="9" column="1">
+ <widget class="QComboBox" name="blob_color">
<property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Dynamic pose resolution</string>
- </property>
+ <item>
+ <property name="text">
+ <string>Grayscale BT.709</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Grayscale (from hardware)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Red only</string>
+ </property>
+ </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="0" column="0">
- <widget class="QLabel" name="label_2">
+ <item row="5" column="1">
+ <widget class="QSpinBox" name="fov">
<property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Device</string>
+ <property name="suffix">
+ <string>°</string>
</property>
- <property name="buddy">
- <cstring>camdevice_combo</cstring>
+ <property name="prefix">
+ <string/>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>90</number>
</property>
</widget>
</item>
- <item row="7" column="1">
- <widget class="QPushButton" name="camera_settings">
+ <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="text">
- <string>Open</string>
+ <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="7" column="0">
- <widget class="QLabel" name="label_9">
+ <item row="10" column="0">
+ <widget class="QLabel" name="label_17">
<property name="text">
- <string>Camera settings (when available)</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>
<item>
<widget class="QGroupBox" name="groupBox_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="title">
<string>Point extraction</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
- <item row="3" column="0">
- <widget class="QLabel" name="label_8">
- <property name="text">
- <string>Max size</string>
- </property>
- <property name="buddy">
- <cstring>maxdiam_spin</cstring>
- </property>
- </widget>
- </item>
<item row="1" column="0">
<widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
<string>Threshold</string>
</property>
@@ -319,8 +536,20 @@
</property>
</widget>
</item>
- <item row="2" column="0">
+ <item row="3" column="0">
<widget class="QLabel" name="label_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <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>
@@ -329,10 +558,26 @@
</property>
</widget>
</item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max size</string>
+ </property>
+ <property name="buddy">
+ <cstring>maxdiam_spin</cstring>
+ </property>
+ </widget>
+ </item>
<item row="1" column="1" colspan="2">
<widget class="QSlider" name="threshold_slider">
<property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -360,22 +605,46 @@
</property>
</widget>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_11">
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="auto_threshold">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
- <string>Automatic threshold</string>
+ <string>Enable, slider sets point size</string>
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QCheckBox" name="auto_threshold">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <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>Enable, slider sets point size</string>
+ <string>Automatic threshold</string>
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="4" column="1">
<widget class="QDoubleSpinBox" name="maxdiam_spin">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="toolTip">
<string>Maximum point diameter</string>
</property>
@@ -391,7 +660,33 @@
</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">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="toolTip">
<string>Minimum point diameter</string>
</property>
@@ -409,6 +704,19 @@
</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">
@@ -512,7 +820,7 @@
<string/>
</property>
<property name="pixmap">
- <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/clip_side.png</pixmap>
+ <pixmap resource="tracker_pt_base.qrc">:/Resources/clip_side.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="label_50">
@@ -592,7 +900,7 @@
<string/>
</property>
<property name="pixmap">
- <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/clip_front.png</pixmap>
+ <pixmap resource="tracker_pt_base.qrc">:/Resources/clip_front.png</pixmap>
</property>
</widget>
</widget>
@@ -628,7 +936,7 @@
<string/>
</property>
<property name="pixmap">
- <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/cap_side.png</pixmap>
+ <pixmap resource="tracker_pt_base.qrc">:/Resources/cap_side.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="label_48">
@@ -676,7 +984,7 @@
<string/>
</property>
<property name="pixmap">
- <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/cap_front.png</pixmap>
+ <pixmap resource="tracker_pt_base.qrc">:/Resources/cap_front.png</pixmap>
</property>
</widget>
<widget class="QSpinBox" name="cap_width_spin">
@@ -1110,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">
@@ -1136,7 +1669,7 @@ Don't roll or change position.</string>
<string/>
</property>
<property name="pixmap">
- <pixmap resource="ftnoir_tracker_pt.qrc">:/Resources/Logo_IR.png</pixmap>
+ <pixmap resource="module/tracker_pt.qrc">:/Resources/Logo_IR.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
@@ -1147,7 +1680,7 @@ 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">
@@ -1159,73 +1692,69 @@ Don't roll or change position.</string>
<string>Status</string>
</property>
<layout class="QGridLayout" name="gridLayout_10">
- <item row="1" column="0">
- <widget class="QLabel" name="label_3">
+ <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>Extracted Points:</string>
+ <string/>
</property>
</widget>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_38">
+ <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>Camera Info:</string>
+ <string/>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QLabel" name="pointinfo_label">
- <property name="minimumSize">
- <size>
- <width>50</width>
- <height>0</height>
- </size>
+ <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/>
+ <string>Extracted Points:</string>
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QLabel" name="caminfo_label">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_38">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>120</width>
- <height>0</height>
- </size>
- </property>
<property name="text">
- <string/>
+ <string>Camera Info:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
- <item row="1" column="0">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Expanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="3" column="0">
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
@@ -1239,8 +1768,18 @@ 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>
+ <tabstop>maxdiam_spin</tabstop>
<tabstop>model_tabs</tabstop>
<tabstop>clip_tlength_spin</tabstop>
<tabstop>clip_theight_spin</tabstop>
@@ -1258,9 +1797,27 @@ Don't roll or change position.</string>
<tabstop>tx_spin</tabstop>
<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="ftnoir_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="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/>
<slots>
diff --git a/tracker-pt/affine.cpp b/tracker-pt/affine.cpp
deleted file mode 100644
index 9018e107..00000000
--- a/tracker-pt/affine.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#include "affine.hpp"
-
-Affine::Affine() : R(mat33::eye()), t(0,0,0) {}
-
-Affine::Affine(const mat33& R, const vec3& t) : R(R),t(t) {}
-
-Affine operator*(const Affine& X, const Affine& Y)
-{
- return Affine(X.R*Y.R, X.R*Y.t + X.t);
-}
-
-Affine operator*(const mat33& X, const Affine& Y)
-{
- return Affine(X*Y.R, X*Y.t);
-}
-
-Affine operator*(const Affine& X, const mat33& Y)
-{
- return Affine(X.R*Y, X.t);
-}
-
-vec3 operator*(const Affine& X, const vec3& v)
-{
- return X.R*v + X.t;
-}
diff --git a/tracker-pt/affine.hpp b/tracker-pt/affine.hpp
deleted file mode 100644
index aedb0bc8..00000000
--- a/tracker-pt/affine.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#pragma once
-
-#include <opencv2/core.hpp>
-#include "numeric.hpp"
-using namespace types;
-
-
-
-class Affine final
-{
-public:
- Affine();
- Affine(const mat33& R, const vec3& t);
-
- mat33 R;
- vec3 t;
-};
-
-Affine operator*(const Affine& X, const Affine& Y);
-Affine operator*(const mat33& X, const Affine& Y);
-Affine operator*(const Affine& X, const mat33& Y);
-vec3 operator*(const Affine& X, const vec3& v);
diff --git a/tracker-pt/camera.cpp b/tracker-pt/camera.cpp
deleted file mode 100644
index 6a724fc7..00000000
--- a/tracker-pt/camera.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#include "camera.h"
-#include "compat/sleep.hpp"
-
-constexpr double Camera::dt_eps;
-
-QString Camera::get_desired_name() const
-{
- return desired_name;
-}
-
-QString Camera::get_active_name() const
-{
- return active_name;
-}
-
-void CamInfo::get_focal_length(double& fx) const
-{
- using std::tan;
- using std::atan;
- using std::sqrt;
-
- const double diag_len = sqrt(double(res_x*res_x + res_y*res_y));
- const double 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*atan(tan(diag_fov*.5) * aspect_x);
- //const double fov_y = 2*atan(tan(diag_fov*.5) * aspect_y);
- fx = .5 / tan(fov_x * .5);
- //fy = .5 / tan(fov_y * .5);
- //static bool once = false; if (!once) { once = true; qDebug() << "f" << ret << "fov" << (fov * 180/M_PI); }
-}
-
-DEFUN_WARN_UNUSED Camera::result Camera::get_info() const
-{
- if (cam_info.res_x == 0 || cam_info.res_y == 0)
- return result(false, CamInfo());
- return result(true, cam_info);
-}
-
-DEFUN_WARN_UNUSED Camera::result Camera::get_frame(cv::Mat& frame)
-{
- bool new_frame = _get_frame(frame);
-
- if (new_frame)
- {
- const double dt = t.elapsed_seconds();
- t.start();
-
- // measure fps of valid frames
- static constexpr double RC = .1; // seconds
- const double alpha = dt/(dt + RC);
-
- if (dt_mean < dt_eps)
- dt_mean = dt;
- else
- dt_mean = (1-alpha) * dt_mean + alpha * dt;
-
- cam_info.fps = dt_mean > dt_eps ? 1 / dt_mean : 0;
- cam_info.res_x = frame.cols;
- cam_info.res_y = frame.rows;
- cam_info.fov = fov;
- }
- else
- qDebug() << "pt camera: can't get frame";
-
- return result(new_frame, cam_info);
-}
-
-DEFUN_WARN_UNUSED Camera::open_status Camera::start(int idx, int fps, int res_x, int res_y)
-{
- if (idx >= 0 && fps >= 0 && res_x >= 0 && res_y >= 0)
- {
- if (!cap || !cap->isOpened() ||
- cam_desired.idx != idx ||
- cam_desired.fps != fps ||
- cam_desired.res_x != res_x ||
- cam_desired.res_y != res_y)
- {
- cam_desired.idx = idx;
- cam_desired.fps = fps;
- cam_desired.res_x = res_x;
- cam_desired.res_y = res_y;
- cam_desired.fov = fov;
-
- cap = camera_ptr(new cv::VideoCapture(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())
- {
- qDebug() << "pt: opening camera";
-
- cam_info = CamInfo();
- active_name = QString();
- cam_info.idx = -1;
- dt_mean = 0;
- active_name = desired_name;
- t.start();
-
- return open_ok_change;
- }
- else
- {
- stop();
- return open_error;
- }
- }
- }
- else
- {
- stop();
- return open_error;
- }
-
- return open_ok_no_change;
-}
-
-void Camera::stop()
-{
- cap = nullptr;
- desired_name = QString();
- active_name = QString();
- cam_info = CamInfo();
- cam_desired = CamInfo();
-}
-
-DEFUN_WARN_UNUSED bool Camera::_get_frame(cv::Mat& frame)
-{
- if (cap && cap->isOpened())
- {
- for (int i = 0; i < 5; i++)
- {
- if (cap->read(frame))
- return true;
- portable::sleep(14);
- }
- }
- return false;
-}
-
-void Camera::camera_deleter::operator()(cv::VideoCapture* cap)
-{
- if (cap)
- {
- if (cap->isOpened())
- cap->release();
- delete cap;
- }
-}
diff --git a/tracker-pt/camera.h b/tracker-pt/camera.h
deleted file mode 100644
index 449a2d3f..00000000
--- a/tracker-pt/camera.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#pragma once
-
-#include "compat/ndebug-guard.hpp"
-
-#undef NDEBUG
-#include <cassert>
-
-#include "compat/util.hpp"
-#include "compat/timer.hpp"
-
-#include <opencv2/core/core.hpp>
-#include <opencv2/videoio.hpp>
-
-#include <memory>
-#include <tuple>
-#include <QString>
-
-struct CamInfo final
-{
- CamInfo() : fov(0), fps(0), res_x(0), res_y(0), idx(-1) {}
- void get_focal_length(double& fx) const;
-
- double fov;
- double fps;
-
- int res_x;
- int res_y;
- int idx;
-};
-
-struct Camera final
-{
- enum open_status : unsigned { open_error, open_ok_no_change, open_ok_change };
-
- using result = std::tuple<bool, CamInfo>;
-
- Camera() : dt_mean(0), fov(0) {}
-
- DEFUN_WARN_UNUSED open_status start(int idx, int fps, int res_x, int res_y);
- void stop();
-
- DEFUN_WARN_UNUSED result get_frame(cv::Mat& frame);
- DEFUN_WARN_UNUSED result get_info() const;
-
- CamInfo get_desired() const { return cam_desired; }
- QString get_desired_name() const;
- QString get_active_name() const;
-
- cv::VideoCapture& operator*() { assert(cap); return *cap; }
- const cv::VideoCapture& operator*() const { assert(cap); return *cap; }
- cv::VideoCapture* operator->() { assert(cap); return cap.get(); }
- const cv::VideoCapture* operator->() const { return cap.get(); }
- operator bool() const { return cap && cap->isOpened(); }
-
- void set_fov(double value) { fov = value; }
-
-private:
- DEFUN_WARN_UNUSED bool _get_frame(cv::Mat& frame);
-
- double dt_mean;
- double fov;
-
- Timer t;
-
- CamInfo cam_info;
- CamInfo 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;
-
- static constexpr double dt_eps = 1./384;
-};
diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp
index e35acc0e..0f8495d9 100644
--- a/tracker-pt/ftnoir_tracker_pt.cpp
+++ b/tracker-pt/ftnoir_tracker_pt.cpp
@@ -6,244 +6,221 @@
* copyright notice and this permission notice appear in all copies.
*/
+#undef NDEBUG
#include "ftnoir_tracker_pt.h"
-#include "compat/camera-names.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 <cassert>
#include <QHBoxLayout>
-#include <cmath>
#include <QDebug>
#include <QFile>
#include <QCoreApplication>
-#include <functional>
-//#define PT_PERF_LOG //log performance
+using namespace options;
-//-----------------------------------------------------------------------------
-Tracker_PT::Tracker_PT() :
- point_count(0),
- commands(0),
- ever_success(false)
+namespace pt_impl {
+
+Tracker_PT::Tracker_PT(pointer<pt_runtime_traits> const& traits) :
+ traits { traits },
+ s { traits->get_module_name() },
+ point_extractor { traits->make_point_extractor() },
+ frame { traits->make_frame() }
{
- 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);
+ opencv_init();
+
+ connect(&*s.b, &bundle_::saving, this, [this]{ reopen_camera_flag = true; }, Qt::DirectConnection);
}
Tracker_PT::~Tracker_PT()
{
- set_command(ABORT);
+ requestInterruption();
wait();
- QMutexLocker l(&camera_mtx);
- camera.stop();
+ if (camera)
+ camera->stop();
}
-void Tracker_PT::set_command(Command command)
+bool Tracker_PT::check_camera()
{
- //QMutexLocker lock(&mutex);
- commands |= command;
-}
+ if (reopen_camera_flag)
+ {
+ reopen_camera_flag = false;
-void Tracker_PT::reset_command(Command command)
-{
- //QMutexLocker lock(&mutex);
- commands &= ~command;
+ 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;
}
void Tracker_PT::run()
{
- cv::setNumThreads(0);
+ portable::set_curthread_name("tracker/pt");
-#ifdef PT_PERF_LOG
- QFile log_file(QCoreApplication::applicationDirPath() + "/PointTrackerPerformance.txt");
- if (!log_file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
- QTextStream log_stream(&log_file);
-#endif
-
- maybe_reopen_camera();
-
- while((commands & ABORT) == 0)
+ while(!isInterruptionRequested())
{
- CamInfo cam_info;
- bool new_frame;
+ if (!check_camera())
+ break;
+
+ pt_camera_info info;
+ bool new_frame = false;
{
- QMutexLocker l(&camera_mtx);
- std::tie(new_frame, cam_info) = camera.get_frame(frame);
+ camera->set_fov(s.fov);
+ std::tie(new_frame, info) = camera->get_frame(*frame);
}
if (new_frame)
{
- cv::resize(frame, preview_frame, cv::Size(preview_size.width(), preview_size.height()), 0, 0, cv::INTER_NEAREST);
+ 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);
- f fx;
- cam_info.get_focal_length(fx);
+ 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),
- cam_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);
}
- Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z)); // 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
- vec2 p_(p[0] / p[2] * fx, p[1] / p[2] * fx); // projected to screen
-
- static constexpr int len = 9;
-
- cv::Point p2(iround(p_[0] * preview_frame.cols + preview_frame.cols/2),
- iround(-p_[1] * preview_frame.cols + preview_frame.rows/2));
- static const cv::Scalar color(0, 0, 255);
- cv::line(preview_frame,
- cv::Point(p2.x - len, p2.y),
- cv::Point(p2.x + len, p2.y),
- color,
- 1);
- cv::line(preview_frame,
- cv::Point(p2.x, p2.y - len),
- cv::Point(p2.x, p2.y + len),
- color,
- 1);
- }
- video_widget->update_image(preview_frame);
+ if (p[2] > f(.1))
+ preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]);
+
+ widget->update_image(preview_frame->get_bitmap());
+ }
}
}
- qDebug() << "pt: thread stopped";
}
-void Tracker_PT::maybe_reopen_camera()
+module_status Tracker_PT::start_tracker(QFrame* video_frame)
{
- QMutexLocker l(&camera_mtx);
-
- Camera::open_status status = camera.start(camera_name_to_index(s.camera_name), s.cam_fps, s.cam_res_x, s.cam_res_y);
-
- switch (status)
{
- case Camera::open_error:
- qDebug() << "can't start camera" << s.camera_name;
- break;
- case Camera::open_ok_change:
- frame = cv::Mat();
- break;
- case Camera::open_ok_no_change:
- break;
+ auto camera = traits->make_camera();
+ if (!camera || !camera->start(s))
+ return error(tr("Failed to open camera '%1'").arg(s.camera_name));
}
-}
-void Tracker_PT::set_fov(int value)
-{
- QMutexLocker l(&camera_mtx);
- camera.set_fov(value);
-}
-
-void Tracker_PT::start_tracker(QFrame* video_frame)
-{
- //video_frame->setAttribute(Qt::WA_NativeWindow);
- preview_size = video_frame->size();
-
- preview_frame = cv::Mat(video_frame->height(), video_frame->width(), CV_8UC3);
- preview_frame.setTo(cv::Scalar(0, 0, 0));
-
- video_widget = qptr<cv_video_widget>(video_frame);
- layout = qptr<QHBoxLayout>(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.data());
- video_frame->setLayout(layout.data());
+ layout->addWidget(&*widget);
+ video_frame->setLayout(&*layout);
//video_widget->resize(video_frame->width(), video_frame->height());
video_frame->show();
- start();
+
+ double dpi = screen_dpi(video_frame);
+ preview_frame = traits->make_preview(iround(preview_width * dpi),
+ iround(preview_height * dpi));
+
+ start(QThread::HighPriority);
+
+ return {};
}
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();
-
- using std::atan2;
- using std::sqrt;
- using std::atan;
- using std::fabs;
- using std::copysign;
+ 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;
}
}
-Affine Tracker_PT::pose()
+bool Tracker_PT::center()
{
- QMutexLocker l(&data_mtx);
+ QMutexLocker l(&center_lock);
- return point_tracker.pose();
+ point_tracker.reset_state();
+ return false;
}
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(CamInfo* 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;
}
-#include "ftnoir_tracker_pt_dialog.h"
-OPENTRACK_DECLARE_TRACKER(Tracker_PT, TrackerDialog_PT, PT_metadata)
+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 b7b0019a..a793f94b 100644
--- a/tracker-pt/ftnoir_tracker_pt.h
+++ b/tracker-pt/ftnoir_tracker_pt.h
@@ -9,90 +9,71 @@
#pragma once
#include "api/plugin-api.hpp"
-#include "ftnoir_tracker_pt_settings.h"
-
-#include "numeric.hpp"
-
-#include "camera.h"
-#include "point_extractor.h"
+#include "pt-api.hpp"
#include "point_tracker.h"
-#include "cv/video-widget.hpp"
-#include "compat/util.hpp"
+#include "cv/numeric.hpp"
+#include "video/video-widget.hpp"
+#include "point-filter.hpp"
-#include <QCoreApplication>
-#include <QThread>
-#include <QMutex>
-#include <QMutexLocker>
-#include <QTime>
-#include <QLayout>
-#include <QSize>
#include <atomic>
#include <memory>
#include <vector>
-class TrackerDialog_PT;
+#include <QThread>
+#include <QMutex>
+#include <QLayout>
-namespace impl {
+namespace pt_impl {
-using namespace types;
+class TrackerDialog_PT;
+
+using namespace numeric_types;
-class Tracker_PT : public QThread, public ITracker
+struct Tracker_PT : QThread, ITracker
{
- Q_OBJECT
- friend class camera_dialog;
- friend class ::TrackerDialog_PT;
-public:
- Tracker_PT();
+ friend class TrackerDialog_PT;
+
+ template<typename t> using pointer = pt_pointer<t>;
+
+ explicit Tracker_PT(pointer<pt_runtime_traits> const& pt_runtime_traits);
~Tracker_PT() override;
- void start_tracker(QFrame* parent_window) 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(CamInfo* info);
-public slots:
- void 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:
- // thread commands
- enum Command : unsigned char
- {
- ABORT = 1<<0
- };
- void set_command(Command command);
- void reset_command(Command command);
-
- QMutex camera_mtx;
- QMutex data_mtx;
- Camera camera;
- PointExtractor point_extractor;
- PointTracker point_tracker;
-
- qshared<cv_video_widget> video_widget;
- qshared<QLayout> layout;
-
- settings_pt s;
- cv::Mat frame, preview_frame;
- std::vector<vec2> points;
+ void run() override;
+ [[nodiscard]] bool check_camera();
- QSize preview_size;
+ pointer<pt_runtime_traits> traits;
- volatile unsigned point_count;
- volatile unsigned char commands;
- volatile bool ever_success;
+ PointTracker point_tracker;
- static constexpr f rad2deg = f(180/M_PI);
- //static constexpr float deg2rad = float(M_PI/180);
-};
+ pt_settings s;
-} // ns impl
+ std::unique_ptr<QLayout> layout;
+ std::vector<vec2> points;
-class PT_metadata : public Metadata
-{
- QString name() { return QString(QCoreApplication::translate("PT_metadata", "PointTracker 1.1")); }
- QIcon icon() { return QIcon(":/Resources/Logo_IR.png"); }
+ int preview_width = 320, preview_height = 240;
+
+ pointer<pt_point_extractor> point_extractor;
+ pointer<pt_camera> camera;
+ pointer<video_widget> widget;
+ pointer<pt_frame> frame;
+ pointer<pt_preview> preview_frame;
+
+ std::atomic<unsigned> point_count { 0 };
+ std::atomic<bool> ever_success = false;
+ 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};
};
-using impl::Tracker_PT;
+} // ns pt_impl
+
+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 98e8e424..d67f79a7 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp
@@ -7,28 +7,39 @@
*/
#include "ftnoir_tracker_pt_dialog.h"
-#include "cv/video-property-page.hpp"
+#include "compat/math.hpp"
+#include "video/camera.hpp"
-#include "compat/camera-names.hpp"
-#include <opencv2/core.hpp>
#include <QString>
+#include <QtGlobal>
#include <QDebug>
-TrackerDialog_PT::TrackerDialog_PT()
- : tracker(nullptr),
- timer(this),
- trans_calib(1, 2, 0)
+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)
{
+ 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, ui.threshold_slider);
+ tie_setting(s.threshold_slider, ui.threshold_slider);
tie_setting(s.min_point_size, ui.mindiam_spin);
tie_setting(s.max_point_size, ui.maxdiam_spin);
@@ -63,7 +74,7 @@ TrackerDialog_PT::TrackerDialog_PT()
tie_setting(s.auto_threshold, ui.auto_threshold);
- connect( ui.tcalib_button,SIGNAL(toggled(bool)), this,SLOT(startstop_trans_calib(bool)));
+ connect(ui.tcalib_button,SIGNAL(toggled(bool)), this, SLOT(startstop_trans_calib(bool)));
connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
@@ -76,15 +87,94 @@ TrackerDialog_PT::TrackerDialog_PT()
timer.setInterval(250);
connect(&calib_timer, &QTimer::timeout, this, &TrackerDialog_PT::trans_calib_step);
- calib_timer.setInterval(100);
+ calib_timer.setInterval(35);
poll_tracker_info_impl();
- connect(this, &TrackerDialog_PT::poll_tracker_info, this, &TrackerDialog_PT::poll_tracker_info_impl, Qt::QueuedConnection);
+ constexpr pt_color_type color_types[] = {
+ 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++)
+ ui.blob_color->setItemData(k, int(color_types[k]));
+
+ 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.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)
+{
+ if (!s.auto_threshold)
+ return tr("Brightness %1/255").arg(threshold_value);
+ else
+ {
+ pt_camera_info info;
+ int w = s.cam_res_x, h = s.cam_res_y;
+
+ if (w * h <= 0)
+ {
+ 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;
+ }
+
+ double value = (double)pt_point_extractor::threshold_radius_value(w, h, threshold_value);
+
+ return tr("LED radius %1 pixels").arg(value, 0, 'f', 2);
+ }
}
void TrackerDialog_PT::startstop_trans_calib(bool start)
{
+ QMutexLocker l(&calibrator_mutex);
+
if (start)
{
qDebug() << "pt: starting translation calibration";
@@ -101,17 +191,28 @@ void TrackerDialog_PT::startstop_trans_calib(bool start)
calib_timer.stop();
qDebug() << "pt: stopping translation calibration";
{
- cv::Vec3f tmp;
- unsigned 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]);
- static constexpr unsigned min_samples = 80;
- const QString sample_feedback = nsamples >= min_samples
- ? tr("%1 samples. Over %2, good!").arg(nsamples).arg(min_samples)
- : tr("%1 samples. Try for at least %2 for a precise calibration.").arg(nsamples).arg(min_samples);
+ constexpr int min_yaw_samples = 15;
+ constexpr int min_pitch_samples = 15;
+ constexpr int min_samples = min_yaw_samples+min_pitch_samples;
+
+ // Don't bother counting roll samples. Roll calibration is hard enough
+ // that it's a hidden unsupported feature anyway.
+
+ 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];
+ sample_feedback = tr("%1 samples. Over %2, good!").arg(nsamples_total).arg(min_samples);
+ }
ui.sample_count_display->setText(sample_feedback);
}
@@ -119,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()
{
- CamInfo info;
- if (tracker && tracker->get_cam_info(&info))
+ pt_camera_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)));
@@ -145,35 +245,37 @@ void TrackerDialog_PT::poll_tracker_info_impl()
}
}
-void TrackerDialog_PT::set_camera_settings_available(const QString& camera_name)
+void TrackerDialog_PT::set_camera_settings_available(const QString& /* camera_name */)
{
- const bool avail = video_property_page::should_show_dialog(camera_name);
- ui.camera_settings->setEnabled(avail);
+ ui.camera_settings->setEnabled(true);
}
void TrackerDialog_PT::show_camera_settings()
{
- const int idx = ui.camdevice_combo->currentIndex();
-
if (tracker)
- {
- if (tracker->camera)
- {
- cv::VideoCapture& cap = *tracker->camera;
+ tracker->open_camera_dialog_flag = true;
+ else
+ (void)video::show_dialog(s.camera_name);
+}
- CamInfo info;
- bool status;
- std::tie(status, info) = tracker->camera.get_info();
- if (status)
- video_property_page::show_from_capture(cap, info.idx);
- }
+void TrackerDialog_PT::chroma_key_controls_enable(const QString&)
+{
+ bool enabled = false;
+ QVariant data = ui.blob_color->currentData();
+ if (data.isValid())
+ {
+ pt_color_type blob_color = pt_color_type(data.toInt());
+ enabled = blob_color >= pt_color_red_chromakey && blob_color <= pt_color_magenta_chromakey;
}
- else
- video_property_page::show(idx);
+ 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()
{
+ QMutexLocker l(&calibrator_mutex);
+
if (tracker)
{
Affine X_CM = tracker->pose();
@@ -203,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 2932252c..79cd91bd 100644
--- a/tracker-pt/ftnoir_tracker_pt_dialog.h
+++ b/tracker-pt/ftnoir_tracker_pt_dialog.h
@@ -5,26 +5,31 @@
* copyright notice and this permission notice appear in all copies.
*/
-#ifndef FTNOIR_TRACKER_PT_DIALOG_H
-#define FTNOIR_TRACKER_PT_DIALOG_H
+#pragma once
+
+#include "pt-api.hpp"
-#include "api/plugin-api.hpp"
-#include "ftnoir_tracker_pt_settings.h"
#include "ftnoir_tracker_pt.h"
-#include "ui_FTNoIR_PT_Controls.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
public:
- TrackerDialog_PT();
+ 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,15 +39,20 @@ 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();
-private:
- settings_pt s;
+ void chroma_key_controls_enable(const QString&);
+
+protected:
+ QString threshold_display_text(int threshold_value);
+
+ pt_settings s;
Tracker_PT* tracker;
QTimer timer, calib_timer;
TranslationCalibrator trans_calib;
+ QMutex calibrator_mutex;
Ui::UICPTClientControls ui;
};
-#endif //FTNOIR_TRACKER_PT_DIALOG_H
+} // ns pt_impl
+
+using TrackerDialog_PT = pt_impl::TrackerDialog_PT;
diff --git a/tracker-pt/ftnoir_tracker_pt_settings.cpp b/tracker-pt/ftnoir_tracker_pt_settings.cpp
deleted file mode 100644
index 8a5257d3..00000000
--- a/tracker-pt/ftnoir_tracker_pt_settings.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "numeric.hpp"
-
-namespace types {
-
-constexpr f constants::eps;
-
-}
diff --git a/tracker-pt/ftnoir_tracker_pt_settings.h b/tracker-pt/ftnoir_tracker_pt_settings.h
deleted file mode 100644
index d890bbd2..00000000
--- a/tracker-pt/ftnoir_tracker_pt_settings.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- * Copyright (c) 2014-2015 Stanislaw Halik <sthalik@misaki.pl>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#pragma once
-
-#include "options/options.hpp"
-using namespace options;
-
-struct settings_pt : opts
-{
- value<QString> camera_name;
- value<int> cam_res_x,
- cam_res_y,
- cam_fps,
- threshold;
- value<double> min_point_size, max_point_size;
-
- value<int> m01_x, m01_y, m01_z;
- value<int> m02_x, m02_y, m02_z;
-
- value<int> t_MH_x, t_MH_y, t_MH_z;
-
- value<int> clip_ty, clip_tz, clip_by, clip_bz;
- value<int> active_model_panel, cap_x, cap_y, cap_z;
-
- value<int> fov;
-
- value<bool> dynamic_pose;
- value<int> init_phase_timeout;
- value<bool> auto_threshold;
-
- settings_pt() :
- opts("tracker-pt"),
- camera_name(b, "camera-name", ""),
- cam_res_x(b, "camera-res-width", 640),
- cam_res_y(b, "camera-res-height", 480),
- cam_fps(b, "camera-fps", 30),
- threshold(b, "threshold-primary", 128),
- min_point_size(b, "min-point-size", 1),
- max_point_size(b, "max-point-size", 50),
- m01_x(b, "m_01-x", 0),
- m01_y(b, "m_01-y", 0),
- m01_z(b, "m_01-z", 0),
- m02_x(b, "m_02-x", 0),
- m02_y(b, "m_02-y", 0),
- m02_z(b, "m_02-z", 0),
- t_MH_x(b, "model-centroid-x", 0),
- t_MH_y(b, "model-centroid-y", 0),
- t_MH_z(b, "model-centroid-z", 0),
- clip_ty(b, "clip-ty", 40),
- clip_tz(b, "clip-tz", 30),
- clip_by(b, "clip-by", 70),
- clip_bz(b, "clip-bz", 80),
- active_model_panel(b, "active-model-panel", 0),
- cap_x(b, "cap-x", 40),
- cap_y(b, "cap-y", 60),
- cap_z(b, "cap-z", 100),
- fov(b, "camera-fov", 56),
- dynamic_pose(b, "dynamic-pose-resolution", true),
- init_phase_timeout(b, "init-phase-timeout", 500),
- auto_threshold(b, "automatic-threshold", true)
- {}
-};
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 a81f94e2..fc44b0f1 100644
--- a/tracker-pt/lang/nl_NL.ts
+++ b/tracker-pt/lang/nl_NL.ts
@@ -2,292 +2,376 @@
<!DOCTYPE TS>
<TS version="2.1" language="nl_NL">
<context>
- <name>PT_metadata</name>
- <message>
- <location filename="../ftnoir_tracker_pt.h" line="+95"/>
- <source>PointTracker 1.1</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
- <location filename="../FTNoIR_PT_Controls.ui" line="+23"/>
<source>PointTracker Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
<source>Camera</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<source>Camera settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
<source>°</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
<source>Diagonal field of view</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>Width</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>FPS</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Desired capture height</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <location line="+55"/>
- <location line="+175"/>
- <location line="+16"/>
<source> px</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-233"/>
<source>Dynamic pose timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>Desired capture framerate</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
<source> Hz</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
<source>Desired capture width</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
<source>Height</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<source> ms</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
- <source>Dynamic pose resolution</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+13"/>
<source>Device</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Open</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<source>Camera settings (when available)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
<source>Point extraction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<source>Max size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
<source>Threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
<source>Min size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Intensity threshold for point extraction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
<source>Automatic threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<source>Enable, slider sets point size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <source>Color channels used</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic pose (for caps only, never clips)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Maximum point diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Minimum point diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <source>Value</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Model</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+28"/>
<source>Clip</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <location line="+154"/>
- <location line="+129"/>
<source>Model Dimensions</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-271"/>
- <location line="+19"/>
- <location line="+48"/>
- <location line="+19"/>
- <location line="+97"/>
- <location line="+35"/>
- <location line="+32"/>
- <location line="+40"/>
- <location line="+26"/>
- <location line="+13"/>
- <location line="+13"/>
- <location line="+13"/>
- <location line="+26"/>
- <location line="+132"/>
- <location line="+26"/>
- <location line="+26"/>
<source> mm</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-511"/>
- <location line="+116"/>
<source>Side</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-65"/>
- <location line="+132"/>
<source>Front</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-107"/>
<source>Cap</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+135"/>
<source>Custom</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <location line="+169"/>
- <location line="+106"/>
<source>z:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-249"/>
- <location line="+104"/>
- <location line="+93"/>
<source>x:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-132"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
- <location line="+65"/>
- <location line="+67"/>
<source>y:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-106"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+46"/>
<source>Model position</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+105"/>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/model-calibration-for-PT-and-Aruco-trackers&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Instructions on the opentrack wiki&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>Use only yaw and pitch while calibrating.
+Don&apos;t roll or change position.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>Start calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
<source>Status</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<source>Extracted Points:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<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::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/tracker-pt/lang/ru_RU.ts b/tracker-pt/lang/ru_RU.ts
index f6094d27..7ff4657e 100644
--- a/tracker-pt/lang/ru_RU.ts
+++ b/tracker-pt/lang/ru_RU.ts
@@ -2,293 +2,381 @@
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
- <name>PT_metadata</name>
- <message>
- <location filename="../ftnoir_tracker_pt.h" line="+92"/>
- <source>PointTracker 1.1</source>
- <translation></translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
- <location filename="../FTNoIR_PT_Controls.ui" line="+23"/>
<source>PointTracker Settings</source>
<translation>Настройки PointTracker</translation>
</message>
<message>
- <location line="+38"/>
<source>Camera</source>
<translation>Камера</translation>
</message>
<message>
- <location line="+6"/>
<source>Camera settings</source>
<translation>Настройка камеры</translation>
</message>
<message>
- <location line="+19"/>
<source>°</source>
<translation></translation>
</message>
<message>
- <location line="+22"/>
<source>Diagonal field of view</source>
<translation>Угол обзора камеры</translation>
</message>
<message>
- <location line="+13"/>
<source>Width</source>
<translation>Ширина</translation>
</message>
<message>
- <location line="+13"/>
<source>FPS</source>
<translation>FPS (Кадров в секунду)</translation>
</message>
<message>
- <location line="+16"/>
<source>Desired capture height</source>
<translation></translation>
</message>
<message>
- <location line="+3"/>
- <location line="+55"/>
- <location line="+175"/>
- <location line="+16"/>
<source> px</source>
<translation></translation>
</message>
<message>
- <location line="-233"/>
<source>Dynamic pose timeout</source>
<translation>Динамическая поза (время ожидания)</translation>
</message>
<message>
- <location line="+13"/>
<source>Desired capture framerate</source>
<translation>Желаемая частота кадров</translation>
</message>
<message>
- <location line="+3"/>
<source> Hz</source>
<translation> Гц</translation>
</message>
<message>
- <location line="+23"/>
<source>Desired capture width</source>
<translation>Желаемая ширина захвата</translation>
</message>
<message>
- <location line="+22"/>
<source>Height</source>
<translation>Высота</translation>
</message>
<message>
- <location line="+7"/>
<source> ms</source>
<translation> мс</translation>
</message>
<message>
- <location line="+19"/>
- <source>Dynamic pose resolution</source>
- <translation>Динамическая поза (активация) </translation>
+ <source>Dynamic pose (for caps only, never clips)</source>
+ <translation>Динамическая поза (Только для модели &quot;Кепка&quot;)</translation>
</message>
<message>
- <location line="+13"/>
<source>Device</source>
<translation>Устройство</translation>
</message>
<message>
- <location line="+16"/>
<source>Open</source>
<translation>Открыть</translation>
</message>
<message>
- <location line="+7"/>
<source>Camera settings (when available)</source>
<translation>Параметры камеры (если доступно)</translation>
</message>
<message>
- <location line="+10"/>
<source>Point extraction</source>
<translation>Извлечение точек</translation>
</message>
<message>
- <location line="+6"/>
<source>Max size</source>
<translation>Макс.размер</translation>
</message>
<message>
- <location line="+10"/>
<source>Threshold</source>
<translation>Порог</translation>
</message>
<message>
- <location line="+10"/>
<source>Min size</source>
<translation>Мин.размер</translation>
</message>
<message>
- <location line="+16"/>
<source>Intensity threshold for point extraction</source>
<translation>Порог интенсивности для извлечения точки</translation>
</message>
<message>
- <location line="+25"/>
<source>Automatic threshold</source>
<translation>Автоматич. порог</translation>
</message>
<message>
- <location line="+7"/>
<source>Enable, slider sets point size</source>
<translation>Полузнок устанавливает размер точек</translation>
</message>
<message>
- <location line="+7"/>
+ <source>Color channels used</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Maximum point diameter</source>
<translation></translation>
</message>
<message>
- <location line="+16"/>
<source>Minimum point diameter</source>
<translation></translation>
</message>
<message>
- <location line="+20"/>
+ <source>Value</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Model</source>
<translation>Модель</translation>
</message>
<message>
- <location line="+28"/>
<source>Clip</source>
<translation>Клипса</translation>
</message>
<message>
- <location line="+18"/>
- <location line="+154"/>
- <location line="+129"/>
<source>Model Dimensions</source>
<translation>Размеры модели</translation>
</message>
<message>
- <location line="-271"/>
- <location line="+19"/>
- <location line="+48"/>
- <location line="+19"/>
- <location line="+97"/>
- <location line="+35"/>
- <location line="+32"/>
- <location line="+40"/>
- <location line="+26"/>
- <location line="+13"/>
- <location line="+13"/>
- <location line="+13"/>
- <location line="+26"/>
- <location line="+132"/>
- <location line="+26"/>
- <location line="+26"/>
<source> mm</source>
<translation> мм</translation>
</message>
<message>
- <location line="-511"/>
- <location line="+116"/>
<source>Side</source>
<translation>Сбоку</translation>
</message>
<message>
- <location line="-65"/>
- <location line="+132"/>
<source>Front</source>
<translation>Спереди</translation>
</message>
<message>
- <location line="-107"/>
<source>Cap</source>
<translation>Кепка</translation>
</message>
<message>
- <location line="+135"/>
<source>Custom</source>
<translation>Свой</translation>
</message>
<message>
- <location line="+18"/>
- <location line="+169"/>
- <location line="+106"/>
<source>z:</source>
<translation></translation>
</message>
<message>
- <location line="-249"/>
- <location line="+104"/>
- <location line="+93"/>
<source>x:</source>
<translation></translation>
</message>
<message>
- <location line="-132"/>
<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>
<translatorcomment>Расположение двух оставшихся точек модели относительно опорной точки в стандартной позе. Возможно исп-ть любые единицы измерения, не обязательно сантиметры.</translatorcomment>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt; Расположение двух оставшихся точек модели&lt;br/&gt;относительно опорной точки в стандартной позе. &lt;/p&gt;&lt;p&gt;Возможно использовать любые единицы измерения.&lt;/p&gt;&lt;/body&gt;&lt;/html</translation>
</message>
<message>
- <location line="+26"/>
- <location line="+65"/>
- <location line="+67"/>
<source>y:</source>
<translation></translation>
</message>
<message>
- <location line="-106"/>
<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></translation>
</message>
<message>
- <location line="+13"/>
<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></translation>
</message>
<message>
- <location line="+46"/>
<source>Model position</source>
<translation>Положение модели</translation>
</message>
<message>
- <location line="+105"/>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/model-calibration-for-PT-and-Aruco-trackers&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Instructions on the opentrack wiki&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;a href=&quot;https://github.com/opentrack/opentrack/wiki/model-calibration-for-PT-and-Aruco-trackers&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Инструкция на opentrack-вики&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ <source>Use only yaw and pitch while calibrating.
+Don&apos;t roll or change position.</source>
+ <translation>Во время калибровки
+используйте только оси
+YAW и PITCH.
+Не используйте оси
+ROLL или X/Y-смещения.</translation>
</message>
<message>
- <location line="+13"/>
<source>Start calibration</source>
<translation>Начать калибровку</translation>
</message>
<message>
- <location line="+17"/>
<source>About</source>
<translation>О программе</translation>
</message>
<message>
- <location line="+6"/>
<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;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;Руководство (PointTracker)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
- <location line="+36"/>
<source>Status</source>
<translation>Статус</translation>
</message>
<message>
- <location line="+6"/>
<source>Extracted Points:</source>
<translation>Извлечено точек:</translation>
</message>
<message>
- <location line="+7"/>
<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::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/tracker-pt/lang/stub.ts b/tracker-pt/lang/stub.ts
index c81750dc..3dbe208d 100644
--- a/tracker-pt/lang/stub.ts
+++ b/tracker-pt/lang/stub.ts
@@ -2,292 +2,376 @@
<!DOCTYPE TS>
<TS version="2.1">
<context>
- <name>PT_metadata</name>
- <message>
- <location filename="../ftnoir_tracker_pt.h" line="+95"/>
- <source>PointTracker 1.1</source>
- <translation type="unfinished"></translation>
- </message>
-</context>
-<context>
<name>UICPTClientControls</name>
<message>
- <location filename="../FTNoIR_PT_Controls.ui" line="+23"/>
<source>PointTracker Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
<source>Camera</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<source>Camera settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
<source>°</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
<source>Diagonal field of view</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>Width</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>FPS</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Desired capture height</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <location line="+55"/>
- <location line="+175"/>
- <location line="+16"/>
<source> px</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-233"/>
<source>Dynamic pose timeout</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>Desired capture framerate</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
<source> Hz</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
<source>Desired capture width</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
<source>Height</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<source> ms</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
- <source>Dynamic pose resolution</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+13"/>
<source>Device</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Open</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<source>Camera settings (when available)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
<source>Point extraction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<source>Max size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
<source>Threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
<source>Min size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Intensity threshold for point extraction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
<source>Automatic threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<source>Enable, slider sets point size</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <source>Color channels used</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic pose (for caps only, never clips)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Maximum point diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
<source>Minimum point diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <source>Value</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>Model</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+28"/>
<source>Clip</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <location line="+154"/>
- <location line="+129"/>
<source>Model Dimensions</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-271"/>
- <location line="+19"/>
- <location line="+48"/>
- <location line="+19"/>
- <location line="+97"/>
- <location line="+35"/>
- <location line="+32"/>
- <location line="+40"/>
- <location line="+26"/>
- <location line="+13"/>
- <location line="+13"/>
- <location line="+13"/>
- <location line="+26"/>
- <location line="+132"/>
- <location line="+26"/>
- <location line="+26"/>
<source> mm</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-511"/>
- <location line="+116"/>
<source>Side</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-65"/>
- <location line="+132"/>
<source>Front</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-107"/>
<source>Cap</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+135"/>
<source>Custom</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <location line="+169"/>
- <location line="+106"/>
<source>z:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-249"/>
- <location line="+104"/>
- <location line="+93"/>
<source>x:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-132"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
- <location line="+65"/>
- <location line="+67"/>
<source>y:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-106"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+46"/>
<source>Model position</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+105"/>
- <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/model-calibration-for-PT-and-Aruco-trackers&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Instructions on the opentrack wiki&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <source>Use only yaw and pitch while calibrating.
+Don&apos;t roll or change position.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
<source>Start calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<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 type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
<source>Status</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
<source>Extracted Points:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<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::metadata_pt</name>
+ <message>
+ <source>PointTracker 1.1</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/tracker-pt/lang/zh_CN.ts b/tracker-pt/lang/zh_CN.ts
new file mode 100644
index 00000000..3519d719
--- /dev/null
+++ b/tracker-pt/lang/zh_CN.ts
@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>UICPTClientControls</name>
+ <message>
+ <source>PointTracker Settings</source>
+ <translation>PointTracker设置</translation>
+ </message>
+ <message>
+ <source>Camera</source>
+ <translation>摄像头</translation>
+ </message>
+ <message>
+ <source>Camera settings</source>
+ <translation>摄像头设置</translation>
+ </message>
+ <message>
+ <source>°</source>
+ <translation>度</translation>
+ </message>
+ <message>
+ <source>Diagonal field of view</source>
+ <translation>对角线</translation>
+ </message>
+ <message>
+ <source>Width</source>
+ <translation>宽度</translation>
+ </message>
+ <message>
+ <source>FPS</source>
+ <translation>帧数</translation>
+ </message>
+ <message>
+ <source>Desired capture height</source>
+ <translation>期望高度</translation>
+ </message>
+ <message>
+ <source> px</source>
+ <translation> 像素点</translation>
+ </message>
+ <message>
+ <source>Dynamic pose timeout</source>
+ <translation>动态姿态超时时间</translation>
+ </message>
+ <message>
+ <source>Desired capture framerate</source>
+ <translation>期望帧数</translation>
+ </message>
+ <message>
+ <source> Hz</source>
+ <translation> 赫兹</translation>
+ </message>
+ <message>
+ <source>Desired capture width</source>
+ <translation>期望宽度</translation>
+ </message>
+ <message>
+ <source>Height</source>
+ <translation>高度</translation>
+ </message>
+ <message>
+ <source> ms</source>
+ <translation> 毫秒</translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation>设备名称</translation>
+ </message>
+ <message>
+ <source>Open</source>
+ <translation>打开</translation>
+ </message>
+ <message>
+ <source>Camera settings (when available)</source>
+ <translation>摄像头设置 (连接时)</translation>
+ </message>
+ <message>
+ <source>Point extraction</source>
+ <translation>跟踪点解析</translation>
+ </message>
+ <message>
+ <source>Max size</source>
+ <translation>最大</translation>
+ </message>
+ <message>
+ <source>Threshold</source>
+ <translation>大小门限值</translation>
+ </message>
+ <message>
+ <source>Min size</source>
+ <translation>最小</translation>
+ </message>
+ <message>
+ <source>Intensity threshold for point extraction</source>
+ <translation>点密度</translation>
+ </message>
+ <message>
+ <source>Automatic threshold</source>
+ <translation>自动门限值</translation>
+ </message>
+ <message>
+ <source>Enable, slider sets point size</source>
+ <translation>激活,滑动,设置跟踪点大小</translation>
+ </message>
+ <message>
+ <source>Maximum point diameter</source>
+ <translation>最大点直径</translation>
+ </message>
+ <message>
+ <source>Minimum point diameter</source>
+ <translation>最小点直径</translation>
+ </message>
+ <message>
+ <source>Model</source>
+ <translation>点模式</translation>
+ </message>
+ <message>
+ <source>Clip</source>
+ <translation>夹子式</translation>
+ </message>
+ <message>
+ <source>Model Dimensions</source>
+ <translation>尺寸</translation>
+ </message>
+ <message>
+ <source> mm</source>
+ <translation> 毫米</translation>
+ </message>
+ <message>
+ <source>Side</source>
+ <translation>侧面</translation>
+ </message>
+ <message>
+ <source>Front</source>
+ <translation>正面</translation>
+ </message>
+ <message>
+ <source>Cap</source>
+ <translation>帽子式</translation>
+ </message>
+ <message>
+ <source>Custom</source>
+ <translation>自定义模式</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;三点中的两点位置是相对第一个点的&lt;/p&gt;&lt;p&gt;单位不一定要用厘米&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>姿态空间位置</translation>
+ </message>
+ <message>
+ <source>Start calibration</source>
+ <translation>开始校准</translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation>关于</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;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;参考手册 (外部链接)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation>状态</translation>
+ </message>
+ <message>
+ <source>Extracted Points:</source>
+ <translation>解析出的点:</translation>
+ </message>
+ <message>
+ <source>Camera Info:</source>
+ <translation>设备信息:</translation>
+ </message>
+ <message>
+ <source>Color channels used</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Red only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Blue only</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Dynamic pose (for caps only, never clips)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Value</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Use only yaw and pitch while calibrating.
+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_impl::Tracker_PT</name>
+ <message>
+ <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
new file mode 100644
index 00000000..b7fc974f
--- /dev/null
+++ b/tracker-pt/module/CMakeLists.txt
@@ -0,0 +1,10 @@
+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(${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/Resources/Logo_IR.png b/tracker-pt/module/Resources/Logo_IR.png
new file mode 100644
index 00000000..95032a25
--- /dev/null
+++ b/tracker-pt/module/Resources/Logo_IR.png
Binary files differ
diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp
new file mode 100644
index 00000000..1beba474
--- /dev/null
+++ b/tracker-pt/module/camera.cpp
@@ -0,0 +1,159 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include "camera.h"
+#include "frame.hpp"
+#include <opencv2/core/mat.hpp>
+
+namespace pt_module {
+
+Camera::Camera(const QString& module_name) : s { module_name }
+{
+}
+
+QString Camera::get_desired_name() const
+{
+ return cam_desired.name;
+}
+
+QString Camera::get_active_name() const
+{
+ return cam_info.name;
+}
+
+void Camera::show_camera_settings()
+{
+ if (cap)
+ (void)cap->show_dialog();
+}
+
+Camera::result Camera::get_info() const
+{
+ if (cam_info.res_x == 0 || cam_info.res_y == 0)
+ return { false, pt_camera_info() };
+ else
+ return { true, cam_info };
+}
+
+Camera::result Camera::get_frame(pt_frame& frame_)
+{
+ cv::Mat& frame = frame_.as<Frame>()->mat;
+
+ const bool new_frame = get_frame_(frame);
+
+ if (new_frame)
+ {
+ const f dt = (f)t.elapsed_seconds();
+ t.start();
+
+ // measure fps of valid frames
+ constexpr f RC = f{1}/10; // seconds
+ const f alpha = dt/(dt + RC);
+
+ if (dt_mean < dt_eps)
+ dt_mean = dt;
+ else
+ dt_mean = (1-alpha) * dt_mean + alpha * dt;
+
+ cam_info.fps = dt_mean > dt_eps ? 1 / dt_mean : 0;
+ cam_info.res_x = frame.cols;
+ cam_info.res_y = frame.rows;
+ cam_info.fov = fov;
+
+ return { true, cam_info };
+ }
+ else
+ return { false, {} };
+}
+
+bool Camera::start(const pt_settings& s)
+{
+ 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.name != name ||
+ (int)cam_desired.fps != fps ||
+ cam_desired.res_x != res_x ||
+ cam_desired.res_y != res_y ||
+ cam_desired.use_mjpeg != use_mjpeg ||
+ !cap || !cap->is_open())
+ {
+ stop();
+
+ 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 = 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;
+
+ 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;
+}
+
+void Camera::stop()
+{
+ cap = nullptr;
+ cam_info = {};
+ cam_desired = {};
+}
+
+bool Camera::get_frame_(cv::Mat& img)
+{
+ if (cap && cap->is_open())
+ {
+ auto [ frame, ret ] = cap->get_frame();
+ if (ret)
+ {
+ 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;
+}
+
+} // ns pt_module
diff --git a/tracker-pt/module/camera.h b/tracker-pt/module/camera.h
new file mode 100644
index 00000000..e4772178
--- /dev/null
+++ b/tracker-pt/module/camera.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#pragma once
+
+#include "pt-api.hpp"
+#include "compat/timer.hpp"
+#include "video/camera.hpp"
+
+#include <memory>
+
+#include <QString>
+
+namespace pt_module {
+
+struct Camera final : pt_camera
+{
+ Camera(const QString& module_name);
+
+ bool start(const pt_settings& s) override;
+ void stop() override;
+
+ result get_frame(pt_frame& Frame) override;
+ result get_info() const override;
+
+ pt_camera_info get_desired() const override { return cam_desired; }
+ QString get_desired_name() const override;
+ QString get_active_name() const override;
+
+ void set_fov(f value) override { fov = value; }
+ void show_camera_settings() override;
+
+private:
+ using camera = video::impl::camera;
+
+ [[nodiscard]] bool get_frame_(cv::Mat& frame);
+
+ f dt_mean = 0, fov = 30;
+ Timer t;
+ pt_camera_info cam_info;
+ pt_camera_info cam_desired;
+
+ std::unique_ptr<camera> cap;
+ pt_settings s;
+
+ 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
new file mode 100644
index 00000000..1a276f16
--- /dev/null
+++ b/tracker-pt/module/frame.cpp
@@ -0,0 +1,83 @@
+#include "frame.hpp"
+#include "compat/math.hpp"
+#include <opencv2/imgproc.hpp>
+
+namespace pt_module {
+
+void Preview::set_last_frame(const pt_frame& frame_)
+{
+ const cv::Mat& frame = frame_.as_const<const Frame>()->mat;
+ const bool need_resize = frame.size != frame_copy.size;
+
+ if (frame.channels() == 1)
+ {
+ 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);
+ }
+ else
+ {
+ 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)
+{
+ frame_out.create(h, w, CV_8UC4);
+ frame_copy.create(h, w, CV_8UC3);
+ frame_copy.setTo({0});
+}
+
+QImage Preview::get_bitmap()
+{
+ int stride = (int)frame_out.step.p[0];
+
+ if (stride < frame_out.cols * 4)
+ {
+ eval_once(qDebug() << "bad stride" << stride
+ << "for bitmap size" << frame_copy.cols << frame_copy.rows);
+ return QImage();
+ }
+
+ cv::cvtColor(frame_copy, frame_out, cv::COLOR_BGR2BGRA);
+
+ return QImage((const unsigned char*) frame_out.data,
+ frame_out.cols, frame_out.rows,
+ stride,
+ QImage::Format_ARGB32);
+}
+
+void Preview::draw_head_center(f x, f y)
+{
+ auto [px_, py_] = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows);
+
+ int px = iround(px_), py = iround(py_);
+
+ constexpr int len = 9;
+
+ static const cv::Scalar color(0, 255, 255);
+ cv::line(frame_copy,
+ cv::Point(px - len, py),
+ cv::Point(px + len, py),
+ color, 1);
+ cv::line(frame_copy,
+ cv::Point(px, py - len),
+ cv::Point(px, py + len),
+ color, 1);
+}
+
+} // ns pt_module
diff --git a/tracker-pt/module/frame.hpp b/tracker-pt/module/frame.hpp
new file mode 100644
index 00000000..0569a323
--- /dev/null
+++ b/tracker-pt/module/frame.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "pt-api.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
+{
+ cv::Mat mat;
+
+ operator const cv::Mat&() const& { return mat; }
+ operator cv::Mat&() & { return mat; }
+};
+
+struct Preview final : pt_preview
+{
+ Preview(int w, int h);
+
+ void set_last_frame(const pt_frame& frame) override;
+ QImage get_bitmap() 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:
+ 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
new file mode 100644
index 00000000..4679971e
--- /dev/null
+++ b/tracker-pt/module/lang/nl_NL.ts
@@ -0,0 +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
new file mode 100644
index 00000000..c3611ef0
--- /dev/null
+++ b/tracker-pt/module/lang/ru_RU.ts
@@ -0,0 +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
new file mode 100644
index 00000000..03d19f4e
--- /dev/null
+++ b/tracker-pt/module/lang/stub.ts
@@ -0,0 +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
new file mode 100644
index 00000000..c39728a1
--- /dev/null
+++ b/tracker-pt/module/lang/zh_CN.ts
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<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
new file mode 100644
index 00000000..f665face
--- /dev/null
+++ b/tracker-pt/module/module.cpp
@@ -0,0 +1,72 @@
+#include "ftnoir_tracker_pt.h"
+
+#include "module.hpp"
+#include "camera.h"
+#include "frame.hpp"
+#include "point_extractor.h"
+#include "ftnoir_tracker_pt_dialog.h"
+
+#include "pt-api.hpp"
+
+#include <memory>
+
+static const QString module_name = "tracker-pt";
+
+#ifdef __clang__
+# pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+namespace pt_module {
+
+struct pt_module_traits final : pt_runtime_traits
+{
+ pointer<pt_camera> make_camera() const override
+ {
+ return pointer<pt_camera>(new Camera(module_name));
+ }
+
+ pointer<pt_point_extractor> make_point_extractor() const override
+ {
+ return pointer<pt_point_extractor>(new PointExtractor(module_name));
+ }
+
+ QString get_module_name() const override
+ {
+ return module_name;
+ }
+
+ pointer<pt_frame> make_frame() const override
+ {
+ return pointer<pt_frame>(new Frame);
+ }
+
+ pointer<pt_preview> make_preview(int w, int h) const override
+ {
+ return pointer<pt_preview>(new Preview(w, h));
+ }
+};
+
+struct tracker_pt : Tracker_PT
+{
+ tracker_pt() : Tracker_PT(pointer<pt_runtime_traits>(new pt_module_traits))
+ {
+ }
+};
+
+struct dialog_pt : TrackerDialog_PT
+{
+ dialog_pt();
+};
+
+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;
+
+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
new file mode 100644
index 00000000..3329fafc
--- /dev/null
+++ b/tracker-pt/module/point_extractor.cpp
@@ -0,0 +1,458 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ * Copyright (c) 2015-2017 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include "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/imgproc.hpp>
+
+#undef PREVIEW
+//#define PREVIEW
+
+#if defined PREVIEW
+# include <opencv2/highgui.hpp>
+#endif
+
+#include <cmath>
+#include <algorithm>
+#include <cinttypes>
+#include <memory>
+
+#include <QDebug>
+
+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
+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
+from the thresholded area.
+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
+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
+algorithm for tracking single particles with variable size and shape." (2008).
+*/
+static vec2 MeanShiftIteration(const cv::Mat1b &frame_gray, const vec2 &current_center, f filter_width)
+{
+ const f s = 1 / filter_width;
+
+ f m = 0;
+ vec2 com { 0, 0 };
+ for (int i = 0; i < frame_gray.rows; 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 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;
+ }
+ }
+ if (m > f(.1))
+ {
+ 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);
+}
+
+void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame)
+{
+ 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;
+
+ frame_gray.create(H, W);
+ frame_bin.create(H, W);
+}
+
+void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest)
+{
+ ensure_channel_buffers(orig_frame);
+
+ const int from_to[] = {
+ idx, 0,
+ };
+
+ cv::mixChannels(&orig_frame, 1, &dest, 1, from_to, 1);
+}
+
+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);
+
+ // 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);
+ break;
+ }
+ case pt_color_red_only:
+ {
+ extract_single_channel(frame, 2, output);
+ break;
+ }
+ 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:
+ {
+ 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:
+ 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;
+ }
+}
+
+void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output)
+{
+ const int threshold_slider_value = s.threshold_slider.to<int>();
+
+ if (!s.auto_threshold)
+ {
+ cv::threshold(frame_gray, output, threshold_slider_value, 255, cv::THRESH_BINARY);
+ }
+ else
+ {
+ const int hist_size = 256;
+ const float ranges_[] = { 0, 256 };
+ float const* ranges = (const float*) ranges_;
+
+ cv::calcHist(&frame_gray,
+ 1,
+ nullptr,
+ cv::noArray(),
+ hist,
+ 1,
+ &hist_size,
+ &ranges);
+
+ const f radius = threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value);
+
+ 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 = 1;
+ for (unsigned i = sz-1, cnt = 0; i > 1; i--)
+ {
+ cnt += (unsigned)ptr[i];
+ if (cnt >= area)
+ break;
+ thres = i;
+ }
+
+ cv::threshold(frame_gray, output, thres, 255, cv::THRESH_BINARY);
+ }
+}
+
+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;
+
+ ensure_buffers(frame);
+ color_to_grayscale(frame, frame_gray);
+
+#if defined PREVIEW
+ cv::imshow("capture", frame_gray);
+ cv::waitKey(1);
+#endif
+
+ threshold_image(frame_gray, frame_bin);
+
+ const f region_size_min = (f)s.min_point_size;
+ const f region_size_max = (f)s.max_point_size;
+
+ unsigned idx = 0;
+
+ blobs.clear();
+
+ for (int y=0; y < frame_bin.rows; y++)
+ {
+ 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_bin,
+ cv::Point(x,y),
+ cv::Scalar(idx),
+ &rect,
+ cv::Scalar(0),
+ cv::Scalar(0),
+ 4 | cv::FLOODFILL_FIXED_RANGE);
+
+ unsigned cnt = 0;
+ unsigned norm = 0;
+
+ const int ymax = rect.y+rect.height,
+ xmax = rect.x+rect.width;
+
+ for (int i=rect.y; i < ymax; 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)
+ continue;
+
+ //ptr_blobs[j] = 0;
+ norm += ptr_gray[j];
+ cnt++;
+ }
+ }
+
+ 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/f(2), rect.height/f(2)),
+ std::pow(f(norm), f(1.1))/cnt,
+ rect);
+
+ if (idx >= max_blobs)
+ goto end;
+
+ // 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
+ //break;
+ }
+ }
+end:
+
+ const int W = frame_gray.cols;
+ const int H = frame_gray.rows;
+
+ const unsigned sz = blobs.size();
+
+ std::sort(blobs.begin(), blobs.end(), [](const blob& b1, const blob& b2) { return b2.brightness < b1.brightness; });
+
+ for (idx = 0; idx < sz; ++idx)
+ {
+ 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 = 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) < f(1e-3))
+ break;
+ }
+
+ b.pos[0] = pos[0] + rect.x;
+ b.pos[1] = pos[1] + rect.y;
+ }
+
+ if (preview_visible)
+ draw_blobs(preview_frame_.as<Frame>()->mat,
+ blobs.data(), blobs.size(),
+ frame_gray.size());
+
+
+ // End of mean shift code. At this point, blob positions are updated with hopefully less noisy less biased values.
+ points.reserve(max_blobs);
+ points.clear();
+
+ for (const auto& b : blobs)
+ {
+ // note: H/W is equal to fx/fy
+
+ vec2 p;
+ std::tie(p[0], p[1]) = to_screen_pos(b.pos[0], b.pos[1], W, H);
+ points.push_back(p);
+ }
+}
+
+blob::blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect) :
+ radius(radius), brightness(brightness), pos(pos), 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
new file mode 100644
index 00000000..fbfdbb0b
--- /dev/null
+++ b/tracker-pt/module/point_extractor.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ * Copyright (c) 2015-2016 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#pragma once
+
+#include "pt-api.hpp"
+#include <opencv2/core/mat.hpp>
+#include <opencv2/core/types.hpp>
+#include <vector>
+
+namespace pt_module {
+
+using namespace numeric_types;
+
+struct blob final
+{
+ f radius, brightness;
+ vec2 pos;
+ cv::Rect rect;
+
+ blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect);
+};
+
+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, bool preview_visible,
+ std::vector<vec2>& points) override;
+
+ explicit PointExtractor(const QString& module_name);
+
+private:
+ static constexpr int max_blobs = 16;
+
+ pt_settings s;
+
+ cv::Mat1b frame_bin, frame_gray;
+ cv::Mat1f hist;
+ std::vector<blob> blobs;
+ cv::Mat1b ch[3];
+
+ 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::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);
+};
+
+} // ns impl
+
diff --git a/tracker-pt/module/tracker_pt.qrc b/tracker-pt/module/tracker_pt.qrc
new file mode 100644
index 00000000..dfeb7369
--- /dev/null
+++ b/tracker-pt/module/tracker_pt.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>Resources/Logo_IR.png</file>
+ </qresource>
+</RCC>
diff --git a/tracker-pt/numeric.hpp b/tracker-pt/numeric.hpp
deleted file mode 100644
index 9d37086d..00000000
--- a/tracker-pt/numeric.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <opencv2/core.hpp>
-#include <limits>
-
-namespace types {
- using f = double;
-
- struct constants final
- {
- constants() = delete;
- static constexpr f eps = std::numeric_limits<f>::epsilon();
- };
-
- template<int n> using vec = cv::Vec<f, n>;
- using vec2 = vec<2>;
- using vec3 = vec<3>;
-
- template<int y, int x> using mat = cv::Matx<f, y, x>;
- using mat33 = mat<3, 3>;
- using mat22 = mat<2, 2>;
-}
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_extractor.cpp b/tracker-pt/point_extractor.cpp
deleted file mode 100644
index 4bd92d44..00000000
--- a/tracker-pt/point_extractor.cpp
+++ /dev/null
@@ -1,291 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- * Copyright (c) 2015-2016 Stanislaw Halik <sthalik@misaki.pl>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#include "point_extractor.h"
-#include "compat/util.hpp"
-#include "point_tracker.h"
-#include <QDebug>
-
-#include <opencv2/videoio.hpp>
-
-#include <cmath>
-#include <algorithm>
-#include <cinttypes>
-
-#include <QDebug>
-
-using namespace types;
-
-/*
-http://en.wikipedia.org/wiki/Mean-shift
-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
-from the thresholded area.
-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
-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
-algorithm for tracking single particles with variable size and shape." (2008).
-*/
-static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const cv::Vec2d &current_center, double filter_width)
-{
- // Most amazingling this function runs faster with doubles than with floats.
- const double s = 1.0 / filter_width;
-
- double m = 0;
- cv::Vec2d com(0.0, 0.0);
- for (int i = 0; i < frame_gray.rows; i++)
- {
- auto frame_ptr = (uint8_t *)frame_gray.ptr(i);
- for (int j = 0; j < frame_gray.cols; j++)
- {
- double val = frame_ptr[j];
- val = val * val; // taking the square wights brighter parts of the image stronger.
- {
- double dx = (j - current_center[0])*s;
- double dy = (i - current_center[1])*s;
- double f = std::max(0.0, 1.0 - dx*dx - dy*dy);
- val *= f;
- }
- m += val;
- com[0] += j * val;
- com[1] += i * val;
- }
- }
- if (m > 0.1)
- {
- com *= 1.0 / m;
- return com;
- }
- else
- return current_center;
-}
-
-PointExtractor::PointExtractor()
-{
- blobs.reserve(max_blobs);
-}
-
-void PointExtractor::extract_points(const cv::Mat& frame, cv::Mat& preview_frame, std::vector<vec2>& points)
-{
- using std::sqrt;
- using std::max;
- using std::round;
- using std::sort;
-
- if (frame_gray.rows != frame.rows || frame_gray.cols != frame.cols)
- {
- frame_gray = cv::Mat(frame.rows, frame.cols, CV_8U);
- frame_bin = cv::Mat(frame.rows, frame.cols, CV_8U);
- frame_blobs = cv::Mat(frame.rows, frame.cols, CV_8U);
- }
-
- // convert to grayscale
- cv::cvtColor(frame, frame_gray, cv::COLOR_RGB2GRAY);
-
- const double region_size_min = s.min_point_size;
- const double region_size_max = s.max_point_size;
-
- if (!s.auto_threshold)
- {
- const int thres = s.threshold;
- cv::threshold(frame_gray, frame_bin, thres, 255, cv::THRESH_BINARY);
- }
- else
- {
- cv::calcHist(std::vector<cv::Mat> { frame_gray },
- std::vector<int> { 0 },
- cv::Mat(),
- hist,
- std::vector<int> { 256 },
- std::vector<float> { 0, 256 },
- false);
-
- static constexpr double min_radius = 2.5;
- static constexpr double max_radius = 15;
-
- const double radius = max(0., (max_radius-min_radius) * s.threshold / 255 + min_radius);
- const float* ptr = reinterpret_cast<const float*>(hist.ptr(0));
- const unsigned area = unsigned(round(3 * M_PI * radius*radius));
- const unsigned sz = unsigned(hist.cols * hist.rows);
- unsigned thres = 1;
- for (unsigned i = sz-1, cnt = 0; i > 1; i--)
- {
- cnt += ptr[i];
- if (cnt >= area)
- {
- thres = i;
- break;
- }
- }
- //val *= 240./256.;
- //qDebug() << "thres" << thres;
-
- cv::threshold(frame_gray, frame_bin, thres, 255, CV_THRESH_BINARY);
- }
-
- blobs.clear();
- frame_bin.copyTo(frame_blobs);
-
- unsigned idx = 0;
- for (int y=0; y < frame_blobs.rows; y++)
- {
- const unsigned char* ptr_bin = frame_blobs.ptr(y);
- for (int x=0; x < frame_blobs.cols; x++)
- {
- if (ptr_bin[x] != 255)
- continue;
- idx = blobs.size() + 1;
-
- cv::Rect rect;
- cv::floodFill(frame_blobs,
- cv::Point(x,y),
- cv::Scalar(idx),
- &rect,
- cv::Scalar(0),
- cv::Scalar(0),
- 8);
-
- // these are doubles since m10 and m01 could overflow theoretically
- // log2(255^2 * 640^2 * pi) > 36
- double m10 = 0;
- double m01 = 0;
- // norm can't overflow since there's no 640^2 component
- int norm = 0;
- int cnt = 0;
-
- for (int i=rect.y; i < (rect.y+rect.height); i++)
- {
- unsigned char* ptr_blobs = frame_blobs.ptr(i);
- const unsigned char* ptr_gray = frame_gray.ptr(i);
- for (int j=rect.x; j < (rect.x+rect.width); j++)
- {
- if (ptr_blobs[j] != idx)
- continue;
-
- ptr_blobs[j] = 0;
-
- // square as a weight gives better results
- const int val(int(ptr_gray[j]) * int(ptr_gray[j]));
-
- norm += val;
- m01 += i * val;
- m10 += j * val;
- cnt++;
- }
- }
- if (norm > 0)
- {
- const double radius = sqrt(cnt / M_PI), N = double(norm);
- if (radius > region_size_max || radius < region_size_min)
- continue;
-
- blob b(radius, cv::Vec2d(m10 / N, m01 / N), N/sqrt(double(cnt)), rect);
- blobs.push_back(b);
-
- {
- static const f offx = 10, offy = 7.5;
- const f cx = preview_frame.cols / f(frame.cols),
- cy = preview_frame.rows / f(frame.rows);
- cv::Point p(iround(b.pos[0] * cx), iround(b.pos[1] * cy));
-
- static const cv::Scalar color(0, 255, 0);
-
- cv::circle(preview_frame, p, b.radius, color);
-
- {
- const int len = iround(b.radius * 1.25) + 1;
- cv::line(preview_frame,
- cv::Point(p.x - len, p.y),
- cv::Point(p.x + len, p.y),
- color);
- cv::line(preview_frame,
- cv::Point(p.x, p.y - len),
- cv::Point(p.x, p.y + len),
- color);
- }
-
- char buf[64];
- sprintf(buf, "%.2fpx", radius);
-
- cv::putText(preview_frame,
- buf,
- cv::Point(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy)),
- cv::FONT_HERSHEY_PLAIN,
- 1,
- cv::Scalar(0, 0, 255),
- 1);
- }
-
- if (idx >= max_blobs) goto end;
- }
- }
- }
-end:
-
- sort(blobs.begin(), blobs.end(), [](const blob& b1, const blob& b2) -> bool { return b2.brightness < b1.brightness; });
-
- const int W = frame.cols;
- const int H = frame.rows;
-
- for (idx = 0; idx < std::min(PointModel::N_POINTS, unsigned(blobs.size())); ++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
-
- cv::Mat frame_roi = frame_gray(rect);
-
- // smaller values mean more changes. 1 makes too many changes while 1.5 makes about .1
- // seems values close to 1.3 reduce noise best with about .15->.2 changes
- static constexpr double radius_c = 1.3;
-
- const double kernel_radius = b.radius * radius_c;
- cv::Vec2d pos(b.pos[0] - rect.x, b.pos[1] - rect.y); // position relative to ROI.
-
- for (int iter = 0; iter < 10; ++iter)
- {
- cv::Vec2d com_new = MeanShiftIteration(frame_roi, pos, kernel_radius);
- cv::Vec2d delta = com_new - pos;
- pos = com_new;
- if (delta.dot(delta) < 1e-3)
- break;
- }
-
- b.pos[0] = pos[0] + rect.x;
- b.pos[1] = pos[1] + rect.y;
- }
-
- // End of mean shift code. At this point, blob positions are updated which hopefully less noisy less biased values.
- points.reserve(max_blobs);
- points.clear();
-
- for (const auto& b : blobs)
- {
- // note: H/W is equal to fx/fy
-
- vec2 p((b.pos[0] - W/2)/W, -(b.pos[1] - H/2)/W);
- points.push_back(p);
- }
-}
-
-PointExtractor::blob::blob(double radius, const cv::Vec2d& pos, double brightness, cv::Rect& rect) :
- radius(radius), brightness(brightness), pos(pos), rect(rect)
-{
- //qDebug() << "radius" << radius << "pos" << pos[0] << pos[1];
-}
diff --git a/tracker-pt/point_extractor.h b/tracker-pt/point_extractor.h
deleted file mode 100644
index f931edd5..00000000
--- a/tracker-pt/point_extractor.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright (c) 2012 Patrick Ruoff
- * Copyright (c) 2015-2016 Stanislaw Halik <sthalik@misaki.pl>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
-#pragma once
-
-#include "ftnoir_tracker_pt_settings.h"
-#include "camera.h"
-#include "numeric.hpp"
-
-#include <opencv2/core.hpp>
-#include <opencv2/imgproc.hpp>
-
-#include <vector>
-
-namespace impl {
-
-using namespace types;
-
-class PointExtractor final
-{
-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 cv::Mat& frame, cv::Mat& preview_frame, std::vector<vec2>& points);
- PointExtractor();
-
- settings_pt s;
-private:
- static constexpr int max_blobs = 16;
-
- cv::Mat frame_gray;
- cv::Mat frame_bin;
- cv::Mat hist;
- cv::Mat frame_blobs;
-
- struct blob
- {
- double radius, brightness;
- vec2 pos;
- cv::Rect rect;
-
- blob(double radius, const cv::Vec2d& pos, double brightness, cv::Rect &rect);
- };
-
- std::vector<blob> blobs;
-};
-
-} // ns impl
-
-using impl::PointExtractor;
diff --git a/tracker-pt/point_tracker.cpp b/tracker-pt/point_tracker.cpp
index cae68bf3..39e96038 100644
--- a/tracker-pt/point_tracker.cpp
+++ b/tracker-pt/point_tracker.cpp
@@ -6,9 +6,7 @@
*/
#include "point_tracker.h"
-#include "compat/nan.hpp"
-
-using namespace types;
+#include "compat/math-imports.hpp"
#include <vector>
#include <algorithm>
@@ -16,7 +14,9 @@ using namespace types;
#include <QDebug>
-constexpr unsigned PointModel::N_POINTS;
+namespace pt_impl {
+
+using namespace numeric_types;
static void get_row(const mat33& m, int i, vec3& v)
{
@@ -32,12 +32,12 @@ static void set_row(mat33& m, int i, const vec3& v)
m(i,2) = v[2];
}
-PointModel::PointModel(settings_pt& s)
+PointModel::PointModel(const pt_settings& s)
{
set_model(s);
// calculate u
u = M01.cross(M02);
- u /= norm(u);
+ u = cv::normalize(u);
// calculate projection matrix on M01,M02 plane
f s11 = M01.dot(M01);
@@ -46,17 +46,20 @@ PointModel::PointModel(settings_pt& s)
P = 1/(s11*s22-s12*s12) * mat22(s22, -s12, -s12, s11);
}
-void PointModel::set_model(settings_pt& s)
+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);
@@ -65,69 +68,62 @@ void PointModel::set_model(settings_pt& 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() : init_phase(true)
-{
-}
+PointTracker::PointTracker() = default;
PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2* points,
const PointModel& model,
- const CamInfo& info)
+ const pt_camera_info& info)
{
- f fx; info.get_focal_length(fx);
- 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(double(info.res_x*info.res_x + info.res_y*info.res_y)));
- static 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;
@@ -139,55 +135,61 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2*
void PointTracker::track(const std::vector<vec2>& points,
const PointModel& model,
- const CamInfo& info,
- int init_phase_timeout)
+ const pt_camera_info& info,
+ int init_phase_timeout,
+ point_filter& filter,
+ f deadzone_amount)
{
- f fx;
- info.get_focal_length(fx);
+ 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 (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
@@ -196,17 +198,16 @@ 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(mat33::eye());
+ const mat33& R_expected{X_CM_expected.R};
// initial pose = last (predicted) pose
vec3 k;
get_row(R_expected, 2, k);
- f Z0 = f(1000);
+ f Z0 = X_CM.t[2] < f(1e-4) ? f(1000) : X_CM.t[2];
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;
@@ -215,16 +216,10 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
mat33 R_1, R_2;
mat33* R_current = &R_1;
- static constexpr int max_iter = 100;
-
- using std::sqrt;
- using std::atan;
- using std::cos;
- using std::sin;
- using std::fabs;
+ 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;
@@ -249,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);
}
@@ -267,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;
@@ -286,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;
@@ -299,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;
@@ -315,18 +310,24 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
- if (nanp(r(i, j)))
+ {
+ int ret = std::fpclassify(r(i, j));
+ if (ret == FP_NAN || ret == FP_INFINITE)
{
- qDebug() << "posit nan";
+ qDebug() << "posit nan R";
return -1;
}
+ }
- for (unsigned i = 0; i < 3; i++)
- if (nanp(t[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";
+ qDebug() << "posit nan T";
return -1;
}
+ }
// apply results
X_CM.R = r;
@@ -334,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);
@@ -349,3 +356,11 @@ vec2 PointTracker::project(const vec3& v_M, f focal_length, const Affine& X_CM)
vec3 v_C = X_CM * v_M;
return vec2(focal_length*v_C[0]/v_C[2], focal_length*v_C[1]/v_C[2]);
}
+
+void PointTracker::reset_state()
+{
+ init_phase = true;
+ X_CM_expected = {};
+}
+
+} // ns pt_impl
diff --git a/tracker-pt/point_tracker.h b/tracker-pt/point_tracker.h
index 63caf0dd..7492b4eb 100644
--- a/tracker-pt/point_tracker.h
+++ b/tracker-pt/point_tracker.h
@@ -8,26 +8,24 @@
#pragma once
#include "compat/timer.hpp"
-#include "ftnoir_tracker_pt_settings.h"
-#include "affine.hpp"
-#include "numeric.hpp"
-#include "camera.h"
-
-#include <opencv2/core.hpp>
-#include <cstddef>
-#include <memory>
+#include "cv/affine.hpp"
+#include "cv/numeric.hpp"
+#include "pt-api.hpp"
+#include "point-filter.hpp"
+
#include <vector>
#include <array>
+
#include <QObject>
-namespace impl {
+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
{
@@ -42,9 +40,10 @@ struct PointModel final
enum Model { Clip, Cap, Custom };
- PointModel(settings_pt& s);
- void set_model(settings_pt& s);
- void get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const;
+ explicit PointModel(const pt_settings& s);
+ void set_model(const pt_settings& s);
+
+ static void get_d_order(const vec2* points, unsigned* d_order, const vec2& d);
};
// ----------------------------------------------------------------------------
@@ -58,26 +57,33 @@ 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 CamInfo& 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();
private:
// the points in model order
using PointOrder = std::array<vec2, 3>;
- PointOrder find_correspondences(const vec2* projected_points, const PointModel &model);
- PointOrder find_correspondences_previous(const vec2* points, const PointModel &model, const CamInfo& info);
- int POSIT(const PointModel& point_model, const PointOrder& order, f focal_length); // The POSIT algorithm, returns the number of iterations
-
- Affine X_CM; // trafo from model to camera
+ 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);
+ // 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
+ Affine X_CM_expected;
Timer t;
- bool init_phase;
+ bool init_phase = true;
};
-} // ns types
+} // ns pt_impl
-using impl::PointTracker;
-using impl::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
new file mode 100644
index 00000000..d71c6e13
--- /dev/null
+++ b/tracker-pt/pt-api.cpp
@@ -0,0 +1,53 @@
+#include "pt-api.hpp"
+#include "cv/numeric.hpp"
+
+using namespace numeric_types;
+
+pt_camera_info::pt_camera_info() = default;
+
+f pt_camera_info::get_focal_length(f fov, int res_x, int res_y)
+{
+ 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 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 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() = 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;
+
+f pt_point_extractor::threshold_radius_value(int w, int h, int threshold)
+{
+ f cx = w / f{640}, cy = h / f{480};
+
+ const f min_radius = f{1.75} * cx;
+ const f max_radius = f{30} * cy;
+
+ const f radius = std::fmax(f{0}, (max_radius-min_radius) * threshold / f(255) + min_radius);
+
+ return radius;
+}
+
+std::tuple<f, f> pt_pixel_pos_mixin::to_pixel_pos(f x, f y, int w, int h)
+{
+ return { w*(x+f{.5}), f{.5}*(h - 2*y*w) };
+}
+
+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() = default;
+pt_frame::~pt_frame() = default;
diff --git a/tracker-pt/pt-api.hpp b/tracker-pt/pt-api.hpp
new file mode 100644
index 00000000..15021ff3
--- /dev/null
+++ b/tracker-pt/pt-api.hpp
@@ -0,0 +1,139 @@
+#pragma once
+
+#include "pt-settings.hpp"
+
+#include "cv/numeric.hpp"
+#include "options/options.hpp"
+
+#include <tuple>
+#include <vector>
+#include <memory>
+
+#include <QImage>
+#include <QString>
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+struct pt_camera_info final
+{
+ using f = numeric_types::f;
+
+ pt_camera_info();
+ static f get_focal_length(f fov, int res_x, int res_y);
+
+ f fov = 0;
+ f fps = 0;
+
+ int res_x = 0;
+ int res_y = 0;
+ QString name;
+ bool use_mjpeg = false;
+};
+
+struct pt_pixel_pos_mixin
+{
+ 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 pt_frame : pt_pixel_pos_mixin
+{
+ pt_frame();
+ virtual ~pt_frame();
+
+ template<typename t>
+ t* as() &
+ {
+ return static_cast<t*>(this);
+ }
+
+ template<typename t>
+ t const* as_const() const&
+ {
+ 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 pt_preview : pt_frame
+{
+ 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(f x, f y) = 0;
+};
+
+struct pt_camera
+{
+ using result = std::tuple<bool, pt_camera_info>;
+ using f = numeric_types::f;
+
+ pt_camera();
+ virtual ~pt_camera();
+
+ OTR_DISABLE_MOVE_COPY(pt_camera);
+
+ [[nodiscard]] virtual bool start(const pt_settings& s) = 0;
+ virtual void stop() = 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(f value) = 0;
+ virtual void show_camera_settings() = 0;
+ virtual f deadzone_amount() const { return 1; }
+};
+
+struct pt_point_extractor : pt_pixel_pos_mixin
+{
+ 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, bool preview_visible, std::vector<vec2>& points) = 0;
+
+ static f threshold_radius_value(int w, int h, int threshold);
+};
+
+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();
+
+ virtual pointer<pt_camera> make_camera() const = 0;
+ virtual pointer<pt_point_extractor> make_point_extractor() const = 0;
+ virtual pointer<pt_frame> make_frame() const = 0;
+ 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
new file mode 100644
index 00000000..5d16d973
--- /dev/null
+++ b/tracker-pt/pt-settings.hpp
@@ -0,0 +1,87 @@
+#pragma once
+
+#include "options/options.hpp"
+
+#include <QString>
+
+enum pt_color_type
+{
+ // explicit values, gotta preserve the numbering in .ini
+ // don't reuse when removing some of the modes
+ pt_color_bt709 = 2,
+ pt_color_hardware = 14,
+ pt_color_red_only = 3,
+ 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;
+
+ 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 },
+ cam_fps { b, "camera-fps", 30 };
+ value<double> min_point_size { b, "min-point-size", 2.5 },
+ max_point_size { b, "max-point-size", 50 };
+
+ value<int> m01_x { b, "m_01-x", 0 }, m01_y { b, "m_01-y", 0 }, m01_z { b, "m_01-z", 0 };
+ value<int> m02_x { b, "m_02-x", 0 }, m02_y { b, "m_02-y", 0 }, m02_z { b, "m_02-z", 0 };
+
+ value<int> t_MH_x { b, "model-centroid-x", 0 },
+ t_MH_y { b, "model-centroid-y", 0 },
+ t_MH_z { b, "model-centroid-z", 0 };
+
+ value<int> clip_ty { b, "clip-ty", 40 },
+ clip_tz { b, "clip-tz", 30 },
+ clip_by { b, "clip-by", 70 },
+ clip_bz { b, "clip-bz", 80 };
+
+ value<int> active_model_panel { b, "active-model-panel", 0 },
+ cap_x { b, "cap-x", 40 },
+ cap_y { b, "cap-y", 60 },
+ cap_z { b, "cap-z", 100 };
+
+ value<int> fov { b, "camera-fov", 56 };
+
+ 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_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<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;
diff --git a/tracker-pt/ftnoir_tracker_pt.qrc b/tracker-pt/tracker_pt_base.qrc
index a8f9a1af..8c270540 100644
--- a/tracker-pt/ftnoir_tracker_pt.qrc
+++ b/tracker-pt/tracker_pt_base.qrc
@@ -4,6 +4,5 @@
<file>Resources/cap_side.png</file>
<file>Resources/clip_front.png</file>
<file>Resources/clip_side.png</file>
- <file>Resources/Logo_IR.png</file>
</qresource>
</RCC>