首先说明一下,该人脸识别算法不是用代码实现的,是调用微软云的人脸识别,此处仅仅叙述调用的方法,以及数据的处理。
话不多说,正文开始。
- 首先注册微软云的账户,在产品中查找“人脸”,获取账户的免费api使用。微软云提供终结点和秘钥。(“快速入门指南”中,微软提供了一个C#控制台应用程序的demo,本博客的关键代码参考该项目)如图:
- 使用vs2017,新建C#wpf应用程序。
- 桌面布局如下,这个是wpf应用程序的代码布局,效果,如下图
<Window x:Class="WpfFaceDetect.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfFaceDetect" mc:Ignorable="d" Title="MainWindow" Height="400" Width="650"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="413*"/> <ColumnDefinition Width="230*"/> </Grid.ColumnDefinitions> <Image x:Name="image" HorizontalAlignment="Left" Height="300" VerticalAlignment="Top" Width="400"/> <Button Content="打开文件" HorizontalAlignment="Left" Margin="56.95,25,0,0" VerticalAlignment="Top" Width="100" Height="50" Cursor="Hand" FontSize="18" FontFamily="STSong" RenderTransformOrigin="0.303,0.36" Click="Button_Click" FontWeight="Bold" Grid.Column="1"/> <ListBox x:Name="listBox" HorizontalAlignment="Left" Height="220" Margin="400,80,0,0" VerticalAlignment="Top" Width="240" FontSize="16" FontFamily="STSong" Grid.ColumnSpan="2"/> <Label x:Name="lab_result" Content="未选择图片" HorizontalAlignment="Left" Margin="0,300,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" Height="70" Width="640" FontFamily="STSong" FontSize="16"/> </Grid> </Window>
下面就是全部的代码
faceDetect.cs
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace WpfFaceDetect
{
class faceDetect
{
// Replace <Subscription Key> with your valid subscription key.
const string subscriptionKey = "1415759b75fb46ff9c45ad7db517c7fe";
// NOTE: You must use the same region in your REST call as you used to
// obtain your subscription keys. For example, if you obtained your
// subscription keys from westus, replace "westcentralus" in the URL
// below with "westus".
//
// Free trial subscription keys are generated in the westcentralus region.
// If you use a free trial subscription key, you shouldn't need to change
// this region.
const string uriBase =
"https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect";
/// <summary>
/// Gets the analysis of the specified image by using the Face REST API.
/// </summary>
/// <param name="imageFilePath">The image file.</param>
public static async Task<string> MakeAnalysisRequest(string imageFilePath)
{
HttpClient client = new HttpClient();
// Request headers.
client.DefaultRequestHeaders.Add(
"Ocp-Apim-Subscription-Key", subscriptionKey);
// Request parameters. A third optional parameter is "details".
string requestParameters = "returnFaceId=true&returnFaceLandmarks=false" +
"&returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses," +
"emotion,hair,makeup,occlusion,accessories,blur,exposure,noise";
// Assemble the URI for the REST API Call.
string uri = uriBase + "?" + requestParameters;
HttpResponseMessage response;
// Request body. Posts a locally stored JPEG image.
byte[] byteData = GetImageAsByteArray(imageFilePath);
using (ByteArrayContent content = new ByteArrayContent(byteData))
{
// This example uses content type "application/octet-stream".
// The other content types you can use are "application/json"
// and "multipart/form-data".
content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
// Execute the REST API call.
response = await client.PostAsync(uri, content);
// Get the JSON response.
string contentString = await response.Content.ReadAsStringAsync();
// Display the JSON response.
//Console.WriteLine("\nResponse:\n");
//Console.WriteLine(JsonPrettyPrint(contentString));
//Console.WriteLine("\nPress Enter to exit...");
//return JsonPrettyPrint(contentString);
return contentString;
}
}
/// <summary>
/// Returns the contents of the specified file as a byte array.
/// </summary>
/// <param name="imageFilePath">The image file to read.</param>
/// <returns>The byte array of the image data.</returns>
public static byte[] GetImageAsByteArray(string imageFilePath)
{
using (FileStream fileStream =
new FileStream(imageFilePath, FileMode.Open, FileAccess.Read))
{
BinaryReader binaryReader = new BinaryReader(fileStream);
return binaryReader.ReadBytes((int)fileStream.Length);
}
}
}
}
MainWindow.cs
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
namespace WpfFaceDetect
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
listBox.Items.Add("初始化成功");
}
/// <summary>
/// 按钮响应事件,执行以下几个功能
/// 打开文件对话框,选择文件
/// 将选择的文件显示在界面上
/// 将图片进行识别,获取返回信息(返回的信息是标准的json格式)
/// 处理json文件,获取信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Button_Click(object sender, RoutedEventArgs e)
{
listBox.Items.Add("选择文件");
//打开文件资源管理器
OpenFileDialog openFileDialog = new OpenFileDialog();
//默认打开路径
openFileDialog.InitialDirectory = "E:\\pictreTest\\";
//文件过滤器
openFileDialog.Filter = openFileDialog.Filter = "jpg文件|*.jpg*|png文件|*.png*|全部文件|*.*";
openFileDialog.RestoreDirectory = true;
//确认选择的文件
if (openFileDialog.ShowDialog() == true)
{
lab_result.Content = "请稍等。。";
string filename = openFileDialog.FileName;
listBox.Items.Add("确认选择文件,文件是:\n\t" + filename);
//显示文件在界面上
bool openImageResult = showImage(filename);
listBox.Items.Add("正在打开文件。。");
//文件打开结果判定
if (openImageResult)
{
listBox.Items.Add("文件打开成功");
listBox.Items.Add("请稍后。。");
//人脸识别的判定
var result = await faceDetect.MakeAnalysisRequest(filename);
/*
* use reference "Newtonsoft.Json"
* 需要注意的是,注意json文件的格式,定义类的时候注意相应的内容
* json文件'[]'内的内容是一个数组,需要使用下面形式的List<Object>的样式,
* 然后使用获取数组中元素的方式获取每个对象即可
*
* json'{}'内的内容是一个对象,直接获取就可以,使用
* Object name = JsonConvert.DeserializeObject<Object>(jsonString)
* 获取对象之后直接使用即可
*/
//处理返回数据
List<RootObject> root = JsonConvert.DeserializeObject<List<RootObject>>(result);
RootObject rootObject = root[0];
string genger = rootObject.faceAttributes.gender;
string age = rootObject.faceAttributes.age;
string emotion = ForeachClass.GetMaxProperties(rootObject.faceAttributes.emotion);
string glasses = rootObject.faceAttributes.glasses;
listBox.Items.Add("请查看结果");
//打印处理新年西
printInformation(genger,age,emotion,glasses);
}
else
{
listBox.Items.Add("文件打开失败,请重试!");
}
}
else
{
listBox.Items.Add("取消文件选择");
listBox.Items.Add("您未选择任何文件");
}
}
/// <summary>
/// 打印处理过的信息,信息显示在label上
/// </summary>
/// <param name="genger">gender</param>
/// <param name="age">age</param>
/// <param name="emotion">emotion</param>
/// <param name="glasses">glasses</param>
private void printInformation(string genger, string age, string emotion, string glasses)
{
string answer = "性别是:" + genger + "\t\t年龄是:" + age + "\n"
+ "情感是:" + emotion + "\t\t眼镜:\t" + glasses;
lab_result.Content = answer;
}
/// <summary>
/// 将选中的文件显示在界面上
/// </summary>
/// <param name="filename">文件路径</param>
/// <returns>是否显示成功</returns>
private bool showImage(string filename)
{
try
{
string path = filename;//获取图片绝对路径
BitmapImage im = new BitmapImage(new Uri(path, UriKind.Absolute));//打开图片
image.Source = im;//将控件和图片绑定,logo为Image控件名称
return true;
}
catch
{
return false;
}
}
}
}
指的一提的是,微软提供的api的返回内容是标准的json格式文件,json文件解析可以参考https://www.cnblogs.com/zxtceq/p/6610214.html 针对该api的返回内容,有这样的返回文件,也就是说,所有的处理结果返回的json都是这样的标准结构,那么处理的类就有RootObject.cs文件的内容
[
{
"faceId": "a9aca673-34c6-4215-9c00-38edde342fe1",
"faceRectangle": {
"top": 255,
"left": 504,
"width": 469,
"height": 469
},
"faceAttributes": {
"smile": 0.001,
"headPose": {
"pitch": 0.0,
"roll": -3.1,
"yaw": -8.6
},
"gender": "female",
"age": 22.0,
"facialHair": {
"moustache": 0.0,
"beard": 0.0,
"sideburns": 0.0
},
"glasses": "NoGlasses",
"emotion": {
"anger": 0.0,
"contempt": 0.007,
"disgust": 0.0,
"fear": 0.0,
"happiness": 0.001,
"neutral": 0.986,
"sadness": 0.006,
"surprise": 0.0
},
"blur": {
"blurLevel": "medium",
"value": 0.33
},
"exposure": {
"exposureLevel": "goodExposure",
"value": 0.66
},
"noise": {
"noiseLevel": "high",
"value": 1.0
},
"makeup": {
"eyeMakeup": true,
"lipMakeup": false
},
"accessories": [
],
"occlusion": {
"foreheadOccluded": false,
"eyeOccluded": false,
"mouthOccluded": false
},
"hair": {
"bald": 0.23,
"invisible": false,
"hairColor": [
{
"color": "gray",
"confidence": 0.92
},
{
"color": "blond",
"confidence": 0.91
},
{
"color": "other",
"confidence": 0.55
},
{
"color": "black",
"confidence": 0.45
},
{
"color": "brown",
"confidence": 0.16
},
{
"color": "red",
"confidence": 0.05
}
]
}
}
}
]
RootObject.cs
using System;
using System.Collections.Generic;
using System.Reflection;
namespace WpfFaceDetect
{
public class FaceRectangle
{
public string top { get; set; }
public string left { get; set; }
public string width { get; set; }
public string height { get; set; }
}
public class HeadPose
{
public string pitch { get; set; }
public string roll { get; set; }
public string yaw { get; set; }
}
public class FacialHair
{
public string moustache { get; set; }
public string beard { get; set; }
public string sideburns { get; set; }
}
public class Emotion
{
public string anger { get; set; }
public string contempt { get; set; }
public string disgust { get; set; }
public string fear { get; set; }
public string happiness { get; set; }
public string neutral { get; set; }
public string sadness { get; set; }
public string surprise { get; set; }
}
public class ForeachClass
{
/// <summary>
/// C#反射遍历对象属性
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="model">对象</param>
public static void ForeachClassProperties<T>(T model)
{
Type t = model.GetType();
PropertyInfo[] PropertyList = t.GetProperties();
foreach (PropertyInfo item in PropertyList)
{
string name = item.Name;
object value = item.GetValue(model, null);
}
}
public static string GetMaxProperties(Emotion model)
{
string resultName = "";
double resultValue = 0.0;
Type t = model.GetType();
PropertyInfo[] PropertyList = t.GetProperties();
foreach (PropertyInfo item in PropertyList)
{
string name = item.Name;
object value = item.GetValue(model, null);
double inValue = Double.Parse(value.ToString());
if(inValue > resultValue)
{
resultValue = inValue;
resultName = name;
}
}
return resultName;
}
}
public class Blur
{
public string blurLevel { get; set; }
public string value { get; set; }
}
public class Exposure
{
public string exposureLevel { get; set; }
public string value { get; set; }
}
public class Noise
{
public string noiseLevel { get; set; }
public string value { get; set; }
}
public class Makeup
{
public string eyeMakeup { get; set; }
public string lipMakeup { get; set; }
}
public class Accessories
{
}
public class Occlusion
{
public string foreheadOccluded { get; set; }
public string eyeOccluded { get; set; }
public string mouthOccluded { get; set; }
}
public class HairColor
{
public string color { get; set; }
public string confidence { get; set; }
}
public class Hair
{
public string bald { get; set; }
public string invisible { get; set; }
public List<HairColor> hairColor { get; set; }
}
public class FaceAttributes
{
public string smile { get; set; }
public HeadPose headPose { get; set; }
public string gender { get; set; }
public string age { get; set; }
public FacialHair facialHair { get; set; }
public string glasses { get; set; }
public Emotion emotion { get; set; }
public Blur blur { get; set; }
public Exposure exposure { get; set; }
public Noise noise { get; set; }
public Makeup makeup { get; set; }
public List<Accessories> accessories { get; set; }
public Occlusion occlusion { get; set; }
public Hair hair { get; set; }
}
public class RootObject
{
public string faceId { get; set; }
public FaceRectangle faceRectangle { get; set; }
public FaceAttributes faceAttributes { get; set; }
}
}