AffdexMe upgraded to include a new selection ui as well as enable /

disable all expressions and emotions
This commit is contained in:
Abdelrahman Mahmoud 2015-09-03 16:31:43 -04:00
parent e4ba8321e0
commit 383bb912e7
5 changed files with 461 additions and 217 deletions

View file

@ -37,7 +37,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Affdex, Version=1.0.5589.23093, Culture=neutral, processorArchitecture=x86">
<Reference Include="Affdex, Version=2.0.0.10, Culture=neutral, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>c:\Program Files (x86)\Affectiva\Affdex SDK\bin\release\Affdex.dll</HintPath>
</Reference>
@ -55,8 +55,14 @@
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WpfAnimatedGif">
<HintPath>$(ProjectDir)packages\WpfAnimatedGif.1.4.13\lib\net\WpfAnimatedGif.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="MetricSelectionUI.xaml.cs" />
<Compile Include="NameToResourceConverter.cs" />
<Compile Include="UpperCaseConverter.cs" />
<Page Include="$(ProjectDir)MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -68,6 +74,10 @@
<DependentUpon>$(ProjectDir)MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="MetricSelectionUI.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="$(ProjectDir)Properties\AssemblyInfo.cs">
@ -102,9 +112,59 @@
<Resource Include="$(ProjectDir)Resources\AffdexMe_Logo.ico" />
</ItemGroup>
<ItemGroup>
<Content Include="affdex-native.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="affdex-native.dll" />
<Resource Include="anger.jpg" />
<Resource Include="attention.jpg" />
<Resource Include="brow_furrow.jpg" />
<Resource Include="brow_raise.jpg" />
<Resource Include="chin_raise.jpg" />
<Resource Include="contempt.jpg" />
<Resource Include="disgust.jpg" />
<Resource Include="engagement.jpg" />
<Resource Include="eye_closure.jpg" />
<Resource Include="fear.jpg" />
<Resource Include="frown.jpg" />
<Resource Include="inner_brow_raise.jpg" />
<Resource Include="joy.jpg" />
<Resource Include="lip_press.jpg" />
<Resource Include="lip_pucker.jpg" />
<Resource Include="lip_suck.jpg" />
<Resource Include="mouth_open.jpg" />
<Resource Include="negative_valence.jpg" />
<Resource Include="nose_wrinkle.jpg" />
<Resource Include="positive_valence.jpg" />
<Resource Include="sadness.jpg" />
<Resource Include="smile.jpg" />
<Resource Include="smirk.jpg" />
<Resource Include="surprise.jpg" />
<Resource Include="upper_lip_raise.jpg" />
<Resource Include="valence.jpg" />
<Resource Include="anger.gif" />
<Resource Include="attention.gif" />
<Resource Include="brow_furrow.gif" />
<Resource Include="brow_raise.gif" />
<Resource Include="chin_raise.gif" />
<Resource Include="contempt.gif" />
<Resource Include="disgust.gif" />
<Resource Include="engagement.gif" />
<Resource Include="eye_closure.gif" />
<Resource Include="fear.gif" />
<Resource Include="frown.gif" />
<Resource Include="inner_brow_raise.gif" />
<Resource Include="joy.gif" />
<Resource Include="lip_press.gif" />
<Resource Include="lip_pucker.gif" />
<Resource Include="lip_suck.gif" />
<Resource Include="mouth_open.gif" />
<Resource Include="negative_valence.gif" />
<Resource Include="nose_wrinkle.gif" />
<Resource Include="positive_valence.gif" />
<Resource Include="sadness.gif" />
<Resource Include="smile.gif" />
<Resource Include="smirk.gif" />
<Resource Include="surprise.gif" />
<Resource Include="upper_lip_raise.gif" />
<Resource Include="valence.gif" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View file

