Skip to content

Commit bc847f9

Browse files
authored
Merge pull request #157 from AIRLegend/dev
v0.6.6
2 parents fba4fb6 + 19fd880 commit bc847f9

28 files changed

+389
-224
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,9 @@ Client/models
350350
prefs.ini
351351
log.txt
352352

353+
353354
# CMake
354355
CMakeCache.txt
355356
CMakeFiles/
356-
357+
/cmake-build-debug/
358+
cmake_install.cmake

AITracker/AITracker.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@
189189
<ConformanceMode>true</ConformanceMode>
190190
<AdditionalIncludeDirectories>$(SolutionDir)Dependencies\OpenCV\include\;$(SolutionDir)Dependencies\onnxruntime\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
191191
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
192-
<Optimization>Disabled</Optimization>
192+
<Optimization>MaxSpeed</Optimization>
193193
</ClCompile>
194194
<Link>
195195
<SubSystem>Console</SubSystem>

AITracker/src/PositionSolver.cpp

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "PositionSolver.h"
22

3+
#define USE_FOV // Use FOV correction for the camera matrix.
34

45
PositionSolver::PositionSolver(
56
int width,
@@ -20,16 +21,16 @@ PositionSolver::PositionSolver(
2021
{
2122
this->prior_pitch = -1.57;
2223
this->prior_yaw = -1.57;
23-
this->prior_distance = prior_distance * -2.;
24+
this->prior_distance = prior_distance * -2.0;
2425

2526
this->rv[0] = this->prior_pitch;
2627
this->rv[1] = this->prior_yaw;
2728
this->rv[2] = -1.57;
2829
this->tv[2] = this->prior_distance;
2930

3031
head3dScale = (cv::Mat_<double>(3, 3) <<
31-
x_scale, 0.0, 0,
32-
0.0, y_scale, 0,
32+
y_scale, 0.0, 0, // pitch is rv[0], pitch involves y-axis
33+
0.0, x_scale, 0, // yaw is rv[1], yaw involves x-axis
3334
0.0, 0.0, z_scale
3435
);
3536

@@ -63,9 +64,9 @@ PositionSolver::PositionSolver(
6364
{
6465
contour_indices = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,27,28,29,30,31,32,33,34,35,36,39,42,45 };
6566

66-
landmark_points_buffer = cv::Mat(contour_indices.size(), 1, CV_32FC2);
67+
landmark_points_buffer = cv::Mat((int)contour_indices.size(), 1, CV_32FC2);
6768

68-
mat3dcontour = (cv::Mat_<double>(contour_indices.size(), 3) <<
69+
mat3dcontour = (cv::Mat_<double>((int)contour_indices.size(), 3) <<
6970
0.45517698, -0.30089578, 0.76442945,
7071
0.44899884, -0.16699584, 0.76514298,
7172
0.43743154, -0.02265548, 0.73926717,
@@ -102,15 +103,44 @@ PositionSolver::PositionSolver(
102103
// https://github.com/opentrack/opentrack/blob/3cc3ef246ad71c463c8952bcc96984b25d85b516/tracker-aruco/ftnoir_tracker_aruco.cpp#L193
103104
// Taking into account the camera FOV instead of assuming raw image dims is more clever and
104105
// will make the solver more camera-agnostic.
105-
float diag_fov = fov * TO_RAD;
106+
float diag_fov = (float)(fov * TO_RAD);
106107

107108
// Get expressed in sensor-size units
108109

110+
#ifdef USE_FOV
111+
// field of view is a rectangular viewport with corners on a circular lens
112+
// the diagonal of the rectangle can be expressed as the angular field of view or pixels
113+
// the width of the rectangle can be expressed as either the field of view width or width in pixels
114+
// the height of the rectangle can be expressed as either the field of view height or height in pixes
115+
double width_squared = (double)width * width;
116+
double height_squared = (double)height * height;
117+
double diagonal_squared = width_squared + height_squared;
118+
double diagonal = sqrt(diagonal_squared); // hypotenuse of triangle
119+
120+
// set default focalLength for width and heigh if field of view is not set
121+
double focalLength_width = width;
122+
double focalLength_height = height;
123+
if (fov != 0.0)
124+
{
125+
double fov_w = (double)diag_fov * width / diagonal;
126+
double fov_h = (double)diag_fov * height / diagonal;
127+
128+
focalLength_width = 0.5 * width / tan(0.5 * fov_w);
129+
focalLength_height = 0.5 * height / tan(0.5 * fov_h);
130+
}
131+
132+
camera_matrix = (cv::Mat_<double>(3, 3) <<
133+
focalLength_height, 0, height / 2,
134+
0, focalLength_width, width / 2,
135+
0, 0, 1
136+
);
137+
138+
#else
109139
double fov_w = 2. * atan(tan(diag_fov / 2.) / sqrt(1. + height / (double)width * height / (double)width));
110140
double fov_h = 2. * atan(tan(diag_fov / 2.) / sqrt(1. + width / (double)height * width / (double)height));
111141

112-
float i_height = .5 * height / (tan(.5*fov_w));
113-
float i_width = .5* width / (tan(.5*fov_h));
142+
float i_height = (float)(0.5f * height / (tan(0.5 * fov_w)));
143+
float i_width = (float)(0.5f * width / (tan(0.5 * fov_h)));
114144

115145
/*camera_matrix = (cv::Mat_<double>(3, 3) <<
116146
height, 0, height / 2,
@@ -122,7 +152,10 @@ PositionSolver::PositionSolver(
122152
i_width, 0, height / 2,
123153
0, i_height, width / 2,
124154
0, 0, 1
125-
);
155+
);
156+
157+
158+
#endif
126159

127160
camera_distortion = (cv::Mat_<double>(4, 1) << 0, 0, 0, 0);
128161

@@ -131,6 +164,7 @@ PositionSolver::PositionSolver(
131164
if(complex) std::cout << "Using complex solver" << std::endl;
132165
}
133166

167+
134168
void PositionSolver::solve_rotation(FaceData* face_data)
135169
{
136170
int contour_idx = 0;
@@ -139,8 +173,7 @@ void PositionSolver::solve_rotation(FaceData* face_data)
139173
for (int i = 0; i < contour_indices.size(); i++)
140174
{
141175
contour_idx = contour_indices[i];
142-
landmark_points_buffer.at<float>(i, j) = (int)face_data->landmark_coords[2 * contour_idx + j];
143-
176+
landmark_points_buffer.at<float>(i, j) = (float)(int)face_data->landmark_coords[2 * contour_idx + j]; // fix complation warnings.
144177
}
145178
}
146179

@@ -164,15 +197,18 @@ void PositionSolver::solve_rotation(FaceData* face_data)
164197
for (int i = 0; i < 3; i++)
165198
{
166199
face_data->rotation[i] = rvec.at<double>(i, 0);
167-
face_data->translation[i] = tvec.at<double>(i, 0) * 10;
200+
face_data->translation[i] = tvec.at<double>(i, 0) * 10; // scale solvePnP coordinates to opentrack units in centimeters
168201
}
169202

170-
// We dont want the Z axis oversaturated.
203+
// We dont want the Z axis oversaturated since opentrack has +/-600 centimeter range
171204
face_data->translation[2] /= 100;
172205

173-
std::cout << face_data->to_string() << std::endl;
206+
#ifdef _DEBUG
207+
std::cout << face_data->to_string() << std::endl; // disable copy constructor and output to std::cout
208+
#endif
174209

175210
correct_rotation(*face_data);
211+
clip_rotations(*face_data);
176212

177213
}
178214

@@ -218,14 +254,38 @@ void PositionSolver::get_euler(cv::Mat& rvec, cv::Mat& tvec)
218254

219255
void PositionSolver::correct_rotation(FaceData& face_data)
220256
{
221-
float distance = -(face_data.translation[2]);
222-
float lateral_offset = face_data.translation[1];
223-
float verical_offset = face_data.translation[0];
257+
float distance = (float) -(face_data.translation[2]);
258+
float lateral_offset = (float)face_data.translation[1];
259+
float verical_offset = (float)face_data.translation[0];
224260

225-
float correction_yaw = std::atan(std::tan(lateral_offset / distance)) * TO_DEG;
226-
float correction_pitch = std::atan(std::tan(verical_offset / distance)) * TO_DEG;
261+
float correction_yaw = (float)(std::atan(lateral_offset / distance) * TO_DEG); // (lateral_offset / distance) is already tangent, so only need atan to obtain radians
262+
float correction_pitch = (float)(std::atan(verical_offset / distance) * TO_DEG); // (verical_offset / distance) is already tangent, so only need atan to obtain radians
227263

228264
face_data.rotation[1] += correction_yaw;
229265
face_data.rotation[0] += correction_pitch;
266+
267+
// Note: We could saturate pitch here, but its better to let the user do it via Opentrack.
268+
// The coefficient could be problematic for some users.
269+
//face_data.rotation[0] = face_data.rotation[0] * 1.5;
270+
}
271+
272+
273+
void PositionSolver::clip_rotations(FaceData& face_data)
274+
{
275+
// Limit yaw between -90.0 and +90.0 degrees
276+
if (face_data.rotation[1] >= 90.0)
277+
face_data.rotation[1] = 90.0;
278+
else if (face_data.rotation[1] <= -90.0)
279+
face_data.rotation[1] = -90.0;
280+
// Limit pitch between -90.0 and +90.0
281+
if (face_data.rotation[0] >= 90.0)
282+
face_data.rotation[0] = 90.0;
283+
else if (face_data.rotation[0] <= -90.0)
284+
face_data.rotation[0] = -90.0;
285+
// Limit roll between -90.0 and +90.0
286+
if (face_data.rotation[2] >= 90.0)
287+
face_data.rotation[2] = 90.0;
288+
else if (face_data.rotation[2] <= -90.0)
289+
face_data.rotation[2] = -90.0;
230290
}
231291

AITracker/src/PositionSolver.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include "opencv2/core/matx.hpp"
4-
#include "opencv.hpp"
4+
#include "opencv2/opencv.hpp"
55
#include "data.h"
66

77
/**
@@ -68,5 +68,12 @@ class PositionSolver
6868
* This method corrects them.
6969
*/
7070
void correct_rotation(FaceData& face_data);
71+
72+
73+
/**
74+
* Ensures all rotations are in -90/90 range.
75+
* (No human head can rotate more supposing camera view is frontal).
76+
*/
77+
void clip_rotations(FaceData& face_data);
7178
};
7279

AITracker/src/data.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ FaceData::FaceData():
99
face_detected = false;
1010
}
1111

12-
1312
std::string FaceData::to_string()
1413
{
1514
std::string datastring =
@@ -21,7 +20,7 @@ std::string FaceData::to_string()
2120
std::string(" X: ") +
2221
std::to_string(this->translation[0]) + ", Y: " +
2322
std::to_string(this->translation[1]) + ", Z: " +
24-
std::to_string(this->translation[2]) ;
23+
std::to_string(this->translation[2]);
2524

2625
return datastring;
2726
}

AITracker/src/filters.cpp

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,57 @@
11
#include "filters.h"
2-
32
#include <cstring>
3+
#include <cmath>
4+
45

56
MAFilter::MAFilter(int steps, int array_size)
67
{
78
this->n_steps = steps;
89
this->array_size = array_size;
9-
this->idx = 0;
10+
this->idx = 0; // this->idx < this->n_steps
1011

11-
this->circular_buffer = new float[steps * array_size];
12+
this->circular_buffer = (float *)new float[steps * array_size]; // float[steps][array_size]
13+
this->sum = (float *)new float[array_size]; // use this array to cache the sum
14+
for (int i = 0; i < array_size; i++)
15+
this->sum[i] = nanf("");
1216
}
1317

1418
MAFilter::~MAFilter()
1519
{
1620
delete[] this->circular_buffer;
21+
delete[] this->sum;
1722
}
1823

1924
void MAFilter::filter(float* in_array, float* out_array)
2025
{
2126
int offset = this->idx * this->array_size;
27+
// equivalent to:
28+
// typedef float (*CIRCULAR_BUFFER_IDX)[this->array_size];
29+
// typedef float (*CIRCULAR_BUFFER)[this->n_steps][this->array_size];
30+
float *circular_buffer_idx = &this->circular_buffer[offset]; // CIRCULAR_BUFFER_IDX circular_buffer_idx = &((CIRCULAR_BUFFER)this->circular_buffer)[this->idx][0];
31+
2232
for (int i = 0; i < this->array_size; i++)
2333
{
24-
// Insert current position
25-
this->circular_buffer[offset + i] = in_array[i];
26-
out_array[i] = 0;
27-
28-
// get mean of all steps for this position
29-
for (int j = 0; j < this->n_steps; j++)
34+
if (isnan(this->sum[i]))
3035
{
31-
out_array[i] += this->circular_buffer[j * this->array_size + i];
36+
// initialize sum
37+
this->sum[i] = in_array[i] * this->n_steps;
38+
// calculate average
39+
out_array[i] = this->sum[i] / this->n_steps;
40+
// initialize empty circular_buffer with new value
41+
for (int j = 0; j < this->n_steps; j++)
42+
{
43+
this->circular_buffer[j * this->array_size + i] = in_array[i];
44+
}
45+
}
46+
else
47+
{
48+
// Recalculate sum by subtracting old value and adding new value
49+
this->sum[i] = this->sum[i] - circular_buffer_idx[i] + in_array[i];
50+
// calculate average
51+
out_array[i] = this->sum[i] / this->n_steps;
52+
// Insert current position
53+
circular_buffer_idx[i] = in_array[i];
3254
}
33-
34-
out_array[i] /= this->n_steps;
3555
}
3656

3757
this->idx = (this->idx + 1) % this->n_steps;
@@ -56,7 +76,7 @@ void EAFilter::filter(float* in_array, float* out_array)
5676
{
5777
for (int i = 0; i < array_size; i++)
5878
{
59-
out_array[i] = 0.6 * in_array[i] + 0.4 * this->last_value[i];
79+
out_array[i] = (0.6f * in_array[i] + 0.4f * this->last_value[i]);
6080
this->last_value[i] = in_array[i];
6181
}
6282

AITracker/src/filters.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ class MAFilter : public IFilter
1616
int n_steps;
1717

1818
float *circular_buffer;
19-
20-
19+
float *sum;
2120

2221
public:
2322
MAFilter(int steps, int array_size);
@@ -34,8 +33,6 @@ class EAFilter : public IFilter
3433
int array_size;
3534
float* last_value;
3635

37-
38-
3936
public:
4037
EAFilter(int array_size);
4138
~EAFilter();

0 commit comments

Comments
 (0)