You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

724 lines
30 KiB

using IOTContainer.Model;
using Newtonsoft.Json;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static IOTContainer.Common.BaiduFace;
namespace IOTContainer.Common
{
public class BaiduFace
{
private BaiduFace() { }
/// <summary>
/// 初始化
/// </summary>
/// <returns>true-成功 false-失败</returns>
public bool Init()
{
try
{
//更新sdk配置文件
UpdateConfig();
//初始化
int n = sdk_init(Path.Combine(FaceParameters.Parameters._baseDir, "BaiduSDK"));
if (n != 0)
{
FaceParameters.Parameters.isInit = false;
Log.MyLog.WriteLogFile("百度人脸Sdk初始化失败:"+n, "BaiduFace");
return false;
}
if (is_auth())
{
FaceParameters.Parameters.isInit = true;
return true;
}
else
{
FaceParameters.Parameters.isInit = false;
Log.MyLog.WriteLogFile("设备未授权!", "BaiduFace");
return false;
}
}
catch (Exception ex)
{
FaceParameters.Parameters.isInit = false;
Log.MyLog.WriteLogFile("百度人脸Sdk初始化异常:" + ex.Message, "BaiduFace");
return false;
}
}
/// <summary>
/// 使用完毕,销毁sdk,释放内存
/// </summary>
public void Destory()
{
try
{
sdk_destroy();
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("百度人脸Sdk销毁异常:" + ex.Message, "BaiduFace");
}
}
/// <summary>
/// usb摄像头实时人脸检测示例
/// </summary>
public void USBVideoTract()
{
using (var window = new Window("face"))
using (VideoCapture cap = VideoCapture.FromCamera(FaceParameters.Parameters._cameraIndex))
{
if (!cap.IsOpened())
{
Log.MyLog.WriteLogFile("打开摄像头失败!","BaiduFace");
//var faceData = new FaceSendModel
//{
// age = 28,
// genderMale = "男",
// vipId = "",
// faceID = "",
// carNo = "",
// isOK = "1",
// faceImage = ""
//};
//ComParameters.Parameters.EdgeWindow.pushFaceBase(JsonConvert.SerializeObject(faceData));
return;
}
Mat image = new Mat();
while (true)
{
try
{
//读取相机帧
cap.Read(image); // same as cvQueryFrame
if (!image.Empty())
{
FaceDectData? currentFace = null;
#region 推送流文件
if (FaceParameters.Parameters._isOpenLive && null != image)
{
using (var newFrame = new Mat(image, new Rect(new Point(FaceParameters.Parameters._liveX, FaceParameters.Parameters._liveY), new Size(FaceParameters.Parameters._liveWidth, FaceParameters.Parameters._liveHeight))))
{
try
{
var encodeBuff = newFrame.ImEncode(".jpg");
var base64Str = Convert.ToBase64String(encodeBuff);
var faceData = new FaceSendModel
{
age = 28,
genderMale = "男",
vipId = "",
faceID = "",
carNo = "",
isOK = "0",
faceImage = base64Str
};
//获取特征属性值
try
{
var speInfos = GetFaceInfoAttr(image, 1);
// var attrInfo = GetFaceAttr(image, 1);
bool isSuccess = false;
if (speInfos != null && speInfos.Any())
{
faceData.age = speInfos[0].age;
faceData.genderMale = speInfos[0].gender == BDFaceGender.BDFACE_GENDER_MALE ? "男" : "女";
faceData.isOK = "1";
Log.MyLog.WriteLogFile($"发送视频流:{JsonConvert.SerializeObject(faceData)}", "EdgeFunction");
}
//推送视频流
//Log.MyLog.WriteLogFile($"发送视频流:{JsonConvert.SerializeObject(faceData)}", "EdgeFunction");
ComParameters.Parameters.EdgeWindow.pushFaceBase(JsonConvert.SerializeObject(faceData));
}
catch (Exception e)
{
Log.MyLog.WriteLogFile("获取特征属性值异常:" + e.Message, "BaiduFace");
}
}
catch (Exception e)
{
Log.MyLog.WriteLogFile("推送流文件异常:" + e.Message,"BaiduFace");
}
}
}
#endregion
try
{
//展示
window.ShowImage(image);
Cv2.WaitKey(2);
WinApi.Windows_Minimize("face");
}
catch (Exception suex)
{
Log.MyLog.WriteLogFile("图像展示异常:" + suex.ToString(), "BaiduFace");
}
}
else
{
Log.MyLog.WriteLogFile("mat is empty", "BaiduFace");
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("usb摄像头实时人脸检测示例异常:" + ex.ToString(), "BaiduFace");
}
}
}
}
/// <summary>
/// 获取人脸检测属性值
/// </summary>
/// <param name="mat">图像帧</param>
/// <param name="maxDectNum">最大检测人数</param>
/// <param name="Info">传出的检测值</param>
/// <returns></returns>
private FaceDectData[] GetFaceInfoAttr(Mat mat, int maxDectNum)
{
try
{
var Info = DetectFace(mat, maxDectNum);
if (Info.Any())
{
var attrInfo = GetFaceAttr(mat, maxDectNum);
var resu = Info.Select(p => new FaceDectData
{
index = p.index,
center_x = p.center_x,
center_y = p.center_y,
width = p.width,
height = p.height,
angle = p.angle,
score = p.score,
}).ToArray();
for (int i = 0; i < Info.Length; i++)
{
resu[i].age = i < attrInfo.Length ? attrInfo[i].age : int.MaxValue;
resu[i].gender = i < attrInfo.Length ? attrInfo[i].gender : BDFaceGender.BDFACE_GENDER_MALE;
}
return resu;
}
return _emptyInfo;
}
catch (Exception)
{
return _emptyInfo;
}
}
/// <summary>
/// 人脸检测
/// </summary>
/// <param name="mat"></param>
/// <param name="maxDectNum"></param>
/// <returns></returns>
private BDFaceBBox[] DetectFace(Mat mat, int maxDectNum)
{
try
{
int sizeTrack = Marshal.SizeOf(typeof(BDFaceBBox));
IntPtr ptT = Marshal.AllocHGlobal(sizeTrack * maxDectNum);
int faceNum = detect(ptT, mat.CvPtr, _trackType);
if (faceNum > 0)
{
// 因为需预分配内存,所以返回的人脸数若大于预先分配的内存数,则仅仅显示预分配的人脸数
if (faceNum > maxDectNum)
{
faceNum = maxDectNum;
}
BDFaceBBox[] info = new BDFaceBBox[faceNum];
for (int index = 0; index < faceNum; index++)
{
IntPtr ptr = new IntPtr();
if (8 == IntPtr.Size)
{
ptr = (IntPtr)(ptT.ToInt64() + sizeTrack * index);
}
else if (4 == IntPtr.Size)
{
ptr = (IntPtr)(ptT.ToInt32() + sizeTrack * index);
}
info[index] = (BDFaceBBox)Marshal.PtrToStructure(ptr, typeof(BDFaceBBox));
}
Marshal.FreeHGlobal(ptT);
info = info.Where(p => p.score > FaceParameters.Parameters._miniThreshold).ToArray();
return info;
}
else
{
return _emptyBox;
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("人脸检测失败" + ex.Message, "BaiduFace");
return _emptyBox;
}
}
/// <summary>
/// 获取人脸属性值
/// </summary>
/// <param name="mat"></param>
/// <param name="maxDectNum"></param>
/// <returns></returns>
private BDFaceAttribute[] GetFaceAttr(Mat mat, int maxDectNum)
{
try
{
int size = Marshal.SizeOf(typeof(BDFaceAttribute));
IntPtr ptT = Marshal.AllocHGlobal(size * maxDectNum);
int faceNum = face_attr(ptT, mat.CvPtr);
if (faceNum > 0)
{
if (faceNum > maxDectNum)
{
faceNum = maxDectNum;
}
BDFaceAttribute[] attr_info = new BDFaceAttribute[faceNum];
for (int index = 0; index < faceNum; index++)
{
IntPtr ptr = new IntPtr();
if (8 == IntPtr.Size)
{
ptr = (IntPtr)(ptT.ToInt64() + size * index);
}
else if (4 == IntPtr.Size)
{
ptr = (IntPtr)(ptT.ToInt32() + size * index);
}
attr_info[index] = (BDFaceAttribute)Marshal.PtrToStructure(ptr, typeof(BDFaceAttribute));
}
Marshal.FreeHGlobal(ptT);
return attr_info;
}
else
{
return _emptyAttr;
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("获取人脸属性值失败" + ex.Message);
return _emptyAttr;
}
}
public void FaceContact(Mat mat, FaceDectData faceBox)
{
if (_faceDic.ContainsKey(faceBox.index))
{
int x = Convert.ToInt32(faceBox.center_x - faceBox.width / 2.0);
int y = Convert.ToInt32(faceBox.center_y - faceBox.height / 2.0);
int w = Convert.ToInt32(faceBox.width);
int h = Convert.ToInt32(faceBox.height);
try
{
using (var image = new Mat(mat, new Rect(new Point(x, y), new Size(w, h))))
{
var encodeBuff = image.ImEncode(".jpg");
var base64Str = Convert.ToBase64String(encodeBuff);
var gender = faceBox.gender == BDFaceGender.BDFACE_GENDER_MALE ? 1 : 0;
var param = $"Face={base64Str}&ip={SystemManage.GetLocalIp()}&Age={faceBox.age}&GenderMale={gender}";
if (!string.IsNullOrEmpty(FaceParameters.Parameters._method))
{
param += $"&method={FaceParameters.Parameters._method}";
FaceParameters.Parameters._method = null;
}
//HttpComm.Http.UploadFace(param);
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("上传人脸数据失败:" + ex.Message,"BaiduFace");
}
}
}
/// <summary>
/// 更新Sdk配置文件
/// </summary>
public static void UpdateConfig()
{
try
{
var filePath = Path.Combine(FaceParameters.Parameters._baseDir, "BaiduSDK","config", "detect.json");
if (!File.Exists(filePath))
{
Log.MyLog.WriteLogFile("Sdk配置文件路径异常:" + filePath);
return;
}
var content = FileManage.ReadJsonFile(filePath);
if (!string.IsNullOrEmpty(content))
{
var model = JsonConvert.DeserializeObject<DetectModel>(content);
model.max_detect_num = FaceParameters.Parameters._maxDectNum;
FileManage.WriteJsonFile(filePath, JsonConvert.SerializeObject(model));
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("更新百度Sdk配置文件异常:" + ex.Message);
}
}
public static BaiduFace SDK
{
get
{
if (_sdk == null)
_sdk = new BaiduFace();
return _sdk;
}
}
private static BaiduFace _sdk;
private const string DLLPath = @"BaiduSDK\BaiduFaceApi.dll";
#region 百度DLL导入
// sdk初始化
[DllImport(DLLPath, EntryPoint = "sdk_init", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
private static extern int sdk_init(string model_path);
// 是否授权
[DllImport(DLLPath, EntryPoint = "is_auth", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
private static extern bool is_auth();
// sdk销毁
[DllImport(DLLPath, EntryPoint = "sdk_destroy", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
private static extern void sdk_destroy();
// type 为0时候执行RGB人脸跟踪,1时候执行NIR人脸跟踪
[DllImport(DLLPath, EntryPoint = "track", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern int track(IntPtr ptr, IntPtr mat, int type);
// type 为0时候执行RGB人脸跟踪,1时候执行NIR人脸跟踪
[DllImport(DLLPath, EntryPoint = "clear_track_history", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern void clear_track_history(int type);
// 获取人脸属性
[DllImport(DLLPath, EntryPoint = "face_attr", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern int face_attr(IntPtr ptr, IntPtr mat);
//人脸检测 type 0: 表示rgb 人脸检测 1:表示nir人脸检测
[DllImport(DLLPath, EntryPoint = "detect", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern int detect(IntPtr ptr, IntPtr mat, int type);
#endregion
#region 百度结构体
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BDFaceBBox
{
public int index; // 人脸索引值
public float center_x; // 人脸中心点x坐标
public float center_y; // 人脸中心点y坐标
public float width; // 人脸宽度
public float height; // 人脸高度
public float angle; // 人脸角度
public float score; // 人脸置信度
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BDFaceLandmark
{
public int index; // 人脸关键点索引值
public int size; // 人脸关键点数量
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 144)]
public float[] data;// = new float[144];
public float score; // 人脸关键点置信度
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BDFaceTrackInfo
{
public int face_id;
[MarshalAs(UnmanagedType.Struct)]
public BDFaceBBox box;
[MarshalAs(UnmanagedType.Struct)]
public BDFaceLandmark landmark;
}
/// <summary>
/// 人脸表情属性枚举
/// </summary>
enum BDFaceAttributeEmotionType
{
BDFACE_ATTRIBUTE_EMOTION_FROWN = 0, // 皱眉
BDFACE_ATTRIBUTE_EMOTION_SMILE = 1, // 笑
BDFACE_ATTRIBUTE_EMOTION_CALM = 2, // 平静
};
/// <summary>
/// 人脸种族属性枚举
/// </summary>
enum BDFaceRace
{
BDFACE_RACE_YELLOW = 0, // 黄种人
BDFACE_RACE_WHITE = 1, // 白种人
BDFACE_RACE_BLACK = 2, // 黑种人
BDFACE_RACE_INDIAN = 3, // 印第安人
};
/// <summary>
/// 眼镜状态属性枚举
/// </summary>
enum BDFaceGlasses
{
BDFACE_NO_GLASSES = 0, // 无眼镜
BDFACE_GLASSES = 1, // 有眼镜
BDFACE_SUN_GLASSES = 2, // 墨镜
};
/// <summary>
/// 性别属性枚举
/// </summary>
public enum BDFaceGender
{
BDFACE_GENDER_FEMAILE = 0, // 女性
BDFACE_GENDER_MALE = 1, // 男性
};
/// <summary>
/// 人脸属性结构体
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BDFaceAttribute
{
public int age; // 年龄
public BDFaceRace race; // 种族
public BDFaceAttributeEmotionType emotion; // 表情
public BDFaceGlasses glasses; // 戴眼镜状态
public BDFaceGender gender; // 性别
};
#endregion
#region 业务逻辑类
/// <summary>
/// 综合识别类
/// </summary>
public struct FaceDectData
{
public int index; // 人脸索引值
public float center_x; // 人脸中心点x坐标
public float center_y; // 人脸中心点y坐标
public float width; // 人脸宽度
public float height; // 人脸高度
public float angle; // 人脸角度
public float score; // 人脸置信度
public int age;// 年龄
public BDFaceGender gender;// 性别
}
#endregion
#region 业务逻辑变量
/// <summary>
/// type 为0时候执行RGB人脸跟踪,1时候执行NIR人脸跟踪
/// </summary>
private int _trackType = 0;
/// <summary>
/// 人脸属性值空默认值
/// </summary>
private BDFaceAttribute[] _emptyAttr = new BDFaceAttribute[0];
/// <summary>
/// 人脸检测空默认值
/// </summary>
private BDFaceBBox[] _emptyBox = new BDFaceBBox[0];
/// <summary>
/// 人脸特征空默认值
/// </summary>
private BDFaceTrackInfo[] _emptyTrack = new BDFaceTrackInfo[0];
/// <summary>
/// 人脸综合空默认值
/// </summary>
private FaceDectData[] _emptyInfo = new FaceDectData[0];
/// <summary>
/// 停留两秒后被判定的当前检测到的主 人脸
/// </summary>
public FaceDectData? _masterFace = null;
/// <summary>
/// 近期人脸特征字典(从未检测到人脸到检测到人脸期间的人脸,当未检测到人脸时会清空)
/// </summary>
private Dictionary<int, FaceDectData> _faceDic = new Dictionary<int, FaceDectData>();
/// <summary>
/// 首次进入时的人脸时间字典
/// </summary>
private Dictionary<int, DateTime> _faceEnterDic = new Dictionary<int, DateTime>();
/// <summary>
/// 首次未被识别到的人脸时间字典(检测到不同人脸时会记录上一个人脸id和(第一次未检测到这个人的)时间)
/// </summary>
private Dictionary<int, DateTime?> _faceLeaveDic = new Dictionary<int, DateTime?>();
private bool _isSendLive;
#endregion
}
// 绘制类,画人脸框,画关键点等
class FaceDraw
{
// 画人脸框
public static int draw_rects(ref Mat img, int face_num, BDFaceBBox[] info)
{
if (face_num <= 0)
{
return 0;
}
Scalar color = new Scalar(0, 255, 0);
for (int i = 0; i < face_num; i++)
{
int x = Convert.ToInt32(info[i].center_x - info[i].width / 2.0);
int y = Convert.ToInt32(info[i].center_y - info[i].height / 2.0);
int w = Convert.ToInt32(info[i].width);
int h = Convert.ToInt32(info[i].height);
Rect rect = new Rect(x, y, w, h);
Cv2.Rectangle(img, rect, color);
}
return 0;
}
// 画人脸框
public static int draw_rects(ref Mat img, int face_num, FaceDectData[] info)
{
if (face_num <= 0)
{
return 0;
}
Scalar color = new Scalar(0, 255, 0);
for (int i = 0; i < face_num; i++)
{
int x = Convert.ToInt32(info[i].center_x - info[i].width / 2.0);
int y = Convert.ToInt32(info[i].center_y - info[i].height / 2.0);
int w = Convert.ToInt32(info[i].width);
int h = Convert.ToInt32(info[i].height);
Rect rect = new Rect(x, y, w, h);
Cv2.Rectangle(img, rect, color);
}
return 0;
}
// 画人脸框
public static int draw_rects(ref Mat img, int face_num, BDFaceTrackInfo[] track_info)
{
if (face_num <= 0)
{
return 0;
}
Scalar color = new Scalar(0, 255, 0);
for (int i = 0; i < face_num; i++)
{
int x = Convert.ToInt32(track_info[i].box.center_x - track_info[i].box.width / 2.0);
int y = Convert.ToInt32(track_info[i].box.center_y - track_info[i].box.height / 2.0);
int w = Convert.ToInt32(track_info[i].box.width);
int h = Convert.ToInt32(track_info[i].box.height);
Rect rect = new Rect(x, y, w, h);
Cv2.Rectangle(img, rect, color);
}
return 0;
}
// 画人脸关键点
public static int draw_shape(ref Mat img, int face_num, BDFaceTrackInfo[] track_info)
{
if (face_num <= 0)
{
return 0;
}
int face_id = 0;
Scalar color = new Scalar(0, 255, 255);
Scalar color2 = new Scalar(0, 0, 255);
for (int i = 0; i < face_num; ++i)
{
int point_size = track_info[i].landmark.size / 2;
int radius = 2;
face_id = track_info[i].face_id;
for (int j = 0; j < point_size; ++j)
{
Cv2.Circle(img, (int)track_info[i].landmark.data[j * 2], (int)track_info[i].landmark.data[j * 2 + 1], radius, color);
}
if (point_size == 72)
{
const int components = 9;
int[] comp1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
int[] comp2 = { 13, 14, 15, 16, 17, 18, 19, 20, 13, 21 };
int[] comp3 = { 22, 23, 24, 25, 26, 27, 28, 29, 22 };
int[] comp4 = { 30, 31, 32, 33, 34, 35, 36, 37, 30, 38 };
int[] comp5 = { 39, 40, 41, 42, 43, 44, 45, 46, 39 };
int[] comp6 = { 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 47 };
int[] comp7 = { 51, 57, 52 };
int[] comp8 = { 58, 59, 60, 61, 62, 63, 64, 65, 58 };
int[] comp9 = { 58, 66, 67, 68, 62, 69, 70, 71, 58 };
int[][] idx = { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9 };
int[] npoints = { 13, 10, 9, 10, 9, 11, 3, 9, 9 };
for (int m = 0; m < components; ++m)
{
for (int n = 0; n < npoints[m] - 1; ++n)
{
Point p1 = new Point(track_info[i].landmark.data[idx[m][n] * 2], track_info[i].landmark.data[idx[m][n] * 2 + 1]);
Point p2 = new Point(track_info[i].landmark.data[idx[m][n + 1] * 2], track_info[i].landmark.data[idx[m][n + 1] * 2 + 1]);
Cv2.Line(img, p1, p2, color2);
}
}
}
string s_face_id = face_id.ToString();
double font_scale = 2;
Point pos = new Point(track_info[i].box.center_x, track_info[i].box.center_y);
Cv2.PutText(img, s_face_id, pos, HersheyFonts.HersheyComplex, font_scale, new Scalar(0, 255, 255));
}
return 0;
}
// 画人脸关键点
public static int draw_landmark(ref Mat img, int face_num, BDFaceLandmark[] landmark)
{
if (face_num <= 0)
{
return 0;
}
Scalar color = new Scalar(0, 255, 255);
Scalar color2 = new Scalar(0, 0, 255);
for (int i = 0; i < face_num; ++i)
{
int point_size = landmark[i].size / 2;
int radius = 2;
for (int j = 0; j < point_size; ++j)
{
Cv2.Circle(img, (int)landmark[i].data[j * 2], (int)landmark[i].data[j * 2 + 1], radius, color);
}
if (point_size == 72)
{
const int components = 9;
int[] comp1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
int[] comp2 = { 13, 14, 15, 16, 17, 18, 19, 20, 13, 21 };
int[] comp3 = { 22, 23, 24, 25, 26, 27, 28, 29, 22 };
int[] comp4 = { 30, 31, 32, 33, 34, 35, 36, 37, 30, 38 };
int[] comp5 = { 39, 40, 41, 42, 43, 44, 45, 46, 39 };
int[] comp6 = { 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 47 };
int[] comp7 = { 51, 57, 52 };
int[] comp8 = { 58, 59, 60, 61, 62, 63, 64, 65, 58 };
int[] comp9 = { 58, 66, 67, 68, 62, 69, 70, 71, 58 };
int[][] idx = { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9 };
int[] npoints = { 13, 10, 9, 10, 9, 11, 3, 9, 9 };
for (int m = 0; m < components; ++m)
{
for (int n = 0; n < npoints[m] - 1; ++n)
{
Point p1 = new Point(landmark[i].data[idx[m][n] * 2], landmark[i].data[idx[m][n] * 2 + 1]);
Point p2 = new Point(landmark[i].data[idx[m][n + 1] * 2], landmark[i].data[idx[m][n + 1] * 2 + 1]);
Cv2.Line(img, p1, p2, color2);
}
}
}
}
return 0;
}
}
}