本章主要講述unity開發(fā)中,人臉識別SDK的選擇以及調用,實現(xiàn)簡單的圖片檢測。
一 人臉識別引擎
(一)集成WebAPI
目前以百度AI,騰訊等的互聯(lián)網(wǎng)公司提供了基于WEBAPI的集成方式,可以通過HTTP的方式提交識別請求,識別結果通過JSON串的方式返回?;贖TTP的方式識別人臉是比較慢的,慢的原因在于IO性能,相對來講,離線版本的API則能夠充分利用本機的機器資源,不用往返于所謂的算法云服務器,直接在本地就能完成人臉識別和比對工作。
(二)集成SDK
以Face++和訊飛語音為例,這些公司即提供了在線識別的方式也提供了基于SDK的本地識別方式。本地識別的優(yōu)點是速度快,集成度高。而且,作為C#,我們還可以搭建自己的云識別平臺。如果采用了WEBAPI的,每一筆請求都需要再經(jīng)過WEBAPI中轉,性能上會大打折扣。
軟件的成本包括人力成本和采購成本,在考慮成本的時候,自然會想到,我們使用的引擎是否收費呢?即使收費再便宜,一旦流量上來了,也是一筆不小的開支。那么有沒有免費的呢。虹軟公司開發(fā)的人臉識別SDK即可以實現(xiàn)本地識別而且對于中小型企業(yè)基本無成本消耗。
二 引用ArcFace人臉識別離線SDK
(一)下載ArcFace人臉識別離線SDK
(1)打開虹軟官網(wǎng),登陸成功后,點擊開發(fā)者中心,點擊創(chuàng)建應用。
(2)創(chuàng)建完成后,點擊添加SDK,選擇ArcFace,然后填寫相關選項,最后確認。(沒有認證信息的需要認證一下信息,大約需要半天左右)
(3)添加完成后,點擊查看激活碼,這里的APP_ID和SDK_KEY需要記下啦,在項目中需要使用,最后點擊下載SDK。
(二)將libarcsoft_face_engine.dll,libarcsoft_face.dll文件導入Unity
(1)打開下載的壓縮包,找到bin下的x64文件夾內的libarcsoft_face.dll以及l(fā)ibarcsoft_face_engine.dll文件。
(2)Unity中新建Plugins文件夾,導入剛剛的兩個dll文件。
System.Drawing.dll文件從網(wǎng)上https://cn./system.drawing.dll.html下載一個。
注意:這里Unity的Scripting Runtime Version以及Api Compatbility Level*都改為.Net4.X的版本
(三)C#調用(官網(wǎng)的vs窗體Demo含有以下內容)
(1) 自定義方法ASFFunction.cs
using System;
using System.Runtime.InteropServices;//添加引用,調用dll
namespace Ycx_Tool.ArcFace
{
public class ASFFunctions
{
/// <summary>
/// SDK動態(tài)鏈接庫路徑
/// </summary>
public const string Dll_PATH = "libarcsoft_face_engine";
/// <summary>
/// 激活人臉識別SDK引擎函數(shù)
/// </summary>
/// <param name="appID">SDK對應的AppID</param>
/// <param name="sdkKey">SDK對應的SDKKey</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH,CallingConvention=CallingConvention.Cdecl)]
public static extern int ASFActivation(string appID, string sdkKey);
/// <summary>
/// 初始化引擎
/// </summary>
/// <param name="detectMode">AF_DETECT_MODE_VIDEO 視頻模式 | AF_DETECT_MODE_IMAGE 圖片模式</param>
/// <param name="detectFaceOrientPriority">檢測臉部的角度優(yōu)先值,推薦:ASF_OrientPriority.ASF_OP_0_HIGHER_EXT</param>
/// <param name="detectFaceScaleVal">用于數(shù)值化表示的最小人臉尺寸</param>
/// <param name="detectFaceMaxNum">最大需要檢測的人臉個數(shù)</param>
/// <param name="combinedMask">用戶選擇需要檢測的功能組合,可單個或多個</param>
/// <param name="pEngine">初始化返回的引擎handle</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH,CallingConvention=CallingConvention.Cdecl)]
public static extern int ASFInitEngine(uint detectMode, int detectFaceOrientPriority, int detectFaceScaleVal, int detectFaceMaxNum, int combinedMask, ref IntPtr pEngine);
/// <summary>
/// 人臉檢測
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="width">圖像寬度</param>
/// <param name="height">圖像高度</param>
/// <param name="format">圖像顏色空間</param>
/// <param name="imgData">圖像數(shù)據(jù)</param>
/// <param name="detectedFaces">人臉檢測結果</param>
/// <returns></returns>
[DllImport(Dll_PATH, CallingConvention = CallingConvention.Cdecl)]
public static extern int ASFDetectFaces(IntPtr pEngine, int width, int height, int format, IntPtr imgData, IntPtr detectedFaces);
/// <summary>
/// 人臉信息檢測(年齡/性別/人臉3D角度)
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="width">圖像寬度</param>
/// <param name="height">圖像高度</param>
/// <param name="format">圖像顏色空間</param>
/// <param name="imgData">圖像數(shù)據(jù)</param>
/// <param name="detectedFaces">人臉信息,用戶根據(jù)待檢測的功能裁減選擇需要使用的人臉</param>
/// <param name="combinedMask">只支持初始化時候指定需要檢測的功能,在proces時進一步在這個已經(jīng)指定的功能集中篩選例如初始化的時候指定檢測年齡和性別,在process的時候可以只檢測年齡,但是不能檢測除年齡和性別
/// 之外的功能</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH,CallingConvention=CallingConvention.Cdecl)]
public static extern int ASFProcess(IntPtr pEngine, int width, int height, int format, IntPtr imgData, IntPtr detectedFaces, int combinedMask);
/// <summary>
/// 單人臉特征提取
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="width">圖像寬度</param>
/// <param name="height">圖像高度</param>
/// <param name="format">圖像顏色空間</param>
/// <param name="imgData">圖像數(shù)據(jù)</param>
/// <param name="faceInfo">單張人臉位置和角度信息</param>
/// <param name="faceFeature">人臉特征</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH, CallingConvention = CallingConvention.Cdecl)]
public static extern int ASFFaceFeatureExtract(IntPtr pEngine,int width,int height,int format,IntPtr imgData,IntPtr faceInfo,IntPtr faceFeature);
/// <summary>
/// 人臉特征比對
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="faceFeature1">待比較人臉特征1</param>
/// <param name="faceFeature2">帶比較人臉特征2</param>
/// <param name="similarity">相似度(0~1)</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH,CallingConvention=CallingConvention.Cdecl)]
public static extern int ASFFaceFeatureCompare(IntPtr pEngine,IntPtr faceFeature1,IntPtr faceFeature2,ref float similarity);
/// <summary>
/// 獲取性別信息
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="GenderInfo">檢測到的性別信息</param>
/// <returns></returns>
[DllImport(Dll_PATH,CallingConvention =CallingConvention.Cdecl)]
public static extern int ASFGetGender(IntPtr pEngine, IntPtr GenderInfo);
/// <summary>
/// 獲取年齡信息
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="ageInfo">檢測到的性別信息</param>
/// <returns></returns>
[DllImport(Dll_PATH, CallingConvention = CallingConvention.Cdecl)]
public static extern int ASFGetAge(IntPtr pEngine, IntPtr ageInfo);
/// <summary>
/// 獲取3D角度信息
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <param name="p3DAngleInfo">檢測到臉部3D角度信息</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH,CallingConvention =CallingConvention.Cdecl)]
public static extern int ASFGetFace3DAngle(IntPtr pEngine, IntPtr p3DAngleInfo);
/// <summary>
/// 銷毀引擎
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH, CallingConvention = CallingConvention.Cdecl)]
public static extern int ASFUninitEngine(IntPtr pEngine);
/// <summary>
/// 獲取版本信息
/// </summary>
/// <param name="pEngine">引擎handle</param>
/// <returns>調用結果</returns>
[DllImport(Dll_PATH, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ASFGetVersion(IntPtr pEngine);
}
}
(2)自定義數(shù)據(jù)類型
ImageInfo.cs
namespace Ycx_Tool.ArcFace
{
public class ImageInfo
{
/// <summary>
/// 圖片的像素數(shù)據(jù)
/// </summary>
public IntPtr imgData { get; set; }
/// <summary>
/// 圖片像素寬
/// </summary>
public int width { get; set; }
/// <summary>
/// 圖片像素高
/// </summary>
public int height { get; set; }
/// <summary>
/// 圖片格式
/// </summary>
public int format { get; set; }
}
}
(3)自定義結構體
ASF_AgeInfo.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 年齡結果結構體
/// </summary>
public struct ASF_AgeInfo
{
/// <summary>
/// 年齡檢測結果集合
/// </summary>
public IntPtr ageArray;
/// <summary>
/// 結果集大小
/// </summary>
public int num;
}
}
ASF_Face3DAngle.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 3D人臉角度檢測結構體,可參考https://ai./bbs/forum.php?mod=viewthread&tid=1459&page=1&extra=&_dsign=fd9e1a7a
/// </summary>
public struct ASF_Face3DAngle
{
public IntPtr roll;
public IntPtr yaw;
public IntPtr pitch;
/// <summary>
/// 是否檢測成功,0成功,其他為失敗
/// </summary>
public IntPtr status;
public int num;
}
}
ASF_FaceFeature.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 人臉特征結構體
/// </summary>
public struct ASF_FaceFeature
{
/// <summary>
/// 特征值 byte[]
/// </summary>
public IntPtr feature;
/// <summary>
/// 結果集大小
/// </summary>
public int featureSize;
}
}
ASF_GenderInfo.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 性別結構體
/// </summary>
public struct ASF_GenderInfo
{
/// <summary>
/// 性別檢測結果集合
/// </summary>
public IntPtr genderArray;
/// <summary>
/// 結果集大小
/// </summary>
public int num;
}
}
ASF_ImagePixelFormat.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ycx_Tool.ArcFace
{
public struct ASF_ImagePixelFormat
{
/// <summary>
/// RGB24圖片格式
/// </summary>
public const int ASVL_PAF_RGB24_B8G8R8 = 0x201;
}
}
ASF_MultiFaceInfo.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 多人臉檢測結構體
/// </summary>
public struct ASF_MultiFaceInfo
{
/// <summary>
/// 人臉Rect結果集
/// </summary>
public IntPtr faceRects;
/// <summary>
/// 人臉角度結果集,與faceRects一一對應 對應ASF_OrientCode
/// </summary>
public IntPtr faceOrients;
/// <summary>
/// 結果集大小
/// </summary>
public int faceNum;
}
}
ASF_OrientCode.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 人臉角度結構體
/// </summary>
public struct ASF_OrientCode
{
public const int ASF_OC_0 = 0x1;
public const int ASF_OC_90 = 0x2;
public const int ASF_OC_270 = 0x3;
public const int ASF_OC_180 = 0x4;
public const int ASF_OC_30 = 0x5;
public const int ASF_OC_60 = 0x6;
public const int ASF_OC_120 = 0x7;
public const int ASF_OC_150 = 0x8;
public const int ASF_OC_210 = 0x9;
public const int ASF_OC_240 = 0xa;
public const int ASF_OC_300 = 0xb;
public const int ASF_OC_330 = 0xc;
}
}
ASF_OrientPriority.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 人臉檢測優(yōu)先角度結構體,推薦ASF_OP_0_HIGHER_EXT
/// </summary>
public struct ASF_OrientPriority
{
public const int ASF_OP_0_ONLY = 0x1;
public const int ASF_OP_90_ONLY = 0x2;
public const int ASF_OP_270_ONLY = 0x3;
public const int ASF_OP_180_ONLY = 0x4;
public const int ASF_OP_0_HIGHER_EXT = 0x5;
}
}
ASF_SingleFaceInfo.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 單人臉檢測結構體
/// </summary>
public struct ASF_SingleFaceInfo
{
/// <summary>
/// 人臉坐標Rect結果
/// </summary>
public MRECT faceRect;
/// <summary>
/// 人臉角度
/// </summary>
public int faceOrient;
}
}
ASF_VERSION.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// SDK版本信息結構體
/// </summary>
public struct ASF_VERSION
{
public string version;
public string buildDate;
public string copyRight;
}
}
DetectionMode.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 圖片檢測模式
/// </summary>
public struct DetectionMode
{
/// <summary>
/// Video模式,一般用于多幀連續(xù)檢測
/// </summary>
public const uint ASF_DETECT_MODE_VIDEO = 0x00000000;
/// <summary>
/// Image模式,一般用于靜態(tài)圖的單次檢測
/// </summary>
public const uint ASF_DETECT_MODE_IMAGE = 0xFFFFFFFF;
}
}
FaceEngineMask.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 引擎方法類型結構體,在初始化時將用到的類型用|連接傳入,如 ASF_NONE|ASF_FACE_DETECT|ASF_FACERECOGNITION
/// </summary>
public struct FaceEngineMask
{
/// <summary>
/// 不做方法初始化方法類型
/// </summary>
public const int ASF_NONE = 0x00000000;
/// <summary>
/// 人臉檢測方法類型常量
/// </summary>
public const int ASF_FACE_DETECT = 0x00000001;
/// <summary>
/// 人臉識別方法類型常量,包含圖片feature提取和feature比對
/// </summary>
public const int ASF_FACERECOGNITION = 0x00000004;
/// <summary>
/// 年齡檢測方法類型常量
/// </summary>
public const int ASF_AGE = 0x00000008;
/// <summary>
/// 性別檢測方法類型常量
/// </summary>
public const int ASF_GENDER = 0x00000010;
/// <summary>
/// 3D角度檢測方法類型常量
/// </summary>
public const int ASF_FACE3DANGLE = 0x00000020;
}
}
MRECT.cs
namespace Ycx_Tool.ArcFace
{
/// <summary>
/// 人臉框信息結構體
/// </summary>
public struct MRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
}
(3)內存管理類
MemoryUtil.cs
using System;
using System.Runtime.InteropServices;
namespace Ycx_Tool.ArcFace
{
public class MemoryUtil
{
/// <summary>
/// 申請內存
/// </summary>
/// <param name="len">內存長度(單位:字節(jié))</param>
/// <returns>內存首地址</returns>
public static IntPtr Malloc(int len)
{
return Marshal.AllocHGlobal(len);
}
/// <summary>
/// 釋放ptr托管的內存
/// </summary>
/// <param name="ptr">托管指針</param>
public static void Free(IntPtr ptr)
{
Marshal.FreeHGlobal(ptr);
}
/// <summary>
/// 將字節(jié)數(shù)組的內容拷貝到托管內存中
/// </summary>
/// <param name="source">元數(shù)據(jù)</param>
/// <param name="startIndex">元數(shù)據(jù)拷貝起始位置</param>
/// <param name="destination">托管內存</param>
/// <param name="length">拷貝長度</param>
public static void Copy(byte[] source, int startIndex, IntPtr destination, int length)
{
Marshal.Copy(source, startIndex, destination, length);
}
/// <summary>
/// 將托管內存的內容拷貝到字節(jié)數(shù)組中
/// </summary>
/// <param name="source">托管內存</param>
/// <param name="destination">目標字節(jié)數(shù)組</param>
/// <param name="startIndex">拷貝起始位置</param>
/// <param name="length">拷貝長度</param>
public static void Copy(IntPtr source, byte[] destination, int startIndex, int length)
{
Marshal.Copy(source, destination, startIndex, length);
}
/// <summary>
/// 將ptr托管的內存轉化為結構體對象
/// </summary>
/// <typeparam name="T">泛型</typeparam>
/// <param name="ptr">托管指針</param>
/// <returns>轉化后的對象</returns>
public static T PtrToStructure<T>(IntPtr ptr)
{
return Marshal.PtrToStructure<T>(ptr);
}
/// <summary>
/// 將結構體對象復制到ptr托管的內存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="ptr"></param>
public static void StructureToPtr<T>(T t, IntPtr ptr)
{
Marshal.StructureToPtr(t, ptr, false);
}
/// <summary>
/// 獲取類型的大小
/// </summary>
/// <typeparam name="T">泛型</typeparam>
/// <returns>類型的大小</returns>
public static int SizeOf<T>()
{
return Marshal.SizeOf<T>();
}
}
}
(4)圖片處理類
ImageUtil.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using UnityEngine;
using System.IO;
namespace Ycx_Tool.ArcFace
{
public class ImageUtil
{
/// <summary>
/// 獲取Image
/// </summary>
/// <returns></returns>
public static Image GetImage(string path)
{
Image image;
FileInfo file = new FileInfo(path);
long maxSize = 1024 * 1024 * 2;
if (file.Length > maxSize)
{
Debug.Log("圖像文件最大為2MB,請壓縮后在導入!");
return null;
}
image = Image.FromFile(path);
image = ScaleImage(image, 256, 256);
return image;
}
/// <summary>
/// 獲取圖片信息
/// </summary>
/// <param name="image">圖片</param>
/// <returns>成功或失敗</returns>
public static ImageInfo ReadBMP(Image image)
{
ImageInfo imageInfo = new ImageInfo();
//將Image轉換為Format24bppRgb格式的BMP
Bitmap bm = new Bitmap(image);
BitmapData data = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
try
{
//位圖中第一個像素數(shù)據(jù)的地址。它也可以看成是位圖中的第一個掃描行
IntPtr ptr = data.Scan0;
//定義數(shù)組長度
int soureBitArrayLength = data.Height * Math.Abs(data.Stride);
byte[] sourceBitArray = new byte[soureBitArrayLength];
//將bitmap中的內容拷貝到ptr_bgr數(shù)組中
MemoryUtil.Copy(ptr, sourceBitArray, 0, soureBitArrayLength);
//填充引用對象字段值
imageInfo.width = data.Width;
imageInfo.height = data.Height;
imageInfo.format = ASF_ImagePixelFormat.ASVL_PAF_RGB24_B8G8R8;
//獲取去除對齊位后度圖像數(shù)據(jù)
int line = imageInfo.width * 3;
int pitch = Math.Abs(data.Stride);
int bgr_len = line * imageInfo.height;
byte[] destBitArray = new byte[bgr_len];
/*
* 圖片像素數(shù)據(jù)在內存中是按行存儲,一般圖像庫都會有一個內存對齊,在每行像素的末尾位置
* 每行的對齊位會使每行多出一個像素空間(三通道如RGB會多出3個字節(jié),四通道RGBA會多出4個字節(jié))
* 以下循環(huán)目的是去除每行末尾的對齊位,將有效的像素拷貝到新的數(shù)組
*/
for (int i = 0; i < imageInfo.height; ++i)
{
Array.Copy(sourceBitArray, i * pitch, destBitArray, i * line, line);
}
imageInfo.imgData = MemoryUtil.Malloc(destBitArray.Length);
MemoryUtil.Copy(destBitArray, 0, imageInfo.imgData, destBitArray.Length);
return imageInfo;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
bm.UnlockBits(data);
}
return null;
}
/// <summary>
/// 用矩形框標記圖片指定區(qū)域
/// </summary>
/// <param name="image">圖片</param>
/// <param name="startX">矩形框左上角X坐標</param>
/// <param name="startY">矩形框左上角Y坐標</param>
/// <param name="width">矩形框寬度</param>
/// <param name="height">矩形框高度</param>
/// <returns>標記后的圖片</returns>
public static Image MarkRect(Image image, int startX, int startY, int width, int height)
{
Image clone = (Image)image.Clone();
System.Drawing.Graphics g =System.Drawing.Graphics.FromImage(clone);
try
{
Brush brush = new SolidBrush(System.Drawing.Color.Red);
Pen pen = new Pen(brush, 2);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawRectangle(pen, new Rectangle(startX, startY, width, height));
return clone;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
g.Dispose();
}
return null;
}
/// <summary>
/// 用矩形框標記圖片指定區(qū)域,添加年齡和性別標注
/// </summary>
/// <param name="image">圖片</param>
/// <param name="startX">矩形框左上角X坐標</param>
/// <param name="startY">矩形框左上角Y坐標</param>
/// <param name="width">矩形框寬度</param>
/// <param name="height">矩形框高度</param>
/// <param name="age">年齡</param>
/// <param name="gender">性別</param>
/// <returns>標記后的圖片</returns>
public static Image MarkRectAndString(Image image, int startX, int startY, int width, int height, int age, int gender)
{
Image clone = (Image)image.Clone();
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(clone);
try
{
Brush brush = new SolidBrush(System.Drawing.Color.Red);
Pen pen = new Pen(brush, 2);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawRectangle(pen, new Rectangle(startX < 1 ? 0 : startX, startY < 1 ? 0 : startY, width, height));
string genderStr = "";
if (gender >= 0)
{
if (gender == 0)
{
genderStr = "男";
}
else if (gender == 1)
{
genderStr = "女";
}
}
g.DrawString(string.Format("Age:{0} Gender:{1}", age, genderStr), new System.Drawing.Font(FontFamily.GenericSerif, 12), brush, startX < 1 ? 0 : startX, (startY - 20) < 1 ? 0 : startY - 20);
return clone;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
g.Dispose();
}
return null;
}
/// <summary>
/// 按指定寬高縮放圖片
/// </summary>
/// <param name="image">原圖片</param>
/// <param name="dstWidth">目標圖片寬</param>
/// <param name="dstHeight">目標圖片高</param>
/// <returns></returns>
public static Image ScaleImage(Image image, int dstWidth, int dstHeight)
{
System.Drawing.Graphics g = null;
try
{
//按比例縮放
float scaleRate = 0.0f;
if (image.Width >= dstWidth && image.Height >= dstHeight)
{
int widthDis = image.Width - dstWidth;
int heightDis = image.Height - dstHeight;
if (widthDis > heightDis)
{
scaleRate = dstWidth * 1f / image.Width;
}
else
{
scaleRate = dstHeight * 1f / image.Height;
}
}
else if (image.Width >= dstWidth && image.Height < dstHeight)
{
scaleRate = dstWidth * 1f / image.Width;
}
else if (image.Width < dstWidth && image.Height >= dstHeight)
{
scaleRate = dstHeight * 1f / image.Height;
}
else
{
int widthDis = dstWidth - image.Width;
int heightDis = dstHeight - image.Height;
if (widthDis > heightDis)
{
scaleRate = dstHeight * 1f / image.Height;
}
else
{
scaleRate = dstWidth * 1f / image.Width;
}
}
int width = (int)(image.Width * scaleRate);
int height = (int)(image.Height * scaleRate);
//將寬度調整為4的整數(shù)倍
if (width % 4 != 0)
{
width = width - width % 4;
}
Bitmap destBitmap = new Bitmap(width, height);
g = System.Drawing.Graphics.FromImage(destBitmap);
g.Clear(System.Drawing.Color.Transparent);
//設置畫布的描繪質量
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(image, new Rectangle((width - width) / 2, (height - height) / 2, width, height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
//設置壓縮質量
EncoderParameters encoderParams = new EncoderParameters();
long[] quality = new long[1];
quality[0] = 100;
EncoderParameter encoderParam = new EncoderParameter(Encoder.Quality, quality);
encoderParams.Param[0] = encoderParam;
return destBitmap;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
if (g != null)
{
g.Dispose();
}
}
return null;
}
/// <summary>
/// 剪裁圖片
/// </summary>
/// <param name="src">原圖片</param>
/// <param name="left">左坐標</param>
/// <param name="top">頂部坐標</param>
/// <param name="right">右坐標</param>
/// <param name="bottom">底部坐標</param>
/// <returns>剪裁后的圖片</returns>
public static Image CutImage(Image src, int left, int top, int right, int bottom)
{
try
{
Bitmap srcBitmap = new Bitmap(src);
Bitmap dstBitmap = srcBitmap.Clone(new Rectangle(left, top, right - left, bottom - top), PixelFormat.DontCare);
return dstBitmap;
}
catch (Exception e)
{
Console.WriteLine(e);
}
return null;
}
}
}
(5)人臉識別類
FaceUtil.cs
using System;
using System.Drawing;
namespace Ycx_Tool.ArcFace
{
public class FaceUtil
{
/// <summary>
/// 人臉檢測(PS:檢測RGB圖像的人臉時,必須保證圖像的寬度能被4整除,否則會失敗)
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="imageInfo">圖像數(shù)據(jù)</param>
/// <returns>人臉檢測結果</returns>
public static ASF_MultiFaceInfo DetectFace(IntPtr pEngine, ImageInfo imageInfo)
{
ASF_MultiFaceInfo multiFaceInfo = new ASF_MultiFaceInfo();
IntPtr pMultiFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_MultiFaceInfo>());
int retCode = ASFFunctions.ASFDetectFaces(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo);
multiFaceInfo = MemoryUtil.PtrToStructure<ASF_MultiFaceInfo>(pMultiFaceInfo);
return multiFaceInfo;
}
/// <summary>
/// 人臉檢測
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="image">圖像</param>
/// <returns></returns>
public static ASF_MultiFaceInfo DetectFace(IntPtr pEngine, string path)
{
ASF_MultiFaceInfo multiFaceInfo = new ASF_MultiFaceInfo();
Image image=ImageUtil.GetImage(path);
if (image != null)
{
image = ImageUtil.ScaleImage(image, image.Width, image.Height);
ImageInfo imageInfo = ImageUtil.ReadBMP(image);
multiFaceInfo = DetectFace(pEngine, imageInfo);
MemoryUtil.Free(imageInfo.imgData);
return multiFaceInfo;
}
else
{
return multiFaceInfo;
}
}
/// <summary>
/// 提取人臉特征
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="imageInfo">圖像數(shù)據(jù)</param>
/// <param name="multiFaceInfo">人臉檢測結果</param>
/// <returns>保存人臉特征結構體指針</returns>
public static IntPtr ExtractFeature(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out ASF_SingleFaceInfo singleFaceInfo)
{
singleFaceInfo = new ASF_SingleFaceInfo();
singleFaceInfo.faceRect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
singleFaceInfo.faceOrient = MemoryUtil.PtrToStructure<int>(multiFaceInfo.faceOrients);
IntPtr pSingleFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_SingleFaceInfo>());
MemoryUtil.StructureToPtr(singleFaceInfo, pSingleFaceInfo);
IntPtr pFaceFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
int retCode = ASFFunctions.ASFFaceFeatureExtract(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pSingleFaceInfo, pFaceFeature);
Console.WriteLine("FR Extract Feature result:" + retCode);
if (retCode != 0)
{
//釋放指針
MemoryUtil.Free(pSingleFaceInfo);
MemoryUtil.Free(pFaceFeature);
ASF_FaceFeature emptyFeature = new ASF_FaceFeature();
IntPtr pEmptyFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
MemoryUtil.StructureToPtr(emptyFeature, pEmptyFeature);
return pEmptyFeature;
}
//人臉特征feature過濾
ASF_FaceFeature faceFeature = MemoryUtil.PtrToStructure<ASF_FaceFeature>(pFaceFeature);
byte[] feature = new byte[faceFeature.featureSize];
MemoryUtil.Copy(faceFeature.feature, feature, 0, faceFeature.featureSize);
ASF_FaceFeature localFeature = new ASF_FaceFeature();
localFeature.feature = MemoryUtil.Malloc(feature.Length);
MemoryUtil.Copy(feature, 0, localFeature.feature, feature.Length);
localFeature.featureSize = feature.Length;
IntPtr pLocalFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
MemoryUtil.StructureToPtr(localFeature, pLocalFeature);
//釋放指針
MemoryUtil.Free(pSingleFaceInfo);
MemoryUtil.Free(pFaceFeature);
return pLocalFeature;
}
/// <summary>
/// 提取人臉特征
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="image">圖像</param>
/// <returns>保存人臉特征結構體指針</returns>
public static IntPtr ExtractFeature(IntPtr pEngine, Image image, out ASF_SingleFaceInfo singleFaceInfo)
{
image = ImageUtil.ScaleImage(image, image.Width, image.Height);
ImageInfo imageInfo = ImageUtil.ReadBMP(image);
ASF_MultiFaceInfo multiFaceInfo = DetectFace(pEngine, imageInfo);
singleFaceInfo = new ASF_SingleFaceInfo();
IntPtr pFaceModel = ExtractFeature(pEngine, imageInfo, multiFaceInfo, out singleFaceInfo);
MemoryUtil.Free(imageInfo.imgData);
return pFaceModel;
}
/// <summary>
/// 年齡檢測
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="imageInfo">圖像數(shù)據(jù)</param>
/// <param name="multiFaceInfo">人臉檢測結果</param>
/// <returns>年齡檢測結構體</returns>
public static ASF_AgeInfo AgeEstimation(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out int retCode)
{
retCode = -1;
IntPtr pMultiFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_MultiFaceInfo>());
MemoryUtil.StructureToPtr(multiFaceInfo, pMultiFaceInfo);
if (multiFaceInfo.faceNum == 0)
{
return new ASF_AgeInfo();
}
//人臉信息處理
retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_AGE);
if (retCode == 0)
{
//獲取年齡信息
IntPtr pAgeInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_AgeInfo>());
retCode = ASFFunctions.ASFGetAge(pEngine, pAgeInfo);
Console.WriteLine("Get Age Result:" + retCode);
ASF_AgeInfo ageInfo = MemoryUtil.PtrToStructure<ASF_AgeInfo>(pAgeInfo);
//釋放內存
MemoryUtil.Free(pMultiFaceInfo);
MemoryUtil.Free(pAgeInfo);
return ageInfo;
}
else
{
return new ASF_AgeInfo();
}
}
/// <summary>
/// 性別檢測
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="imageInfo">圖像數(shù)據(jù)</param>
/// <param name="multiFaceInfo">人臉檢測結果</param>
/// <returns>保存性別檢測結果結構體</returns>
public static ASF_GenderInfo GenderEstimation(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out int retCode)
{
retCode = -1;
IntPtr pMultiFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_MultiFaceInfo>());
MemoryUtil.StructureToPtr(multiFaceInfo, pMultiFaceInfo);
if (multiFaceInfo.faceNum == 0)
{
return new ASF_GenderInfo();
}
//人臉信息處理
retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_GENDER);
if (retCode == 0)
{
//獲取性別信息
IntPtr pGenderInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_GenderInfo>());
retCode = ASFFunctions.ASFGetGender(pEngine, pGenderInfo);
Console.WriteLine("Get Gender Result:" + retCode);
ASF_GenderInfo genderInfo = MemoryUtil.PtrToStructure<ASF_GenderInfo>(pGenderInfo);
//釋放內存
MemoryUtil.Free(pMultiFaceInfo);
MemoryUtil.Free(pGenderInfo);
return genderInfo;
}
else
{
return new ASF_GenderInfo();
}
}
/// <summary>
/// 人臉3D角度檢測
/// </summary>
/// <param name="pEngine">引擎Handle</param>
/// <param name="imageInfo">圖像數(shù)據(jù)</param>
/// <param name="multiFaceInfo">人臉檢測結果</param>
/// <returns>保存人臉3D角度檢測結果結構體</returns>
public static ASF_Face3DAngle Face3DAngleDetection(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out int retCode)
{
IntPtr pMultiFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_MultiFaceInfo>());
MemoryUtil.StructureToPtr(multiFaceInfo, pMultiFaceInfo);
if (multiFaceInfo.faceNum == 0)
{
retCode = -1;
return new ASF_Face3DAngle();
}
//人臉信息處理
retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_FACE3DANGLE);
if (retCode == 0)
{
//獲取人臉3D角度
IntPtr pFace3DAngleInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_Face3DAngle>());
retCode = ASFFunctions.ASFGetFace3DAngle(pEngine, pFace3DAngleInfo);
Console.WriteLine("Get Face3D Angle Result:" + retCode);
ASF_Face3DAngle face3DAngle = MemoryUtil.PtrToStructure<ASF_Face3DAngle>(pFace3DAngleInfo);
//釋放內存
MemoryUtil.Free(pMultiFaceInfo);
MemoryUtil.Free(pFace3DAngleInfo);
return face3DAngle;
}
else
{
return new ASF_Face3DAngle();
}
}
}
}
(6)功能封裝
SDKUtil.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Drawing;
namespace Ycx_Tool.ArcFace
{
public class SDKUtil
{
public static IntPtr _PEngine = IntPtr.Zero;
/// <summary>
/// 初始化SDK
/// </summary>
/// <param name="appid"></param>
/// <param name="sdkkey"></param>
/// <returns></returns>
public static void Init_Sdk(string appid,string sdkkey)
{
try
{
var ret_0 = ASFFunctions.ASFActivation(appid, sdkkey);
Debug.Log("SDK激活成功");
}
catch (Exception e)
{
Debug.Log("SDK激活失敗" + e.Message);
return;
}
try
{
//初始化引擎
uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
//檢測臉部的角度優(yōu)先值
int detectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
//人臉在圖片中所占比例,如果需要調整檢測人臉尺寸請修改此值,有效數(shù)值為2-32
int detectFaceScaleVal = 16;
//最大需要檢測的人臉個數(shù)
int detectFaceMaxNum = 5;
//引擎初始化時需要初始化的檢測功能組合
int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
var ret_1 = ASFFunctions.ASFInitEngine(detectMode,detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref _PEngine);
Debug.Log("引擎初始化成功");
}
catch (Exception e)
{
Debug.Log("引擎初始化失敗" + e.Message);
return;
}
}
/// <summary>
/// 選擇圖片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <param name="path"></param>
public static ImageInfo ChooseImg(string path)
{
if (_PEngine == IntPtr.Zero)
{
Debug.Log("請先初始化引擎");
return null;
}
ImageInfo imageInfo = ImageUtil.ReadBMP(ImageUtil.GetImage(path));
return imageInfo;
}
/// <summary>
/// 人臉檢測
/// </summary>
/// <param name="image_Info"></param>
public static void Face_Detect(ImageInfo image_Info)
{
if (_PEngine == IntPtr.Zero)
{
Debug.Log("請先初始化引擎");
return;
}
if (image_Info == null)
{
Debug.Log("請先選擇圖片");
return;
}
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(_PEngine, image_Info);
if (multiFaceInfo.faceNum < 1)
{
Debug.Log("未檢測到人臉");
}
else
{
Debug.Log("檢測到" + multiFaceInfo.faceNum + "人");
}
}
/// <summary>
/// 人臉對別
/// </summary>
/// <param name="imageInfo_0"></param>
/// <param name="imageInfo_1"></param>
public static string Face_Compare(string image_0_path, string image_1_path)
{
string result = "";
if (_PEngine == IntPtr.Zero)
{
Debug.Log("請先初始化引擎");
result = "請先初始化引擎";
return result;
}
if (image_0_path == null || image_1_path == null)
{
Debug.Log("圖片不能為空");
result = "圖片不能為空";
return result;
}
ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
IntPtr feature_0 = FaceUtil.ExtractFeature(_PEngine, Image.FromFile(image_0_path), out singleFaceInfo);
IntPtr feature_1 = FaceUtil.ExtractFeature(_PEngine, Image.FromFile(image_1_path), out singleFaceInfo);
float similarity = 0f;
ASFFunctions.ASFFaceFeatureCompare(_PEngine, feature_0, feature_1, ref similarity);
Debug.Log("兩張臉的相似度為:" + similarity);
result = similarity.ToString();
return result;
}
/// <summary>
/// 人臉對別
/// </summary>
/// <param name="imageInfo_0"></param>
/// <param name="imageInfo_1"></param>
public static string Face_Compare(IntPtr image_0_path, IntPtr image_1_path)
{
string result = "";
if (_PEngine == IntPtr.Zero)
{
Debug.Log("請先初始化引擎");
result = "請先初始化引擎";
return result;
}
float similarity = 0f;
ASFFunctions.ASFFaceFeatureCompare(_PEngine, image_0_path, image_1_path, ref similarity);
Debug.Log("兩張臉的相似度為:" + similarity);
result = similarity.ToString();
return result;
}
}
}
|