Standalone head pose module with ZeroMQ streaming

This commit is contained in:
Tadas Baltrusaitis 2017-04-17 15:47:27 -04:00
parent ba1e55abe7
commit a5fd98f822
26 changed files with 2875 additions and 0 deletions

1
.gitignore vendored
View File

@ -71,3 +71,4 @@ lib/local/CppInerop/Release/
lib/local/CppInerop/x64/
lib/local/FaceAnalyser/Release/
lib/local/LandmarkDetector/Release/
gui/HeadPose-live/obj/

View File

@ -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

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

View File

@ -0,0 +1,9 @@
<Application x:Class="HeadPose_live.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HeadPose_live"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -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
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F396362D-821E-4EA6-9BBF-1F6050844118}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HeadPoseLive</RootNamespace>
<AssemblyName>HeadPoseLive</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>..\..\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>logo1.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="ZeroMQ, Version=4.1.0.22, Culture=neutral, PublicKeyToken=4a9630883fd6c563, processorArchitecture=MSIL">
<HintPath>..\..\packages\ZeroMQ.4.1.0.22\lib\net40\ZeroMQ.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Liability.xaml.cs">
<DependentUpon>Liability.xaml</DependentUpon>
</Compile>
<Compile Include="TextEntryWindow.xaml.cs">
<DependentUpon>TextEntryWindow.xaml</DependentUpon>
</Compile>
<Page Include="Liability.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="TextEntryWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\local\CppInerop\CppInerop.vcxproj">
<Project>{78196985-ee54-411f-822b-5a23edf80642}</Project>
<Name>CppInerop</Name>
</ProjectReference>
<ProjectReference Include="..\OpenFaceDemo\OpenFaceDemo.csproj">
<Project>{e143a2aa-312e-4dfe-b61d-9a87ccbc8e90}</Project>
<Name>OpenFaceDemo</Name>
</ProjectReference>
<ProjectReference Include="..\OpenFaceOffline\OpenFaceOffline.csproj">
<Project>{a4760f41-2b1f-4144-b7b2-62785affe79b}</Project>
<Name>OpenFaceOffline</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="logo1.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>xcopy /I /E /Y /D "$(ProjectDir)logo1.ico" "$(ProjectDir)$(OutDir)"
xcopy /I /E /Y /D "$(ProjectDir)logo1.png" "$(ProjectDir)$(OutDir)"</PostBuildEvent>
</PropertyGroup>
<Import Project="..\..\packages\ZeroMQ.4.1.0.22\build\net40\ZeroMQ.targets" Condition="Exists('..\..\packages\ZeroMQ.4.1.0.22\build\net40\ZeroMQ.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>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}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\ZeroMQ.4.1.0.22\build\net40\ZeroMQ.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\ZeroMQ.4.1.0.22\build\net40\ZeroMQ.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,26 @@
<Window x:Class="HeadPoseLive.Liability"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Liability" Height="300" Width="400" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBlock TextWrapping="WrapWithOverflow" Margin="10,10,10,10" TextAlignment="Justify" Grid.Row="0">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.</TextBlock>
<Button Grid.Row="1" HorizontalAlignment="Center" Click="Button_Click" Name="ContinueButton">
Continue
</Button>
</Grid>
</Window>

View File

@ -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
{
/// <summary>
/// Interaction logic for Liability.xaml
/// </summary>
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;
}
}
}
}

View File

