先来看下效果图
#下载AForge第三方库用于调取摄像头
#下载baidu.ai
在项目的NuGet包中下载
就可以开始写代码逻辑了
参考百度接口文档:https://ai.baidu.com/ai-doc/FACE/Ck37c1ri0#%E4%BA%BA%E8%84%B8%E6%A3%80%E6%B5%8B
主窗体代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AForge.Video.DirectShow;
using Newtonsoft.Json;
using static FaceRecognitionSystem.Common;
using CCWin;
using System.Threading;
namespace FaceRecognitionSystem
{
public partial class Main : Skin_Mac
{
/// <summary>
/// 总摄像头
/// </summary>
private FilterInfoCollection VideoDevices { get; set; }
/// <summary>
/// 当前选中的摄像头
/// </summary>
private VideoCaptureDevice VideoDevice { get; set; }
/// <summary>
/// 当前选中摄像头的所有分辨率
/// </summary>
private VideoCapabilities[] VideoCapabilities { get; set; }
private VideoCapabilities[] SnapshotCapabilities { get; set; }
private delegate void SetTextCallback(TextBox textBox, object result, string title, bool IsClearText = false);
private static int Max_face_num = 2;
public Main()
{
InitializeComponent();
this.MaximizeBox = false;
this.CanResize = false;
}
/// <summary>
/// 初始化加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Main_Load(object sender, EventArgs e)
{
VideoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (VideoDevices.Count != 0)
{
foreach (FilterInfo device in VideoDevices)
{
skincbxCamera.Items.Add(device.Name);
}
}
else
{
skincbxCamera.Items.Add("没有找到摄像头");
}
skincbxCamera.SelectedIndex = 0;
this.skincbxnumber.SelectedIndex = 1;
}
/// <summary>
/// 选择摄像头改变事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void skincbxCamera_SelectedIndexChanged(object sender, EventArgs e)
{
if (VideoDevices.Count != 0)
{
VideoDevice = new VideoCaptureDevice(VideoDevices[skincbxCamera.SelectedIndex].MonikerString);
GetDeviceResolution(VideoDevice);
}
}
/// <summary>
/// 获取摄像头分辨率
/// </summary>
/// <param name="videoCaptureDevice"></param>
private void GetDeviceResolution(VideoCaptureDevice videoCaptureDevice)
{
skincbxRP.Items.Clear();
VideoCapabilities = videoCaptureDevice.VideoCapabilities;
foreach (VideoCapabilities capabilty in VideoCapabilities)
{
skincbxRP.Items.Add($"{capabilty.FrameSize.Width} x {capabilty.FrameSize.Height}");
}
if (skincbxRP != null && skincbxRP.Items != null && skincbxRP.Items.Count > 0)
skincbxRP.SelectedIndex = skincbxRP.Items.Count - 1;
else
skincbxRP.SelectedIndex = 0;
}
/// <summary>
/// 连接摄像头
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnConnect_Click(object sender, EventArgs e)
{
if (VideoDevice != null)
{
if ((VideoCapabilities != null) && (VideoCapabilities.Length != 0))
{
VideoDevice.VideoResolution = VideoCapabilities[skincbxRP.SelectedIndex];
vispShoot.VideoSource = VideoDevice;
vispShoot.Start();
EnableControlStatus(false);
}
}
}
/// <summary>
/// 设置控件属性
/// </summary>
/// <param name="status"></param>
private void EnableControlStatus(bool status)
{
skincbxCamera.Enabled = status;
skincbxRP.Enabled = status;
btnConnect.Enabled = status;
btnShoot.Enabled = !status;
btnPhotograph.Enabled = !status;
}
/// <summary>
/// 断开连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnShoot_Click(object sender, EventArgs e)
{
DisConnect();
EnableControlStatus(true);
}
/// <summary>
/// 断开连接,删除vdieo影像
/// </summary>
private void DisConnect()
{
if (vispShoot.VideoSource != null)
{
vispShoot.SignalToStop();
vispShoot.WaitForStop();
vispShoot.VideoSource = null;
}
}
/// <summary>
/// 窗体关闭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
//断开摄像头连接
DisConnect();
//关闭当前应用程序
Application.Exit();
}
/// <summary>
/// 拍照
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPhotograph_Click(object sender, EventArgs e)
{
Bitmap img = vispShoot.GetCurrentVideoFrame();
picbPreview.Image = img;
}
/// <summary>
/// 图像转base64编码
/// </summary>
/// <param name="bmp"></param>
/// <returns></returns>
public string ImgToBase64String(Bitmap bmp)
{
try
{
MemoryStream ms = new MemoryStream();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(arr, 0, (int)ms.Length);
ms.Close();
return Convert.ToBase64String(arr);
}
catch (Exception ex)
{
MessageBox.Show("ImgToBase64String 转换失败 Exception:" + ex.Message);
return "";
}
}
/// <summary>
/// 人脸检测
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void skinButton1_Click(object sender, EventArgs e)
{
if (picbPreview.Image is null)
{
MessageBox.Show("请先拍照或者选取一张图片,再进行检测操作");
return;
}
// var APP_ID = "17894506";
Thread threadadd = new Thread(() =>
{
try
{
var API_KEY = "自己的api_key";
var SECRET_KEY = "自己的secret_key";
var client = new Baidu.Aip.Face.Face(API_KEY, SECRET_KEY)
{
Timeout = 60000 // 修改超时时间
};
var image = ImgToBase64String((Bitmap)this.picbPreview.Image);
var imageType = "BASE64";
// 调用人脸检测,可能会抛出网络等异常,请使用try/catch捕获
var result = client.Detect(image, imageType);
// 如果有可选参数
var options = new Dictionary<string, object>
{
{"face_field","age,beauty,expression,face_shape,gender,glasses," +
",race,quality,eye_status,emotion,face_type,eye_status"},
{"max_face_num", Max_face_num},
{"face_type", "LIVE"},
{"liveness_control", "LOW"}
};
// 带参数调用人脸检测
result = client.Detect(image, imageType, options);
if (result != null && result.ToString() != null && result.ToString().Length > 0)
{
var json = JsonConvert.SerializeObject(result);
FaceCheckModel model = DeserializeJsonToObject<GetApiJson<FaceCheckModel>>(json)?.Result ?? new FaceCheckModel();
FaceTestBind(model);
}
}
catch (Exception ex)
{
MessageBox.Show("网络错误!错误信息:"+ex.Message);
}
});
threadadd.Start();
}
/// <summary>
/// 绑定检测信息
/// </summary>
public void FaceTestBind(FaceCheckModel model)
{
//清空文本框
TestTextBoxAdd(textBox1, "", "", true);
if (model.Face_num > 0)
{
if (model.Face_list != null && model.Face_list.AsEnumerable() != null && model.Face_list.AsEnumerable().Any())
{
TestTextBoxAdd(textBox1, model.Face_num, "人脸数量为:");
TestTextBoxAdd(textBox1, "", "--------------------------------------");
int count = 0;
foreach (var item in model.Face_list)
{
count++;
TestTextBoxAdd(textBox1, "", $"当前检测第{count}张人脸{item.Face_probability * 100}%为一张真实人脸");
TestTextBoxAdd(textBox1, $"{item.Quality.Blur * 100}%", "人脸模糊程度 :");
TestTextBoxAdd(textBox1, item.Age, "年龄 :");
TestTextBoxAdd(textBox1, item.Beauty, "美丑打分 :");
TestTextBoxAdd(textBox1, item.Gender.Type.GetEnumDescription() + $" 概率为{item.Expression.Probability * 100}%", "性别 :");
TestTextBoxAdd(textBox1, item.Expression.Type.GetEnumDescription() + $" 概率为{item.Expression.Probability * 100}%", "表情 :");
TestTextBoxAdd(textBox1, item.Emotion.Type.GetEnumDescription() + $" 概率为{item.Emotion.Probability * 100}%", "情绪 :");
TestTextBoxAdd(textBox1, item.Glasses.Type.GetEnumDescription() + $" 概率为{item.Glasses.Probability * 100}%", "是否带眼镜 :");
TestTextBoxAdd(textBox1, $"左眼未闭合概率为{item.Eye_status.Left_eye*100}%,\r\n\r\n右眼未闭合概率为{item.Eye_status.Right_eye * 100}%", "右眼闭合状态 :");
TestTextBoxAdd(textBox1, item.Race.Type.GetEnumDescription() + $" 概率为{item.Race.Probability * 100}%", "人种 :");
TestTextBoxAdd(textBox1, item.Face_shape.Type.GetEnumDescription() + $" 概率为{item.Face_shape.Probability * 100}%", "脸型 :");
TestTextBoxAdd(textBox1, item.Face_Type.Type.GetEnumDescription() + $" 概率为{item.Face_Type.Probability * 100}%", "真实人脸/卡通人脸 :");
TestTextBoxAdd(textBox1, "", "--------------------------------------");
}
}
}
else
{
TestTextBoxAdd(textBox1, "", "未获取到人脸信息,或者网络异常!");
}
}
/// <summary>
/// 多行文本框
/// </summary>
/// <param name="textBox"></param>
/// <param name="result"></param>
/// <param name="title"></param>
/// <param name="IsClearText"></param>
private void TestTextBoxAdd(TextBox textBox, object result, string title,bool IsClearText=false)
{
// InvokeRequired需要比较调用线程ID和创建线程ID
// 如果它们不相同则返回true
if (textBox.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(TestTextBoxAdd);
textBox1.Invoke(d, new object[] { textBox, result, title, IsClearText });
}
else
{
if (IsClearText)
textBox1.Text = "";
else
textBox1.Text += $"{title}{result}{Environment.NewLine}{Environment.NewLine}";
}
}
/// <summary>
/// 解析JSON字符串生成对象实体
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="json">json字符串(eg.{"ID":"112","Name":"石子儿"})</param>
/// <returns>对象实体</returns>
public static T DeserializeJsonToObject<T>(string json) where T : class
{
JsonSerializer serializer = new JsonSerializer();
StringReader sr = new StringReader(json);
object o = serializer.Deserialize(new JsonTextReader(sr), typeof(T));
T t = o as T;
return t;
}
/// <summary>
/// 选择图片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void skinButton2_Click(object sender, EventArgs e)
{
OpenFileDialog openfile = new OpenFileDialog();
if (openfile.ShowDialog() == DialogResult.OK && (openfile.FileName != ""))
{
Bitmap image = new Bitmap(openfile.FileName);
picbPreview.Image = image;
}
openfile.Dispose();
}
/// <summary>
/// 下拉框改变事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void skincbxnumber_TextChanged(object sender, EventArgs e)
{
Max_face_num = Convert.ToInt32(this.skincbxnumber.SelectedItem+"");
}
}
}
新建一个GetApiJson.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FaceRecognitionSystem
{
public class GetApiJson<T>
{
/// <summary>
/// 错误代码
/// </summary>
public int Error_code { get; set; }
/// <summary>
/// 返回消息 成功还是失败
/// </summary>
public string Error_msg { get; set; }
public string Log_id { get; set; }
public string Timestamp { get; set; }
public string Cached { get; set; }
public T Result { get; set; }
}
}
新建一个FaceCheckModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static FaceRecognitionSystem.Common;
namespace FaceRecognitionSystem
{
public class FaceCheckModel
{
/// <summary>
/// 检测到的图片中的人脸数量
/// </summary>
public int Face_num { get; set; }
/// <summary>
/// 人脸信息列表
/// </summary>
public List<Face_listItem> Face_list { get; set; }
}
/// <summary>
/// 人脸在图片中的位置
/// </summary>
public class Location
{
/// <summary>
/// 人脸区域离左边界的距离
/// </summary>
public double Left { get; set; }
/// <summary>
/// 人脸区域离上边界的距离
/// </summary>
public double Top { get; set; }
/// <summary>
/// 人脸区域的宽度
/// </summary>
public double Width { get; set; }
/// <summary>
/// 人脸区域的高度
/// </summary>
public double Height { get; set; }
/// <summary>
/// 人脸框相对于竖直方向的顺时针旋转角,[-180,180]
/// </summary>
public int Rotation { get; set; }
}
/// <summary>
/// 人脸旋转角度参数
/// </summary>
public class Angle
{
/// <summary>
/// 三维旋转之左右旋转角[-90(左), 90(右)]
/// </summary>
public double Yaw { get; set; }
/// <summary>
/// 三维旋转之俯仰角度[-90(上), 90(下)]
/// </summary>
public double Pitch { get; set; }
/// <summary>
/// 平面内旋转角[-180(逆时针), 180(顺时针)]
/// </summary>
public double Roll { get; set; }
}
/// <summary>
/// 4个关键点位置,左眼中心、右眼中心、鼻尖、嘴中心。face_field包含landmark时返回
/// </summary>
public class LandmarkItem
{
/// <summary>
///
/// </summary>
public double X { get; set; }
/// <summary>
///
/// </summary>
public double Y { get; set; }
}
/// <summary>
/// 72个特征点位置 face_field包含landmark72时返回
/// </summary>
public class Landmark72Item
{
/// <summary>
///
/// </summary>
public double X { get; set; }
/// <summary>
///
/// </summary>
public double Y { get; set; }
}
/// <summary>
/// 表情,当 face_field包含expression时返回
/// </summary>
public class Expression
{
/// <summary>
/// none:不笑;smile:微笑;laugh:大笑
/// </summary>
public ExpressionEnum Type { get; set; }
/// <summary>
/// 表情置信度,范围【0~1】,0最小、1最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 性别,face_field包含gender时返回
/// </summary>
public class Gender
{
/// <summary>
/// male:男性 female:女性
/// </summary>
public GenderEnum Type { get; set; }
/// <summary>
/// 性别置信度,范围【0~1】,0代表概率最小、1代表最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 是否带眼镜,face_field包含glasses时返回
/// </summary>
public class Glasses
{
/// <summary>
/// none:无眼镜,common:普通眼镜,sun:墨镜
/// </summary>
public GlassesEnum Type { get; set; }
/// <summary>
/// 眼镜置信度,范围【0~1】,0代表概率最小、1代表最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 人种
/// </summary>
public class Race
{
/// <summary>
/// yellow: 黄种人 white: 白种人 black:黑种人 arabs: 阿拉伯人
/// </summary>
public RaceEnum Type { get; set; }
/// <summary>
/// 人种置信度,范围【0~1】,0代表概率最小、1代表最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 脸型
/// </summary>
public class Face_shape
{
/// <summary>
/// square: 正方形 triangle:三角形 oval: 椭圆 heart: 心形 round: 圆形
/// </summary>
public Face_shapeEnum Type { get; set; }
/// <summary>
/// 置信度,范围【0~1】,代表这是人脸形状判断正确的概率,0最小、1最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 真实人脸/卡通人脸
/// </summary>
public class Face_type
{
/// <summary>
/// human: 真实人脸 cartoon: 卡通人脸
/// </summary>
public Face_typeEnum Type { get; set; }
/// <summary>
/// 置信度,范围【0~1】,代表这是人脸形状判断正确的概率,0最小、1最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 情绪
/// </summary>
public class Emotion
{
/// <summary>
///angry:愤怒 disgust:厌恶 fear:恐惧 happy:高兴sad:伤心 surprise:惊讶 neutral:无情绪
/// </summary>
public EmotionEnum Type { get; set; }
/// <summary>
/// 置信度,范围【0~1】,代表这是人脸形状判断正确的概率,0最小、1最大。
/// </summary>
public double Probability { get; set; }
}
/// <summary>
/// 左右眼 未闭合概率
/// </summary>
public class Eye_status
{
/// <summary>
/// 左眼状态 [0,1]取值,越接近0闭合的可能性越大
/// </summary>
public double Left_eye { get; set; }
/// <summary>
/// 右眼状态 [0,1]取值,越接近0闭合的可能性越大
/// </summary>
public double Right_eye { get; set; }
}
/// <summary>
/// 人脸各部分遮挡的概率,范围[0~1],0表示完整,1表示不完整
/// </summary>
public class Occlusion
{
/// <summary>
/// 左眼遮挡比例,[0-1] ,1表示完全遮挡
/// </summary>
public double Left_eye { get; set; }
/// <summary>
/// 右眼遮挡比例,[0-1] , 1表示完全遮挡
/// </summary>
public double Right_eye { get; set; }
/// <summary>
/// 鼻子遮挡比例,[0-1] , 1表示完全遮挡
/// </summary>
public double Nose { get; set; }
/// <summary>
/// 嘴巴遮挡比例,[0-1] , 1表示完全遮挡
/// </summary>
public double Mouth { get; set; }
/// <summary>
/// 左脸颊遮挡比例,[0-1] , 1表示完全遮挡
/// </summary>
public double Left_cheek { get; set; }
/// <summary>
/// 右脸颊遮挡比例,[0-1] , 1表示完全遮挡
/// </summary>
public double Right_cheek { get; set; }
/// <summary>
/// 下巴遮挡比例,,[0-1] , 1表示完全遮挡
/// </summary>
public double Chin { get; set; }
}
/// <summary>
/// 人脸质量信息
/// </summary>
public class Quality
{
/// <summary>
/// 人脸各部分遮挡的概率,范围[0~1],0表示完整,1表示不完整
/// </summary>
public Occlusion Occlusion { get; set; }
/// <summary>
/// 人脸模糊程度,范围[0~1],0表示清晰,1表示模糊
/// </summary>
public double Blur { get; set; }
/// <summary>
/// 取值范围在[0~255], 表示脸部区域的光照程度 越大表示光照越好
/// </summary>
public double Illumination { get; set; }
/// <summary>
/// 人脸完整度,0或1, 0为人脸溢出图像边界,1为人脸都在图像边界内
/// </summary>
public int Completeness { get; set; }
}
public class Face_listItem
{
/// <summary>
/// 人脸图片的唯一标识
/// </summary>
public string Face_token { get; set; }
/// <summary>
/// 人脸在图片中的位置
/// </summary>
public Location Location { get; set; }
/// <summary>
/// 人脸置信度,范围【0~1】,代表这是一张人脸的概率,0最小、1最大。
/// </summary>
public double Face_probability { get; set; }
/// <summary>
/// 人脸旋转角度参数
/// </summary>
public Angle Angle { get; set; }
/// <summary>
/// 4个关键点位置,左眼中心、右眼中心、鼻尖、嘴中心
/// </summary>
public List<LandmarkItem> Landmark { get; set; }
/// <summary>
/// 72个特征点位置
/// </summary>
public List<Landmark72Item> Landmark72 { get; set; }
/// <summary>
/// 年龄
/// </summary>
public double Age { get; set; }
/// <summary>
/// 美丑打分
/// </summary>
public double Beauty { get; set; }
/// <summary>
/// 表情
/// </summary>
public Expression Expression { get; set; }
/// <summary>
/// 性别
/// </summary>
public Gender Gender { get; set; }
/// <summary>
/// 是否带眼镜
/// </summary>
public Glasses Glasses { get; set; }
/// <summary>
/// 人种
/// </summary>
public Race Race { get; set; }
/// <summary>
/// 脸型
/// </summary>
public Face_shape Face_shape { get; set; }
/// <summary>
/// 真实人脸/卡通人脸
/// </summary>
public Face_type Face_Type { get; set; }
/// <summary>
/// 人脸质量信息
/// </summary>
public Quality Quality { get; set; }
/// <summary>
/// 情绪
/// </summary>
public Emotion Emotion { get; set; }
/// <summary>
/// 双眼状态(睁开/闭合) face_field包含eye_status时返回
/// </summary>
public Eye_status Eye_status { get; set; }
}
}
新建一个Common.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace FaceRecognitionSystem
{
public static class Common
{
/// <summary>
/// 获取当前枚举描述
/// </summary>
/// <param name="enumValue"></param>
/// <param name="name"></param>
/// <returns></returns>
public static string GetEnumDescription(this Enum enumValue)
{
try
{
Type type = enumValue.GetType();
MemberInfo[] memInfo = type.GetMember(enumValue.ToString());
if (null != memInfo && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (null != attrs && attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return enumValue.ToString();
}
catch (Exception)
{
return "Unknown";
}
}
/// <summary>
/// 表情
/// </summary>
public enum ExpressionEnum
{
[Description("不笑")]
none,
[Description("微笑")]
smile,
[Description("大笑")]
laugh,
End
}
/// <summary>
/// 脸型
/// </summary>
public enum Face_shapeEnum
{
[Description("正方形")]
square,
[Description("三角形")]
triangle,
[Description("椭圆")]
oval,
[Description("心形")]
heart,
[Description("圆形")]
round,
End
}
/// <summary>
/// 性别
/// </summary>
public enum GenderEnum
{
[Description("男性")]
male,
[Description("女性")]
female,
End
}
/// <summary>
/// 是否戴眼镜
/// </summary>
public enum GlassesEnum
{
[Description("无眼镜")]
none,
[Description("普通眼镜")]
common,
[Description("墨镜")]
sun,
End
}
/// <summary>
/// 人种
/// </summary>
public enum RaceEnum
{
[Description("黄种人")]
yellow,
[Description("白种人")]
white,
[Description("黑种人")]
black,
[Description("阿拉伯人")]
arabs,
End
}
/// <summary>
/// 真实人脸/卡通人脸
/// </summary>
public enum Face_typeEnum
{
[Description("真实人脸")]
human,
[Description("卡通人脸")]
cartoon
}
/// <summary>
/// 情绪
/// </summary>
public enum EmotionEnum
{
[Description("愤怒")]
angry,
[Description("厌恶")]
disgust,
[Description("恐惧")]
fear,
[Description("高兴")]
happy,
[Description("伤心")]
sad,
[Description("惊讶")]
surprise,
[Description("无情绪")]
neutral,
[Description("痛苦,厌恶")] ///白度返回了这个属性 但文档上并没有对应的解释,就自己百度翻译了一下
grimace,
End
}
}
}
可以直接运行了 ,关于那个API_KEY和SECRET_KEY需要自己去百度云控制台获取自己的key
链接:https://pan.baidu.com/s/1SOGkE8zT5Riwp4cUjbcKYw
提取码:a3xg
尽量使用vs2019打开源码调试,低版本可能存在语法错误