summaryrefslogtreecommitdiffhomepage
path: root/ftnoir_tracker_pt
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2015-09-30 16:21:41 +0200
committerStanislaw Halik <sthalik@misaki.pl>2015-09-30 16:22:00 +0200
commitac4dc94acc5d1e1d8fd4867730b835e2aa1fcc89 (patch)
tree8ca3dc84958e572e3f890ac3f56dfda670c63f60 /ftnoir_tracker_pt
parent03a164ed74d616e8d5203142ec7e9738c8aae547 (diff)
parenteb7703b4ecea5540a4c4a65faa1ff4c53fe97631 (diff)
Merge branch 'unstable' into trackhat-ui
Sponsored-by: TrackHat * unstable: (112 commits) Revert "accela: try more complex deadzone algorithm" pt: add wiki link for calibration instructions qfc: more gray colors when graph is disabled qfc, mapping: disallow editing and change color when checkbox disabled qfc: don't allow editing when disabled plugin-support: workaround multiple copies of modules accela: change translation gain accela: change rotation gain pt: change extraction code freepie-udp: add license freepie-udp: update accela: try more complex deadzone algorithm accela: add helpful comment accela: revert to more quadratic spline at start also save if save pending when start tracking ui: force saving if saving timer is pending accela: slightly more linear function pose-widget: simplify freepie-udp: nix harmless warning pose-widget: add comment simple-mat: add __restrict for GNU ui: save settings on a timer ui: show a warning if configuration directory can't be used aruco: tune min/max marker size win32-joystick: fix duplicate device name selection logic ui: no need to reset settings on tracking start ui: save profile when combobox text changes, not merely index ui: save current tracker/filter/proto, not last win32-joystick: fix COM failure path win32-joystick: nix unused variable win32-joystick: allow for unique selection of joysticks qfc: also draw line from function start while moving no need to save on tracking start anymore save axis opts, not just spline control points tracker: change centering order again fix typo cmake: don't say "opentrack" twice for osx build ui: really don't switch profiles while refreshing combobox pt: fix type mismatch ui: save prior to switching configs tracker_rs: adjusted tracker's name ui: don't reload settings if config wasn't changed ui: save everything before switching profiles options: return .ini basename easier tracker_rs: more readable icon tracker_rs: prevent potential thread starvation ui: switch to newly-created config ui: decruft ui: also bail on new config if its name is ".ini" ui: auto-refresh config list ui: display up to 20 profiles w/o scrolling ui: create an empty config properly in another code branch udp-tracker: silence harmless warning plugin-support: fix typo plugin-support: set more sane RTLD flags plugin-support: free modules from address space when needed ui: rename symbol to reflect current usage ui: move some slots to private ui, shortcuts: these are slots, actually ui: no need to write anything to new empty config ui: avoid potential memory leak ft: new game support game-data: chomp newlines in extractor ui: append extension in the right place simple-mat: fix arglist SFINAE simple-mat: can use static_cast here nix default empty config. it gets created on its own. ui: these aren't qt slots ui: nix wrong headers ui: remove GNU GPL ui: simplify condition props changed only main, ui: store config files in a predefined directory pt: show extracted points' areas and success count close dialogs on switch to new module ...
Diffstat (limited to 'ftnoir_tracker_pt')
-rw-r--r--ftnoir_tracker_pt/ftnoir_tracker_pt.cpp10
-rw-r--r--ftnoir_tracker_pt/point_extractor.cpp217
2 files changed, 76 insertions, 151 deletions
diff --git a/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp b/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp
index 9cfd31eb..04f1b8d9 100644
--- a/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp
+++ b/ftnoir_tracker_pt/ftnoir_tracker_pt.cpp
@@ -63,15 +63,15 @@ bool Tracker_PT::get_focal_length(float& ret)
break;
}
- const float diag_fov = static_cast<int>(fov_) * pi / 180.f;
+ const double diag_fov = static_cast<int>(fov_) * pi / 180.f;
QMutexLocker l(&camera_mtx);
CamInfo info;
const bool res = camera.get_info(info);
if (res)
{
const int w = info.res_x, h = info.res_y;
- const double diag = sqrt(w * w + h * h)/w;
- const double fov = 2.*atan(tan(diag_fov/2.0)/sqrt(1. + diag*diag));
+ const double diag = sqrt(1. + h/(double)w * h/(double)w);
+ const double fov = 2.*atan(tan(diag_fov/2.0)/diag);
ret = .5 / tan(.5 * fov);
return true;
}
@@ -105,6 +105,10 @@ void Tracker_PT::run()
QMutexLocker lock(&mutex);
std::vector<cv::Vec2f> points = point_extractor.extract_points(frame);
+
+ // blobs are sorted in order of circularity
+ if (points.size() > PointModel::N_POINTS)
+ points.resize(PointModel::N_POINTS);
bool success = points.size() == PointModel::N_POINTS;
diff --git a/ftnoir_tracker_pt/point_extractor.cpp b/ftnoir_tracker_pt/point_extractor.cpp
index 7174d719..fcdbbaed 100644
--- a/ftnoir_tracker_pt/point_extractor.cpp
+++ b/ftnoir_tracker_pt/point_extractor.cpp
@@ -28,198 +28,119 @@ std::vector<cv::Vec2f> PointExtractor::extract_points(cv::Mat& frame)
cv::Mat frame_gray;
cv::cvtColor(frame, frame_gray, cv::COLOR_RGB2GRAY);
- int min_size = s.min_point_size;
- int max_size = s.max_point_size;
+ const int region_size_min = s.min_point_size;
+ const int region_size_max = s.max_point_size;
- 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;
-
- // testing indicates threshold difference of 45 from lowest to highest
- // that's applicable to poor lighting conditions.
-
- static constexpr int diff = 20;
- static constexpr int steps = 5;
- static constexpr int successes = 5;
-
- int thres = s.threshold;
-
- struct blob {
- std::vector<cv::Vec2d> pos;
- std::vector<double> confids;
- std::vector<double> areas;
-
- cv::Vec2d effective_pos() const
- {
- double x = 0;
- double y = 0;
- double norm = 0;
- for (unsigned i = 0; i < pos.size(); i++)
- {
- const double w = confids[i] * areas[i];
- x += pos[i][0] * w;
- y += pos[i][1] * w;
- norm += w;
- }
- cv::Vec2d ret(x, y);
- ret *= 1./norm;
- return ret;
- }
- };
+ const int thres = s.threshold;
struct simple_blob
{
- double radius_2;
+ double radius;
cv::Vec2d pos;
double confid;
bool taken;
double area;
- simple_blob(double radius, const cv::Vec2d& pos, double confid, double area) : radius_2(radius*radius), pos(pos), confid(confid), taken(false), area(area)
+ simple_blob(double radius, const cv::Vec2d& pos, double confid, double area) : radius(radius), pos(pos), confid(confid), taken(false), area(area)
{
//qDebug() << "radius" << radius << "pos" << pos[0] << pos[1] << "confid" << confid;
}
bool inside(const simple_blob& other)
{
cv::Vec2d tmp = pos - other.pos;
- return tmp.dot(tmp) < radius_2;
- }
- static std::vector<blob> merge(std::vector<simple_blob>& blobs)
- {
-#ifdef DEBUG_EXTRACTION
- static Timer t;
- bool debug = t.elapsed_ms() > 100;
- if (debug) t.start();
-#endif
-
- std::vector<blob> ret;
- for (unsigned i = 0; i < blobs.size(); i++)
- {
- auto& b = blobs[i];
- if (b.taken)
- continue;
- b.taken = true;
- blob b_;
- b_.pos.push_back(b.pos);
- b_.confids.push_back(b.confid);
- b_.areas.push_back(b.area);
-
- for (unsigned j = i+1; j < blobs.size(); j++)
- {
- auto& b2 = blobs[j];
- if (b2.taken)
- continue;
- if (b.inside(b2) || b2.inside(b))
- {
- b2.taken = true;
- b_.pos.push_back(b2.pos);
- b_.confids.push_back(b2.confid);
- b_.areas.push_back(b2.area);
- }
- }
- if (b_.pos.size() >= successes)
- ret.push_back(b_);
- }
-#ifdef DEBUG_EXTRACTION
- if (debug)
- {
- double diff = 0;
- for (unsigned j = 0; j < ret.size(); j++)
- {
- auto& b = ret[j];
- cv::Vec2d pos = b.effective_pos();
- for (unsigned i = 0; i < b.pos.size(); i++)
- {
- auto tmp = pos - b.pos[i];
- diff += std::abs(tmp.dot(tmp));
- }
- }
- qDebug() << "diff" << diff;
- }
-#endif
- return ret;
+ return sqrt(tmp.dot(tmp)) < radius;
}
};
// mask for everything that passes the threshold (or: the upper threshold of the hysteresis)
cv::Mat frame_bin = cv::Mat::zeros(H, W, CV_8U);
- const int min = std::max(0, thres - diff/2);
- const int max = std::min(255, thres + diff/2);
- const int step = std::max(1, diff / steps);
-
std::vector<simple_blob> blobs;
-
- // this code is based on OpenCV SimpleBlobDetector
- for (int i = min; i < max; i += step)
+ std::vector<std::vector<cv::Point>> contours;
{
cv::Mat frame_bin_;
- cv::threshold(frame_gray, frame_bin_, i, 255, cv::THRESH_BINARY);
+ cv::threshold(frame_gray, frame_bin_, thres, 255, cv::THRESH_BINARY);
frame_bin.setTo(170, frame_bin_);
-
- std::vector<std::vector<cv::Point>> contours;
cv::findContours(frame_bin_, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
-
- int cnt = 0;
-
- for (auto& c : contours)
+ }
+
+ int cnt = 0;
+
+ for (auto& c : contours)
+ {
+ if (cnt++ > 30)
+ break;
+
+ const auto m = cv::moments(cv::Mat(c));
+ const double area = m.m00;
+ if (area == 0.)
+ continue;
+ const cv::Vec2d pos(m.m10 / m.m00, m.m01 / m.m00);
+
+ double radius;
+// following based on OpenCV SimpleBlobDetector
{
- if (cnt++ > 30)
- break;
-
- auto m = cv::moments(cv::Mat(c));
- const double area = m.m00;
- if (area == 0.)
- continue;
- cv::Vec2d pos(m.m10 / m.m00, m.m01 / m.m00);
- if (area < region_size_min || area > region_size_max)
- continue;
-
- double radius = 0;
-
+ std::vector<double> dists;
for (auto& k : c)
{
- cv::Vec2d pos_(k.x, k.y);
- cv::Vec2d tmp = pos_ - pos;
- radius = std::max(radius, sqrt(1e-2 + tmp.dot(tmp)));
+ dists.push_back(cv::norm(pos - cv::Vec2d(k.x, k.y)));
}
- double confid = 1;
+ std::sort(dists.begin(), dists.end());
+ radius = (dists[(dists.size() - 1)/2] + dists[dists.size()/2])/2;
+ }
+
+ if (radius < region_size_min || radius > region_size_max)
+ continue;
+
+ double confid = 1;
+ {
+ double denominator = std::sqrt(std::pow(2 * m.mu11, 2) + std::pow(m.mu20 - m.mu02, 2));
+ const double eps = 1e-2;
+ if (denominator > eps)
{
- double denominator = std::sqrt(std::pow(2 * m.mu11, 2) + std::pow(m.mu20 - m.mu02, 2));
- const double eps = 1e-2;
- if (denominator > eps)
- {
- double cosmin = (m.mu20 - m.mu02) / denominator;
- double sinmin = 2 * m.mu11 / denominator;
- double cosmax = -cosmin;
- double sinmax = -sinmin;
-
- double imin = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmin - m.mu11 * sinmin;
- double imax = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmax - m.mu11 * sinmax;
- confid = imin / imax;
- }
+ double cosmin = (m.mu20 - m.mu02) / denominator;
+ double sinmin = 2 * m.mu11 / denominator;
+ double cosmax = -cosmin;
+ double sinmax = -sinmin;
+
+ double imin = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmin - m.mu11 * sinmin;
+ double imax = 0.5 * (m.mu20 + m.mu02) - 0.5 * (m.mu20 - m.mu02) * cosmax - m.mu11 * sinmax;
+ confid = imin / imax;
}
- blobs.push_back(simple_blob(radius, pos, confid, area));
}
+// end SimpleBlobDetector
+
+ {
+ char buf[64];
+ sprintf(buf, "%.2fpx %.2fc", radius, confid);
+ cv::putText(frame, buf, cv::Point(pos[0]+30, pos[1]+20), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 255), 1);
+ }
+
+ blobs.push_back(simple_blob(radius, pos, confid, area));
}
// clear old points
points.clear();
+
+ using b = const simple_blob;
+ std::sort(blobs.begin(), blobs.end(), [](b& b1, b& b2) {return b1.confid > b2.confid;});
- for (auto& b : simple_blob::merge(blobs))
+ for (auto& b : blobs)
{
- auto pos = b.effective_pos();
- cv::Vec2f p((pos[0] - W/2)/W, -(pos[1] - H/2)/W);
+ cv::Vec2f p((b.pos[0] - W/2)/W, -(b.pos[1] - H/2)/W);
points.push_back(p);
}
+ // draw output image
std::vector<cv::Mat> channels_;
cv::split(frame, channels_);
- // draw output image
- cv::Mat frame_bin_ = frame_bin * .5;
std::vector<cv::Mat> channels;
- channels.push_back(channels_[0] + frame_bin_);
- channels.push_back(channels_[1] - frame_bin_);
- channels.push_back(channels_[2] - frame_bin_);
- cv::merge(channels, frame);
+ {
+ cv::Mat frame_bin__ = frame_bin * .5;
+ channels.push_back(channels_[0] + frame_bin__);
+ channels.push_back(channels_[1] - frame_bin__);
+ channels.push_back(channels_[2] - frame_bin__);
+ cv::merge(channels, frame);
+ }
return points;
}