summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui32
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp1
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp2
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h1
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.cpp2
-rw-r--r--FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h2
-rw-r--r--FTNoIR_Tracker_PT/point_extractor.cpp80
-rw-r--r--FTNoIR_Tracker_PT/point_extractor.h6
-rw-r--r--README.md7
9 files changed, 124 insertions, 9 deletions
diff --git a/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui b/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui
index 1495249a..9ad4f83c 100644
--- a/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui
+++ b/FTNoIR_Tracker_PT/FTNoIR_PT_Controls.ui
@@ -848,6 +848,38 @@
</item>
</layout>
</item>
+
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_secondary">
+ <item>
+ <widget class="QLabel" name="label_secondary">
+ <property name="text">
+ <string>Hysteresis</string>
+ </property>
+ <property name="buddy">
+ <cstring>threshold_secondary_slider</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="threshold_secondary_slider">
+ <property name="toolTip">
+ <string>Per pixel hysteresis width (leave left if there is little difference between dot and non-dot, move right for increased stability against pixel noise)</string>
+ </property>
+ <property name="maximum">
+ <number>255</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp
index 639053b7..e3af446f 100644
--- a/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt.cpp
@@ -113,6 +113,7 @@ void Tracker::apply(const TrackerSettings& settings)
camera.set_f(settings.cam_f);
frame_rotation.rotation = static_cast<RotationType>(settings.cam_roll);
point_extractor.threshold_val = settings.threshold;
+ point_extractor.threshold_secondary_val = settings.threshold_secondary;
point_extractor.min_size = settings.min_point_size;
point_extractor.max_size = settings.max_point_size;
point_tracker.point_model = boost::shared_ptr<PointModel>(new PointModel(settings.M01, settings.M02));
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp
index fe995163..f8afa790 100644
--- a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.cpp
@@ -63,6 +63,7 @@ TrackerDialog::TrackerDialog()
ui.campitch_spin->setValue(settings.cam_pitch);
ui.camyaw_spin->setValue(settings.cam_yaw);
ui.threshold_slider->setValue(settings.threshold);
+ ui.threshold_secondary_slider->setValue(settings.threshold_secondary);
ui.chkEnableRoll->setChecked(settings.bEnableRoll);
ui.chkEnablePitch->setChecked(settings.bEnablePitch);
@@ -105,6 +106,7 @@ TrackerDialog::TrackerDialog()
connect( ui.campitch_spin,SIGNAL(valueChanged(int)), this,SLOT(set_cam_pitch(int)) );
connect( ui.camyaw_spin,SIGNAL(valueChanged(int)), this,SLOT(set_cam_yaw(int)) );
connect( ui.threshold_slider,SIGNAL(sliderMoved(int)), this,SLOT(set_threshold(int)) );
+ connect( ui.threshold_secondary_slider,SIGNAL(sliderMoved(int)), this,SLOT(set_threshold_secondary(int)) );
connect( ui.chkEnableRoll,SIGNAL(toggled(bool)), this,SLOT(set_ena_roll(bool)) );
connect( ui.chkEnablePitch,SIGNAL(toggled(bool)), this,SLOT(set_ena_pitch(bool)) );
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h
index 3e1af50a..de743ad8 100644
--- a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_dialog.h
@@ -54,6 +54,7 @@ protected slots:
void set_min_point_size(int val) { settings.min_point_size = val; settings_changed(); }
void set_max_point_size(int val) { settings.max_point_size = val; settings_changed(); }
void set_threshold(int val) { settings.threshold = val; settings_changed(); }
+ void set_threshold_secondary(int val) { settings.threshold_secondary = val; settings_changed(); }
void set_ena_roll(bool val) { settings.bEnableRoll = val; settings_changed(); }
void set_ena_pitch(bool val) { settings.bEnablePitch = val; settings_changed(); }
void set_ena_yaw(bool val) { settings.bEnableYaw = val; settings_changed(); }
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.cpp b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.cpp
index 62ba1e3e..50835cb8 100644
--- a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.cpp
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.cpp
@@ -29,6 +29,7 @@ void TrackerSettings::load_ini()
cam_pitch = iniFile.value("CameraPitch", 0).toInt();
cam_yaw = iniFile.value("CameraYaw", 0).toInt();
threshold = iniFile.value("PointExtractThreshold", 128).toInt();
+ threshold_secondary = iniFile.value("PointExtractThresholdSecondary", 128).toInt();
min_point_size = iniFile.value("PointExtractMinSize", 2).toInt();
max_point_size = iniFile.value("PointExtractMaxSize", 50).toInt();
M01[0] = iniFile.value("PointModelM01x", 0).toFloat();
@@ -74,6 +75,7 @@ void TrackerSettings::save_ini() const
iniFile.setValue("CameraPitch", cam_pitch);
iniFile.setValue("CameraYaw", cam_yaw);
iniFile.setValue("PointExtractThreshold", threshold);
+ iniFile.setValue("PointExtractThresholdSecondary", threshold_secondary);
iniFile.setValue("PointExtractMinSize", min_point_size);
iniFile.setValue("PointExtractMaxSize", max_point_size);
iniFile.setValue("PointModelM01x", M01[0]);
diff --git a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h
index 1cf60853..91a7a8d5 100644
--- a/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h
+++ b/FTNoIR_Tracker_PT/ftnoir_tracker_pt_settings.h
@@ -28,6 +28,8 @@ struct TrackerSettings
// point extraction
int threshold;
+ int threshold_secondary;
+
int min_point_size;
int max_point_size;
diff --git a/FTNoIR_Tracker_PT/point_extractor.cpp b/FTNoIR_Tracker_PT/point_extractor.cpp
index 261de60f..27a14713 100644
--- a/FTNoIR_Tracker_PT/point_extractor.cpp
+++ b/FTNoIR_Tracker_PT/point_extractor.cpp
@@ -8,15 +8,27 @@
#include "point_extractor.h"
#include <QDebug>
+
using namespace cv;
using namespace std;
+
+PointExtractor::PointExtractor(){
+ first = true;
+
+
+ //if (!AllocConsole()){}
+ //else SetConsoleTitle("debug");
+ //freopen("CON", "w", stdout);
+ //freopen("CON", "w", stderr);
+}
// ----------------------------------------------------------------------------
const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool draw_output)
{
const int W = frame.cols;
const int H = frame.rows;
+
// clear old points
points.clear();
@@ -24,10 +36,41 @@ const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool dr
Mat frame_gray;
cvtColor(frame, frame_gray, CV_RGB2GRAY);
- // convert to binary
+ int secondary = threshold_secondary_val;
+
+ // mask for everything that passes the threshold (or: the upper threshold of the hysteresis)
Mat frame_bin;
- threshold(frame_gray, frame_bin, threshold_val, 255, THRESH_BINARY);
+ // only used if draw_output
+ Mat frame_bin_copy;
+ // mask for everything that passes
+ Mat frame_bin_low;
+ // mask for lower-threshold && combined result of last, needs to remain in scope until drawing, but is only used if secondary != 0
+ Mat frame_last_and_low;
+ if(secondary==0){
+ threshold(frame_gray, frame_bin, threshold_val, 255, THRESH_BINARY);
+ }else{
+ // we recombine a number of buffers, this might be slower than a single loop of per-pixel logic
+ // but it might as well be faster if openCV makes good use of SIMD
+ float t = threshold_val;
+ //float hyst = float(threshold_secondary_val)/512.;
+ //threshold(frame_gray, frame_bin, (t + ((255.-t)*hyst)), 255, THRESH_BINARY);
+ float hyst = float(threshold_secondary_val)/256.;
+ threshold(frame_gray, frame_bin, t, 255, THRESH_BINARY);
+ threshold(frame_gray, frame_bin_low,std::max(float(1), t - (t*hyst)), 255, THRESH_BINARY);
+
+ if(draw_output) frame_bin.copyTo(frame_bin_copy);
+ if(first){
+ frame_bin.copyTo(frame_last);
+ first = false;
+ }else{
+ // keep pixels from last if they are above lower threshold
+ bitwise_and(frame_last, frame_bin_low, frame_last_and_low);
+ // union of pixels >= higher threshold and pixels >= lower threshold
+ bitwise_or(frame_bin, frame_last_and_low, frame_last);
+ frame_last.copyTo(frame_bin);
+ }
+ }
unsigned int region_size_min = 3.14*min_size*min_size/4.0;
unsigned int region_size_max = 3.14*max_size*max_size/4.0;
@@ -42,6 +85,7 @@ const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool dr
// find connected components with floodfill
if (frame_bin.at<unsigned char>(y,x) != 255) continue;
Rect rect;
+
floodFill(frame_bin, Point(x,y), Scalar(blob_index), &rect, Scalar(0), Scalar(0), FLOODFILL_FIXED_RANGE);
blob_index++;
@@ -70,9 +114,17 @@ const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool dr
for (int j=rect.x; j < (rect.x+rect.width); j++)
{
if (frame_bin.at<unsigned char>(i,j) != blob_index-1) continue;
- float val = frame_gray.at<unsigned char>(i,j);
- val = float(val - threshold_val)/(256 - threshold_val);
- val = val*val; // makes it more stable (less emphasis on low values, more on the peak)
+ float val;
+
+ if(secondary==0){
+ val = frame_gray.at<unsigned char>(i,j);
+ val = float(val - threshold_val)/(256 - threshold_val);
+ val = val*val; // makes it more stable (less emphasis on low values, more on the peak)
+ }else{
+ //hysteresis point detection gets stability from ignoring pixel noise so we decidedly leave the actual pixel values out of the picture
+ val = frame_last.at<unsigned char>(i,j) / 256.;
+ }
+
m += val;
mx += j * val;
my += i * val;
@@ -83,6 +135,7 @@ const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool dr
Vec2f c;
c[0] = (mx/m - W/2)/W;
c[1] = -(my/m - H/2)/W;
+ //qDebug()<<blob_index<<" => "<<c[0]<<" "<<c[1];
points.push_back(c);
}
}
@@ -90,10 +143,19 @@ const vector<Vec2f>& PointExtractor::extract_points(Mat frame, float dt, bool dr
// draw output image
if (draw_output) {
vector<Mat> channels;
- frame_bin.setTo(170, frame_bin);
- channels.push_back(frame_gray + frame_bin);
- channels.push_back(frame_gray - frame_bin);
- channels.push_back(frame_gray - frame_bin);
+ if(secondary==0){
+ frame_bin.setTo(170, frame_bin);
+ channels.push_back(frame_gray + frame_bin);
+ channels.push_back(frame_gray - frame_bin);
+ channels.push_back(frame_gray - frame_bin);
+ }else{
+ frame_bin_copy.setTo(120, frame_bin_copy);
+ frame_bin_low.setTo(90, frame_bin_low);
+ channels.push_back(frame_gray + frame_bin_copy);
+ channels.push_back(frame_gray + frame_last_and_low);
+ channels.push_back(frame_gray + frame_bin_low);
+ //channels.push_back(frame_gray + frame_bin);
+ }
merge(channels, frame);
}
diff --git a/FTNoIR_Tracker_PT/point_extractor.h b/FTNoIR_Tracker_PT/point_extractor.h
index c62d34f9..b9f46666 100644
--- a/FTNoIR_Tracker_PT/point_extractor.h
+++ b/FTNoIR_Tracker_PT/point_extractor.h
@@ -21,12 +21,18 @@ public:
// WARNING: returned reference is valid as long as object
const std::vector<cv::Vec2f>& extract_points(cv::Mat frame, float dt, bool draw_output);
const std::vector<cv::Vec2f>& get_points() { return points; }
+ PointExtractor();
int threshold_val;
+ int threshold_secondary_val;
int min_size, max_size;
protected:
std::vector<cv::Vec2f> points;
+ cv::Mat frame_last;
+
+
+ bool first;
};
#endif //POINTEXTRACTOR_H
diff --git a/README.md b/README.md
index fbbead33..8475ecfe 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
Windows binary builds are available at <http://ananke.laggy.pk/opentrack/>
Source code access available at <http://github.com/opentrack/opentrack/>
@@ -56,3 +57,9 @@ compatible with it unless resides in separate address space.
It's recommended to submit new code under ISC license, it's a shorter
boilerplate header than MIT/X11 or new BSD.
+
+# this fork/branch: usrusr/opentrack feature/pt-hysteresis
+
+Per pixel hysteresis for point tracker: sacrifice some precision, cpu and sensitivity to minor movements for greatly reduced jitter from pixel noise at full responsivity (usable without postprocessing filters!)
+-- Ulf Schreiber
+Ulf Schreiber