@ -0,0 +1,94 @@
<Window x:Class="HeadPoseLive.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Ophthalm_experiments="clr-namespace:HeadPoseLive"
Title="Cambridge Face Tracker - Head Pose Experiments" MinHeight="400" MinWidth="640" WindowStartupLocation="CenterScreen" UseLayoutRounding="True" Closing="Window_Closing">
<Window.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="RenderOptions.BitmapScalingMode"
Value="Fant" />
</Style>
</Window.Resources>
<Grid Name="MainGrid" HorizontalAlignment="Stretch" MinWidth="620">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition MinWidth="300"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition MinWidth="10" Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition MinHeight="200"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Label HorizontalAlignment="Left" VerticalAlignment="Bottom" Grid.Column="1" Grid.Row="1" Background="DarkSalmon" Canvas.ZIndex="1" MouseDown="ResetButton_Click" Name="ResetButton" Content="Reset"/>
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Width="110" Height="26" FontSize="16" Click="startRecordingButton_Click" Name="RecordingButton">Record trial 0</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2" VerticalAlignment="Top">
<Button Name="PauseButton" Click="PauseButton_Click">
Pause
</Button>
<Button Name="ScreenshotButton" Click="ScreenshotButton_Click" Margin="0,10,0,0">
Take a screenshot
</Button>
<Button Name="MirrorImageButton" Click="MirrorButton_Click" Margin="0,10,0,0">
Mirror image
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Width="185">
<Label Name="headOrientationLabel" Margin="0,0,0,0" FontSize="18" HorizontalContentAlignment="Left">Head Orientation</Label>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" Width="80" HorizontalContentAlignment="Left">Turn:</Label>
<Label Name="YawLabel" FontSize="16" MinWidth="30" HorizontalContentAlignment="Right">0°</Label>
<Label Name="YawLabelDir" FontSize="16" Width="70" HorizontalContentAlignment="Left">straight</Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" Width="80" HorizontalContentAlignment="Left">Up/down:</Label>
<Label Name="PitchLabel" FontSize="16" Width="30" HorizontalContentAlignment="Right">0°</Label>
<Label Name="PitchLabelDir" FontSize="16" Width="70" HorizontalContentAlignment="Left">straight</Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" Width="80" HorizontalContentAlignment="Left">Tilt:</Label>
<Label Name="RollLabel" FontSize="16" Width="30" HorizontalContentAlignment="Right">0°</Label>
<Label Name="RollLabelDir" FontSize="16" Width="70" HorizontalContentAlignment="Left">straight</Label>
</StackPanel>
<Label Name="gazeLabel" Margin="0,0,0,0" FontSize="18" HorizontalContentAlignment="Left">Gaze Orientation</Label>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" Width="80" HorizontalContentAlignment="Left">Left-right:</Label>
<Label Name="YawLabelGaze" FontSize="16" MinWidth="30" HorizontalContentAlignment="Right">0°</Label>
<Label Name="YawLabelGazeDir" FontSize="16" Width="70" HorizontalContentAlignment="Left">straight</Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" Width="80" HorizontalContentAlignment="Left">Up/down:</Label>
<Label Name="PitchLabelGaze" FontSize="16" Width="30" HorizontalContentAlignment="Right">0°</Label>
<Label Name="PitchLabelGazeDir" FontSize="16" Width="70" HorizontalContentAlignment="Left">straight</Label>
</StackPanel>
<Label Name="headPoseLabel" Margin="0,0,0,0" FontSize="18" HorizontalContentAlignment="Left">Pose</Label>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" HorizontalContentAlignment="Left" Width="20">X:</Label>
<Label Name="XPoseLabel" FontSize="16" HorizontalContentAlignment="Right" Width="70">0 mm</Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" HorizontalContentAlignment="Left" Width="20">Y:</Label>
<Label Name="YPoseLabel" FontSize="16" HorizontalContentAlignment="Right" Width="70">0 mm</Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="5,0,0,0" FontSize="16" HorizontalContentAlignment="Left" Width="20">Z:</Label>
<Label Name="ZPoseLabel" FontSize="16" HorizontalContentAlignment="Right" Width="70">0 mm</Label>
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="2" VerticalAlignment="Bottom">
<Button FontSize="16" HorizontalAlignment="Center" Click="CompleteButton_Click" Name="CompleteButton" >Start new subject</Button>
</StackPanel>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Width="80" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2">
<Image RenderOptions.BitmapScalingMode="Fant" RenderOptions.EdgeMode="Aliased" x:Name="logoLabel" Source="/logo1.png" Stretch="Uniform" />
</StackPanel>
</Grid>
</Window>

View File

@ -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
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
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<Tuple<RawImage, bool, List<double>>> 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<RawImage, bool, List<double>> 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<double> pose = new List<double>();
face_model.GetPose(pose, fx, fy, cx, cy);
RawImage image = new RawImage(frame);
recording_objects.Enqueue(new Tuple<RawImage, bool, List<double>>(image, detectionSucceeding, pose));
}
}
List<Tuple<System.Windows.Point, System.Windows.Point>> lines = null;
List<Tuple<double, double>> eye_landmarks = null;
List<System.Windows.Point> landmarks = new List<System.Windows.Point>();
List<Tuple<System.Windows.Point, System.Windows.Point>> gaze_lines = null;
Tuple<double, double> gaze_angle = new Tuple<double, double>(0, 0);
double scale = 0;
if (detectionSucceeding)
{
List<Tuple<double, double>> 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<double> pose = new List<double>();
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<System.Windows.Point> eye_landmark_points = new List<System.Windows.Point>();
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<Tuple<RawImage, bool, List<double>>>();
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);
}
}
}
}
}

View File

@ -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
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> 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")]

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HeadPoseLive.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -0,0 +1,17 @@
<Window x:Class="HeadPoseLive.TextEntryWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Configuration and subject information" Height="210" Width="300">
<Grid>
<StackPanel FocusManager.FocusedElement="{Binding ElementName=ResponseTextBox}">
<TextBlock HorizontalAlignment="Center" Text="Enter subject ID" FontSize="20"/>
<TextBox Margin="0,4,0,0" x:Name="ResponseTextBox" FontSize="20" Width="120" TextChanged="ResponseTextBox_TextChanged" />
<Label Name="warningLabel" Visibility="Collapsed" FontStyle="Italic" Foreground="Red" HorizontalAlignment="Center">Can't use the following characters: " \ / | &lt; > : * ?</Label>
<CheckBox HorizontalAlignment="Center" x:Name="RecordVideoCheckBox" Margin="-25,10,0,0" Content="Record video"/>
<CheckBox Margin="0,6,0,0" IsChecked="True" HorizontalAlignment="Center" x:Name="RecordHeadPoseCheckBox" Content="Record head pose"/>
<Button Margin="0,8,0,0" Content="OK" Click="OKButton_Click" Width="100" VerticalAlignment="Bottom"/>
</StackPanel>
</Grid>
</Window>

View File

@ -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
{
/// <summary>
/// Interaction logic for TextEntryWindow.xaml
/// </summary>
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;
}
}
}
}

BIN
gui/HeadPose-live/logo1.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
gui/HeadPose-live/logo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ZeroMQ" version="4.1.0.22" targetFramework="net452" />
</packages>

View File

@ -24,6 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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()