summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rwxr-xr-xbin/NPClient.dllbin18944 -> 15872 bytes
-rwxr-xr-xbin/NPClient64.dllbin20480 -> 19968 bytes
-rw-r--r--csv/csv.cpp3
-rw-r--r--freetrackclient/fttypes.h52
-rw-r--r--ftnoir_filter_accela/ftnoir_filter_accela.cpp67
-rw-r--r--ftnoir_filter_accela/ftnoir_filter_accela.h4
-rw-r--r--ftnoir_protocol_ft/ftnoir_protocol_ft.cpp78
-rw-r--r--ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx30
-rw-r--r--ftnoir_tracker_pt/point_extractor.cpp267
-rw-r--r--qfunctionconfigurator/qfunctionconfigurator.cpp7
11 files changed, 322 insertions, 196 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2cd3f477..1571556a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -188,6 +188,8 @@ endif()
# ----
+set(opentrack-perms PERMISSIONS WORLD_READ WORLD_EXECUTE OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE)
+
if(WIN32)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_RC_COMPILER_INIT i686-w64-mingw32-windres)
@@ -249,6 +251,7 @@ set_target_properties(opentrack-version PROPERTIES
"IN_VERSION_UNIT;OPENTRACK_VERSION=\"${OPENTRACK__COMMIT}\"")
opentrack_library(opentrack-filter-accela ftnoir_filter_accela STATIC TRUE)
+target_link_libraries(opentrack-filter-accela opentrack-spline-widget)
#opentrack_library(opentrack-filter-ewma ftnoir_filter_ewma2)
opentrack_library(opentrack-proto-fgfs ftnoir_protocol_fg)
@@ -477,8 +480,9 @@ endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/3rdparty-notices DESTINATION .)
-install(FILES "${CMAKE_SOURCE_DIR}/bin/freetrackclient.dll" DESTINATION .)
-install(FILES "${CMAKE_SOURCE_DIR}/bin/NPClient.dll" "${CMAKE_SOURCE_DIR}/bin/NPClient64.dll" "${CMAKE_SOURCE_DIR}/bin/TrackIR.exe" DESTINATION .)
+install(FILES "${CMAKE_SOURCE_DIR}/bin/freetrackclient.dll" DESTINATION . ${opentrack-perms})
+install(FILES "${CMAKE_SOURCE_DIR}/bin/NPClient.dll" "${CMAKE_SOURCE_DIR}/bin/NPClient64.dll" "${CMAKE_SOURCE_DIR}/bin/TrackIR.exe" DESTINATION .
+ ${opentrack-perms})
install(DIRECTORY "${CMAKE_SOURCE_DIR}/bin/settings" "${CMAKE_SOURCE_DIR}/clientfiles" DESTINATION .)
if(NOT WIN32 AND SDK_WINE_PREFIX AND NOT SDK_WINE_NO_WRAPPER)
@@ -489,7 +493,7 @@ endif()
install(TARGETS opentrack DESTINATION .)
if(SDK_VJOY)
- install(FILES "${SDK_VJOY}/VJoy.dll" DESTINATION . PERMISSIONS WORLD_READ WORLD_EXECUTE OWNER_WRITE OWNER_READ OWNER_EXECUTE)
+ install(FILES "${SDK_VJOY}/VJoy.dll" DESTINATION . ${opentrack-perms})
endif()
if(WIN32)
diff --git a/bin/NPClient.dll b/bin/NPClient.dll
index 47172c22..bf971c03 100755
--- a/bin/NPClient.dll
+++ b/bin/NPClient.dll
Binary files differ
diff --git a/bin/NPClient64.dll b/bin/NPClient64.dll
index f7b30a90..fd3164e5 100755
--- a/bin/NPClient64.dll
+++ b/bin/NPClient64.dll
Binary files differ
diff --git a/csv/csv.cpp b/csv/csv.cpp
index 71db8ecb..e55b429c 100644
--- a/csv/csv.cpp
+++ b/csv/csv.cpp
@@ -97,7 +97,8 @@ void CSV::getGameData( const int id, unsigned char* table, QString& gamename)
QString gameID = QString::number(id);
/* zero table first, in case unknown game is connecting */
- memset(table, 0, 8);
+ for (int i = 0; i < 8; i++)
+ table[i] = 0;
QStringList gameLine;
qDebug() << "getGameData, ID = " << gameID;
diff --git a/freetrackclient/fttypes.h b/freetrackclient/fttypes.h
index ad974110..8611017a 100644
--- a/freetrackclient/fttypes.h
+++ b/freetrackclient/fttypes.h
@@ -31,39 +31,39 @@ typedef __int32 int32_t;
/* only 6 headpose floats and the data id are filled -sh */
typedef struct __FTData {
- int DataID;
- int CamWidth;
- int CamHeight;
+ volatile int DataID;
+ volatile int CamWidth;
+ volatile int CamHeight;
/* virtual pose */
- float Yaw; /* positive yaw to the left */
- float Pitch; /* positive pitch up */
- float Roll; /* positive roll to the left */
- float X;
- float Y;
- float Z;
+ volatile float Yaw; /* positive yaw to the left */
+ volatile float Pitch; /* positive pitch up */
+ volatile float Roll; /* positive roll to the left */
+ volatile float X;
+ volatile float Y;
+ volatile float Z;
/* raw pose with no smoothing, sensitivity, response curve etc. */
- float RawYaw;
- float RawPitch;
- float RawRoll;
- float RawX;
- float RawY;
- float RawZ;
+ volatile float RawYaw;
+ volatile float RawPitch;
+ volatile float RawRoll;
+ volatile float RawX;
+ volatile float RawY;
+ volatile float RawZ;
/* raw points, sorted by Y, origin top left corner */
- float X1;
- float Y1;
- float X2;
- float Y2;
- float X3;
- float Y3;
- float X4;
- float Y4;
+ volatile float X1;
+ volatile float Y1;
+ volatile float X2;
+ volatile float Y2;
+ volatile float X3;
+ volatile float Y3;
+ volatile float X4;
+ volatile float Y4;
} FTData;
/* we add some shit at the end for other legacy proto, sadly */
typedef struct __FTHeap {
FTData data;
- int32_t GameID;
- unsigned char table[8];
- int32_t GameID2;
+ volatile int32_t GameID;
+ volatile unsigned char table[8];
+ volatile int32_t GameID2;
} FTHeap;
diff --git a/ftnoir_filter_accela/ftnoir_filter_accela.cpp b/ftnoir_filter_accela/ftnoir_filter_accela.cpp
index f0b0b468..5adaea23 100644
--- a/ftnoir_filter_accela/ftnoir_filter_accela.cpp
+++ b/ftnoir_filter_accela/ftnoir_filter_accela.cpp
@@ -12,20 +12,42 @@
#include "opentrack/plugin-api.hpp"
using namespace std;
-FTNoIR_Filter::FTNoIR_Filter() : first_run(true)
-{
-}
+static constexpr double rot_gains[][2] = {
+ { 7, 200 },
+ { 6, 100 },
+ { 5, 45 },
+ { 4, 15 },
+ { 3, 5 },
+ { 2, 1.4 },
+ { 1, .4 },
+ { 0, .2 },
+ { -1, 0 }
+};
+static constexpr double trans_gains[][2] = {
+ { 5, 180 },
+ { 4, 64 },
+ { 3, 20 },
+ { 2, 5 },
+ { 1, .7 },
+ { 0, .1 },
+ { -1, 0 }
+};
-double FTNoIR_Filter::f(double val, const double gains[][2])
+FTNoIR_Filter::FTNoIR_Filter() : first_run(true)
{
- for (int i = 0; gains[i][0] >= 0; i++)
+ rot.setMaxInput(rot_gains[0][0]);
+ trans.setMaxInput(trans_gains[0][0]);
+ rot.setMaxOutput(rot_gains[0][1]);
+ trans.setMaxOutput(trans_gains[0][1]);
+
+ for (int i = 0; rot_gains[i][0] >= 0; i++)
{
- if (val >= gains[i][0])
- {
- return gains[i][1] * val;
- }
+ rot.addPoint(QPointF(rot_gains[i][0], rot_gains[i][1]));
+ }
+ for (int i = 0; trans_gains[i][0] >= 0; i++)
+ {
+ trans.addPoint(QPointF(trans_gains[i][0], trans_gains[i][1]));
}
- return 0;
}
void FTNoIR_Filter::filter(const double* input, double *output)
@@ -43,25 +65,6 @@ void FTNoIR_Filter::filter(const double* input, double *output)
return;
}
- static const double rot_gains[][2] = {
- { 6, 15 },
- { 5, 8 },
- { 4, 4 },
- { 3, 1.6 },
- { 2, .7 },
- { 1, .4 },
- { 0, .2 },
- { -1, 0 }
- };
- static const double trans_gains[][2] = {
- { 4, 8 },
- { 3, 4 },
- { 2, 2 },
- { 1, .5 },
- { 0, .1 },
- { -1, 0 }
- };
-
const double rot_t = 10. * (1+s.rot_threshold) / 100.;
const double trans_t = 5. * (1+s.trans_threshold) / 100.;
@@ -75,6 +78,8 @@ void FTNoIR_Filter::filter(const double* input, double *output)
for (int i = 0; i < 6; i++)
{
+ Map& m = i >= 3 ? rot : trans;
+
smoothed_input[i] = smoothed_input[i] * (1.-alpha) + input[i] * alpha;
const double in = smoothed_input[i];
@@ -83,14 +88,12 @@ void FTNoIR_Filter::filter(const double* input, double *output)
const double dz = i >= 3 ? rot_dz : trans_dz;
const double vec_ = max(0., fabs(vec) - dz);
const double thres = i >= 3 ? rot_t : trans_t;
- const double val = f(vec_ / thres, i >= 3 ? rot_gains : trans_gains) * thres;
+ const double val = m.getValue(vec_ / thres) * thres;
const double result = last_output[i] + (vec < 0 ? -1 : 1) * dt * val;
const bool negp = vec < 0.;
const bool done = negp
? result <= in
: result >= in;
- if (i == 3 && val > 0.1 && done)
- qDebug() << "done";
const double ret = done ? in : result;
last_output[i] = output[i] = ret;
diff --git a/ftnoir_filter_accela/ftnoir_filter_accela.h b/ftnoir_filter_accela/ftnoir_filter_accela.h
index 17698922..e2b29ee4 100644
--- a/ftnoir_filter_accela/ftnoir_filter_accela.h
+++ b/ftnoir_filter_accela/ftnoir_filter_accela.h
@@ -1,5 +1,6 @@
#pragma once
#include "opentrack/plugin-api.hpp"
+#include "qfunctionconfigurator/functionconfig.h"
#include <atomic>
#include <QMutex>
#include <QTimer>
@@ -25,12 +26,11 @@ class FTNoIR_Filter : public IFilter
public:
FTNoIR_Filter();
void filter(const double* input, double *output);
+ Map rot, trans;
private:
settings_accela s;
bool first_run;
double last_output[6];
double smoothed_input[6];
Timer t;
-
- double f(double val, const double gains[][2]);
};
diff --git a/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp b/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp
index 312cf127..56ae061f 100644
--- a/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp
+++ b/ftnoir_protocol_ft/ftnoir_protocol_ft.cpp
@@ -54,44 +54,51 @@ void FTNoIR_Protocol::pose(const double* headpose) {
float tx = headpose[TX] * 10.f;
float ty = headpose[TY] * 10.f;
float tz = headpose[TZ] * 10.f;
-
- shm.lock();
-
- pMemData->data.RawX = 0;
- pMemData->data.RawY = 0;
- pMemData->data.RawZ = 0;
- pMemData->data.RawPitch = 0;
- pMemData->data.RawYaw = 0;
- pMemData->data.RawRoll = 0;
-
- pMemData->data.X = tx;
- pMemData->data.Y = ty;
- pMemData->data.Z = tz;
- pMemData->data.Yaw = yaw;
- pMemData->data.Pitch = pitch;
- pMemData->data.Roll = roll;
-
- pMemData->data.X1 = pMemData->data.DataID;
- pMemData->data.X2 = 0;
- pMemData->data.X3 = 0;
- pMemData->data.X4 = 0;
- pMemData->data.Y1 = 0;
- pMemData->data.Y2 = 0;
- pMemData->data.Y3 = 0;
- pMemData->data.Y4 = 0;
-
- if (intGameID != pMemData->GameID)
+
+ FTHeap* ft = pMemData;
+ FTData* data = &ft->data;
+
+ data->RawX = 0;
+ data->RawY = 0;
+ data->RawZ = 0;
+ data->RawPitch = 0;
+ data->RawYaw = 0;
+ data->RawRoll = 0;
+
+ data->X = tx;
+ data->Y = ty;
+ data->Z = tz;
+ data->Yaw = yaw;
+ data->Pitch = pitch;
+ data->Roll = roll;
+
+ data->X1 = data->DataID;
+ data->X2 = 0;
+ data->X3 = 0;
+ data->X4 = 0;
+ data->Y1 = 0;
+ data->Y2 = 0;
+ data->Y3 = 0;
+ data->Y4 = 0;
+
+ int32_t id = ft->GameID;
+
+ if (intGameID != id)
{
QString gamename;
- CSV::getGameData(pMemData->GameID, pMemData->table, gamename);
- pMemData->GameID2 = pMemData->GameID;
- intGameID = pMemData->GameID;
+ {
+ unsigned char table[8];
+ for (int i = 0; i < 8; i++) table[i] = pMemData->table[i];
+ CSV::getGameData(id, table, gamename);
+ for (int i = 0; i < 8; i++) pMemData->table[i] = table[i];
+ }
+ ft->GameID2 = id;
+ intGameID = id;
QMutexLocker foo(&this->game_name_mutex);
connected_game = gamename;
}
-
- pMemData->data.DataID += 1;
- shm.unlock();
+
+ data->DataID += 1;
}
void FTNoIR_Protocol::start_tirviews() {
@@ -165,9 +172,10 @@ bool FTNoIR_Protocol::correct()
pMemData->data.CamWidth = 100;
pMemData->data.CamHeight = 250;
pMemData->GameID2 = 0;
- memset(pMemData->table, 0, 8);
+ for (int i = 0; i < 8; i++)
+ pMemData->table[i] = 0;
- return true;
+ return true;
}
extern "C" OPENTRACK_EXPORT IProtocol* GetConstructor()
diff --git a/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx b/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx
index d5d03011..896d8078 100644
--- a/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx
+++ b/ftnoir_protocol_wine/opentrack-wrapper-wine-main.cxx
@@ -40,27 +40,23 @@ private:
int main(void)
{
- ShmPosix lck_posix(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM));
+ ShmPosix lck_posix(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM));
ShmWine lck_wine("FT_SharedMem", "FT_Mutext", sizeof(FTHeap));
if(!lck_posix.success()) {
- printf("Can't open posix map: %d\n", errno);
- return 1;
- }
- if(!lck_wine.success()) {
- printf("Can't open Wine map\n");
- return 1;
- }
+ printf("Can't open posix map: %d\n", errno);
+ return 1;
+ }
+ if(!lck_wine.success()) {
+ printf("Can't open Wine map\n");
+ return 1;
+ }
WineSHM* shm_posix = (WineSHM*) lck_posix.ptr();
FTHeap* shm_wine = (FTHeap*) lck_wine.ptr();
FTData* data = &shm_wine->data;
create_registry_key();
- while (1) {
- lck_posix.lock();
- if (shm_posix->stop) {
- lck_posix.unlock();
- break;
- }
- lck_wine.lock();
+ while (1) {
+ if (shm_posix->stop)
+ break;
data->Yaw = -shm_posix->data[Yaw];
data->Pitch = -shm_posix->data[Pitch];
data->Roll = shm_posix->data[Roll];
@@ -74,8 +70,6 @@ int main(void)
shm_posix->gameid = shm_wine->GameID;
for (int i = 0; i < 8; i++)
shm_wine->table[i] = shm_posix->table[i];
- lck_wine.unlock();
- lck_posix.unlock();
(void) Sleep(4);
- }
+ }
}
diff --git a/ftnoir_tracker_pt/point_extractor.cpp b/ftnoir_tracker_pt/point_extractor.cpp
index c186f5f2..e81e3aa0 100644
--- a/ftnoir_tracker_pt/point_extractor.cpp
+++ b/ftnoir_tracker_pt/point_extractor.cpp
@@ -8,6 +8,9 @@
#include "point_extractor.h"
#include <QDebug>
+#ifdef DEBUG_EXTRACTION
+# include "opentrack/timer.hpp"
+#endif
using namespace cv;
using namespace std;
@@ -25,94 +28,202 @@ std::vector<Vec2f> PointExtractor::extract_points(Mat& frame)
const int W = frame.cols;
const int H = frame.rows;
- // clear old points
- points.clear();
-
- // convert to grayscale
- Mat frame_gray;
+ // convert to grayscale
+ Mat frame_gray;
cvtColor(frame, frame_gray, cv::COLOR_RGB2GRAY);
+
+ int min_size = s.min_point_size;
+ int max_size = 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;
+ }
+ };
+
+ struct simple_blob
+ {
+ double radius_2;
+ 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)
+ {
+ //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;
+ }
+ };
- int primary = s.threshold;
-
// mask for everything that passes the threshold (or: the upper threshold of the hysteresis)
- Mat frame_bin;
+ Mat frame_bin = cv::Mat::zeros(H, W, CV_8U);
- threshold(frame_gray, frame_bin, primary, 255, THRESH_BINARY);
+ 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);
- int min_size = s.min_point_size;
- int max_size = s.max_point_size;
+ std::vector<simple_blob> blobs;
- 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;
-
- int blob_index = 1;
- for (int y=0; y<H; y++)
- {
- if (blob_index >= 255) break;
- for (int x=0; x<W; x++)
- {
- if (blob_index >= 255) break;
-
- // 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++;
-
- // calculate the size of the connected component
- unsigned int region_size = 0;
- for (int i=rect.y; i < (rect.y+rect.height); i++)
- {
- for (int j=rect.x; j < (rect.x+rect.width); j++)
- {
- if (frame_bin.at<unsigned char>(i,j) != blob_index-1) continue;
- region_size++;
- }
- }
-
- if (region_size < region_size_min || region_size > region_size_max) continue;
-
- // calculate the center of mass:
- // mx = (sum_ij j*f(frame_grey_ij)) / (sum_ij f(frame_grey_ij))
- // my = ...
- // f maps from [threshold,256] -> [0, 1], lower values are mapped to 0
- float m = 0;
- float mx = 0;
- float my = 0;
- for (int i=rect.y; i < (rect.y+rect.height); i++)
- {
- 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;
-
- val = frame_gray.at<unsigned char>(i,j);
- val = float(val - primary)/(256 - primary);
- val = val*val; // makes it more stable (less emphasis on low values, more on the peak)
-
- m += val;
- mx += j * val;
- my += i * val;
- }
- }
-
- // convert to centered camera coordinate system with y axis upwards
- 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);
- }
+ // this code is based on OpenCV SimpleBlobDetector
+ for (int i = min; i < max; i += step)
+ {
+ Mat frame_bin_;
+ threshold(frame_gray, frame_bin_, i, 255, 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)
+ {
+ 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;
+
+ 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)));
+ }
+ 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 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));
+ }
}
+ // clear old points
+ points.clear();
+
+ for (auto& b : simple_blob::merge(blobs))
+ {
+ auto pos = b.effective_pos();
+ Vec2f p((pos[0] - W/2)/W, -(pos[1] - H/2)/W);
+ points.push_back(p);
+ }
+
+ vector<Mat> channels_;
+ cv::split(frame, channels_);
// draw output image
+ Mat frame_bin_ = frame_bin * .5;
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);
+ channels.push_back(channels_[0] + frame_bin_);
+ channels.push_back(channels_[1] - frame_bin_);
+ channels.push_back(channels_[2] - frame_bin_);
merge(channels, frame);
-
+
return points;
}
diff --git a/qfunctionconfigurator/qfunctionconfigurator.cpp b/qfunctionconfigurator/qfunctionconfigurator.cpp
index c1f11ffa..e1c40396 100644
--- a/qfunctionconfigurator/qfunctionconfigurator.cpp
+++ b/qfunctionconfigurator/qfunctionconfigurator.cpp
@@ -131,8 +131,8 @@ void QFunctionConfigurator::drawFunction()
QPen pen(spline_color, 1.2, Qt::SolidLine);
- static constexpr double step = 1.02;
const double max = _config->maxInput();
+ const double step = std::max(.1, max / 300.);
QPointF prev = point_to_pixel(QPointF(0, 0));
for (double i = 0; i < max; i += step) {
@@ -141,6 +141,11 @@ void QFunctionConfigurator::drawFunction()
drawLine(&painter, prev, cur, pen);
prev = cur;
}
+ if (points.size())
+ {
+ auto last = point_to_pixel(points[points.size()-1]);
+ drawLine(&painter, prev, last, pen);
+ }
}
void QFunctionConfigurator::paintEvent(QPaintEvent *e)