@ -28,7 +28,7 @@
<Setter Property="Margin" Value="1"/>
<Setter Property="Height" Value="30"/>
<Setter Property="MinWidth" Value="20"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
<Setter Property="Template">
<Setter.Value>
@ -81,7 +81,7 @@
<Setter Property="Margin" Value="1"/>
<Setter Property="Height" Value="30"/>
<Setter Property="MinWidth" Value="20"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
<Setter Property="Template">
<Setter.Value>
@ -103,7 +103,8 @@
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" TargetName="rectangle" Value="0.2"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Trigger Property
="IsPressed" Value="True">
<Setter Property="Opacity" TargetName="rectangle" Value="0.3"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
@ -127,13 +128,16 @@
<StackPanel Name="stackPanelImage" Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid Name="gridAffdexFaceDisplay" VerticalAlignment="Stretch" Width="auto" Height="560" >
<Image Name="imgAffdexFaceDisplay" Visibility="Hidden" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="UniformToFill"/>
<Image Name="imgAffdexLogoDisplay" Margin="40" Width="auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" Source="Resources/AffectivaLogo1.png" Visibility="Visible"/>
Height="auto" Source="AffectivaLogo1.png" Visibility="Visible"/>
<Canvas Name="canvasFacePoints" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Name="interocularDistanceDisplay" Visibility="Hidden" Text="Interocular Distance: " FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center" Margin="10,131,0,413"/>
<TextBlock Name="pitchDisplay" Visibility="Hidden" Text="pitch: " FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center" Margin="10,131,0,380"/>
<TextBlock Name="yawDisplay" Visibility="Hidden" Text="Yaw: " FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center" Margin="10,131,0,347"/>
<TextBlock Name="rollDisplay" Visibility="Hidden" Text="Roll: " FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center" Margin="10,131,0,314"/>
</Grid>
</StackPanel>
@ -158,51 +162,51 @@
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Row="0" Grid.Column="0" Margin="0,0,0,0">
<TextBlock Name="txtSmileClassifierName" Text="SMILE" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<StackPanel Name="stackPanel0" Orientation="Vertical" Grid.Row="0" Grid.Column="0" Margin="0,0,0,0">
<TextBlock Name="stackPanel0Name" Text="STACKPANEL0" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<Grid>
<TextBlock Name="txtSmileClassifierValueBackground" Width="50" Background="LimeGreen" HorizontalAlignment="Center"/>
<TextBlock Name="txtSmileClassifierValue" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center"/>
<TextBlock Name="stackPanel0ValueBackgroud" Width="50" Background="LimeGreen" HorizontalAlignment="Center"/>
<TextBlock Name="stackPanel0Value" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center"/>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="0" Grid.Column="2" Margin="0,0,0,0" >
<TextBlock Name="txtFrownClassifierName" Text="FROWN" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<StackPanel Name="stackPanel1" Orientation="Vertical" Grid.Row="0" Grid.Column="2" Margin="0,0,0,0" >
<TextBlock Name="stackPanel1Name" Text="STACKPANEL1" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<Grid>
<TextBlock Name="txtFrownClassifierValueBackground" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="txtFrownClassifierValue" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel1ValueBackgroud" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel1Value" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="0" Margin="0,0,0,0" >
<TextBlock Name="txtBrowRaiseClassifierName" Text="BROW RAISE" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<StackPanel Name="stackPanel2" Orientation="Vertical" Grid.Row="1" Grid.Column="0" Margin="0,0,0,0" >
<TextBlock Name="stackPanel2Name" Text="STACKPANEL2" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<Grid>
<TextBlock Name="txtBrowRaiseClassifierValueBackground" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="txtBrowRaiseClassifierValue" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel2ValueBackgroud" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel2Value" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="2" Margin="0,0,0,0" >
<TextBlock Name="txtValenceClassifierName" Text="VALENCE" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<StackPanel Name="stackPanel3" Orientation="Vertical" Grid.Row="1" Grid.Column="2" Margin="0,0,0,0" >
<TextBlock Name="stackPanel3Name" Text="STACKPANEL3" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<Grid>
<TextBlock Name="txtValenceClassifierValueBackground" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="txtValenceClassifierValue" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center"/>
<TextBlock Name="stackPanel3ValueBackgroud" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel3Value" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center"/>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="2" Grid.Column="0" Margin="0,0,0,0" >
<TextBlock Name="txtBrowLowerClassifierName" Text="BROW FURROW" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<StackPanel Name="stackPanel4" Orientation="Vertical" Grid.Row="2" Grid.Column="0" Margin="0,0,0,0" >
<TextBlock Name="stackPanel4Name" Text="STACKPANEL4" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center"/>
<Grid>
<TextBlock Name="txtBrowLowerClassifierValueBackground" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="txtBrowLowerClassifierValue" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel4ValueBackgroud" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel4Value" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="2" Grid.Column="2" Margin="0,0,0,0" >
<TextBlock Name="txtEngagementClassifierName" Text="ENGAGEMENT" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center" />
<StackPanel Name="stackPanel5" Orientation="Vertical" Grid.Row="2" Grid.Column="2" Margin="0,0,0,0" >
<TextBlock Name="stackPanel5Name" Text="STACKPANEL5" Foreground="OrangeRed" FontWeight="Bold" FontSize="16" HorizontalAlignment="Center" />
<Grid>
<TextBlock Name="txtEngagementClassifierValueBackground" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="txtEngagementClassifierValue" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel5ValueBackgroud" Width="50" Background="LimeGreen" HorizontalAlignment="Center" />
<TextBlock Name="stackPanel5Value" Width="50" TextAlignment="Center" Text="10%" FontWeight="SemiBold" Foreground="Black" HorizontalAlignment="Center" />
</Grid>
</StackPanel>
</Grid>
@ -224,7 +228,7 @@
<Image Name="imgAffdexLogoBackground" Width="auto" Grid.Column="1" Grid.Row="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" Source="Resources/AffectivaLogo1.png" Margin="0,0,0,-34" Grid.RowSpan="2"/>
Height="auto" Source="AffectivaLogo1.png" Margin="0,0,0,-34" Grid.RowSpan="2"/>
</Grid>
</StackPanel>
</Grid>
@ -234,11 +238,13 @@
<Grid HorizontalAlignment="Center">
<GroupBox Margin="0,5,0,0" Height="40" VerticalAlignment="Stretch" BorderBrush="Transparent" BorderThickness="0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Name="btnStartCamera" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="55" Content="Start" />
<Button Name="btnResetCamera" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="56" Content="Reset" />
<Button Name="btnShowPoints" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="90" Content="Show Points" />
<Button Name="btnStopCamera" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="55" Content="Stop" />
<Button Name="btnExit" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Bottom" Width="55" Content="Exit" />
<Button Name="btnStartCamera" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="40" Content="Start" />
<Button Name="btnResetCamera" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="40" Content="Reset" />
<Button Name="btnShowPoints" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="80" Content="Show Points" />
<Button Name="btnShowMeasurements" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="120" Content="Show Measurements" />
<Button Name="btnChooseWin" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="60" Content="Classifiers" Click="btnChooseWin_Click" />
<Button Name="btnStopCamera" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Top" Width="40" Content="Stop" />
<Button Name="btnExit" Style="{StaticResource CustomButtonStyle}" HorizontalAlignment="Center" Margin="0,0,5,0" VerticalAlignment="Bottom" Width="35" Content="Exit" />
</StackPanel>
</GroupBox>
</Grid>

