diff options
Diffstat (limited to 'tracker-pt')
27 files changed, 1837 insertions, 434 deletions
diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt index 078cd4bc..0c2e9ce3 100644 --- a/tracker-pt/CMakeLists.txt +++ b/tracker-pt/CMakeLists.txt @@ -1,9 +1,12 @@ include(opentrack-opencv) find_package(OpenCV QUIET) if(OpenCV_FOUND) + foreach(k core) + otr_install_lib("opencv_${k}" "${opentrack-libexec}") + endforeach() otr_module(tracker-pt-base STATIC) target_include_directories(${self} SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) - target_link_libraries(${self} opencv_imgproc opentrack-cv opencv_core opentrack-video) + 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 061f5351..53a29c1a 100644 --- a/tracker-pt/FTNoIR_PT_Controls.ui +++ b/tracker-pt/FTNoIR_PT_Controls.ui @@ -9,12 +9,12 @@ <rect> <x>0</x> <y>0</y> - <width>418</width> - <height>724</height> + <width>413</width> + <height>630</height> </rect> </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -32,85 +32,24 @@ <property name="autoFillBackground"> <bool>false</bool> </property> - <layout class="QGridLayout" name="gridLayout_9"> - <property name="sizeConstraint"> - <enum>QLayout::SetFixedSize</enum> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>0</number> </property> - <item row="1" column="0" alignment="Qt::AlignVCenter"> - <widget class="QGroupBox" name="groupBox_5"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="title"> - <string>Status</string> - </property> - <layout class="QGridLayout" name="gridLayout_10"> - <item row="1" column="0"> - <widget class="QLabel" name="label_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Extracted Points:</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_38"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Camera Info:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="pointinfo_label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="caminfo_label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item row="0" column="0"> + <property name="leftMargin"> + <number>4</number> + </property> + <property name="topMargin"> + <number>4</number> + </property> + <property name="rightMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <item> <widget class="QTabWidget" name="tabWidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> <property name="locale"> <locale language="English" country="UnitedStates"/> </property> @@ -118,6 +57,12 @@ <number>0</number> </property> <widget class="QWidget" name="tab_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <attribute name="title"> <string>Camera</string> </attribute> @@ -134,64 +79,100 @@ <string>Camera settings</string> </property> <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="1"> - <widget class="QComboBox" name="camdevice_combo"> + <item row="11" column="1"> + <widget class="QCheckBox" name="chroma_key_overexposed"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_4"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="minimumContentsLength"> - <number>10</number> + <property name="cursor"> + <cursorShape>WhatsThisCursor</cursorShape> + </property> + <property name="toolTip"> + <string>This should be 56° or 76° for the PS3 Eye, dependent upon the physical lens setting. It's only neccessary to get position correspond to real-world values.</string> + </property> + <property name="text"> + <string>Diagonal field of view</string> </property> </widget> </item> - <item row="4" column="1"> - <widget class="QSpinBox" name="fov"> + <item row="6" column="0"> + <widget class="QLabel" name="label_5"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="suffix"> - <string>°</string> + <property name="text"> + <string>Dynamic pose (for caps only, never clips)</string> </property> - <property name="prefix"> - <string/> + </widget> + </item> + <item row="9" column="0"> + <widget class="QLabel" name="label_12"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="minimum"> - <number>10</number> + <property name="cursor"> + <cursorShape>WhatsThisCursor</cursorShape> </property> - <property name="maximum"> - <number>90</number> + <property name="toolTip"> + <string><html><head/><body><p>For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.</p></body></html></string> + </property> + <property name="text"> + <string>Color channels used</string> </property> </widget> </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_4"> + <item row="8" column="1"> + <widget class="QPushButton" name="camera_settings"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> - <string>Diagonal field of view</string> + <string>Open</string> </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_36"> + <item row="4" column="1"> + <widget class="QCheckBox" name="use_mjpeg"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_13"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="cursor"> + <cursorShape>WhatsThisCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Enable MJPEG compression for high-speed cameras other than the PS3 Eye. Windows only.</string> + </property> <property name="text"> - <string>Width</string> + <string>MJPEG compression</string> </property> </widget> </item> @@ -211,30 +192,28 @@ </property> </widget> </item> - <item row="2" column="1"> - <widget class="QSpinBox" name="res_y_spin"> + <item row="1" column="0"> + <widget class="QLabel" name="label_36"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="toolTip"> - <string>Desired capture height</string> - </property> - <property name="suffix"> - <string> px</string> - </property> - <property name="maximum"> - <number>2000</number> + <property name="text"> + <string>Width</string> </property> - <property name="singleStep"> - <number>10</number> + </widget> + </item> + <item row="11" column="0"> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>Chroma key includes overexposed pixels</string> </property> </widget> </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_6"> + <item row="8" column="0"> + <widget class="QLabel" name="label_9"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -242,30 +221,30 @@ </sizepolicy> </property> <property name="text"> - <string>Dynamic pose timeout</string> + <string>Camera settings (when available)</string> </property> </widget> </item> - <item row="3" column="1"> - <widget class="QSpinBox" name="fps_spin"> + <item row="7" column="1"> + <widget class="QSpinBox" name="init_phase_timeout"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="toolTip"> - <string>Desired capture framerate</string> - </property> <property name="suffix"> - <string> Hz</string> + <string> ms</string> + </property> + <property name="minimum"> + <number>50</number> </property> <property name="maximum"> - <number>2000</number> + <number>5000</number> </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <widget class="QCheckBox" name="dynamic_pose"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> @@ -300,8 +279,8 @@ </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_41"> + <item row="7" column="0"> + <widget class="QLabel" name="label_6"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -309,26 +288,20 @@ </sizepolicy> </property> <property name="text"> - <string>Height</string> + <string>Dynamic pose timeout</string> </property> </widget> </item> - <item row="6" column="1"> - <widget class="QSpinBox" name="init_phase_timeout"> + <item row="0" column="1"> + <widget class="QComboBox" name="camdevice_combo"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="suffix"> - <string> ms</string> - </property> - <property name="minimum"> - <number>50</number> - </property> - <property name="maximum"> - <number>5000</number> + <property name="minimumContentsLength"> + <number>10</number> </property> </widget> </item> @@ -348,46 +321,39 @@ </property> </widget> </item> - <item row="7" column="1"> - <widget class="QPushButton" name="camera_settings"> + <item row="2" column="0"> + <widget class="QLabel" name="label_41"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> - <string>Open</string> + <string>Height</string> </property> </widget> </item> - <item row="7" column="0"> - <widget class="QLabel" name="label_9"> + <item row="3" column="1"> + <widget class="QSpinBox" name="fps_spin"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>Camera settings (when available)</string> + <property name="toolTip"> + <string>Desired capture framerate</string> </property> - </widget> - </item> - <item row="8" column="0"> - <widget class="QLabel" name="label_12"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <property name="suffix"> + <string> Hz</string> </property> - <property name="text"> - <string>Color channels used</string> + <property name="maximum"> + <number>2000</number> </property> </widget> </item> - <item row="8" column="1"> + <item row="9" column="1"> <widget class="QComboBox" name="blob_color"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> @@ -397,12 +363,12 @@ </property> <item> <property name="text"> - <string>Average</string> + <string>Grayscale BT.709</string> </property> </item> <item> <property name="text"> - <string>Natural</string> + <string>Grayscale (from hardware)</string> </property> </item> <item> @@ -420,21 +386,125 @@ <string>Blue only</string> </property> </item> + <item> + <property name="text"> + <string>Red chroma key</string> + </property> + </item> + <item> + <property name="text"> + <string>Green chroma key</string> + </property> + </item> + <item> + <property name="text"> + <string>Blue chroma key</string> + </property> + </item> + <item> + <property name="text"> + <string>Cyan chroma key</string> + </property> + </item> + <item> + <property name="text"> + <string>Yellow chroma key</string> + </property> + </item> + <item> + <property name="text"> + <string>Magenta chroma key</string> + </property> + </item> </widget> </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_5"> + <item row="5" column="1"> + <widget class="QSpinBox" name="fov"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="suffix"> + <string>°</string> + </property> + <property name="prefix"> + <string/> + </property> + <property name="minimum"> + <number>10</number> + </property> + <property name="maximum"> + <number>90</number> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="res_y_spin"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Desired capture height</string> + </property> + <property name="suffix"> + <string> px</string> + </property> + <property name="maximum"> + <number>2000</number> + </property> + <property name="singleStep"> + <number>10</number> + </property> + </widget> + </item> + <item row="10" column="0"> + <widget class="QLabel" name="label_17"> <property name="text"> - <string>Dynamic pose (for caps only, never clips)</string> + <string>Chroma key strength</string> </property> </widget> </item> + <item row="10" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSlider" name="chroma_key_strength_slider"> + <property name="minimum"> + <number>5</number> + </property> + <property name="maximum"> + <number>40</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="chroma_key_strength_label"> + <property name="focusPolicy"> + <enum>Qt::NoFocus</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::NoButtons</enum> + </property> + <property name="minimum"> + <double>0.500000000000000</double> + </property> + <property name="maximum"> + <double>4.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> </layout> </widget> </item> @@ -474,6 +544,12 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="cursor"> + <cursorShape>WhatsThisCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Set minimum size to avoid small stray lights from being treated as points.</string> + </property> <property name="text"> <string>Min size</string> </property> @@ -550,6 +626,12 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="cursor"> + <cursorShape>WhatsThisCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Track dependent on point size and not absolute brightness. This may allow more stable tracking.</string> + </property> <property name="text"> <string>Automatic threshold</string> </property> @@ -577,6 +659,26 @@ </property> </widget> </item> + <item row="2" column="1"> + <widget class="QLabel" name="threshold_value_display"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_14"> + <property name="text"> + <string>Value</string> + </property> + </widget> + </item> <item row="3" column="1"> <widget class="QDoubleSpinBox" name="mindiam_spin"> <property name="sizePolicy"> @@ -599,29 +701,22 @@ </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLabel" name="threshold_value_display"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_14"> - <property name="text"> - <string>Value</string> - </property> - </widget> - </item> </layout> </widget> </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>1</height> + </size> + </property> + </spacer> + </item> </layout> </widget> <widget class="QWidget" name="tab_4"> @@ -1323,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"> @@ -1360,7 +1680,74 @@ Don't roll or change position.</string> </widget> </widget> </item> - <item row="2" column="0"> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Status</string> + </property> + <layout class="QGridLayout" name="gridLayout_10"> + <item row="1" column="1"> + <widget class="QLabel" name="pointinfo_label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="caminfo_label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Extracted Points:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_38"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Camera Info:</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> @@ -1381,11 +1768,14 @@ Don't roll or change position.</string> <tabstop>res_x_spin</tabstop> <tabstop>res_y_spin</tabstop> <tabstop>fps_spin</tabstop> + <tabstop>use_mjpeg</tabstop> <tabstop>fov</tabstop> <tabstop>dynamic_pose</tabstop> <tabstop>init_phase_timeout</tabstop> <tabstop>camera_settings</tabstop> <tabstop>blob_color</tabstop> + <tabstop>chroma_key_strength_slider</tabstop> + <tabstop>chroma_key_overexposed</tabstop> <tabstop>auto_threshold</tabstop> <tabstop>threshold_slider</tabstop> <tabstop>mindiam_spin</tabstop> @@ -1396,8 +1786,8 @@ Don't roll or change position.</string> <tabstop>clip_bheight_spin</tabstop> <tabstop>clip_blength_spin</tabstop> <tabstop>cap_length_spin</tabstop> - <tabstop>cap_height_spin</tabstop> <tabstop>cap_width_spin</tabstop> + <tabstop>cap_height_spin</tabstop> <tabstop>m1x_spin</tabstop> <tabstop>m1y_spin</tabstop> <tabstop>m1z_spin</tabstop> @@ -1408,6 +1798,14 @@ Don't roll or change position.</string> <tabstop>ty_spin</tabstop> <tabstop>tz_spin</tabstop> <tabstop>tcalib_button</tabstop> + <tabstop>enable_point_filter</tabstop> + <tabstop>point_filter_slider</tabstop> + <tabstop>point_filter_limit_slider</tabstop> + <tabstop>point_filter_deadzone_slider</tabstop> + <tabstop>maxdiam_spin</tabstop> + <tabstop>mindiam_spin</tabstop> + <tabstop>auto_threshold</tabstop> + <tabstop>threshold_slider</tabstop> </tabstops> <resources> <include location="module/tracker_pt.qrc"/> diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp index 194fd423..0f8495d9 100644 --- a/tracker-pt/ftnoir_tracker_pt.cpp +++ b/tracker-pt/ftnoir_tracker_pt.cpp @@ -6,6 +6,7 @@ * copyright notice and this permission notice appear in all copies. */ +#undef NDEBUG #include "ftnoir_tracker_pt.h" #include "pt-api.hpp" #include "cv/init.hpp" @@ -13,7 +14,9 @@ #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 <QDebug> #include <QFile> @@ -27,17 +30,11 @@ Tracker_PT::Tracker_PT(pointer<pt_runtime_traits> const& traits) : traits { traits }, s { traits->get_module_name() }, point_extractor { traits->make_point_extractor() }, - camera { traits->make_camera() }, - frame { traits->make_frame() }, - preview_frame { traits->make_preview(preview_width, preview_height) } + frame { traits->make_frame() } { opencv_init(); - connect(s.b.get(), &bundle_::saving, this, &Tracker_PT::maybe_reopen_camera, Qt::DirectConnection); - connect(s.b.get(), &bundle_::reloading, this, &Tracker_PT::maybe_reopen_camera, Qt::DirectConnection); - - connect(&s.fov, value_::value_changed<int>(), this, &Tracker_PT::set_fov, Qt::DirectConnection); - set_fov(s.fov); + connect(&*s.b, &bundle_::saving, this, [this]{ reopen_camera_flag = true; }, Qt::DirectConnection); } Tracker_PT::~Tracker_PT() @@ -45,24 +42,41 @@ Tracker_PT::~Tracker_PT() requestInterruption(); wait(); - QMutexLocker l(&camera_mtx); - camera->stop(); + if (camera) + camera->stop(); +} + +bool Tracker_PT::check_camera() +{ + if (reopen_camera_flag) + { + reopen_camera_flag = false; + + camera = nullptr; + camera = traits->make_camera(); + if (!camera || !camera->start(s)) + return false; + } + assert(camera); + if (progn(bool x = true; return open_camera_dialog_flag.compare_exchange_strong(x, false);)) + run_in_thread_sync(qApp->thread(), [this] { camera->show_camera_settings(); }); + return true; } void Tracker_PT::run() { portable::set_curthread_name("tracker/pt"); - if (!maybe_reopen_camera()) - return; - while(!isInterruptionRequested()) { + if (!check_camera()) + break; + pt_camera_info info; bool new_frame = false; { - QMutexLocker l(&camera_mtx); + camera->set_fov(s.fov); std::tie(new_frame, info) = camera->get_frame(*frame); } @@ -70,10 +84,10 @@ void Tracker_PT::run() { const bool preview_visible = check_is_visible(); - if (preview_visible) - *preview_frame = *frame; + if (preview_visible && !widget->fresh()) + preview_frame->set_last_frame(*frame); - point_extractor->extract_points(*frame, *preview_frame, points); + 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; @@ -85,14 +99,9 @@ void Tracker_PT::run() if (success) { - int dynamic_pose_ms = s.dynamic_pose && s.active_model_panel != PointModel::Clip - ? s.init_phase_timeout - : 0; - - point_tracker.track(points, - PointModel(s), - info, - dynamic_pose_ms); + 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); } @@ -100,7 +109,7 @@ void Tracker_PT::run() X_CM = point_tracker.pose(); } - if (preview_visible) + 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)); @@ -111,44 +120,31 @@ void Tracker_PT::run() preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]); widget->update_image(preview_frame->get_bitmap()); - - auto [ w, h ] = widget->preview_size(); - if (w != preview_width || h != preview_height) - { - preview_width = w; preview_height = h; - preview_frame = traits->make_preview(w, h); - } } } } } -bool Tracker_PT::maybe_reopen_camera() -{ - QMutexLocker l(&camera_mtx); - - return camera->start(s.camera_name, - s.cam_fps, s.cam_res_x, s.cam_res_y); -} - -void Tracker_PT::set_fov(int value) -{ - QMutexLocker l(&camera_mtx); - camera->set_fov(value); -} - module_status Tracker_PT::start_tracker(QFrame* video_frame) { - //video_frame->setAttribute(Qt::WA_NativeWindow); + { + auto camera = traits->make_camera(); + if (!camera || !camera->start(s)) + return error(tr("Failed to open camera '%1'").arg(s.camera_name)); + } widget = std::make_unique<video_widget>(video_frame); layout = std::make_unique<QHBoxLayout>(video_frame); layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(widget.get()); - video_frame->setLayout(layout.get()); + layout->addWidget(&*widget); + video_frame->setLayout(&*layout); //video_widget->resize(video_frame->width(), video_frame->height()); video_frame->show(); + double dpi = screen_dpi(video_frame); + preview_frame = traits->make_preview(iround(preview_width * dpi), + iround(preview_height * dpi)); + start(QThread::HighPriority); return {}; @@ -214,10 +210,10 @@ int Tracker_PT::get_n_points() bool Tracker_PT::get_cam_info(pt_camera_info& info) { - QMutexLocker l(&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; } diff --git a/tracker-pt/ftnoir_tracker_pt.h b/tracker-pt/ftnoir_tracker_pt.h index 210c6a01..a793f94b 100644 --- a/tracker-pt/ftnoir_tracker_pt.h +++ b/tracker-pt/ftnoir_tracker_pt.h @@ -13,13 +13,12 @@ #include "point_tracker.h" #include "cv/numeric.hpp" #include "video/video-widget.hpp" +#include "point-filter.hpp" #include <atomic> #include <memory> #include <vector> -#include <opencv2/core.hpp> - #include <QThread> #include <QMutex> #include <QLayout> @@ -48,14 +47,10 @@ struct Tracker_PT : QThread, ITracker private: void run() override; - - bool maybe_reopen_camera(); - void set_fov(int value); + [[nodiscard]] bool check_camera(); pointer<pt_runtime_traits> traits; - QMutex camera_mtx; - PointTracker point_tracker; pt_settings s; @@ -73,7 +68,10 @@ private: 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}; }; } // ns pt_impl diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp index edf689a9..d67f79a7 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp +++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp @@ -10,8 +10,6 @@ #include "compat/math.hpp" #include "video/camera.hpp" -#include <opencv2/core.hpp> - #include <QString> #include <QtGlobal> #include <QDebug> @@ -39,6 +37,7 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) : tie_setting(s.cam_res_x, ui.res_x_spin); tie_setting(s.cam_res_y, ui.res_y_spin); tie_setting(s.cam_fps, ui.fps_spin); + tie_setting(s.use_mjpeg, ui.use_mjpeg); tie_setting(s.threshold_slider, ui.threshold_slider); @@ -92,14 +91,18 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) : poll_tracker_info_impl(); - connect(this, &TrackerDialog_PT::poll_tracker_info, this, &TrackerDialog_PT::poll_tracker_info_impl, Qt::DirectConnection); - constexpr pt_color_type color_types[] = { - pt_color_average, - pt_color_natural, + pt_color_bt709, + pt_color_hardware, pt_color_red_only, pt_color_green_only, pt_color_blue_only, + pt_color_red_chromakey, + pt_color_green_chromakey, + pt_color_blue_chromakey, + pt_color_cyan_chromakey, + pt_color_yellow_chromakey, + pt_color_magenta_chromakey, }; for (unsigned k = 0; k < std::size(color_types); k++) @@ -107,14 +110,38 @@ TrackerDialog_PT::TrackerDialog_PT(const QString& module_name) : tie_setting(s.blob_color, ui.blob_color); + tie_setting(s.chroma_key_strength, ui.chroma_key_strength_slider); + connect(&s.chroma_key_strength, value_::value_changed<slider_value>(), ui.chroma_key_strength_label, + [this] { ui.chroma_key_strength_label->setValue(*s.chroma_key_strength); }); + ui.chroma_key_strength_label->setValue(*s.chroma_key_strength); + + tie_setting(s.chroma_key_overexposed, ui.chroma_key_overexposed); + connect(ui.blob_color, &QComboBox::currentTextChanged, this, &TrackerDialog_PT::chroma_key_controls_enable); + + chroma_key_controls_enable(""); + tie_setting(s.threshold_slider, ui.threshold_value_display, [this](const slider_value& val) { return threshold_display_text(int(val)); }); // refresh threshold display on auto-threshold checkbox state change - tie_setting(s.auto_threshold, - this, - [this](bool) { s.threshold_slider.notify(); }); + tie_setting(s.auto_threshold, this, [this](bool) { s.threshold_slider.notify_(); }); + + tie_setting(s.enable_point_filter, ui.enable_point_filter); + tie_setting(s.point_filter_coefficient, ui.point_filter_slider); + tie_setting(s.point_filter_limit, ui.point_filter_limit_slider); + connect(&s.point_filter_coefficient, value_::value_changed<slider_value>(), + ui.point_filter_label, [this] { ui.point_filter_label->setValue(*s.point_filter_coefficient); } ); + connect(&s.point_filter_limit, value_::value_changed<slider_value>(), ui.point_filter_limit_label, + [this] { ui.point_filter_limit_label->setValue(*s.point_filter_limit); }, Qt::QueuedConnection); + ui.point_filter_label->setValue(*s.point_filter_coefficient); + ui.point_filter_limit_label->setValue(*s.point_filter_limit); + + tie_setting(s.point_filter_deadzone, ui.point_filter_deadzone_slider); + ui.point_filter_deadzone_label->setValue(*s.point_filter_deadzone); + + connect(&s.point_filter_deadzone, value_::value_changed<slider_value>(), ui.point_filter_deadzone_label, + [this] { ui.point_filter_deadzone_label->setValue(*s.point_filter_deadzone); }, Qt::QueuedConnection); } QString TrackerDialog_PT::threshold_display_text(int threshold_value) @@ -226,14 +253,25 @@ void TrackerDialog_PT::set_camera_settings_available(const QString& /* camera_na void TrackerDialog_PT::show_camera_settings() { if (tracker) - { - QMutexLocker l(&tracker->camera_mtx); - tracker->camera->show_camera_settings(); - } + tracker->open_camera_dialog_flag = true; else (void)video::show_dialog(s.camera_name); } +void TrackerDialog_PT::chroma_key_controls_enable(const QString&) +{ + bool enabled = false; + QVariant data = ui.blob_color->currentData(); + if (data.isValid()) + { + pt_color_type blob_color = pt_color_type(data.toInt()); + enabled = blob_color >= pt_color_red_chromakey && blob_color <= pt_color_magenta_chromakey; + } + ui.chroma_key_strength_slider->setEnabled(enabled); + ui.chroma_key_strength_label->setEnabled(enabled); + ui.chroma_key_overexposed->setEnabled(enabled); +} + void TrackerDialog_PT::trans_calib_step() { QMutexLocker l(&calibrator_mutex); @@ -267,7 +305,7 @@ 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(); } @@ -275,8 +313,18 @@ void TrackerDialog_PT::unregister_tracker() { 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 66981ee6..79cd91bd 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.h +++ b/tracker-pt/ftnoir_tracker_pt_dialog.h @@ -26,7 +26,10 @@ public: TrackerDialog_PT(const QString& module_name); void register_tracker(ITracker *tracker) override; void unregister_tracker() override; - void save(); + bool embeddable() noexcept override { return true; } + void set_buttons_visible(bool x) override; + void save() override; + void reload() override; public slots: void doOK(); void doCancel(); @@ -36,8 +39,8 @@ public slots: void poll_tracker_info_impl(); void set_camera_settings_available(const QString& camera_name); void show_camera_settings(); -signals: - void poll_tracker_info(); + void chroma_key_controls_enable(const QString&); + protected: QString threshold_display_text(int threshold_value); 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'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><html><head/><body><p>For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.</p></body></html></source> + <translation><html><head/><body><p>‚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.</p></body></html></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><html><head/><body><p>Location of the two remaining model points<br/>with respect to the reference point in default pose</p><p>Use any units you want, not necessarily centimeters.</p></body></html></source> + <translation><html><head/><body><p>Ort der verbleibenden zwei Modellpunkte<br/>unter Berücksichtigung der Referenzpunkte der Standard-Pose</p><p>Dies können beliebige Einheiten sein, nicht zwingend Zentimeter.</p></body></html></translation> + </message> + <message> + <source>y:</source> + <translation>y:</translation> + </message> + <message> + <source><html><head/><body><p><span style=" font-size:16pt;">P</span><span style=" font-size:16pt; vertical-align:sub;">3</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:16pt;">P</span><span style=" font-size:16pt; vertical-align:sub;">3</span></p></body></html></translation> + </message> + <message> + <source><html><head/><body><p><span style=" font-size:16pt;">P</span><span style=" font-size:16pt; vertical-align:sub;">2</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:16pt;">P</span><span style=" font-size:16pt; vertical-align:sub;">2</span></p></body></html></translation> + </message> + <message> + <source>Model position</source> + <translation>Modell-Position</translation> + </message> + <message> + <source>Use only yaw and pitch while calibrating. +Don'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><html><head/><body><p><span style=" font-weight:600;">FTNoIR PointTracker Plugin<br/>Version 1.1</span></p><p><span style=" font-weight:600;">by Patrick Ruoff</span></p><p><a href="http://ftnoirpt.sourceforge.net/"><span style=" font-weight:600; text-decoration: underline; color:#0000ff;">Manual (external)</span></a></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">FTNoIR PointTracker Plugin<br/>Version 1.1</span></p><p><span style=" font-weight:600;">von Patrick Ruoff</span></p><p><a href="http://ftnoirpt.sourceforge.net/"><span style=" font-weight:600; text-decoration: underline; color:#0000ff;">Anleitung (extern)</span></a></p></body></html></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 '%1'</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 1456157c..fc44b0f1 100644 --- a/tracker-pt/lang/nl_NL.ts +++ b/tracker-pt/lang/nl_NL.ts @@ -108,14 +108,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Average</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Natural</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Red only</source> <translation type="unfinished"></translation> </message> @@ -232,6 +224,94 @@ Don't roll or change position.</source> <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'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><html><head/><body><p>For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.</p></body></html></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> @@ -281,6 +361,13 @@ Don't roll or change position.</source> </message> </context> <context> + <name>pt_impl::Tracker_PT</name> + <message> + <source>Failed to open camera '%1'</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/lang/ru_RU.ts b/tracker-pt/lang/ru_RU.ts index c91dd087..7ff4657e 100644 --- a/tracker-pt/lang/ru_RU.ts +++ b/tracker-pt/lang/ru_RU.ts @@ -112,14 +112,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Average</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Natural</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Red only</source> <translation type="unfinished"></translation> </message> @@ -237,6 +229,94 @@ ROLL или X/Y-смещения.</translation> <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'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><html><head/><body><p>For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.</p></body></html></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> @@ -286,6 +366,13 @@ ROLL или X/Y-смещения.</translation> </message> </context> <context> + <name>pt_impl::Tracker_PT</name> + <message> + <source>Failed to open camera '%1'</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/lang/stub.ts b/tracker-pt/lang/stub.ts index 36090e5e..3dbe208d 100644 --- a/tracker-pt/lang/stub.ts +++ b/tracker-pt/lang/stub.ts @@ -108,14 +108,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Average</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Natural</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Red only</source> <translation type="unfinished"></translation> </message> @@ -232,6 +224,94 @@ Don't roll or change position.</source> <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'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><html><head/><body><p>For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.</p></body></html></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> @@ -281,6 +361,13 @@ Don't roll or change position.</source> </message> </context> <context> + <name>pt_impl::Tracker_PT</name> + <message> + <source>Failed to open camera '%1'</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/lang/zh_CN.ts b/tracker-pt/lang/zh_CN.ts index 667c2811..3519d719 100644 --- a/tracker-pt/lang/zh_CN.ts +++ b/tracker-pt/lang/zh_CN.ts @@ -200,14 +200,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Average</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Natural</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Red only</source> <translation type="unfinished"></translation> </message> @@ -232,6 +224,94 @@ Don't roll or change position.</source> <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'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><html><head/><body><p>For LEDs, 'Natural' is the fastest grayscale mode thanks to optimized SIMD code. Color key allows to track regular pieces of colored paper.</p></body></html></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> @@ -281,6 +361,13 @@ Don't roll or change position.</source> </message> </context> <context> + <name>pt_impl::Tracker_PT</name> + <message> + <source>Failed to open camera '%1'</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/module/CMakeLists.txt b/tracker-pt/module/CMakeLists.txt index f7a6cc7f..b7fc974f 100644 --- a/tracker-pt/module/CMakeLists.txt +++ b/tracker-pt/module/CMakeLists.txt @@ -1,7 +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-tracker-pt-base) + target_link_libraries(${self} opentrack-video opencv_imgproc opentrack-tracker-pt-base) target_include_directories(${self} PUBLIC "${CMAKE_SOURCE_DIR}/tracker-pt") endif() diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp index a70698de..1beba474 100644 --- a/tracker-pt/module/camera.cpp +++ b/tracker-pt/module/camera.cpp @@ -7,10 +7,7 @@ #include "camera.h" #include "frame.hpp" - -#include "compat/math-imports.hpp" - -#include <opencv2/core.hpp> +#include <opencv2/core/mat.hpp> namespace pt_module { @@ -73,23 +70,29 @@ Camera::result Camera::get_frame(pt_frame& frame_) return { false, {} }; } -bool Camera::start(const QString& name, int fps, int res_x, int res_y) +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 = fps; + 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); @@ -100,12 +103,16 @@ bool Camera::start(const QString& name, int fps, int res_x, int res_y) 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; @@ -141,7 +148,7 @@ bool Camera::get_frame_(cv::Mat& img) 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, stride); + img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, (size_t)stride); return true; } } diff --git a/tracker-pt/module/camera.h b/tracker-pt/module/camera.h index e2ba159f..e4772178 100644 --- a/tracker-pt/module/camera.h +++ b/tracker-pt/module/camera.h @@ -13,8 +13,6 @@ #include <memory> -#include <opencv2/core.hpp> - #include <QString> namespace pt_module { @@ -23,7 +21,7 @@ struct Camera final : pt_camera { Camera(const QString& module_name); - bool start(const QString& name, int fps, int res_x, int res_y) override; + bool start(const pt_settings& s) override; void stop() override; result get_frame(pt_frame& Frame) override; diff --git a/tracker-pt/module/frame.cpp b/tracker-pt/module/frame.cpp index ab110871..1a276f16 100644 --- a/tracker-pt/module/frame.cpp +++ b/tracker-pt/module/frame.cpp @@ -1,43 +1,52 @@ #include "frame.hpp" - #include "compat/math.hpp" - #include <opencv2/imgproc.hpp> namespace pt_module { -Preview& Preview::operator=(const pt_frame& frame_) +void Preview::set_last_frame(const pt_frame& frame_) { const cv::Mat& frame = frame_.as_const<const Frame>()->mat; + const bool need_resize = frame.size != frame_copy.size; - if (frame.channels() != 3) + if (frame.channels() == 1) { - eval_once(qDebug() << "tracker/pt: camera frame depth: 3 !=" << frame.channels()); - return *this; + if (need_resize) + { + frame_tmp.create(frame.size(), CV_8UC3); + cv::cvtColor(frame, frame_tmp, cv::COLOR_GRAY2BGR); + cv::resize(frame_tmp, frame_copy, frame_copy.size(), 0, 0, cv::INTER_NEAREST); + } + else + cv::cvtColor(frame, frame_copy, cv::COLOR_GRAY2BGR); + } + else if (frame.channels() == 3) + { + if (need_resize) + cv::resize(frame, frame_copy, frame_copy.size(), 0, 0, cv::INTER_NEAREST); + else + frame.copyTo(frame_copy); } - - const bool need_resize = frame.cols != frame_out.cols || frame.rows != frame_out.rows; - if (need_resize) - cv::resize(frame, frame_copy, cv::Size(frame_out.cols, frame_out.rows), 0, 0, cv::INTER_NEAREST); else - frame.copyTo(frame_copy); - - return *this; + { + eval_once(qDebug() << "tracker/pt: camera frame depth" << frame.channels() << "!= 3"); + frame_copy.create(frame_copy.size(), CV_8UC3); + frame_copy.setTo({0}); + } } Preview::Preview(int w, int h) { - ensure_size(frame_out, w, h, CV_8UC4); - ensure_size(frame_copy, w, h, CV_8UC3); - - frame_copy.setTo(cv::Scalar(0, 0, 0)); + frame_out.create(h, w, CV_8UC4); + frame_copy.create(h, w, CV_8UC3); + frame_copy.setTo({0}); } QImage Preview::get_bitmap() { - int stride = frame_out.step.p[0]; + int stride = (int)frame_out.step.p[0]; - if (stride < 64 || stride < frame_out.cols * 4) + if (stride < frame_out.cols * 4) { eval_once(qDebug() << "bad stride" << stride << "for bitmap size" << frame_copy.cols << frame_copy.rows); @@ -71,10 +80,4 @@ void Preview::draw_head_center(f x, f y) color, 1); } -void Preview::ensure_size(cv::Mat& frame, int w, int h, int type) -{ - if (frame.cols != w || frame.rows != h || frame.type() != type) - frame = cv::Mat(h, w, type); -} - } // ns pt_module diff --git a/tracker-pt/module/frame.hpp b/tracker-pt/module/frame.hpp index 89334599..0569a323 100644 --- a/tracker-pt/module/frame.hpp +++ b/tracker-pt/module/frame.hpp @@ -2,7 +2,7 @@ #include "pt-api.hpp" -#include <opencv2/core.hpp> +#include <opencv2/core/mat.hpp> #include <QImage> #ifdef __clang__ @@ -24,7 +24,7 @@ struct Preview final : pt_preview { Preview(int w, int h); - Preview& operator=(const pt_frame& frame) override; + void set_last_frame(const pt_frame& frame) override; QImage get_bitmap() override; void draw_head_center(f x, f y) override; @@ -32,9 +32,7 @@ struct Preview final : pt_preview operator cv::Mat const&() const { return frame_copy; } private: - static void ensure_size(cv::Mat& frame, int w, int h, int type); - - cv::Mat frame_copy, frame_out; + cv::Mat frame_copy, frame_out, frame_tmp; }; } // ns pt_module 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/zh_CN.ts b/tracker-pt/module/lang/zh_CN.ts index 03d19f4e..c39728a1 100644 --- a/tracker-pt/module/lang/zh_CN.ts +++ b/tracker-pt/module/lang/zh_CN.ts @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> -<TS version="2.1"> +<TS version="2.1" language="zh_CN"> <context> <name>pt_module::metadata_pt</name> <message> diff --git a/tracker-pt/module/point_extractor.cpp b/tracker-pt/module/point_extractor.cpp index 7ad9925a..3329fafc 100644 --- a/tracker-pt/module/point_extractor.cpp +++ b/tracker-pt/module/point_extractor.cpp @@ -9,9 +9,11 @@ #include "point_extractor.h" #include "point_tracker.h" #include "frame.hpp" - #include "cv/numeric.hpp" #include "compat/math.hpp" +#include "compat/math-imports.hpp" + +#include <opencv2/imgproc.hpp> #undef PREVIEW //#define PREVIEW @@ -33,7 +35,7 @@ using namespace numeric_types; /* http://en.wikipedia.org/wiki/Mean-shift -In this application the idea, is to eliminate any bias of the point estimate +In this application the idea, is to eliminate any bias of the point estimate which is introduced by the rather arbitrary thresholded area. One must recognize that the thresholded area can only move in one pixel increments since it is binary. Thus, its center of mass might make "jumps" as pixels are added/removed @@ -42,9 +44,9 @@ With mean-shift, a moving "window" or kernel is multiplied with the gray-scale image, and the COM is calculated of the result. This is iterated where the kernel center is set the previously computed COM. Thus, peaks in the image intensity distribution "pull" the kernel towards themselves. Eventually it stops moving, i.e. -then the computed COM coincides with the kernel center. We hope that the +then the computed COM coincides with the kernel center. We hope that the corresponding location is a good candidate for the extracted point. -The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free +The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free algorithm for tracking single particles with variable size and shape." (2008). */ static vec2 MeanShiftIteration(const cv::Mat1b &frame_gray, const vec2 ¤t_center, f filter_width) @@ -87,21 +89,16 @@ PointExtractor::PointExtractor(const QString& module_name) : s(module_name) void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame) { - if (ch[0].rows != orig_frame.rows || ch[0].cols != orig_frame.cols) - for (cv::Mat1b& x : ch) - x = cv::Mat1b(orig_frame.rows, orig_frame.cols); + for (cv::Mat1b& x : ch) + x.create(orig_frame.rows, orig_frame.cols); } void PointExtractor::ensure_buffers(const cv::Mat& frame) { const int W = frame.cols, H = frame.rows; - if (frame_gray.rows != W || frame_gray.cols != H) - { - frame_gray = cv::Mat1b(H, W); - frame_bin = cv::Mat1b(H, W); - frame_gray_unmasked = cv::Mat1b(H, W); - } + frame_gray.create(H, W); + frame_bin.create(H, W); } void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest) @@ -115,8 +112,43 @@ void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, 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: @@ -134,18 +166,44 @@ void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output) extract_single_channel(frame, 2, output); break; } - case pt_color_average: + case pt_color_red_chromakey: + { + filter_single_channel(frame, 1, -half_chr_key_str, -half_chr_key_str, s.chroma_key_overexposed, output); + break; + } + case pt_color_green_chromakey: + { + 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: { - const int W = frame.cols, H = frame.rows, sz = W*H; - cv::reduce(frame.reshape(1, sz), - output.reshape(1, sz), - 1, cv::REDUCE_AVG); + 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_natural: + case pt_color_bt709: +do_grayscale: cv::cvtColor(frame, output, cv::COLOR_BGR2GRAY); break; } @@ -177,11 +235,10 @@ void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& outpu 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 = uround(3 * pi * radius*radius); + const unsigned area = unsigned(iround(3 * pi * radius*radius)); const unsigned sz = unsigned(hist.cols * hist.rows); - constexpr unsigned min_thres = 64; - unsigned thres = min_thres; - for (unsigned i = sz-1, cnt = 0; i > 32; i--) + unsigned thres = 1; + for (unsigned i = sz-1, cnt = 0; i > 1; i--) { cnt += (unsigned)ptr[i]; if (cnt >= area) @@ -209,20 +266,15 @@ static void draw_blobs(cv::Mat& preview_frame, const blob* blobs, unsigned nblob cy = preview_frame.rows / f(size.height), c = std::fmax(f(1), cx+cy)/2; - constexpr unsigned fract_bits = 8; - constexpr int c_fract(1 << fract_bits); - - cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract)); + cv::Point p(iround(b.pos[0] * cx), iround(b.pos[1] * cy)); - auto circle_color = k >= PointModel::N_POINTS - ? cv::Scalar(192, 192, 192) - : cv::Scalar(255, 255, 0); + auto outline_color = k >= PointModel::N_POINTS + ? cv::Scalar(192, 192, 192) + : cv::Scalar(255, 255, 0); - const int overlay_size = iround(dpi); - - cv::circle(preview_frame, p, iround((b.radius + f(3.3) * c) * c_fract), - circle_color, overlay_size, - cv::LINE_AA, fract_bits); + 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); @@ -233,25 +285,51 @@ static void draw_blobs(cv::Mat& preview_frame, const blob* blobs, unsigned nblob cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy)); cv::putText(preview_frame, buf, pos, - cv::FONT_HERSHEY_PLAIN, overlay_size, text_color, + cv::FONT_HERSHEY_PLAIN, iround(dpi), text_color, 1); } } -void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector<vec2>& points) +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_unmasked); + color_to_grayscale(frame, frame_gray); #if defined PREVIEW cv::imshow("capture", frame_gray); cv::waitKey(1); #endif - threshold_image(frame_gray_unmasked, frame_bin); - frame_gray_unmasked.copyTo(frame_gray, frame_bin); + 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; @@ -299,7 +377,7 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_ } } - const f radius = std::sqrt(cnt / pi); + const f radius = std::sqrt((f)cnt) / std::sqrt(pi); if (radius > region_size_max || radius < region_size_min) continue; @@ -329,21 +407,14 @@ end: for (idx = 0; idx < sz; ++idx) { blob& b = blobs[idx]; - cv::Rect rect = b.rect; - - rect.x -= rect.width / 2; - rect.y -= rect.height / 2; - rect.width *= 2; - rect.height *= 2; - rect &= cv::Rect(0, 0, W, H); // crop at frame boundaries - + cv::Rect rect = b.rect & cv::Rect(0, 0, W, H); // crop at frame boundaries cv::Mat frame_roi = frame_gray(rect); // smaller values mean more changes. 1 makes too many changes while 1.5 makes about .1 static constexpr f radius_c = f(1.75); const f kernel_radius = b.radius * radius_c; - vec2 pos(rect.width/f(2), rect.height/f(2)); // position relative to ROI. + vec2 pos = meanshift_initial_guess(rect, frame_roi); // position relative to ROI. for (int iter = 0; iter < 10; ++iter) { @@ -358,9 +429,10 @@ end: b.pos[1] = pos[1] + rect.y; } - draw_blobs(preview_frame_.as<Frame>()->mat, - blobs.data(), blobs.size(), - frame_gray.size()); + 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. diff --git a/tracker-pt/module/point_extractor.h b/tracker-pt/module/point_extractor.h index a6103667..fbfdbb0b 100644 --- a/tracker-pt/module/point_extractor.h +++ b/tracker-pt/module/point_extractor.h @@ -9,12 +9,10 @@ #pragma once #include "pt-api.hpp" - +#include <opencv2/core/mat.hpp> +#include <opencv2/core/types.hpp> #include <vector> -#include <opencv2/core.hpp> -#include <opencv2/imgproc.hpp> - namespace pt_module { using namespace numeric_types; @@ -33,14 +31,18 @@ class PointExtractor final : public pt_point_extractor public: // extracts points from frame and draws some processing info into frame, if draw_output is set // dt: time since last call in seconds - void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector<vec2>& points) override; - PointExtractor(const QString& module_name); + void extract_points(const pt_frame& frame, + pt_preview& preview_frame, bool preview_visible, + std::vector<vec2>& points) override; + + explicit PointExtractor(const QString& module_name); + private: static constexpr int max_blobs = 16; pt_settings s; - cv::Mat1b frame_gray_unmasked, frame_bin, frame_gray; + cv::Mat1b frame_bin, frame_gray; cv::Mat1f hist; std::vector<blob> blobs; cv::Mat1b ch[3]; @@ -49,6 +51,7 @@ private: 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); diff --git a/tracker-pt/point-filter.cpp b/tracker-pt/point-filter.cpp new file mode 100644 index 00000000..10448fe2 --- /dev/null +++ b/tracker-pt/point-filter.cpp @@ -0,0 +1,72 @@ +#include "point-filter.hpp" +#include <algorithm> +#include <cmath> +#include <QDebug> + +namespace pt_point_filter_impl { + +void point_filter::reset() +{ + t = std::nullopt; +} + +const PointOrder& point_filter::operator()(const PointOrder& input, f deadzone_amount) +{ + using std::fmod; + using std::sqrt; + using std::pow; + using std::clamp; + + if (!s.enable_point_filter) + { + t = std::nullopt; + state_ = input; + return state_; + } + + if (!t) + { + t.emplace(); + state_ = input; + return state_; + } + + constexpr auto E = (f)1.75; + const f limit = (f)*s.point_filter_limit; + const f C = progn( + constexpr int A = 1'000'000; + double K = *s.point_filter_coefficient; + f log10_pos = -2 + (int)K, rest = (f)(.999-fmod(K, 1.)*.9); + return A * pow((f)10, (f)-log10_pos) * rest; + ); + + f dist = 0, dz = deadzone_amount * (f)s.point_filter_deadzone / 800; // sqrt(640^2 + 480^2) + + for (unsigned i = 0; i < 3; i++) + { + vec2 tmp = input[i] - state_[i]; + f x = sqrt(tmp.dot(tmp)); + x = std::max((f)0, x - dz); + dist = std::max(dist, x); + } + + if (dist < (f)1e-6) + return state_; + + f dt = (f)t->elapsed_seconds(); t->start(); + f delta = pow(dist, E) * C * dt; // gain + + //qDebug() << "gain" << std::min((f)1, delta); + + for (unsigned i = 0; i < 3; i++) + { + f x = clamp(delta, (f)0, limit); + state_[i] += x*(input[i] - state_[i]); + } + + return state_; +} + +point_filter::point_filter(const pt_settings& s) : s{s} {} + +} // ns pt_point_filter_impl diff --git a/tracker-pt/point-filter.hpp b/tracker-pt/point-filter.hpp new file mode 100644 index 00000000..c3c045dd --- /dev/null +++ b/tracker-pt/point-filter.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "cv/numeric.hpp" +#include "compat/timer.hpp" +#include "pt-settings.hpp" +#include <optional> +#include <array> + +namespace pt_point_filter_impl { + +using namespace numeric_types; +using PointOrder = std::array<numeric_types::vec2, 3>; + +class point_filter final +{ + PointOrder state_; + std::optional<Timer> t; + const pt_settings& s; + +public: + void reset(); + const PointOrder& operator()(const PointOrder& input, f deadzone_amount); + + explicit point_filter(const pt_settings& s); + ~point_filter() = default; + + OTR_DISABLE_MOVE_COPY(point_filter); +}; + +} // ns pt_point_filter_impl + +using point_filter = pt_point_filter_impl::point_filter; diff --git a/tracker-pt/point_tracker.cpp b/tracker-pt/point_tracker.cpp index e209938f..39e96038 100644 --- a/tracker-pt/point_tracker.cpp +++ b/tracker-pt/point_tracker.cpp @@ -68,7 +68,7 @@ void PointModel::set_model(const pt_settings& s) } } -void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const +void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d) { constexpr unsigned cnt = PointModel::N_POINTS; // fit line to orthographically projected points @@ -76,11 +76,10 @@ void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& t d_vals[cnt]; // get sort indices with respect to d scalar product for (unsigned i = 0; i < cnt; ++i) - d_vals[i] = t(d.dot(points[i]), i); + d_vals[i] = {d.dot(points[i]), i}; - std::sort(d_vals, - d_vals + 3, - [](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 < cnt; ++i) d_order[i] = d_vals[i].second; @@ -94,10 +93,11 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2* const pt_camera_info& info) { const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); - PointTracker::PointOrder p; - p[0] = project(vec3(0,0,0), fx); - p[1] = project(model.M01, fx); - p[2] = project(model.M02, fx); + PointTracker::PointOrder p { + project(vec3(0,0,0), fx), + project(model.M01, fx), + project(model.M02, fx) + }; constexpr unsigned sz = PointModel::N_POINTS; @@ -136,7 +136,9 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2* void PointTracker::track(const std::vector<vec2>& points, const PointModel& model, const pt_camera_info& info, - int init_phase_timeout) + int init_phase_timeout, + point_filter& filter, + f deadzone_amount) { const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); PointOrder order; @@ -149,7 +151,7 @@ void PointTracker::track(const std::vector<vec2>& points, 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(); diff --git a/tracker-pt/point_tracker.h b/tracker-pt/point_tracker.h index 70c7a9fc..7492b4eb 100644 --- a/tracker-pt/point_tracker.h +++ b/tracker-pt/point_tracker.h @@ -11,14 +11,11 @@ #include "cv/affine.hpp" #include "cv/numeric.hpp" #include "pt-api.hpp" +#include "point-filter.hpp" -#include <cstddef> -#include <memory> #include <vector> #include <array> -#include <opencv2/core.hpp> - #include <QObject> namespace pt_impl { @@ -46,7 +43,7 @@ struct PointModel final explicit PointModel(const pt_settings& s); void set_model(const pt_settings& s); - void get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const; + static void get_d_order(const vec2* points, unsigned* d_order, const vec2& d); }; // ---------------------------------------------------------------------------- @@ -60,7 +57,12 @@ public: // track the pose using the set of normalized point coordinates (x pos in range -0.5:0.5) // f : (focal length)/(sensor width) // dt : time since last call - void track(const std::vector<vec2>& projected_points, const PointModel& model, const pt_camera_info& info, int init_phase_timeout); + 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); @@ -70,14 +72,13 @@ private: // the points in model order using PointOrder = std::array<vec2, 3>; - PointOrder find_correspondences(const vec2* projected_points, const PointModel &model); + static PointOrder find_correspondences(const vec2* projected_points, const PointModel &model); PointOrder find_correspondences_previous(const vec2* points, const PointModel &model, const pt_camera_info& info); // 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; - PointOrder prev_positions; Timer t; bool init_phase = true; }; diff --git a/tracker-pt/pt-api.cpp b/tracker-pt/pt-api.cpp index f64d5c9a..d71c6e13 100644 --- a/tracker-pt/pt-api.cpp +++ b/tracker-pt/pt-api.cpp @@ -31,7 +31,7 @@ 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{15} * cy; + const f max_radius = f{30} * cy; const f radius = std::fmax(f{0}, (max_radius-min_radius) * threshold / f(255) + min_radius); @@ -40,15 +40,14 @@ f pt_point_extractor::threshold_radius_value(int w, int h, int threshold) std::tuple<f, f> pt_pixel_pos_mixin::to_pixel_pos(f x, f y, int w, int h) { - return std::make_tuple(w*(x+f{.5}), f{.5}*(h - 2*y*w)); + 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 std::make_tuple((px - w/f{2})/w, -(py - h/f{2})/w); + 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 index 741576a1..15021ff3 100644 --- a/tracker-pt/pt-api.hpp +++ b/tracker-pt/pt-api.hpp @@ -6,11 +6,9 @@ #include "options/options.hpp" #include <tuple> -#include <type_traits> +#include <vector> #include <memory> -#include <opencv2/core.hpp> - #include <QImage> #include <QString> @@ -32,6 +30,7 @@ struct pt_camera_info final int res_x = 0; int res_y = 0; QString name; + bool use_mjpeg = false; }; struct pt_pixel_pos_mixin @@ -58,11 +57,21 @@ struct pt_frame : pt_pixel_pos_mixin { 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 { - virtual pt_preview& operator=(const pt_frame&) = 0; + pt_preview() = default; + + OTR_DISABLE_MOVE_COPY(pt_preview); + + virtual void set_last_frame(const pt_frame&) = 0; virtual QImage get_bitmap() = 0; virtual void draw_head_center(f x, f y) = 0; }; @@ -75,7 +84,9 @@ struct pt_camera pt_camera(); virtual ~pt_camera(); - [[nodiscard]] virtual bool start(const QString& name, int fps, int res_x, int res_y) = 0; + OTR_DISABLE_MOVE_COPY(pt_camera); + + [[nodiscard]] virtual bool start(const pt_settings& s) = 0; virtual void stop() = 0; virtual result get_frame(pt_frame& frame) = 0; @@ -87,6 +98,7 @@ struct pt_camera 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 @@ -94,9 +106,11 @@ 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, std::vector<vec2>& points) = 0; + virtual void extract_points(const pt_frame& image, pt_preview& preview_frame, bool preview_visible, std::vector<vec2>& points) = 0; static f threshold_radius_value(int w, int h, int threshold); }; @@ -105,6 +119,8 @@ 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(); diff --git a/tracker-pt/pt-settings.hpp b/tracker-pt/pt-settings.hpp index 723ee08d..5d16d973 100644 --- a/tracker-pt/pt-settings.hpp +++ b/tracker-pt/pt-settings.hpp @@ -8,11 +8,17 @@ enum pt_color_type { // explicit values, gotta preserve the numbering in .ini // don't reuse when removing some of the modes - pt_color_natural = 2, + pt_color_bt709 = 2, + pt_color_hardware = 14, pt_color_red_only = 3, - pt_color_average = 5, pt_color_blue_only = 6, pt_color_green_only = 7, + pt_color_red_chromakey = 8, + pt_color_green_chromakey = 9, + pt_color_blue_chromakey = 10, + pt_color_cyan_chromakey = 11, + pt_color_yellow_chromakey = 12, + pt_color_magenta_chromakey = 13, }; namespace pt_impl { @@ -57,10 +63,18 @@ struct pt_settings final : options::opts value<bool> dynamic_pose { b, "dynamic-pose-resolution", false }; value<int> init_phase_timeout { b, "init-phase-timeout", 250 }; value<bool> auto_threshold { b, "automatic-threshold", true }; - value<pt_color_type> blob_color { b, "blob-color", pt_color_natural }; + value<pt_color_type> blob_color { b, "blob-color", pt_color_bt709 }; + value<bool> use_mjpeg { b, "use-mjpeg", false }; + value<slider_value> chroma_key_strength{ b, "chroma-key-strength", { 1.0, 0.5, 4. } }; + value<bool> chroma_key_overexposed{ b, "chroma-key-overexposed", false }; value<slider_value> threshold_slider { b, "threshold-slider", { 128, 0, 255 } }; + value<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) {} }; |