This repository has been archived by the owner on May 3, 2021. It is now read-only.
forked from thp/psmoveapi
-
Notifications
You must be signed in to change notification settings - Fork 8
/
psmove_tracker.c
2722 lines (2269 loc) · 96.6 KB
/
psmove_tracker.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* PS Move API - An interface for the PS Move Motion Controller
* Copyright (c) 2012 Thomas Perl <m@thp.io>
* Copyright (c) 2012 Benjamin Venditt <benjamin.venditti@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
**/
#include "psmove_platform_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <time.h>
#include <math.h>
#include <sys/stat.h>
#include "opencv2/core/core_c.h"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/highgui/highgui_c.h"
#include "psmove_tracker.h"
#include "../psmove_private.h"
#include "psmove_tracker_private.h"
#include "psmove_kalman_filter.h"
#include "psmove_lowpass_filter.h"
#include "camera_control.h"
#include "camera_control_private.h"
#include "tracker_helpers.h"
#include "tracker_trace.h"
#ifdef __linux
# include "platform/psmove_linuxsupport.h"
#endif
#ifdef _MSC_VER
#include <Windows.h>
#endif
//#define DEBUG_WINDOWS // shall additional windows be shown
#define ROIS 4 // the number of levels of regions of interest (roi)
#define BLINKS 2 // number of diff images to create during calibration
#define COLOR_MAPPING_RING_BUFFER_SIZE 256 /* Has to be 256, so that next_slot automatically wraps */
#define PSEYE_BACKUP_FILE "PSEye_backup.ini"
#define INTRINSICS_XML "intrinsics.xml"
#define DISTORTION_XML "distortion.xml"
#define COLOR_MAPPING_DAT "colormapping.dat"
#define SMOOTHING_SETTINGS_CSV "smoothing_settings.csv"
#define SPHERE_RADIUS_CM 2.25
#define MAX_CAMERA_INIT_QUERY_ATTEMPTS 100
// Smoothing defaults
static const PSMoveTrackerSmoothingSettings tracker_smoothing_default_settings = {
.filter_do_2d_xy = 1,
.filter_do_2d_r = 1,
.filter_3d_type = Smoothing_None,
.acceleration_variance = 10.f,
.measurement_covariance = { 0.01f, 0, 0,
0, 0.01f, 0,
0, 0, 1.f}
};
static const PSMoveTrackerSettings tracker_default_settings = {
.camera_frame_width = 0,
.camera_frame_height = 0,
.camera_frame_rate = 0,
.camera_auto_gain = PSMove_False,
.camera_gain = 0,
.camera_auto_white_balance = PSMove_False,
.camera_exposure = (255 * 15) / 0xFFFF,
.camera_brightness = 0,
.camera_mirror = PSMove_True,
.camera_type = PSMove_Camera_PS3EYE_BLUEDOT,
.exposure_mode = Exposure_LOW,
.calibration_blink_delay = 200,
.calibration_diff_t = 20,
.calibration_min_size = 50,
.calibration_max_distance = 30,
.calibration_size_std = 10,
.color_mapping_max_age = 2 * 60 * 60,
.dimming_factor = 1.f,
.color_hue_filter_range = 20,
.color_saturation_filter_range = 85,
.color_value_filter_range = 85,
.use_fitEllipse = 0,
.color_adaption_quality_t = 35.f,
.color_update_rate = 1.f,
.search_tile_width = 0,
.search_tile_height = 0,
.search_tiles_horizontal = 0,
.search_tiles_count = 0,
.roi_adjust_fps_t = 160,
.tracker_quality_t1 = 0.3f,
.tracker_quality_t2 = 0.7f,
.tracker_quality_t3 = 4.7f,
.color_update_quality_t1 = 0.8,
.color_update_quality_t2 = 0.2,
.color_update_quality_t3 = 6.f,
.color_save_colormapping = PSMove_True,
.color_list_start_ind = 0,
.xorigin_cm = 0.f,
.yorigin_cm = 0.f,
.zorigin_cm = 0.f
};
#define POSITION_FILTER_RESET_TIME 1.0f // time since last position update after which we just reset the
/**
* Syntactic sugar - iterate over all valid controllers of a tracker
*
* Usage example:
*
* TrackedController *tc;
* for_each_controller (tracker, tc) {
* // do something with "tc" here
* }
*
**/
#define for_each_controller(tracker, var) \
for (var=tracker->controllers; var<tracker->controllers+PSMOVE_TRACKER_MAX_CONTROLLERS; var++) \
if (var->move)
struct _TrackedController {
/* Move controller, or NULL if free slot */
PSMove* move;
/* Assigned RGB color of the controller */
struct PSMove_RGBValue color;
CvScalar eFColor; // first estimated color (BGR)
CvScalar eFColorHSV; // first estimated color (HSV)
CvScalar eColor; // estimated color (BGR)
CvScalar eColorHSV; // estimated color (HSV)
int roi_x, roi_y; // x/y - Coordinates of the ROI
int roi_level; // the current index for the level of ROI
enum PSMove_Bool roi_level_fixed; // true if the ROI level should be fixed
float mx, my; // x/y - Coordinates of center of mass of the blob
float x, y, r; // x/y - Coordinates of the controllers sphere and its radius
float old_x, old_y, old_r;
int search_tile; // current search quadrant when controller is not found (reset to 0 if found)
float rs; // a smoothed variant of the radius
// For positional tracker
void *position_filter;
PSMove_3AxisVector position_cm; // x/y/z coordinates of sphere in cm from camera focal point
PSMove_3AxisVector position_offset; // x/y/z offsets. Can be used to define origin in camera-space.
float el_major, el_angle; // For the found ellipse. Used for annotation only.
float q1, q2, q3; // Calculated quality criteria from the tracker
bool quality_check; // If q1, q2, q3 all pass.
bool was_tracked; // tracked previous frame
bool is_tracked; // tracked this frame
PSMove_timestamp last_color_update; // the timestamp when the last color adaption has been performed
PSMove_timestamp last_position_update; // the timestamp when the last position update has been performed
enum PSMove_Bool auto_update_leds;
};
typedef struct _TrackedController TrackedController;
/**
* A ring buffer used for saving color mappings of previous sessions. There
* is a pointer "next_slot" that will point to the next free slot. From there,
* new values can be saved (forward) and old values can be searched (backward).
**/
struct ColorMappingRingBuffer {
struct {
/* The RGB LED value */
struct PSMove_RGBValue from;
/* The dimming factor for which this mapping is valid */
unsigned char dimming;
/* The value of the controller in the camera */
struct PSMove_RGBValue to;
} map[COLOR_MAPPING_RING_BUFFER_SIZE];
unsigned char next_slot;
};
/**
* Parameters of the Pearson type VII distribution
* Source: http://fityk.nieto.pl/model.html
* Used for calculating the distance from the radius
**/
struct PSMoveTracker_DistanceParameters {
float height;
float center;
float hwhm;
float shape;
};
/**
* Experimentally-determined parameters for a PS Eye camera
* in wide angle mode with a PS Move, color = (255, 0, 255)
**/
static struct PSMoveTracker_DistanceParameters
pseye_distance_parameters = {
/* height = */ 517.281f,
/* center = */ 1.297338f,
/* hwhm = */ 3.752844f,
/* shape = */ 0.4762335f,
};
struct _PSMoveTracker {
CameraControl* cc;
PSMoveTrackerSettings settings; // Camera and tracker algorithm settings. Generally do not change after startup & calibration.
// The type of position smoothing to use
enum PSMoveTracker_Smoothing_Type smoothing_type;
// Settings used by the positional smoothing algorithms
PSMoveTrackerSmoothingSettings smoothing_settings;
/* Timestamps for performance measurements */
PSMove_timestamp ts_camera_begin; // when the capture was started
PSMove_timestamp ts_camera_grab; // when the image was grabbed
PSMove_timestamp ts_camera_retrieve; // when the image was retrieved
PSMove_timestamp ts_camera_converted; // when the image was converted
IplImage* frame; // the current frame of the camera
IplImage *frame_rgb; // the frame as tightly packed RGB data
IplImage* roiI[ROIS]; // array of images for each level of roi (colored)
IplImage* roiM[ROIS]; // array of images for each level of roi (greyscale)
IplConvKernel* kCalib; // kernel used for morphological operations during calibration
CvScalar rHSV; // the range of the color filter
// Parameters for psmove_tracker_distance_from_radius()
struct PSMoveTracker_DistanceParameters distance_parameters;
TrackedController controllers[PSMOVE_TRACKER_MAX_CONTROLLERS]; // controller data
struct ColorMappingRingBuffer color_mapping; // remembered color mappings
CvMemStorage* storage; // use to store the result of cvFindContour and cvHughCircles
long duration; // duration of tracking operation, in ms
// internal variables (debug)
float debug_fps; // the current FPS achieved by "psmove_tracker_update"
};
/* Preset colors - use them in ascending order if not used yet */
#define N_PRESET_COLORS 5 // preprocessor def needed to create const array (next line)
static const struct PSMove_RGBValue preset_colors[N_PRESET_COLORS] = {
{ 0xFF, 0x00, 0xFF }, /* magenta */
{ 0x00, 0xFF, 0xFF }, /* cyan */
{ 0xFF, 0xFF, 0x00 }, /* yellow */
{ 0xFF, 0x00, 0x00 }, /* red */
#ifdef __APPLE__
{ 0x00, 0xFF, 0x00 }, /* green */
#else
{ 0x00, 0x00, 0xFF }, /* blue */
#endif
};
/* Last error posted by tracker initialization */
enum PSMoveTracker_ErrorCode g_last_tracker_error_code = PSMove_Camera_Error_None;
// -------- START: internal functions only
/**
* Adapts the cameras exposure to the current lighting conditions
*
* This function will find the most suitable exposure.
*
* tracker - A valid PSMoveTracker * instance
* target_luminance - The target luminance value (higher = brighter)
*
* Returns: the most suitable exposure
**/
int psmove_tracker_adapt_to_light(PSMoveTracker *tracker, float target_luminance);
/**
* Find the TrackedController * for a given PSMove * instance
*
* if move == NULL, the next free slot will be returned
*
* Returns the TrackedController * instance, or NULL if not found
**/
TrackedController *
psmove_tracker_find_controller(PSMoveTracker *tracker, PSMove *move);
/**
* Wait for a given time for a frame from the tracker
*
* tracker - A valid PSMoveTracker * instance
* frame - A pointer to an IplImage * to store the frame
* delay - The delay to wait for the frame
**/
void
psmove_tracker_wait_for_frame(PSMoveTracker *tracker, IplImage **frame, int delay);
/**
* This function switches the sphere of the given PSMove on to the given color and takes
* a picture via the given capture. Then it switches it of and takes a picture again. A difference image
* is calculated from these two images. It stores the image of the lit sphere and
* of the diff-image in the passed parameter "on" and "diff". Before taking
* a picture it waits for the specified delay (in microseconds).
*
* tracker - the tracker that contains the camera control
* move - the PSMove controller to use
* rgb - the RGB color to use to lit the sphere
* on - the pre-allocated image to store the captured image when the sphere is lit
* diff - the pre-allocated image to store the calculated diff-image
* delay - the time to wait before taking a picture (in microseconds)
**/
void
psmove_tracker_get_diff(PSMoveTracker* tracker, PSMove* move,
struct PSMove_RGBValue rgb, IplImage* on, IplImage* diff, int delay,
float dimming_factor);
/**
* This function seths the rectangle of the ROI and assures that the itis always within the bounds
* of the camera image.
*
* tracker - A valid PSMoveTracker * instance
* tc - The TrackableController containing the roi to check & fix
* roi_x - the x-part of the coordinate of the roi
* roi_y - the y-part of the coordinate of the roi
* roi_width - the width of the roi
* roi_height - the height of the roi
* cam_width - the width of the camera image
* cam_height - the height of the camera image
**/
void psmove_tracker_set_roi(PSMoveTracker* tracker, TrackedController* tc, int roi_x, int roi_y, int roi_width, int roi_height);
/**
* This function is just the internal implementation of "psmove_tracker_update"
*/
int psmove_tracker_update_controller(PSMoveTracker* tracker, TrackedController *tc);
/**
* This function takes a vetted contour and calculates sphere 2D position & radius, and 3D position.
*/
void psmove_tracker_update_controller_position_from_contour(PSMoveTracker *tracker, TrackedController *tc, CvSeq* contourBest);
/**
* This function filters newly updated sphere 2D position & radius.
*/
void psmove_tracker_filter_2d(PSMoveTracker *tracker, TrackedController *tc, CvSeq* contourBest);
/**
* This function filters a newly-updated sphere position. It uses tracker->settings to determine filtering technique.
*/
void psmove_tracker_filter_3d(PSMoveTracker *tracker, TrackedController *tc);
/*
* This finds the biggest contour within the given image.
*
* img - (in) the binary image to search for contours
* stor - (out) a storage that can be used to save the result of this function
* resContour - (out) points to the biggest contour found within the image
* resSize - (out) the size of that contour in px²
*/
void psmove_tracker_biggest_contour(IplImage* img, CvMemStorage* stor, CvSeq** resContour, float* resSize);
/*
* This returns a subjective distance between the first estimated (during calibration process) color and the currently estimated color.
* Subjective, because it takes the different color components not equally into account.
* Result calculates like: abs(c1.h-c2.h) + abs(c1.s-c2.s)*0.5 + abs(c1.v-c2.v)*0.5
*
* tc - The controller whose first/current color estimation distance should be calculated.
*
* Returns: a subjective distance
*/
float psmove_tracker_hsvcolor_diff(TrackedController* tc);
/*
* This will estimate the position and the radius of the orb.
* It will calcualte the radius by findin the two most distant points
* in the contour. And its by choosing the mid point of those two.
*
* cont - (in) The contour representing the orb.
* x - (out) The X coordinate of the center.
* y - (out) The Y coordinate of the center.
* radius - (out) The radius of the contour that is calculated here.
*/
void
psmove_tracker_estimate_circle_from_contour(CvSeq* cont, float *x, float *y, float* radius);
/*
* This function return a optimal ROI center point for a given Tracked controller.
* On very fast movements, it may happen that the orb is visible in the ROI, but resides
* at its border. This function will simply look for the biggest blob in the ROI and return a
* point so that that blob would be in the center of the ROI.
*
* tc - (in) The controller whose ROI centerpoint should be adjusted.
* tracker - (in) The PSMoveTracker to use.
* center - (out) The better center point for the current ROI
*
* Returns: nonzero if a new point was found, zero otherwise
*/
int
psmove_tracker_center_roi_on_controller(TrackedController* tc, PSMoveTracker* tracker, CvPoint *center);
int
psmove_tracker_color_is_used(PSMoveTracker *tracker, struct PSMove_RGBValue color);
enum PSMoveTracker_Status
psmove_tracker_enable_with_color_internal(PSMoveTracker *tracker, PSMove *move, struct PSMove_RGBValue color);
/*
* This function reads old calibration color values and tries to track the controller with that color.
* if it works, the function returns 1, 0 otherwise.
* Can help to speed up calibration process on application startup.
*
* tracker - (in) A valid PSMoveTracker
* move - (in) A valid PSMove controller
* rgb - (in) The color the PSMove controller's sphere will be lit.
*/
int
psmove_tracker_old_color_is_tracked(PSMoveTracker* tracker, PSMove* move, struct PSMove_RGBValue rgb);
/**
* Lookup a camera-visible color value
**/
int
psmove_tracker_lookup_color(PSMoveTracker *tracker, struct PSMove_RGBValue rgb, CvScalar *color);
/**
* Remember a color value after calibration
**/
void
psmove_tracker_remember_color(PSMoveTracker *tracker, struct PSMove_RGBValue rgb, CvScalar color);
// -------- END: internal functions only
void
psmove_tracker_get_settings(PSMoveTracker *tracker, PSMoveTrackerSettings *settings)
{
psmove_return_if_fail(tracker != NULL);
*settings = tracker->settings;
}
void
psmove_tracker_set_settings(PSMoveTracker *tracker, PSMoveTrackerSettings *settings)
{
psmove_return_if_fail(tracker != NULL);
tracker->settings = *settings;
}
void
psmove_tracker_settings_set_default(PSMoveTrackerSettings *settings)
{
*settings = tracker_default_settings;
printf("Copied default tracker settings to tracker->settings.\n");
}
PSMoveTracker *psmove_tracker_new() {
PSMoveTrackerSettings settings;
psmove_tracker_settings_set_default(&settings);
return psmove_tracker_new_with_settings(&settings);
}
PSMoveTracker *
psmove_tracker_new_with_settings(PSMoveTrackerSettings *settings) {
int camera = 0;
#if defined(__linux) && defined(PSMOVE_USE_PSEYE)
/**
* On Linux, we might have multiple cameras (e.g. most laptops have
* built-in cameras), so we try looking for the one that is handled
* by the PSEye driver.
**/
camera = linux_find_pseye();
if (camera == -1) {
/* Could not find the PSEye - fallback to first camera */
camera = 0;
}
#endif
int camera_env = psmove_util_get_env_int(PSMOVE_TRACKER_CAMERA_ENV);
if (camera_env != -1) {
camera = camera_env;
psmove_DEBUG("Using camera %d (%s is set)\n", camera,
PSMOVE_TRACKER_CAMERA_ENV);
}
return psmove_tracker_new_with_camera_and_settings(camera, settings);
}
void
psmove_tracker_set_auto_update_leds(PSMoveTracker *tracker, PSMove *move,
enum PSMove_Bool auto_update_leds)
{
psmove_return_if_fail(tracker != NULL);
psmove_return_if_fail(move != NULL);
TrackedController *tc = psmove_tracker_find_controller(tracker, move);
psmove_return_if_fail(tc != NULL);
tc->auto_update_leds = auto_update_leds;
}
enum PSMove_Bool
psmove_tracker_get_auto_update_leds(PSMoveTracker *tracker, PSMove *move)
{
psmove_return_val_if_fail(tracker != NULL, PSMove_False);
psmove_return_val_if_fail(move != NULL, PSMove_False);
TrackedController *tc = psmove_tracker_find_controller(tracker, move);
psmove_return_val_if_fail(tc != NULL, PSMove_False);
return tc->auto_update_leds;
}
void
psmove_tracker_set_dimming(PSMoveTracker *tracker, float dimming)
{
psmove_return_if_fail(tracker != NULL);
tracker->settings.dimming_factor = dimming;
}
float
psmove_tracker_get_dimming(PSMoveTracker *tracker)
{
psmove_return_val_if_fail(tracker != NULL, 0);
return tracker->settings.dimming_factor;
}
void
psmove_tracker_set_exposure(PSMoveTracker *tracker,
enum PSMoveTracker_Exposure exposure)
{
psmove_return_if_fail(tracker != NULL);
tracker->settings.exposure_mode = exposure;
float target_luminance = 0;
switch (tracker->settings.exposure_mode) {
case Exposure_LOW:
target_luminance = 8;
break;
case Exposure_MEDIUM:
target_luminance = 25;
break;
case Exposure_HIGH:
target_luminance = 100;
break;
default:
psmove_DEBUG("Invalid exposure mode: %d\n", exposure);
break;
}
#if defined(__APPLE__) && !defined(CAMERA_CONTROL_USE_PS3EYE_DRIVER)
camera_control_initialize();
#endif
// Determine the camera exposure setting required to hit the target luminance
tracker->settings.camera_exposure = psmove_tracker_adapt_to_light(tracker, target_luminance);
camera_control_set_parameters(tracker->cc, 0, 0, 0, tracker->settings.camera_exposure,
0, 0xffff, 0xffff, 0xffff, -1, -1);
}
enum PSMoveTracker_Exposure
psmove_tracker_get_exposure(PSMoveTracker *tracker)
{
psmove_return_val_if_fail(tracker != NULL, Exposure_INVALID);
return tracker->settings.exposure_mode;
}
void
psmove_tracker_set_focal_length(PSMoveTracker *tracker, float focal_length)
{
tracker->cc->focl_x = focal_length;
tracker->cc->focl_y = focal_length;
}
int
psmove_tracker_get_focal_length(PSMoveTracker *tracker, float* focal_length_out)
{
int success = 1;
*focal_length_out = tracker->cc->focl_x;
return success;
}
void
psmove_tracker_set_smoothing_settings(PSMoveTracker *tracker, PSMoveTrackerSmoothingSettings *smoothing_settings)
{
psmove_return_if_fail(tracker != NULL);
tracker->smoothing_settings = *smoothing_settings;
psmove_tracker_set_smoothing_type(tracker, tracker->smoothing_settings.filter_3d_type);
}
void
psmove_tracker_get_smoothing_settings(PSMoveTracker *tracker, PSMoveTrackerSmoothingSettings *smoothing_settings)
{
psmove_return_if_fail(tracker != NULL);
*smoothing_settings = tracker->smoothing_settings;
}
void
psmove_tracker_smoothing_settings_set_default(PSMoveTrackerSmoothingSettings *smoothing_settings)
{
*smoothing_settings = tracker_smoothing_default_settings;
}
enum PSMove_Bool
psmove_tracker_save_smoothing_settings(PSMoveTrackerSmoothingSettings *smoothing_settings)
{
psmove_return_val_if_fail(smoothing_settings != NULL, PSMove_False);
char *filepath = psmove_util_get_file_path(SMOOTHING_SETTINGS_CSV);
FILE *fp= psmove_file_open(filepath, "w");
free(filepath);
psmove_return_val_if_fail(fp != NULL, PSMove_False);
fprintf(fp, "name,value\n");
fprintf(fp, "filter_do_2d_xy,%d\n", smoothing_settings->filter_do_2d_xy);
fprintf(fp, "filter_do_2d_r,%d\n", smoothing_settings->filter_do_2d_r);
fprintf(fp, "filter_3d_type,%d\n", smoothing_settings->filter_3d_type);
fprintf(fp, "acceleration_variance,%f\n", smoothing_settings->acceleration_variance);
fprintf(fp, "measurement_covariance_00,%f\n", smoothing_settings->measurement_covariance.row0[0]);
fprintf(fp, "measurement_covariance_11,%f\n", smoothing_settings->measurement_covariance.row1[1]);
fprintf(fp, "measurement_covariance_22,%f\n", smoothing_settings->measurement_covariance.row2[2]);
psmove_file_close(fp);
return PSMove_True;
}
enum PSMove_Bool
psmove_tracker_load_smoothing_settings(PSMoveTrackerSmoothingSettings *out_smoothing_settings)
{
PSMoveTrackerSmoothingSettings smoothing_settings;
enum PSMove_Bool success = PSMove_False;
int result = 0;
psmove_tracker_smoothing_settings_set_default(&smoothing_settings);
psmove_return_val_if_fail(out_smoothing_settings != NULL, PSMove_False);
char *filepath = psmove_util_get_file_path(SMOOTHING_SETTINGS_CSV);
FILE *fp = psmove_file_open(filepath, "r");
free(filepath);
psmove_return_val_if_fail(fp != NULL, PSMove_False);
char s_name[64], s_value[64];
result = fscanf(fp, "%4s,%5s\n", s_name, s_value);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_name, "name") == 0, finish);
psmove_goto_if_fail(strcmp(s_value, "value") == 0, finish);
char s_filter_do_2d_xy[64];
result = fscanf(fp, "%15s,%d\n", &s_filter_do_2d_xy, &smoothing_settings.filter_do_2d_xy);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_filter_do_2d_xy, "filter_do_2d_xy") == 0, finish);
char s_filter_do_2d_r[64];
result = fscanf(fp, "%14s,%d\n", &s_filter_do_2d_r, &smoothing_settings.filter_do_2d_r);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_filter_do_2d_r, "filter_do_2d_r") == 0, finish);
char s_filter_3d_type[64];
result = fscanf(fp, "%14s,%d\n", &s_filter_3d_type, &smoothing_settings.filter_3d_type);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_filter_3d_type, "filter_3d_type") == 0, finish);
char s_acceleration_variance[64];
result = fscanf(fp, "%21s,%f\n", &s_acceleration_variance, &smoothing_settings.acceleration_variance);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_acceleration_variance, "acceleration_variance") == 0, finish);
char s_measurement_covariance_00[64];
result = fscanf(fp, "%25s,%f\n", &s_measurement_covariance_00, &smoothing_settings.measurement_covariance.row0[0]);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_measurement_covariance_00, "measurement_covariance_00") == 0, finish);
char s_measurement_covariance_11[64];
result = fscanf(fp, "%25s,%f\n", &s_measurement_covariance_11, &smoothing_settings.measurement_covariance.row1[1]);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_measurement_covariance_11, "measurement_covariance_11") == 0, finish);
char s_measurement_covariance_22[64];
result = fscanf(fp, "%25s,%f\n", &s_measurement_covariance_22, &smoothing_settings.measurement_covariance.row2[2]);
psmove_goto_if_fail(result == 2, finish);
psmove_goto_if_fail(strcmp(s_measurement_covariance_22, "measurement_covariance_22") == 0, finish);
success = PSMove_True;
finish:
if (success == PSMove_True)
{
*out_smoothing_settings = smoothing_settings;
}
psmove_file_close(fp);
return success;
}
void
psmove_tracker_set_smoothing_type(PSMoveTracker *tracker, enum PSMoveTracker_Smoothing_Type smoothing_type)
{
psmove_return_if_fail(tracker != NULL);
if (smoothing_type != tracker->smoothing_type)
{
int controller_index;
for (controller_index = 0; controller_index < PSMOVE_TRACKER_MAX_CONTROLLERS; ++controller_index)
{
TrackedController *controller= &tracker->controllers[controller_index];
// Free the old filter, if any
if (controller->position_filter != NULL)
{
switch (tracker->smoothing_type)
{
case Smoothing_None:
// Nothing to clean up
break;
case Smoothing_LowPass:
psmove_position_lowpass_filter_free((PSMovePositionLowPassFilter *)controller->position_filter);
break;
case Smoothing_Kalman:
psmove_position_kalman_filter_free((PSMovePositionKalmanFilter *)controller->position_filter);
break;
}
}
// Create the new filter, if any
switch (smoothing_type)
{
case Smoothing_None:
// Nothing to clean up
break;
case Smoothing_LowPass:
controller->position_filter = psmove_position_lowpass_filter_new();
break;
case Smoothing_Kalman:
controller->position_filter = psmove_position_kalman_filter_new();
break;
}
}
tracker->smoothing_type = smoothing_type;
}
}
void
psmove_tracker_set_smoothing_2d(PSMoveTracker *tracker, int filter_do_2d_xy, int filter_do_2d_r)
{
psmove_return_if_fail(tracker != NULL);
tracker->smoothing_settings.filter_do_2d_xy = filter_do_2d_xy > 0;
tracker->smoothing_settings.filter_do_2d_r = filter_do_2d_r > 0;
}
void
psmove_tracker_enable_deinterlace(PSMoveTracker *tracker,
enum PSMove_Bool enabled)
{
psmove_return_if_fail(tracker != NULL);
psmove_return_if_fail(tracker->cc != NULL);
camera_control_set_deinterlace(tracker->cc, enabled);
}
void
psmove_tracker_set_mirror(PSMoveTracker *tracker,
enum PSMove_Bool enabled)
{
psmove_return_if_fail(tracker != NULL);
tracker->settings.camera_mirror = enabled;
}
enum PSMove_Bool
psmove_tracker_get_mirror(PSMoveTracker *tracker)
{
psmove_return_val_if_fail(tracker != NULL, PSMove_False);
return tracker->settings.camera_mirror;
}
PSMoveTracker *
psmove_tracker_new_with_camera(int camera) {
PSMoveTrackerSettings settings;
psmove_tracker_settings_set_default(&settings);
return psmove_tracker_new_with_camera_and_settings(camera, &settings);
}
PSMoveTracker *
psmove_tracker_new_with_camera_and_settings(int camera, PSMoveTrackerSettings *settings)
{
PSMoveTracker* tracker = (PSMoveTracker*) calloc(1, sizeof(PSMoveTracker));
tracker->settings = *settings;
tracker->rHSV = cvScalar(
tracker->settings.color_hue_filter_range,
tracker->settings.color_saturation_filter_range,
tracker->settings.color_value_filter_range, 0);
tracker->storage = cvCreateMemStorage(0);
g_last_tracker_error_code= PSMove_Camera_Error_None;
if (psmove_tracker_load_smoothing_settings(&(tracker->smoothing_settings)) == PSMove_False)
{
psmove_tracker_smoothing_settings_set_default(&(tracker->smoothing_settings));
}
psmove_tracker_set_smoothing_type(tracker, tracker->smoothing_settings.filter_3d_type);
#if defined(__APPLE__) && !defined(CAMERA_CONTROL_USE_PS3EYE_DRIVER)
// Assume iSight. Calibration will be done with with sphere against camera
// to avoid auto-balancing due to background light
PSMove *move = psmove_connect();
psmove_set_leds(move, 255, 255, 255);
psmove_update_leds(move);
printf("Cover the iSight camera with the sphere and press the Move button\n");
_psmove_wait_for_button(move, Btn_MOVE);
psmove_set_leds(move, 0, 0, 0);
psmove_update_leds(move);
psmove_set_leds(move, 255, 255, 255);
psmove_update_leds(move);
#endif
// start the video capture device for tracking
// Returns NULL if no control found.
// e.g. PS3EYE set during compile but not plugged in.
tracker->cc = camera_control_new_with_settings(camera,
tracker->settings.camera_frame_width,
tracker->settings.camera_frame_height,
tracker->settings.camera_frame_rate,
tracker->settings.camera_type);
if (!tracker->cc)
{
free(tracker);
return NULL;
}
else
{
psmove_DEBUG("Successfully initialized camera_control.\n");
}
psmove_tracker_load_distortion(tracker);
// backup the systems settings, if not already backuped
char *filename = psmove_util_get_file_path(PSEYE_BACKUP_FILE);
camera_control_backup_system_settings(tracker->cc, filename);
free(filename);
#if !defined(__APPLE__) || defined(CAMERA_CONTROL_USE_PS3EYE_DRIVER)
// try to load color mapping data (not on Mac OS X for now, because the
// automatic white balance means we get different colors every time)
filename = psmove_util_get_file_path(COLOR_MAPPING_DAT);
FILE *fp = NULL;
time_t now = time(NULL);
struct stat st;
memset(&st, 0, sizeof(st));
if (stat(filename, &st) == 0 && now != (time_t)-1) {
if (st.st_mtime >= (now - settings->color_mapping_max_age)) {
fp = psmove_file_open(filename, "rb");
} else {
printf("%s is too old - not restoring colors.\n", filename);
}
}
if (fp) {
if (!fread(&(tracker->color_mapping),
sizeof(struct ColorMappingRingBuffer),
1, fp)) {
psmove_WARNING("Cannot read data from: %s\n", filename);
} else {
printf("color mappings restored.\n");
}
psmove_file_close(fp);
}
free(filename);
#endif
// Default to the distance parameters for the PS Eye camera
tracker->distance_parameters = pseye_distance_parameters;
// Set the exposure to a constant based on a target luminance.
psmove_DEBUG("Setting exposure: %d\n", tracker->settings.exposure_mode);
psmove_tracker_set_exposure(tracker, tracker->settings.exposure_mode);
// just query a frame so that we know the camera works
int query_frame_attempts= 0;
IplImage* frame = NULL;
enum PSMove_Bool new_frame = PSMove_False;
while ((!frame || new_frame == PSMove_False) && (query_frame_attempts < MAX_CAMERA_INIT_QUERY_ATTEMPTS))
{
frame = camera_control_query_frame(tracker->cc, NULL, NULL, &new_frame);
++query_frame_attempts;
}
// Bail if we failed to get a a valid video frame
if (query_frame_attempts >= MAX_CAMERA_INIT_QUERY_ATTEMPTS)
{
psmove_DEBUG("Failed to acquire a video frame from the tracker.\n");
g_last_tracker_error_code= PSMove_Camera_Query_Frame_Failure;
camera_control_delete(tracker->cc);
free(tracker);
return NULL;
}
// prepare ROI data structures
/* Define the size of the biggest ROI */
int size = psmove_util_get_env_int(PSMOVE_TRACKER_ROI_SIZE_ENV);
if (size == -1) {
size = MIN(frame->width, frame->height) / 2;
} else {
psmove_DEBUG("Using ROI size: %d\n", size);
}
int w = size, h = size;
// We need to grab an image from the camera to determine the frame size
psmove_tracker_update_image(tracker);
tracker->settings.search_tile_width = w;
tracker->settings.search_tile_height = h;
tracker->settings.search_tiles_horizontal = (tracker->frame->width +
tracker->settings.search_tile_width - 1) / tracker->settings.search_tile_width;
int search_tiles_vertical = (tracker->frame->height +
tracker->settings.search_tile_height - 1) / tracker->settings.search_tile_height;
tracker->settings.search_tiles_count = tracker->settings.search_tiles_horizontal *
search_tiles_vertical;
if (tracker->settings.search_tiles_count % 2 == 0) {
/**
* search_tiles_count must be uneven, so that when picking every second
* tile, we still "visit" every tile after two scans when we wrap:
*
* ABA
* BAB
* ABA -> OK, first run = A, second run = B
*
* ABAB
* ABAB -> NOT OK, first run = A, second run = A
*
* Incrementing the count will make the algorithm visit the lower right
* item twice, but will then cause the second run to visit 'B's.
*
* We pick every second tile, so that we only need half the time to
* sweep through the whole image (which usually means faster recovery).
**/
tracker->settings.search_tiles_count++;
}
// Allocate memory for images of each possible ROI size, in colour and grayscale
int i;
for (i = 0; i < ROIS; i++) {
tracker->roiI[i] = cvCreateImage(cvSize(w,h), frame->depth, 3); // colour
tracker->roiM[i] = cvCreateImage(cvSize(w,h), frame->depth, 1); // grayscale
/* Smaller rois are always square, and 70% of the previous level */
h = w = ((MIN(w,h) * 70) / 100);
}
// prepare structure used for erode and dilate in calibration process
int ks = 5; // Kernel Size
int kc = (ks + 1) / 2; // Kernel Center
tracker->kCalib = cvCreateStructuringElementEx(ks, ks, kc, kc, CV_SHAPE_RECT, NULL);
#if defined(__APPLE__) && !defined(CAMERA_CONTROL_USE_PS3EYE_DRIVER)
printf("Move the controller away and press the Move button\n");
_psmove_wait_for_button(move, Btn_MOVE);
psmove_set_leds(move, 0, 0, 0);
psmove_update_leds(move);
psmove_disconnect(move);