diff --git a/.gitignore b/.gitignore
index 893c021..cd86514 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,3 +71,4 @@ lib/local/CppInerop/Release/
lib/local/CppInerop/x64/
lib/local/FaceAnalyser/Release/
lib/local/LandmarkDetector/Release/
+gui/HeadPose-live/obj/
diff --git a/OpenFace.sln b/OpenFace.sln
index 5895f19..6fc60b8 100644
--- a/OpenFace.sln
+++ b/OpenFace.sln
@@ -33,6 +33,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CamCom", "lib\local\CamCom\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFaceOffline", "gui\OpenFaceOffline\OpenFaceOffline.csproj", "{A4760F41-2B1F-4144-B7B2-62785AFFE79B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFaceDemo", "gui\OpenFaceDemo\OpenFaceDemo.csproj", "{E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeadPoseLive", "gui\HeadPose-live\HeadPoseLive.csproj", "{F396362D-821E-4EA6-9BBF-1F6050844118}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -129,6 +133,22 @@ Global
{A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|Win32.Build.0 = Release|x86
{A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|x64.ActiveCfg = Release|Any CPU
{A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|x64.Build.0 = Release|Any CPU
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Debug|Win32.ActiveCfg = Debug|x86
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Debug|Win32.Build.0 = Debug|x86
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Debug|x64.Build.0 = Debug|Any CPU
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Release|Win32.ActiveCfg = Release|x86
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Release|Win32.Build.0 = Release|x86
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Release|x64.ActiveCfg = Release|Any CPU
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90}.Release|x64.Build.0 = Release|Any CPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Debug|Win32.ActiveCfg = Debug|x86
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Debug|Win32.Build.0 = Debug|x86
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Debug|x64.Build.0 = Debug|Any CPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Release|Win32.ActiveCfg = Release|Any CPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Release|Win32.Build.0 = Release|Any CPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Release|x64.ActiveCfg = Release|Any CPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -145,5 +165,7 @@ Global
{78196985-EE54-411F-822B-5A23EDF80642} = {D5F7B3E2-01FE-4F4A-8CE1-274D74D395D4}
{0CEC6A75-17BA-4DC5-9405-C74154921E60} = {D5F7B3E2-01FE-4F4A-8CE1-274D74D395D4}
{A4760F41-2B1F-4144-B7B2-62785AFFE79B} = {C9D8D0B0-11EC-41CB-8524-2DDB5BE94297}
+ {E143A2AA-312E-4DFE-B61D-9A87CCBC8E90} = {C9D8D0B0-11EC-41CB-8524-2DDB5BE94297}
+ {F396362D-821E-4EA6-9BBF-1F6050844118} = {C9D8D0B0-11EC-41CB-8524-2DDB5BE94297}
EndGlobalSection
EndGlobal
diff --git a/gui/HeadPose-live/App.config b/gui/HeadPose-live/App.config
new file mode 100644
index 0000000..88fa402
--- /dev/null
+++ b/gui/HeadPose-live/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/HeadPose-live/App.xaml b/gui/HeadPose-live/App.xaml
new file mode 100644
index 0000000..b58dc6f
--- /dev/null
+++ b/gui/HeadPose-live/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/gui/HeadPose-live/App.xaml.cs b/gui/HeadPose-live/App.xaml.cs
new file mode 100644
index 0000000..f36f6f5
--- /dev/null
+++ b/gui/HeadPose-live/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace HeadPose_live
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/gui/HeadPose-live/HeadPoseLive.csproj b/gui/HeadPose-live/HeadPoseLive.csproj
new file mode 100644
index 0000000..0565fb4
--- /dev/null
+++ b/gui/HeadPose-live/HeadPoseLive.csproj
@@ -0,0 +1,179 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {F396362D-821E-4EA6-9BBF-1F6050844118}
+ WinExe
+ Properties
+ HeadPoseLive
+ HeadPoseLive
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+
+
+
+
+ x64
+ true
+ full
+ false
+ ..\..\x64\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x64
+ pdbonly
+ true
+ ..\..\x64\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ ..\..\Debug\
+ DEBUG;TRACE
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ ..\..\Release\
+ TRACE
+ true
+ pdbonly
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ logo1.ico
+
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+ ..\..\packages\ZeroMQ.4.1.0.22\lib\net40\ZeroMQ.dll
+ True
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Liability.xaml
+
+
+ TextEntryWindow.xaml
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+ MainWindow.xaml
+ Code
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+ {78196985-ee54-411f-822b-5a23edf80642}
+ CppInerop
+
+
+ {e143a2aa-312e-4dfe-b61d-9a87ccbc8e90}
+ OpenFaceDemo
+
+
+ {a4760f41-2b1f-4144-b7b2-62785affe79b}
+ OpenFaceOffline
+
+
+
+
+
+
+
+ xcopy /I /E /Y /D "$(ProjectDir)logo1.ico" "$(ProjectDir)$(OutDir)"
+xcopy /I /E /Y /D "$(ProjectDir)logo1.png" "$(ProjectDir)$(OutDir)"
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/HeadPose-live/Liability.xaml b/gui/HeadPose-live/Liability.xaml
new file mode 100644
index 0000000..b770c7f
--- /dev/null
+++ b/gui/HeadPose-live/Liability.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ 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 copyright holders 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.
+
+
+
+
+
diff --git a/gui/HeadPose-live/Liability.xaml.cs b/gui/HeadPose-live/Liability.xaml.cs
new file mode 100644
index 0000000..9641456
--- /dev/null
+++ b/gui/HeadPose-live/Liability.xaml.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace HeadPoseLive
+{
+
+ ///
+ /// Interaction logic for Liability.xaml
+ ///
+ public partial class Liability : Window
+ {
+ public bool continue_pressed = false;
+
+ public Liability()
+ {
+ InitializeComponent();
+
+ this.KeyDown += new KeyEventHandler(TextEntry_KeyDown);
+ FocusManager.SetFocusedElement(this, ContinueButton);
+ }
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ continue_pressed = true;
+ this.Close();
+ }
+
+ private void TextEntry_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
+ {
+ continue_pressed = true;
+ DialogResult = true;
+ }
+ }
+
+ }
+}
diff --git a/gui/HeadPose-live/MainWindow.xaml b/gui/HeadPose-live/MainWindow.xaml
new file mode 100644
index 0000000..a61c6f9
--- /dev/null
+++ b/gui/HeadPose-live/MainWindow.xaml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/HeadPose-live/MainWindow.xaml.cs b/gui/HeadPose-live/MainWindow.xaml.cs
new file mode 100644
index 0000000..cca6a73
--- /dev/null
+++ b/gui/HeadPose-live/MainWindow.xaml.cs
@@ -0,0 +1,747 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing.Imaging;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Microsoft.Win32;
+
+using OpenCVWrappers;
+using CppInterop;
+using CppInterop.LandmarkDetector;
+using CameraInterop;
+using FaceAnalyser_Interop;
+using System.Windows.Threading;
+using FaceAnalyser_Interop;
+
+using ZeroMQ;
+using System.Drawing;
+using System.Collections.Concurrent;
+
+namespace HeadPoseLive
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ // Timing for measuring FPS
+ #region High-Resolution Timing
+ static DateTime startTime;
+ static Stopwatch sw = new Stopwatch();
+
+ static MainWindow()
+ {
+ startTime = DateTime.Now;
+ sw.Start();
+ }
+
+ public static DateTime CurrentTime
+ {
+ get { return startTime + sw.Elapsed; }
+ }
+
+ #endregion
+
+ OpenFaceOffline.FpsTracker processing_fps = new OpenFaceOffline.FpsTracker();
+
+ Thread processing_thread;
+ Thread rec_thread;
+
+ string subject_id;
+ bool record_video;
+ bool record_head_pose;
+
+ // Controls if the view should be mirrored or not
+ volatile bool mirror_image = false;
+
+ // Capturing and displaying the images
+ OpenFaceOffline.OverlayImage webcam_img;
+
+ // Some members for displaying the results
+ private CameraInterop.Capture capture;
+ private WriteableBitmap latest_img;
+
+ // For tracking
+ double fx = 500, fy = 500, cx = 0, cy = 0;
+ bool reset = false;
+
+ // For recording
+ string record_root = "./head_pose_live_recordings/subject";
+ string output_root;
+ private Object recording_lock = new Object();
+ int trial_id = 0;
+ bool recording = false;
+ int img_width;
+ int img_height;
+
+ double seconds_to_record = 10;
+
+ System.IO.StreamWriter recording_success_file = null;
+
+ ConcurrentQueue>> recording_objects;
+
+ // For broadcasting the results
+ ZeroMQ.ZContext zero_mq_context;
+ ZeroMQ.ZSocket zero_mq_socket;
+
+ volatile bool running = true;
+ volatile bool pause = false;
+
+ public void StartExperiment()
+ {
+ // Inquire more from the user
+
+ // Get the entry dialogue now for the subject ID
+ trial_id = 0;
+ TextEntryWindow subject_id_window = new TextEntryWindow();
+ subject_id_window.Icon = this.Icon;
+
+ subject_id_window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
+
+ if (subject_id_window.ShowDialog() == true)
+ {
+
+ subject_id = subject_id_window.ResponseText;
+
+ // Remove trailing spaces and full stops at the end of the folder name
+ int old_length;
+ do
+ {
+ old_length = subject_id.Length;
+ subject_id = subject_id.Trim();
+ if (subject_id.Length > 0)
+ {
+ while (subject_id[subject_id.Length - 1].Equals('.'))
+ {
+ subject_id = subject_id.Substring(0, subject_id.Length - 1);
+ }
+ }
+ } while (subject_id.Length != old_length);
+
+ output_root = record_root + subject_id + "/";
+
+ if (System.IO.Directory.Exists(output_root))
+ {
+ string messageBoxText = "The recording for subject already exists, are you sure you want to continue?";
+ string caption = "Directory exists!";
+ MessageBoxButton button = MessageBoxButton.YesNo;
+ MessageBoxImage icon = MessageBoxImage.Warning;
+ MessageBoxResult result = MessageBox.Show(messageBoxText, caption, button, icon);
+ if (result == MessageBoxResult.No)
+ {
+ this.Close();
+ }
+
+ // Else find the latest trial from which to continu
+ int trial_id_not_found = 0;
+ while (System.IO.File.Exists(output_root + '/' + "trial_" + trial_id_not_found + ".avi"))
+ {
+ trial_id_not_found++;
+ }
+ trial_id = trial_id_not_found;
+ }
+
+ System.IO.Directory.CreateDirectory(output_root);
+
+ record_video = subject_id_window.RecordVideo;
+ record_head_pose = subject_id_window.RecordHeadPose;
+ RecordingButton.Content = "Record trial: " + trial_id;
+
+ }
+ else
+ {
+ this.Close();
+ }
+
+ }
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ DateTime now = DateTime.Now;
+
+ // Set the icon
+ Uri iconUri = new Uri("logo1.ico", UriKind.RelativeOrAbsolute);
+ this.Icon = BitmapFrame.Create(iconUri);
+
+ // Warn about the liability
+ Liability liab = new Liability();
+ liab.Icon = BitmapFrame.Create(iconUri);
+ liab.ShowDialog();
+
+ if (!liab.continue_pressed)
+ {
+ this.Close();
+ return;
+ }
+
+ BitmapImage src = new BitmapImage();
+ src.BeginInit();
+ src.UriSource = new Uri("logo1.png", UriKind.RelativeOrAbsolute);
+ src.CacheOption = BitmapCacheOption.OnLoad;
+ src.EndInit();
+
+ logoLabel.Source = src;
+
+ // First make the user chooose a webcam
+ OpenFaceDemo.CameraSelection cam_select = new OpenFaceDemo.CameraSelection();
+ cam_select.Icon = BitmapFrame.Create(iconUri);
+
+ if (!cam_select.no_cameras_found)
+ {
+ cam_select.ShowDialog();
+ }
+
+ if (cam_select.camera_selected)
+ {
+
+ // Create the capture device
+ int cam_id = cam_select.selected_camera.Item1;
+ img_width = cam_select.selected_camera.Item2;
+ img_height = cam_select.selected_camera.Item3;
+
+ capture = new CameraInterop.Capture(cam_id, img_width, img_height);
+
+ // Set appropriate fx and cx values
+ fx = fx * (img_width / 640.0);
+ fy = fy * (img_height / 480.0);
+
+ fx = (fx + fy) / 2.0;
+ fy = fx;
+
+ cx = img_width / 2.0;
+ cy = img_height / 2.0;
+
+ if (capture.isOpened())
+ {
+
+ // Create the ZeroMQ context for broadcasting the results
+ zero_mq_context = ZeroMQ.ZContext.Create();
+ zero_mq_socket = new ZSocket(zero_mq_context, ZeroMQ.ZSocketType.PUB);
+
+ // Bind on localhost port 5000
+ zero_mq_socket.Bind("tcp://127.0.0.1:5000");
+
+ // Start the tracking now
+ processing_thread = new Thread(VideoLoop);
+ processing_thread.Start();
+ }
+ else
+ {
+
+ string messageBoxText = "Failed to open a webcam";
+ string caption = "Webcam failure";
+ MessageBoxButton button = MessageBoxButton.OK;
+ MessageBoxImage icon = MessageBoxImage.Warning;
+
+ // Display message box
+ MessageBox.Show(messageBoxText, caption, button, icon);
+ this.Close();
+ }
+
+ // Create an overlay image for display purposes
+ webcam_img = new OpenFaceOffline.OverlayImage();
+
+ webcam_img.SetValue(Grid.RowProperty, 1);
+ webcam_img.SetValue(Grid.ColumnProperty, 1);
+ MainGrid.Children.Add(webcam_img);
+
+ StartExperiment();
+
+ }
+ else
+ {
+ cam_select.Close();
+ this.Close();
+ }
+
+ }
+
+ private bool ProcessFrame(CLNF clnf_model, FaceAnalyserManaged face_analyser, FaceModelParameters model_params, RawImage frame, RawImage grayscale_frame, double fx, double fy, double cx, double cy)
+ {
+ bool detection_succeeding = clnf_model.DetectLandmarksInVideo(grayscale_frame, model_params);
+ face_analyser.AddNextFrame(frame, clnf_model, fx, fy, cx, cy, true, false, false);
+ return detection_succeeding;
+
+ }
+
+ // Capturing and processing the video frame by frame
+ private void RecordingLoop()
+ {
+ // Set up the recording objects first
+ Thread.CurrentThread.IsBackground = true;
+
+ System.IO.StreamWriter output_head_pose_file = null;
+
+ if (record_head_pose)
+ {
+ String filename_poses = output_root + "/trial_" + trial_id + ".poses.txt";
+ output_head_pose_file = new System.IO.StreamWriter(filename_poses);
+ output_head_pose_file.WriteLine("time(ms), success, pose_X(mm), pose_Y(mm), pose_Z(mm), pitch(deg), yaw(deg), roll(deg)");
+ }
+
+ VideoWriter video_writer = null;
+
+ if (record_video)
+ {
+ double fps = processing_fps.GetFPS();
+ String filename_video = output_root + "/trial_" + trial_id + ".avi";
+ video_writer = new VideoWriter(filename_video, img_width, img_height, fps, true);
+ }
+
+ // The timiing should be when the item is captured, but oh well
+ Stopwatch stopWatch = new Stopwatch();
+ stopWatch.Start();
+
+ while (recording)
+ {
+ Tuple> recording_object;
+ if (recording_objects.TryDequeue(out recording_object))
+ {
+
+ if (record_video)
+ {
+ video_writer.Write(recording_object.Item1);
+ }
+
+ if (record_head_pose)
+ {
+ String output_pose_line = stopWatch.ElapsedMilliseconds.ToString();
+ if (recording_object.Item2)
+ output_pose_line += ", 1";
+ else
+ output_pose_line += ", 0";
+
+ for (int i = 0; i < recording_object.Item3.Count; ++i)
+ {
+ double num = recording_object.Item3[i];
+ if (i > 2)
+ {
+ output_pose_line += ", " + num * 180 / Math.PI;
+ }
+ else
+ {
+ output_pose_line += ", " + num;
+ }
+ }
+ output_head_pose_file.WriteLine(output_pose_line);
+ }
+ }
+
+ Thread.Sleep(10);
+ }
+
+ // Clean up the recording
+ if (record_head_pose)
+ {
+ output_head_pose_file.Close();
+ }
+ }
+
+ // Capturing and processing the video frame by frame
+ private void VideoLoop()
+ {
+ Thread.CurrentThread.IsBackground = true;
+
+ String root = AppDomain.CurrentDomain.BaseDirectory;
+ FaceModelParameters model_params = new FaceModelParameters(root, false);
+ CLNF face_model = new CLNF(model_params);
+ FaceAnalyserManaged face_analyser = new FaceAnalyserManaged(root, false, 112);
+
+ DateTime? startTime = CurrentTime;
+
+ var lastFrameTime = CurrentTime;
+
+ while (running)
+ {
+
+ //////////////////////////////////////////////
+ // CAPTURE FRAME AND DETECT LANDMARKS FOLLOWED BY THE REQUIRED IMAGE PROCESSING
+ //////////////////////////////////////////////
+
+ RawImage frame = null;
+ try
+ {
+ frame = capture.GetNextFrame(mirror_image);
+ }
+ catch (CameraInterop.CaptureFailedException)
+ {
+ break;
+ }
+
+ lastFrameTime = CurrentTime;
+ processing_fps.AddFrame();
+
+ var grayFrame = capture.GetCurrentFrameGray();
+
+ if (grayFrame == null)
+ continue;
+
+ bool detectionSucceeding = ProcessFrame(face_model, face_analyser, model_params, frame, grayFrame, fx, fy, cx, cy);
+
+ lock (recording_lock)
+ {
+
+ if (recording)
+ {
+ // Add objects to recording queues
+ List pose = new List();
+ face_model.GetPose(pose, fx, fy, cx, cy);
+ RawImage image = new RawImage(frame);
+ recording_objects.Enqueue(new Tuple>(image, detectionSucceeding, pose));
+
+ }
+ }
+
+ List> lines = null;
+ List> eye_landmarks = null;
+ List landmarks = new List();
+ List> gaze_lines = null;
+ Tuple gaze_angle = new Tuple(0, 0);
+ double scale = 0;
+
+ if (detectionSucceeding)
+ {
+ List> landmarks_doubles = face_model.CalculateLandmarks();
+
+ foreach (var p in landmarks_doubles)
+ landmarks.Add(new System.Windows.Point(p.Item1, p.Item2));
+
+ eye_landmarks = face_model.CalculateEyeLandmarks();
+
+ scale = face_model.GetRigidParams()[0];
+
+ gaze_lines = face_analyser.CalculateGazeLines(scale, (float)fx, (float)fy, (float)cx, (float)cy);
+ gaze_angle = face_analyser.GetGazeAngle();
+
+ lines = face_model.CalculateBox((float)fx, (float)fy, (float)cx, (float)cy);
+ }
+
+ if (reset)
+ {
+ face_model.Reset();
+ face_analyser.Reset();
+ reset = false;
+ }
+
+ // Visualisation updating
+ try
+ {
+ Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() =>
+ {
+ if (latest_img == null)
+ latest_img = frame.CreateWriteableBitmap();
+
+ List pose = new List();
+ face_model.GetPose(pose, fx, fy, cx, cy);
+
+ int yaw = (int)(pose[4] * 180 / Math.PI + 0.5);
+ int yaw_abs = Math.Abs(yaw);
+
+ int roll = (int)(pose[5] * 180 / Math.PI + 0.5);
+ int roll_abs = Math.Abs(roll);
+
+ int pitch = (int)(pose[3] * 180 / Math.PI + 0.5);
+ int pitch_abs = Math.Abs(pitch);
+
+ YawLabel.Content = yaw_abs + "°";
+ RollLabel.Content = roll_abs + "°";
+ PitchLabel.Content = pitch_abs + "°";
+
+ if (yaw > 0)
+ YawLabelDir.Content = "Right";
+ else if (yaw < 0)
+ YawLabelDir.Content = "Left";
+ else
+ YawLabelDir.Content = "Straight";
+
+ if (pitch > 0)
+ PitchLabelDir.Content = "Down";
+ else if (pitch < 0)
+ PitchLabelDir.Content = "Up";
+ else
+ PitchLabelDir.Content = "Straight";
+
+ if (roll > 0)
+ RollLabelDir.Content = "Left";
+ else if (roll < 0)
+ RollLabelDir.Content = "Right";
+ else
+ RollLabelDir.Content = "Straight";
+
+ XPoseLabel.Content = (int)pose[0] + " mm";
+ YPoseLabel.Content = (int)pose[1] + " mm";
+ ZPoseLabel.Content = (int)pose[2] + " mm";
+
+ String x_angle = String.Format("{0:F0}°", gaze_angle.Item1 * (180.0 / Math.PI));
+ String y_angle = String.Format("{0:F0}°", gaze_angle.Item2 * (180.0 / Math.PI));
+ YawLabelGaze.Content = x_angle;
+ PitchLabelGaze.Content = y_angle;
+
+ if (gaze_angle.Item1 > 0)
+ YawLabelGazeDir.Content = "Right";
+ else if (gaze_angle.Item1 < 0)
+ YawLabelGazeDir.Content = "Left";
+ else
+ YawLabelGazeDir.Content = "Straight";
+
+ if (gaze_angle.Item2 > 0)
+ PitchLabelGazeDir.Content = "Down";
+ else if (gaze_angle.Item2 < 0)
+ PitchLabelGazeDir.Content = "Up";
+ else
+ PitchLabelGazeDir.Content = "Straight";
+
+ double confidence = (-face_model.GetConfidence() + 1) / 2.0;
+
+ if (confidence < 0)
+ confidence = 0;
+ else if (confidence > 1)
+ confidence = 1;
+
+ frame.UpdateWriteableBitmap(latest_img);
+
+ webcam_img.Source = latest_img;
+ webcam_img.Confidence = confidence;
+ webcam_img.FPS = processing_fps.GetFPS();
+ if (!detectionSucceeding)
+ {
+ webcam_img.OverlayLines.Clear();
+ webcam_img.OverlayPoints.Clear();
+ webcam_img.OverlayEyePoints.Clear();
+ webcam_img.GazeLines.Clear();
+ }
+ else
+ {
+ webcam_img.OverlayLines = lines;
+ webcam_img.OverlayPoints = landmarks;
+ webcam_img.FaceScale = scale;
+
+ List eye_landmark_points = new List();
+ foreach (var p in eye_landmarks)
+ {
+ eye_landmark_points.Add(new System.Windows.Point(p.Item1, p.Item2));
+ }
+
+
+ webcam_img.OverlayEyePoints = eye_landmark_points;
+ webcam_img.GazeLines = gaze_lines;
+
+ // Publish the information for other applications
+ String str_head_pose = String.Format("{0}:{1:F2}, {2:F2}, {3:F2}, {4:F2}, {5:F2}, {6:F2}", "HeadPose", pose[0], pose[1], pose[2],
+ pose[3] * 180 / Math.PI, pose[4] * 180 / Math.PI, pose[5] * 180 / Math.PI);
+
+ zero_mq_socket.Send(new ZFrame(str_head_pose, Encoding.UTF8));
+
+ String str_gaze = String.Format("{0}:{1:F2}, {2:F2}", "GazeAngle", gaze_angle.Item1 * (180.0 / Math.PI), gaze_angle.Item2 * (180.0 / Math.PI));
+
+ zero_mq_socket.Send(new ZFrame(str_gaze, Encoding.UTF8));
+ }
+ }));
+
+ while (running & pause)
+ {
+
+ Thread.Sleep(10);
+ }
+
+ }
+ catch (TaskCanceledException)
+ {
+ // Quitting
+ break;
+ }
+ }
+ System.Console.Out.WriteLine("Thread finished");
+ }
+
+ private void startRecordingButton_Click(object sender, RoutedEventArgs e)
+ {
+ lock (recording_lock)
+ {
+ RecordingButton.IsEnabled = false;
+ CompleteButton.IsEnabled = false;
+ PauseButton.IsEnabled = false;
+
+ recording_objects = new ConcurrentQueue>>();
+
+ recording = true;
+
+ new Thread(() =>
+ {
+ Thread.CurrentThread.IsBackground = true;
+
+ // Start the recording thread
+ rec_thread = new Thread(RecordingLoop);
+ rec_thread.Start();
+
+ double d = seconds_to_record * 1000;
+
+ Stopwatch stopWatch = new Stopwatch();
+ stopWatch.Start();
+
+ while (d > 1000)
+ {
+
+ Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() =>
+ {
+ RecordingButton.Content = ((int)(d / 1000)).ToString() + " seconds remaining";
+ }));
+
+ System.Threading.Thread.Sleep(1000);
+
+ d = seconds_to_record * 1000 - stopWatch.ElapsedMilliseconds;
+ }
+
+ if (d > 0)
+ {
+ System.Threading.Thread.Sleep((int)(d));
+ }
+
+ recording = false;
+
+ Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() =>
+ {
+ RecordingButton.Content = "0 seconds remaining";
+ }));
+
+ Dispatcher.Invoke(() =>
+ {
+ lock (recording_lock)
+ {
+
+ // Wait for the recording thread to finish before enabling
+ rec_thread.Join();
+
+ string messageBoxText = "Was the tracking successful?";
+ string caption = "Success of tracking";
+ MessageBoxButton button = MessageBoxButton.YesNo;
+ MessageBoxImage icon = MessageBoxImage.Question;
+ MessageBoxResult result = MessageBox.Show(messageBoxText, caption, button, icon);
+
+ if (recording_success_file == null)
+ {
+ recording_success_file = new System.IO.StreamWriter(output_root + "/recording_success.txt", true);
+ }
+
+ if (result == MessageBoxResult.Yes)
+ {
+ recording_success_file.WriteLine('1');
+ }
+ else
+ {
+ recording_success_file.WriteLine('0');
+ }
+ recording_success_file.Flush();
+
+ trial_id++;
+ RecordingButton.Content = "Record trial: " + trial_id;
+ RecordingButton.IsEnabled = true;
+ CompleteButton.IsEnabled = true;
+ PauseButton.IsEnabled = true;
+
+ }
+
+ });
+ }).Start();
+ }
+ }
+
+ private void ResetButton_Click(object sender, RoutedEventArgs e)
+ {
+ reset = true;
+ }
+
+ private void CompleteButton_Click(object sender, RoutedEventArgs e)
+ {
+ StartExperiment();
+ }
+
+ private void MirrorButton_Click(object sender, RoutedEventArgs e)
+ {
+ mirror_image = !mirror_image;
+ }
+
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+
+ // Let it finish recording
+ recording = false;
+ if (rec_thread != null)
+ {
+ rec_thread.Join();
+ }
+
+ // Stop capture and tracking
+ running = false;
+ if (processing_thread != null)
+ {
+ processing_thread.Join();
+ }
+
+ if (capture != null)
+ capture.Dispose();
+
+ }
+
+ private void MorrorButton_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void PauseButton_Click(object sender, RoutedEventArgs e)
+ {
+ pause = !pause;
+
+ if (pause)
+ {
+ PauseButton.Content = "Resume";
+ }
+ else
+ {
+ PauseButton.Content = "Pause";
+ }
+ }
+
+ private void ScreenshotButton_Click(object sender, RoutedEventArgs e)
+ {
+
+ int Top = (int)this.Top;
+ int Left = (int)this.Left;
+
+ int Width = (int)this.Width;
+ int Height = (int)this.Height;
+
+ using (Bitmap bmpScreenCapture = new Bitmap(Width,
+ Height))
+ {
+ using (System.Drawing.Graphics g = Graphics.FromImage(bmpScreenCapture))
+ {
+ g.CopyFromScreen(Left,
+ Top,
+ 0, 0,
+ bmpScreenCapture.Size,
+ CopyPixelOperation.SourceCopy);
+
+ // Write out the bitmap here encoded by a time-stamp?
+ String fname = output_root + DateTime.Now.ToString("yyyy-MMM-dd--HH-mm-ss") + ".png";
+ bmpScreenCapture.Save(fname, ImageFormat.Png);
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/gui/HeadPose-live/Properties/AssemblyInfo.cs b/gui/HeadPose-live/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..f0d54a0
--- /dev/null
+++ b/gui/HeadPose-live/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeadPose-live")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("HeadPose-live")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/gui/HeadPose-live/Properties/Resources.Designer.cs b/gui/HeadPose-live/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..80229ed
--- /dev/null
+++ b/gui/HeadPose-live/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace HeadPoseLive.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HeadPoseLive.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/gui/HeadPose-live/Properties/Resources.resx b/gui/HeadPose-live/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/gui/HeadPose-live/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/gui/HeadPose-live/Properties/Settings.Designer.cs b/gui/HeadPose-live/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..70abedd
--- /dev/null
+++ b/gui/HeadPose-live/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace HeadPoseLive.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/gui/HeadPose-live/Properties/Settings.settings b/gui/HeadPose-live/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/gui/HeadPose-live/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/HeadPose-live/TextEntryWindow.xaml b/gui/HeadPose-live/TextEntryWindow.xaml
new file mode 100644
index 0000000..2b330ae
--- /dev/null
+++ b/gui/HeadPose-live/TextEntryWindow.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/HeadPose-live/TextEntryWindow.xaml.cs b/gui/HeadPose-live/TextEntryWindow.xaml.cs
new file mode 100644
index 0000000..e8a5664
--- /dev/null
+++ b/gui/HeadPose-live/TextEntryWindow.xaml.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace HeadPoseLive
+{
+ ///
+ /// Interaction logic for TextEntryWindow.xaml
+ ///
+ public partial class TextEntryWindow : Window
+ {
+ public TextEntryWindow()
+ {
+ InitializeComponent();
+
+ this.KeyDown += new KeyEventHandler(TextEntry_KeyDown);
+
+ }
+
+ public string ResponseText
+ {
+ get { return ResponseTextBox.Text; }
+ set { ResponseTextBox.Text = value; }
+ }
+
+ public bool RecordVideo
+ {
+ get { return (bool)RecordVideoCheckBox.IsChecked; }
+ set { RecordVideoCheckBox.IsChecked = value; }
+ }
+
+ public bool RecordHeadPose
+ {
+ get { return (bool)RecordHeadPoseCheckBox.IsChecked; }
+ set { RecordHeadPoseCheckBox.IsChecked = value; }
+ }
+
+
+ private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
+ {
+ DialogResult = true;
+ }
+
+ private void TextEntry_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
+ {
+ DialogResult = true;
+ }
+ }
+
+ // Do not allow illegal characters like
+ private void ResponseTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ Regex regex = new Regex("[/:*?<>|\"]");
+ Regex regex2 = new Regex(@"[\\]");
+ MatchCollection matches = regex.Matches(ResponseTextBox.Text);
+ MatchCollection matches2 = regex2.Matches(ResponseTextBox.Text);
+ if (matches.Count > 0 || matches2.Count > 0)
+ {
+ for (int i = matches.Count - 1; i >= 0; --i)
+ {
+ // Remove the illegal characters
+ ResponseTextBox.Text = ResponseTextBox.Text.Substring(0, matches[i].Index) + ResponseTextBox.Text.Substring(matches[i].Index + 1);
+ }
+
+ //tell the user
+ for (int i = matches2.Count - 1; i >= 0; --i)
+ {
+ // Remove the illegal characters
+ ResponseTextBox.Text = ResponseTextBox.Text.Substring(0, matches2[i].Index) + ResponseTextBox.Text.Substring(matches2[i].Index + 1);
+
+ }
+ warningLabel.Visibility = System.Windows.Visibility.Visible;
+ ResponseTextBox.SelectionStart = ResponseTextBox.Text.Length;
+ }
+ else
+ {
+ warningLabel.Visibility = System.Windows.Visibility.Collapsed;
+ }
+ }
+
+ }
+}
diff --git a/gui/HeadPose-live/logo1.ico b/gui/HeadPose-live/logo1.ico
new file mode 100644
index 0000000..60a49af
Binary files /dev/null and b/gui/HeadPose-live/logo1.ico differ
diff --git a/gui/HeadPose-live/logo1.png b/gui/HeadPose-live/logo1.png
new file mode 100644
index 0000000..e4e7ae7
Binary files /dev/null and b/gui/HeadPose-live/logo1.png differ
diff --git a/gui/HeadPose-live/packages.config b/gui/HeadPose-live/packages.config
new file mode 100644
index 0000000..da4894e
--- /dev/null
+++ b/gui/HeadPose-live/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/gui/OpenFaceDemo/OpenFaceDemo.csproj b/gui/OpenFaceDemo/OpenFaceDemo.csproj
index 98208ba..ad3591b 100644
--- a/gui/OpenFaceDemo/OpenFaceDemo.csproj
+++ b/gui/OpenFaceDemo/OpenFaceDemo.csproj
@@ -24,6 +24,7 @@
DEBUG;TRACE
prompt
4
+ Auto
x64
diff --git a/packages/ZeroMQ.4.1.0.22/ZeroMQ.4.1.0.22.nupkg b/packages/ZeroMQ.4.1.0.22/ZeroMQ.4.1.0.22.nupkg
new file mode 100644
index 0000000..ab59a37
Binary files /dev/null and b/packages/ZeroMQ.4.1.0.22/ZeroMQ.4.1.0.22.nupkg differ
diff --git a/packages/ZeroMQ.4.1.0.22/lib/net40/ZeroMQ.dll b/packages/ZeroMQ.4.1.0.22/lib/net40/ZeroMQ.dll
new file mode 100644
index 0000000..9a34378
Binary files /dev/null and b/packages/ZeroMQ.4.1.0.22/lib/net40/ZeroMQ.dll differ
diff --git a/packages/ZeroMQ.4.1.0.22/lib/net40/ZeroMQ.xml b/packages/ZeroMQ.4.1.0.22/lib/net40/ZeroMQ.xml
new file mode 100644
index 0000000..ca80592
--- /dev/null
+++ b/packages/ZeroMQ.4.1.0.22/lib/net40/ZeroMQ.xml
@@ -0,0 +1,1271 @@
+
+
+
+ ZeroMQ
+
+
+
+
+ Safe handle for unmanaged libraries. See http://msdn.microsoft.com/msdnmag/issues/05/10/Reliability/ for more about safe handles.
+
+
+
+
+ Utility class to wrap an unmanaged shared lib and be responsible for freeing it.
+
+
+ This is a managed wrapper over the native LoadLibrary, GetProcAddress, and FreeLibrary calls on Windows
+ and dlopen, dlsym, and dlclose on Posix environments.
+
+
+
+
+ Dynamically look up a function in the dll via kernel32!GetProcAddress or libdl!dlsym.
+
+ Delegate type to load
+ Raw name of the function in the export table.
+ A delegate to the unmanaged function.
+ Thrown if the given function name is not found in the library.
+
+ GetProcAddress results are valid as long as the dll is not yet unloaded. This
+ is very very dangerous to use since you need to ensure that the dll is not unloaded
+ until after you're done with any objects implemented by the dll. For example, if you
+ get a delegate that then gets an IUnknown implemented by this dll,
+ you can not dispose this library until that IUnknown is collected. Else, you may free
+ the library and then the CLR may call release on that IUnknown and it will crash.
+
+
+
+
+ Socket transport events (for TCP and IPC sockets) that can be monitored.
+
+
+
+
+ Triggered when a connection has been established to a remote peer.
+
+
+
+
+ Triggered when an immediate connection attempt is delayed and it's completion is being polled for.
+
+
+
+
+ Triggered when a connection attempt is being handled by reconnect timer. The reconnect interval is recomputed for each attempt.
+
+
+
+
+ Triggered when a socket is successfully bound to a an interface.
+
+
+
+
+ Triggered when a socket could not bind to a given interface.
+
+
+
+
+ Triggered when a connection from a remote peer has been established with a socket's listen address.
+
+
+
+
+ Triggered when a connection attempt to a socket's bound address fails.
+
+
+
+
+ Triggered when a connection's underlying descriptor has been closed.
+
+
+
+
+ Triggered when a descriptor could not be released back to the OS.
+
+
+
+
+ Triggered when the stream engine (tcp and ipc specific) detects a corrupted / broken session.
+
+
+
+
+ Monitoring on this socket ended.
+
+
+
+
+ Any event, maybe readable from EventValue.
+
+
+
+
+ Monitors state change events on another socket within the same context.
+
+
+
+
+ The polling interval in milliseconds.
+
+
+
+
+ Create a socket with the current context and the specified socket type.
+
+ A value for the socket.
+ A instance with the current context and the specified socket type.
+
+
+
+ Create a socket with the current context and the specified socket type.
+
+ A value for the socket.
+ A instance with the current context and the specified socket type.
+
+
+
+ Occurs when a new connection is established.
+ NOTE: Do not rely on the value for
+ 'Connected' messages, as the memory address contained in the message may no longer
+ point to the correct value.
+
+
+
+
+ Occurs when a synchronous connection attempt failed, and its completion is being polled for.
+
+
+
+
+ Occurs when an asynchronous connect / reconnection attempt is being handled by a reconnect timer.
+
+
+
+
+ Occurs when a socket is bound to an address and is ready to accept connections.
+
+
+
+
+ Occurs when a socket could not bind to an address.
+
+
+
+
+ Occurs when a connection from a remote peer has been established with a socket's listen address.
+
+
+
+
+ Occurs when a connection attempt to a socket's bound address fails.
+
+
+
+
+ Occurs when a connection was closed.
+ NOTE: Do not rely on the value for
+ 'Closed' messages, as the memory address contained in the message may no longer
+ point to the correct value.
+
+
+
+
+ Occurs when a connection couldn't be closed.
+
+
+
+
+ Occurs when the stream engine (tcp and ipc specific) detects a corrupted / broken session.
+
+
+
+
+ Monitoring on this socket ended.
+
+
+
+
+ Gets the endpoint to which the monitor socket is connected.
+
+
+
+
+ Begins monitoring for state changes, raising the appropriate events as they arrive.
+
+ NOTE: This is a blocking method and should be run from another thread.
+
+
+
+ Releases the unmanaged resources used by the , and optionally disposes of the managed resources.
+
+ true to release both managed and unmanaged resources; false to release only unmanaged resources.
+
+
+
+ A base class for the all ZmqMonitor events.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The that triggered the event.
+ The peer address.
+
+
+
+ Gets the monitor that triggered the event.
+
+
+
+
+ Provides data for , , , and events.
+
+
+
+
+ Gets the monitor descriptor (Posix)
+
+
+
+
+ Gets the monitor descriptor (Windows)
+
+
+
+
+ Provides data for event.
+
+
+
+
+ Gets the computed reconnect interval.
+
+
+
+
+ Specifies socket behavior when
+ an unroutable message is encountered.
+
+
+
+
+ Silently discard messages.
+
+
+
+
+ Force sending to fail with an 'EAGAIN' error code, effectively
+ enabling blocking sends.
+
+
+
+
+ Keep-alive packets behavior for a connection.
+
+
+
+
+ Use Operating System default behavior.
+
+
+
+
+ Disable keep-alive packets.
+
+
+
+
+ Enable keep-alive packets.
+
+
+
+
+ Creates instances within a process boundary.
+
+
+
+
+ Gets and protected sets the default Encoding.
+ Note: Do not set the Encoding after ZContext.Create.
+
+
+
+
+ Create a instance.
+
+
+
+
+
+ Create a instance.
+
+
+
+
+
+ Gets a handle to the native ZeroMQ context.
+
+
+
+
+ Gets or sets the size of the thread pool for the current context (default = 1).
+
+
+
+
+ Gets or sets the maximum number of sockets for the current context (default = 1024).
+
+
+
+
+ Gets or sets the supported socket protocol(s) when using TCP transports. (Default = ).
+
+
+
+
+ Shutdown the ZeroMQ context.
+
+
+
+
+ Shutdown the ZeroMQ context.
+
+
+
+
+ Terminate the ZeroMQ context.
+
+
+
+
+ Terminate the ZeroMQ context.
+
+
+
+
+ An exception thrown by the result of libzmq.
+
+
+
+
+ Gets the error code returned by libzmq.
+
+
+
+
+ Gets the error code returned by libzmq.
+
+
+
+
+ Gets the error text returned by libzmq.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The error code returned by the ZeroMQ library call.
+
+
+
+ Initializes a new instance of the class.
+
+ The error code returned by the ZeroMQ library call.
+
+
+
+ Initializes a new instance of the class.
+
+ The error code returned by the ZeroMQ library call.
+
+
+
+ Initializes a new instance of the class.
+
+ The error code returned by the ZeroMQ library call.
+
+
+
+ Initializes a new instance of the class
+ using zmq_strerror(int errno)
+
+ The error code returned by the ZeroMQ library call.
+
+
+
+ Initializes a new instance of the class.
+
+ that holds the serialized object data about the exception being thrown.
+ that contains contextual information about the source or destination.
+
+
+
+ A single part message, sent or received via a .
+
+
+
+
+ A single or multi-part message, sent or received via a .
+
+
+
+
+ Initializes a new instance of the class.
+ Creates an empty message.
+
+
+
+
+ Initializes a new instance of the class.
+ Creates a message that contains the given objects.
+
+ A collection of objects to be stored by this .
+ is null.
+
+
+
+ Removes ZFrames. Note: Disposes the ZFrame.
+
+ The .
+
+
+
+ Removes ZFrames.
+
+ The .
+ If set to false, do not dispose the ZFrame.
+
+
+
+ Sends and receives messages, single frames and byte frames across ZeroMQ.
+
+
+
+
+ Create a instance.
+
+
+
+
+
+ Create a instance.
+
+
+
+
+
+ Create a instance.
+ You are using ZContext.Current!
+
+
+
+
+
+ Create a instance.
+
+
+
+
+
+ Finalizes an instance of the class.
+
+
+
+
+ Releases the unmanaged resources used by the , and optionally disposes of the managed resources.
+
+ true to release both managed and unmanaged resources; false to release only unmanaged resources.
+
+
+
+ Close the current socket.
+
+
+
+
+ Close the current socket.
+
+
+
+
+ Gets the value for the current socket.
+
+
+
+
+ Bind the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Bind the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Unbind the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Unbind the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Connect the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Connect the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Disconnect the specified endpoint.
+
+
+
+
+ Disconnect the specified endpoint.
+
+ A string consisting of a transport and an address, formatted as transport://address.
+
+
+
+ Receives HARD bytes into a new byte[n]. Please don't use ReceiveBytes, use instead ReceiveFrame.
+
+
+
+
+ Receives HARD bytes into a new byte[n]. Please don't use ReceiveBytes, use instead ReceiveFrame.
+
+
+
+
+ Sends HARD bytes from a byte[n]. Please don't use SendBytes, use instead SendFrame.
+
+
+
+
+ Sends HARD bytes from a byte[n]. Please don't use SendBytes, use instead SendFrame.
+
+
+
+
+ Sends HARD bytes from a byte[n]. Please don't use SendBytes, use instead SendFrame.
+
+
+
+
+ Sends HARD bytes from a byte[n]. Please don't use SendBytes, use instead SendFrame.
+
+
+
+
+ Subscribe to all messages.
+
+
+ Only applies to and sockets.
+
+
+
+
+ Subscribe to messages that begin with a specified prefix.
+
+
+ Only applies to and sockets.
+
+ Prefix for subscribed messages.
+
+
+
+ Subscribe to messages that begin with a specified prefix.
+
+
+ Only applies to and sockets.
+
+ Prefix for subscribed messages.
+
+
+
+ Unsubscribe from all messages.
+
+
+ Only applies to and sockets.
+
+
+
+
+ Unsubscribe from messages that begin with a specified prefix.
+
+
+ Only applies to and sockets.
+
+ Prefix for subscribed messages.
+
+
+
+ Unsubscribe from messages that begin with a specified prefix.
+
+
+ Only applies to and sockets.
+
+ Prefix for subscribed messages.
+
+
+
+ Gets a value indicating whether the multi-part message currently being read has more message parts to follow.
+
+
+
+
+ Gets or sets the I/O thread affinity for newly created connections on this socket.
+
+
+
+
+ Gets or sets the maximum length of the queue of outstanding peer connections. (Default = 100 connections).
+
+
+
+
+ Gets or sets the Identity.
+
+ Identity as byte[]
+
+
+
+ Gets or sets the Identity.
+ Note: The string contains chars like \0 (null terminator,
+ which are NOT printed (in Console.WriteLine)!
+
+ Identity as string
+
+
+
+ Gets or sets the linger period for socket shutdown. (Default = , infinite).
+
+
+
+
+ Gets or sets the maximum size for inbound messages (bytes). (Default = -1, no limit).
+
+
+
+
+ Gets or sets the time-to-live field in every multicast packet sent from this socket (network hops). (Default = 1 hop).
+
+
+
+
+ Gets or sets the maximum send or receive data rate for multicast transports (kbps). (Default = 100 kbps).
+
+
+
+
+ Gets or sets the underlying kernel receive buffer size for the current socket (bytes). (Default = 0, OS default).
+
+
+
+
+ Gets or sets the high water mark for inbound messages (number of messages). (Default = 0, no limit).
+
+
+
+
+ Gets or sets the timeout for receive operations. (Default = , infinite).
+
+
+
+
+ Gets or sets the initial reconnection interval. (Default = 100 milliseconds).
+
+
+
+
+ Gets or sets the maximum reconnection interval. (Default = 0, only use ).
+
+
+
+
+ Gets or sets the recovery interval for multicast transports. (Default = 10 seconds).
+
+
+
+
+ Gets or sets the underlying kernel transmit buffer size for the current socket (bytes). (Default = 0, OS default).
+
+
+
+
+ Gets or sets the high water mark for outbound messages (number of messages). (Default = 0, no limit).
+
+
+
+
+ Gets or sets the timeout for send operations. (Default = , infinite).
+
+
+
+
+ Gets or sets the override value for the SO_KEEPALIVE TCP socket option. (where supported by OS). (Default = -1, OS default).
+
+
+
+
+ Gets or sets the override value for the 'TCP_KEEPCNT' socket option (where supported by OS). (Default = -1, OS default).
+ The default value of '-1' means to skip any overrides and leave it to OS default.
+
+
+
+
+ Gets or sets the override value for the TCP_KEEPCNT (or TCP_KEEPALIVE on some OS). (Default = -1, OS default).
+
+
+
+
+ Gets or sets the override value for the TCP_KEEPINTVL socket option (where supported by OS). (Default = -1, OS default).
+
+
+
+
+ Add a filter that will be applied for each new TCP transport connection on a listening socket.
+ Example: "127.0.0.1", "mail.ru/24", "::1", "::1/128", "3ffe:1::", "3ffe:1::/56"
+
+
+
+ If no filters are applied, then TCP transport allows connections from any IP.
+ If at least one filter is applied then new connection source IP should be matched.
+
+ IPV6 or IPV4 CIDR filter.
+
+
+
+ Reset all TCP filters assigned by
+ and allow TCP transport to accept connections from any IP.
+
+
+
+
+ No socket flags are specified.
+
+
+
+
+ The operation should be performed in non-blocking mode.
+
+
+
+
+ The message being sent is a multi-part message, and that further message parts are to follow.
+
+
+
+
+ Exclusive Pair
+
+
+
+
+ Publish
+
+
+
+
+ Subscribe
+
+
+
+
+ Request
+
+
+
+
+ Reply / Response
+
+
+
+
+ Dealer
+
+
+
+
+ Router
+
+
+ When receiving messages a socket shall prepend a message
+ part containing the identity of the originating peer to the message before
+ passing it to the application. When sending messages a ZMQ_ROUTER socket shall remove
+ the first part of the message and use it to determine the identity of the peer the message
+ shall be routed to. If the peer does not exist anymore the message shall be silently discarded.
+
+
+
+
+ Pull
+
+
+
+
+ Push
+
+
+
+
+ XPublisher
+
+
+ Subscription message is a byte '1' (for subscriptions) or byte '0' (for unsubscriptions) followed by the subscription body.
+
+
+
+
+ XSubscriber
+
+
+ Subscription message is a byte '1' (for subscriptions) or byte '0' (for unsubscriptions) followed by the subscription body.
+
+
+
+
+ Stream
+
+
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Finalizes an instance of the class.
+
+
+
+
+ Gets a value indicating whether the device loop is running.
+
+
+
+
+ Start the device in the current thread.
+
+ The has already been disposed.
+
+
+
+ Blocks the calling thread until the device terminates.
+
+
+
+
+ Blocks the calling thread until the device terminates.
+
+
+
+
+ Blocks the calling thread until the device terminates or the specified time elapses.
+
+
+ A set to the amount of time to wait for the device to terminate.
+
+
+ true if the device terminated; false if the device has not terminated after
+ the amount of time specified by has elapsed.
+
+
+
+
+ Stop the device in such a way that it can be restarted.
+
+
+
+
+ Stop the device and safely terminate the underlying sockets.
+
+
+
+
+ Releases all resources used by the current instance, including the frontend and backend sockets.
+
+
+
+
+ Stops the device and releases the underlying sockets. Optionally disposes of managed resources.
+
+ true to release both managed and unmanaged resources; false to release only unmanaged resources.
+
+
+
+ You are using ZContext.Current!
+
+
+
+
+ You are using ZContext.Current!
+
+
+
+
+ Defines extension methods related to monitoring for instances.
+
+
+
+
+ Spawns a socket that publishes all events for
+ the specified socket over the inproc transport at the given endpoint.
+
+
+
+
+ Spawns a socket that publishes all events for
+ the specified socket over the inproc transport at the given endpoint.
+
+
+
+
+ Spawns a socket that publishes all events for
+ the specified socket over the inproc transport at the given endpoint.
+
+
+
+
+ Spawns a socket that publishes all events for
+ the specified socket over the inproc transport at the given endpoint.
+
+
+
+
+ Forwards messages received by a front-end socket to a back-end socket, from which
+ they are then sent.
+
+
+ The base implementation of is not threadsafe. Do not construct
+ a device with sockets that were created in separate threads or separate contexts.
+
+
+
+
+ The polling interval in milliseconds.
+
+
+
+
+ The ZContext reference, to not become finalized
+
+
+
+
+ The frontend socket that will normally pass messages to .
+
+
+
+
+ The backend socket that will normally receive messages from (and possibly send replies to) .
+
+
+
+
+ You are using ZContext.Current!
+
+
+
+
+ Initializes a new instance of the class.
+ You are using ZContext.Current!
+
+
+ A that will pass incoming messages to .
+
+
+ A that will receive messages from (and optionally send replies to) .
+
+ The for the current device.
+
+
+
+ Initializes a new instance of the class.
+
+
+ A that will pass incoming messages to .
+
+
+ A that will receive messages from (and optionally send replies to) .
+
+ The for the current device.
+
+
+
+ Gets a for configuring the frontend socket.
+
+
+
+
+ Gets a for configuring the backend socket.
+
+
+
+
+ Initializes the frontend and backend sockets. Called automatically when starting the device.
+ If called multiple times, will only execute once.
+
+
+
+
+ Start the device in the current thread. Should be used by implementations of the method.
+
+
+ Initializes the sockets prior to starting the device with .
+
+
+
+
+ Invoked when a message has been received by the frontend socket.
+
+ A object containing the poll event args.
+
+
+
+ Invoked when a message has been received by the backend socket.
+
+ A object containing the poll event args.
+
+
+
+ Stops the device and releases the underlying sockets. Optionally disposes of managed resources.
+
+ true to release both managed and unmanaged resources; false to release only unmanaged resources.
+
+
+
+ Defines a fluent interface for configuring device sockets.
+
+
+
+
+ Configure the socket to bind to a given endpoint. See for details.
+
+ A string representing the endpoint to which the socket will bind.
+ The current object.
+
+
+
+ Configure the socket to connect to a given endpoint. See for details.
+
+ A string representing the endpoint to which the socket will connect.
+ The current object.
+
+
+
+ Configure the socket to subscribe to a specific prefix. See for details.
+
+ A byte array containing the prefix to which the socket will subscribe.
+ The current object.
+
+
+
+ Configure the socket to subscribe to all incoming messages. See for details.
+
+ The current object.
+
+
+
+ Device for a Publisher and Subscribers
+
+
+
+
+ The frontend for a forwarder device.
+
+
+
+
+ The backend for a forwarder device.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Forwards requests from the frontend socket to the backend socket.
+
+
+
+
+ PubSub Forwards the Subscription messages
+
+
+
+
+ Queuing Push-Pull Device
+
+
+
+
+ The frontend for a streamer device.
+
+
+
+
+ The backend for a streamer device.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Forwards requests from the frontend socket to the backend socket.
+
+
+
+
+ Not implemented for the .
+
+
+
+
+ A Device on Routers and Dealers
+
+
+
+
+ The frontend for a queue device.
+
+
+
+
+ The backend for a queue device.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class
+ and binds to the specified Frontend and Backend address.
+
+
+
+
+ Initializes a new instance of the class
+ and binds to the specified Frontend and Backend address.
+
+
+
+
+ Forwards requests from the frontend socket to the backend socket.
+
+
+
+
+ Forwards replies from the backend socket to the frontend socket.
+
+
+
+
+ The Stream to Dealer is a Device for reading
+ and sending REPlies to TCP
+
+
+
+
+ The frontend for a queue device.
+
+
+
+
+ The backend for a queue device.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Forwards requests from the frontend socket to the backend socket.
+
+
+
+
+ Forwards replies from the backend socket to the frontend socket.
+
+
+
+
diff --git a/python_scripts/testing_gaze.py b/python_scripts/testing_gaze.py
new file mode 100644
index 0000000..cb20a00
--- /dev/null
+++ b/python_scripts/testing_gaze.py
@@ -0,0 +1,32 @@
+import time
+
+def main():
+
+ import zmq
+ port = "5000"
+
+ context = zmq.Context()
+ socket = context.socket(zmq.SUB)
+
+ print "Collecting head pose updates..."
+
+ socket.connect ("tcp://localhost:%s" % port)
+ topic_filter = "GazeAngle:"
+ socket.setsockopt(zmq.SUBSCRIBE, topic_filter)
+
+ while True:
+ head_pose = socket.recv()
+ head_pose = head_pose[10:].split(',')
+ X = float(head_pose[0])
+ Y = float(head_pose[1])
+
+ print 'Yaw: %.1f, Pitch: %.1f' % (X, Y)
+
+ time.sleep(0.01)
+
+if __name__ == '__main__':
+ main()
+
+
+
+
diff --git a/python_scripts/testing_head_pose.py b/python_scripts/testing_head_pose.py
new file mode 100644
index 0000000..333efbf
--- /dev/null
+++ b/python_scripts/testing_head_pose.py
@@ -0,0 +1,37 @@
+import time
+
+def main():
+
+ import zmq
+ port = "5000"
+
+ context = zmq.Context()
+ socket = context.socket(zmq.SUB)
+
+ print "Collecting head pose updates..."
+
+ socket.connect ("tcp://localhost:%s" % port)
+ topic_filter = "HeadPose:"
+ socket.setsockopt(zmq.SUBSCRIBE, topic_filter)
+
+ while True:
+ head_pose = socket.recv()
+ head_pose = head_pose[9:].split(',')
+ X = float(head_pose[0])
+ Y = float(head_pose[1])
+ Z = float(head_pose[2])
+
+ pitch = float(head_pose[3])
+ yaw = float(head_pose[4])
+ roll = float(head_pose[5])
+
+ print 'X: %.1f, Y: %.1f, Z:%.1f, pitch: %.1f, yaw: %.1f, roll: %.1f' % (X, Y, Z, pitch, yaw, roll)
+
+ time.sleep(0.01)
+
+if __name__ == '__main__':
+ main()
+
+
+
+