View file

@ -10,38 +10,21 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.Specialized;
using Microsoft.Win32;
using System.Reflection;
namespace AffdexMe
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
public partial class MainWindow : Window, Affdex.ImageListener, Affdex.ProcessStatusListener
{
/// <summary>
/// Location of Affdex Data files
/// </summary>
private const String AFFDEX_DATA_PATH = "C:\\Program Files (x86)\\Affectiva\\Affdex SDK\\data";
/// <summary>
/// Location of AffdexSDK Licence file
/// </summary>
private const String AFFDEX_LICENSE_FILE = "C:\\Program Files (x86)\\Affectiva\\Affdex SDK\\affdex.license";
#region Member Variables and Enums
enum AffdexFaceClassifiers : int
{
Smile = 0,
BrowFurrow = 1,
BrowRaise = 2,
LipCornerDepressor = 3,
Engagement = 4,
Valence = 5
};
/// <summary>
/// The minimum length of the Classifier Value textbox
/// </summary>
@ -71,8 +54,9 @@ namespace AffdexMe
private Affdex.CameraDetector mCameraDetector;
private DateTime mStartTime;
private StringCollection mEnabledClassifiers;
private DateTime mStartTime;
private float mCurrentTimeStamp;
/// <summary>
@ -82,6 +66,7 @@ namespace AffdexMe
private double mImageYScaleFactor;
private bool mShowFacePoints;
private bool mShowMeasurements;
#endregion
@ -109,15 +94,6 @@ namespace AffdexMe
#region Listener Implementation
/// <summary>
///
/// </summary>
class Listener : Affdex.ImageListener, Affdex.ProcessStatusListener
{
public event EventHandler<ImageCaptureDataUpdateArgs> ImageCaptureUpdate;
public event EventHandler<ImageResultsDataUpdateArgs> ImageResultsUpdate;
public event EventHandler<Affdex.AffdexException> ExceptionHandler;
public void onImageResults(Dictionary<int, Affdex.Face> faces, Affdex.Frame image)
{
// For now only single face is supported
@ -125,49 +101,27 @@ namespace AffdexMe
{
Affdex.Face face = faces[0];
if (ImageResultsUpdate != null)
{
ImageResultsDataUpdateArgs imageResultsData = new ImageResultsDataUpdateArgs()
{
Image = image,
ImageResultsTimeStamp = image.getTimestamp(),
Face = face
};
ImageResultsUpdate(this, imageResultsData);
}
UpdateClassifierPanel(face);
DisplayFeaturePoints(image, face);
DisplayMeasurements(face);
}
}
/// <summary>
///
/// </summary>
/// <param name="affdexImage"></param>
public void onImageCapture(Affdex.Frame image)
{
if (ImageCaptureUpdate != null)
{
ImageCaptureDataUpdateArgs imageCaptureData = new ImageCaptureDataUpdateArgs()
{
Image = image,
ImageCaptureTimeStamp = image.getTimestamp()
};
ImageCaptureUpdate(this, imageCaptureData);
}
UpdateClassifierPanel();
DisplayImageToOffscreenCanvas(image);
}
public void onProcessingException(Affdex.AffdexException ex)
{
if (ExceptionHandler != null)
{
ExceptionHandler(this, ex);
}
String message = String.IsNullOrEmpty(ex.Message) ? "AffdexMe error encountered." : ex.Message;
ShowExceptionAndShutDown(message);
}
public void onProcessingFinished()
{
}
};
#endregion
@ -183,6 +137,67 @@ namespace AffdexMe
}));
}
private String GetClassifierDataFolder()
{
// First see if we can get the Install Path from the registry
RegistryKey rkCurrentUser = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
RegistryKey rkAffdexMe = rkCurrentUser.OpenSubKey("Software\\Affectiva\\AffdexMe");
String classifierPath = String.Empty;
if (rkAffdexMe != null && !String.IsNullOrEmpty((String)rkAffdexMe.GetValue("Install Directory")))
{
classifierPath = (String)rkAffdexMe.GetValue("Install Directory") + "\\data";
}
else
{
String affdexClassifierDir = Environment.GetEnvironmentVariable("AFFDEX_DATA_DIR");
if (String.IsNullOrEmpty(affdexClassifierDir))
{
ShowExceptionAndShutDown("AFFDEX_DATA_DIR environment variable (Classifier Data Directory) is not set");
}
else
{
classifierPath = affdexClassifierDir;
}
}
DirectoryInfo directoryInfo = new DirectoryInfo(classifierPath);
if (!directoryInfo.Exists)
{
ShowExceptionAndShutDown("AFFDEX_DATA_DIR (Classifier Data Directory) is set to an invalid folder location");
}
return classifierPath;
}
private String GetAffdexLicense()
{
// First see if we can get the License from the registry
RegistryKey rkCurrentUser = Registry.LocalMachine;
RegistryKey rkAffdexMe = rkCurrentUser.OpenSubKey("Software\\Affectiva\\AffdexMe");
String licensePath = String.Empty;
if ( rkAffdexMe != null && !String.IsNullOrEmpty((string)rkAffdexMe.GetValue("Install Directory")))
{
licensePath = (String)rkAffdexMe.GetValue("Install Directory");
}
else
{
licensePath = Environment.GetEnvironmentVariable("AFFDEX_LICENSE_DIR");
if (String.IsNullOrEmpty(licensePath))
{
ShowExceptionAndShutDown("AFFDEX_LICENSE_DIR environment variable (Affdex License Folder) is not set");
}
}
// Test the directory
DirectoryInfo directoryInfo = new DirectoryInfo(licensePath);
if (!directoryInfo.Exists)
{
ShowExceptionAndShutDown("AFFDEX_License_DIR (Affex License Folder) is set to an invalid folder location");
}
return licensePath + "\\affdex.license";
}
public MainWindow()
{
InitializeComponent();
@ -193,6 +208,7 @@ namespace AffdexMe
{
InitializeCameraApp();
mEnabledClassifiers = AffdexMe.Settings.Default.Classifiers;
// Enable/Disable buttons on start
btnStartCamera.IsEnabled =
btnResetCamera.IsEnabled =
@ -200,6 +216,16 @@ namespace AffdexMe
btnStopCamera.IsEnabled =
btnExit.IsEnabled = true;
if (AffdexMe.Settings.Default.ShowPoints)
{
btnShowPoints_Click(null, null);
}
if (AffdexMe.Settings.Default.ShowMeasurements)
{
btnShowMeasurements_Click(null, null);
}
this.ContentRendered += MainWindow_ContentRendered;
}
@ -248,6 +274,29 @@ namespace AffdexMe
return null;
}
private void DisplayMeasurements(Affdex.Face affdexFace)
{
//Update measurements
try
{
var result = this.Dispatcher.BeginInvoke((Action)(() =>
{
if (mShowMeasurements && (affdexFace != null))
{
interocularDistanceDisplay.Text = String.Format("Interocular Distance: {0}", affdexFace.Measurements.InterocularDistance);
pitchDisplay.Text = String.Format("Pitch Angle: {0}", affdexFace.Measurements.Orientation.Pitch);
yawDisplay.Text = String.Format("Yaw Angle: {0}", affdexFace.Measurements.Orientation.Yaw);
rollDisplay.Text = String.Format("Roll Angle: {0}", affdexFace.Measurements.Orientation.Roll);
}
}));
}
catch(Exception ex)
{
String message = String.IsNullOrEmpty(ex.Message) ? "AffdexMe error encountered." : ex.Message;
ShowExceptionAndShutDown(message);
}
}
private void DisplayFeaturePoints(Affdex.Frame affdexImage, Affdex.Face affdexFace)
{
try
@ -255,7 +304,7 @@ namespace AffdexMe
// Plot Face Points
if ((mShowFacePoints) && (affdexFace != null))
{
canvasFacePoints.Dispatcher.BeginInvoke((Action)(() =>
var result = this.Dispatcher.BeginInvoke((Action)(() =>
{
if ((mCameraDetector != null) && (mCameraDetector.isRunning()))
{
@ -268,7 +317,7 @@ namespace AffdexMe
mImageYScaleFactor = imgAffdexFaceDisplay.ActualHeight / affdexImage.getHeight();
SolidColorBrush pointBrush = new SolidColorBrush(Colors.Cornsilk);
var featurePoints = affdexFace.getFeaturePoints();
var featurePoints = affdexFace.FeaturePoints;
foreach (var point in featurePoints)
{
Ellipse ellipse = new Ellipse()
@ -279,15 +328,15 @@ namespace AffdexMe
};
canvasFacePoints.Children.Add(ellipse);
Canvas.SetLeft(ellipse, point.x * mImageXScaleFactor);
Canvas.SetTop(ellipse, point.y * mImageYScaleFactor);
Canvas.SetLeft(ellipse, point.X * mImageXScaleFactor);
Canvas.SetTop(ellipse, point.Y * mImageYScaleFactor);
}
// Draw Face Bounding Rectangle
var xMax = featurePoints.Max(r => r.x);
var xMin = featurePoints.Min(r => r.x);
var yMax = featurePoints.Max(r => r.y);
var yMin = featurePoints.Min(r => r.y);
var xMax = featurePoints.Max(r => r.X);
var xMin = featurePoints.Min(r => r.X);
var yMax = featurePoints.Max(r => r.Y);
var yMin = featurePoints.Min(r => r.Y);
// Adjust the x/y min to accomodate all points
xMin -= 2;
@ -312,9 +361,6 @@ namespace AffdexMe
Canvas.SetTop(boundingBox, yMin * mImageYScaleFactor);
mFeaturePointsSkipCount = 0;
affdexFace.Dispose();
affdexImage.Dispose();
}
}));
}
@ -343,13 +389,19 @@ namespace AffdexMe
// A Face was found - this comes from ImageResults CallBack
if (face != null)
{
int index = 0;
foreach (String metric in mEnabledClassifiers)
{
PropertyInfo info;
float value = -1;
if ((info = face.Expressions.GetType().GetProperty(NameMappings(metric))) != null) value = (float)info.GetValue(face.Expressions, null);
else if ((info = face.Emotions.GetType().GetProperty(NameMappings(metric))) != null) value = (float)info.GetValue(face.Emotions, null);
// Convert classifier value to Integer (percentage) for display purposes
mAffdexClassifierValues[(int)AffdexFaceClassifiers.Smile] = Convert.ToInt32(Math.Round(face.getSmileScore(), MidpointRounding.AwayFromZero));
mAffdexClassifierValues[(int)AffdexFaceClassifiers.BrowFurrow] = Convert.ToInt32(Math.Round(face.getBrowFurrowScore(), MidpointRounding.AwayFromZero));
mAffdexClassifierValues[(int)AffdexFaceClassifiers.BrowRaise] = Convert.ToInt32(Math.Round(face.getBrowRaiseScore(), MidpointRounding.AwayFromZero));
mAffdexClassifierValues[(int)AffdexFaceClassifiers.LipCornerDepressor] = Convert.ToInt32(Math.Round(face.getLipCornerDepressorScore(), MidpointRounding.AwayFromZero));
mAffdexClassifierValues[(int)AffdexFaceClassifiers.Engagement] = Convert.ToInt32(Math.Round(face.getEngagementScore(), MidpointRounding.AwayFromZero));
mAffdexClassifierValues[(int)AffdexFaceClassifiers.Valence] = Convert.ToInt32(Math.Round(face.getValenceScore(), MidpointRounding.AwayFromZero));
mAffdexClassifierValues[index] = Convert.ToInt32(Math.Round(value, MidpointRounding.AwayFromZero));
index++;
}
// Reset the cache count
mCachedSkipFaceResultsCount = 0;
@ -362,12 +414,7 @@ namespace AffdexMe
}
else if (++mCachedSkipFaceResultsCount > 10)
{
mAffdexClassifierValues[(int)AffdexFaceClassifiers.Smile] =
mAffdexClassifierValues[(int)AffdexFaceClassifiers.BrowFurrow] =
mAffdexClassifierValues[(int)AffdexFaceClassifiers.BrowRaise] =
mAffdexClassifierValues[(int)AffdexFaceClassifiers.LipCornerDepressor] =
mAffdexClassifierValues[(int)AffdexFaceClassifiers.Engagement] =
mAffdexClassifierValues[(int)AffdexFaceClassifiers.Valence] = 0;
for (int r = 0; r < mAffdexClassifierValues.Count(); r++) mAffdexClassifierValues[r] = 0;
// If we haven't seen a face in the past 30 frames (roughly 30/15fps seconds), don't display the classifiers
if (mCachedSkipFaceResultsCount >= 30)
@ -376,21 +423,26 @@ namespace AffdexMe
}
}
var result = this.Dispatcher.BeginInvoke((Action)(() =>
{
// Only display the classifiers and FacePoints if we've had a re
if (displayClassifiers)
{
int r = 0;
foreach (String classifier in mEnabledClassifiers)
{
String stackPanelName = String.Format("stackPanel{0}", r);
TextBlock ClassifierName = (TextBlock) gridClassifierDisplay.FindName(String.Format("{0}Name", stackPanelName));
TextBlock ClassifierValueBackgroud = (TextBlock)gridClassifierDisplay.FindName(String.Format("{0}ValueBackgroud", stackPanelName));
TextBlock ClassifierValue = (TextBlock)gridClassifierDisplay.FindName(String.Format("{0}Value", stackPanelName));
// Update the Classifier Display
UpdateClassifier(txtSmileClassifierName, txtSmileClassifierValue, txtSmileClassifierValueBackground, AffdexFaceClassifiers.Smile);
UpdateClassifier(txtFrownClassifierName, txtFrownClassifierValue, txtFrownClassifierValueBackground, AffdexFaceClassifiers.LipCornerDepressor);
UpdateClassifier(txtBrowRaiseClassifierName, txtBrowRaiseClassifierValue, txtBrowRaiseClassifierValueBackground, AffdexFaceClassifiers.BrowRaise);
UpdateClassifier(txtValenceClassifierName, txtValenceClassifierValue, txtValenceClassifierValueBackground, AffdexFaceClassifiers.Valence);
UpdateClassifier(txtBrowLowerClassifierName, txtBrowLowerClassifierValue, txtBrowLowerClassifierValueBackground, AffdexFaceClassifiers.BrowFurrow);
UpdateClassifier(txtEngagementClassifierName, txtEngagementClassifierValue, txtEngagementClassifierValueBackground, AffdexFaceClassifiers.Engagement);
UpdateClassifier(ClassifierName, ClassifierValue, ClassifierValueBackgroud, classifier, r);
r++;
}
}
// Update the Image control from the UI thread
stackPanelClassifiers.Dispatcher.BeginInvoke((Action)(() =>
{
if ((mCameraDetector != null) && (mCameraDetector.isRunning()))
{
if (imgAffdexFaceDisplay.Visibility == Visibility.Hidden)
@ -400,6 +452,10 @@ namespace AffdexMe
stackPanelLogoBackground.Visibility = Visibility.Visible;
}
stackPanelClassifiers.Visibility = (displayClassifiers)?Visibility.Visible : Visibility.Hidden;
interocularDistanceDisplay.Visibility = (displayClassifiers && mShowMeasurements) ? Visibility.Visible : Visibility.Hidden;
pitchDisplay.Visibility = (displayClassifiers && mShowMeasurements) ? Visibility.Visible : Visibility.Hidden;
yawDisplay.Visibility = (displayClassifiers && mShowMeasurements) ? Visibility.Visible : Visibility.Hidden;
rollDisplay.Visibility = (displayClassifiers && mShowMeasurements) ? Visibility.Visible : Visibility.Hidden;
}
}));
}
@ -411,11 +467,22 @@ namespace AffdexMe
}
}
private String NameMappings(String classifierName)
{
if (classifierName == "Frown")
{
return "LipCornerDepressor";
}
return classifierName;
}
private void UpdateClassifier(TextBlock txtClassifier, TextBlock txtClassifierValue,
TextBlock txtClassifierValueBackground, AffdexFaceClassifiers classifierIndex)
TextBlock txtClassifierValueBackground, String classifierName, int classifierIndex)
{
try
{
UpperCaseConverter conv = new UpperCaseConverter();
txtClassifier.Text = (String)conv.Convert(classifierName, null, null, null);
int classifierValue = mAffdexClassifierValues[(int)classifierIndex];
// Calculate the width
@ -432,13 +499,9 @@ namespace AffdexMe
backgroundColor = Colors.Red;
}
txtClassifierValue.Dispatcher.BeginInvoke((Action)(() =>
{
txtClassifierValueBackground.Background = new SolidColorBrush(backgroundColor);
txtClassifierValueBackground.Width = width;
txtClassifierValue.Text = String.Format("{0}%", classifierValue);
}));
}
catch (Exception ex)
{
@ -450,7 +513,7 @@ namespace AffdexMe
private void DisplayImageToOffscreenCanvas(Affdex.Frame image)
{
// Update the Image control from the UI thread
imgAffdexFaceDisplay.Dispatcher.BeginInvoke((Action)(() =>
var result = this.Dispatcher.BeginInvoke((Action)(() =>
{
try
{
@ -493,6 +556,7 @@ namespace AffdexMe
btnStartCamera.Click += btnStartCamera_Click;
btnStopCamera.Click += btnStopCamera_Click;
btnShowPoints.Click += btnShowPoints_Click;
btnShowMeasurements.Click += btnShowMeasurements_Click;
btnResetCamera.Click += btnResetCamera_Click;
btnExit.Click += btnExit_Click;
@ -510,6 +574,7 @@ namespace AffdexMe
// Face Points are off by default
mShowFacePoints = false;
mShowMeasurements = false;
// Show the logo
imgAffdexLogoDisplay.Visibility = Visibility.Visible;
@ -555,6 +620,44 @@ namespace AffdexMe
}
}
void btnShowMeasurements_Click(object sender, RoutedEventArgs e)
{
try
{
Style style;
String buttonText = String.Empty;
mShowMeasurements = !mShowMeasurements;
if (mShowMeasurements)
{
style = this.FindResource("PointsOnButtonStyle") as Style;
buttonText = "Hide Measurements";
interocularDistanceDisplay.Visibility = Visibility.Visible;
pitchDisplay.Visibility = Visibility.Visible;
yawDisplay.Visibility = Visibility.Visible;
rollDisplay.Visibility = Visibility.Visible;
}
else
{
style = this.FindResource("CustomButtonStyle") as Style;
buttonText = "Show Measurements";
interocularDistanceDisplay.Visibility = Visibility.Hidden;
pitchDisplay.Visibility = Visibility.Hidden;
yawDisplay.Visibility = Visibility.Hidden;
rollDisplay.Visibility = Visibility.Hidden;
}
btnShowMeasurements.Style = style;
btnShowMeasurements.Content = buttonText;
}
catch (Exception ex)
{
String message = String.IsNullOrEmpty(ex.Message) ? "AffdexMe error encountered." : ex.Message;
ShowExceptionAndShutDown(message);
}
}
private void btnResetCamera_Click(object sender, RoutedEventArgs e)
{
ResetCameraProcessing();
@ -581,9 +684,18 @@ namespace AffdexMe
void btnExit_Click(object sender, RoutedEventArgs e)
{
SaveSettings();
Application.Current.Shutdown();
}
void SaveSettings()
{
AffdexMe.Settings.Default.ShowPoints = mShowFacePoints;
AffdexMe.Settings.Default.ShowMeasurements = mShowMeasurements;
AffdexMe.Settings.Default.Classifiers = mEnabledClassifiers;
AffdexMe.Settings.Default.Save();
}
private void ClearClassifiersAndPointsDisplay()
{
// Hide AffdexFace Image
@ -591,6 +703,12 @@ namespace AffdexMe
stackPanelLogoBackground.Visibility =
stackPanelClassifiersBackground.Visibility = Visibility.Hidden;
//Clean measurements
interocularDistanceDisplay.Text = String.Format("Interocular Distance: {0}", 0);
pitchDisplay.Text = String.Format("Pitch Angle: {0}", 0);
yawDisplay.Text = String.Format("Yaw Angle: {0}", 0);
rollDisplay.Text = String.Format("Roll Angle: {0}", 0);
// Hide the Classifier Panel
stackPanelClassifiers.Visibility = Visibility.Hidden;
@ -614,21 +732,33 @@ namespace AffdexMe
}
}
private void TurnOnClassifiers()
{
mCameraDetector.setDetectAllEmotions(false);
mCameraDetector.setDetectAllExpressions(false);
foreach (String metric in mEnabledClassifiers)
{
MethodInfo setMethodInfo = mCameraDetector.GetType().GetMethod(String.Format("setDetect{0}", NameMappings(metric)));
setMethodInfo.Invoke(mCameraDetector, new object[] { true });
}
}
private void StartCameraProcessing()
{
try
{
btnStartCamera.IsEnabled = false;
btnResetCamera.IsEnabled =
btnShowPoints.IsEnabled =
btnStopCamera.IsEnabled =
btnExit.IsEnabled = true;
// Instantiate CameraDetector using default camera ID
mCameraDetector = new Affdex.CameraDetector();
mCameraDetector.setClassifierPath(AFFDEX_DATA_PATH);
mCameraDetector.setClassifierPath(GetClassifierDataFolder());
// Set the Classifiers that we are interested in tracking
mCameraDetector.setDetectSmile(true);
mCameraDetector.setDetectBrowFurrow(true);
mCameraDetector.setDetectBrowRaise(true);
mCameraDetector.setDetectLipCornerDepressor(true);
mCameraDetector.setDetectEngagement(true);
mCameraDetector.setDetectValence(true);
TurnOnClassifiers();
// Initialize Classifier cache
for (int index = 0; index < mAffdexClassifierValues.Count(); index++)
@ -637,23 +767,13 @@ namespace AffdexMe
}
mCachedSkipFaceResultsCount = 0;
mCameraDetector.setImageListener(this);
mCameraDetector.setProcessStatusListener(this);
Listener listener = new Listener();
listener.ImageCaptureUpdate += imageListener_ImageCaptureUpdate;
listener.ImageResultsUpdate += imageListener_ImageResultsUpdate;
listener.ExceptionHandler += imageListener_ExceptionHandler;
mCameraDetector.setImageListener(listener);
mCameraDetector.setProcessStatusListener(listener);
btnStartCamera.IsEnabled = false;
btnResetCamera.IsEnabled =
btnShowPoints.IsEnabled =
btnStopCamera.IsEnabled =
btnExit.IsEnabled = true;
// Set the License Path
mCameraDetector.setLicensePath(AFFDEX_LICENSE_FILE);
mCameraDetector.setLicensePath(GetAffdexLicense());
mStartTime = DateTime.Now;
mCameraDetector.start();
@ -690,24 +810,6 @@ namespace AffdexMe
}
}
void imageListener_ExceptionHandler(object sender, Affdex.AffdexException ex)
{
String message = String.IsNullOrEmpty(ex.Message) ? "AffdexMe error encountered." : ex.Message;
ShowExceptionAndShutDown(message);
}
void imageListener_ImageResultsUpdate(object sender, MainWindow.ImageResultsDataUpdateArgs e)
{
UpdateClassifierPanel(e.Face);
DisplayFeaturePoints(e.Image, e.Face);
}
void imageListener_ImageCaptureUpdate(object sender, MainWindow.ImageCaptureDataUpdateArgs e)
{
UpdateClassifierPanel();
DisplayImageToOffscreenCanvas(e.Image);
}
private void ResetCameraProcessing()
{
try
@ -748,8 +850,28 @@ namespace AffdexMe
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
StopCameraProcessing();
SaveSettings();
Application.Current.Shutdown();
}
private void btnChooseWin_Click(object sender, RoutedEventArgs e)
{
Boolean wasRunning = false;
if ((mCameraDetector != null) && (mCameraDetector.isRunning()))
{
StopCameraProcessing();
ResetDisplayArea();
wasRunning = true;
}
MetricSelectionUI w = new MetricSelectionUI(mEnabledClassifiers);
w.ShowDialog();
mEnabledClassifiers = w.Classifiers;
if (wasRunning)
{
StartCameraProcessing();
}
}
}
}

View file

@ -1,30 +1,70 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34209
// Runtime Version:4.0.30319.18444
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AffdexMe.Properties
{
namespace AffdexMe {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.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
{
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(@"<?xml version=""1.0"" encoding=""utf-16""?>
<ArrayOfString xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<string>Joy</string>
<string>Sadness</string>
<string>Anger</string>
<string>Disgust</string>
<string>Surprise</string>
<string>Fear</string>
</ArrayOfString>")]
public global::System.Collections.Specialized.StringCollection Classifiers {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["Classifiers"]));
}
set {
this["Classifiers"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool ShowPoints {
get {
return ((bool)(this["ShowPoints"]));
}
set {
this["ShowPoints"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool ShowMeasurements {
get {
return ((bool)(this["ShowMeasurements"]));
}
set {
this["ShowMeasurements"] = value;
}
}
}
}

View file

@ -1,7 +1,23 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AffdexMe" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="Classifiers" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
&lt;string&gt;Joy&lt;/string&gt;
&lt;string&gt;Sadness&lt;/string&gt;
&lt;string&gt;Anger&lt;/string&gt;
&lt;string&gt;Disgust&lt;/string&gt;
&lt;string&gt;Surprise&lt;/string&gt;
&lt;string&gt;Fear&lt;/string&gt;
&lt;/ArrayOfString&gt;</Value>
</Setting>
<Setting Name="ShowPoints" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ShowMeasurements" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>