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
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;
|
|
}
|
|
}
|
|
}
|
|
|