一、复制官方文档上的访问代码
using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; //官方示例代码 //https://console.faceplusplus.com.cn/documents/6329752 namespace MgLiveCOM_Eyes { public static class HttpHelper4MultipartForm { public class FileParameter { public byte[] File { get; set; } public string FileName { get; set; } public string ContentType { get; set; } public FileParameter(byte[] file) : this(file, null) { } public FileParameter(byte[] file, string filename) : this(file, filename, null) { } public FileParameter(byte[] file, string filename, string contenttype) { this.File = file; this.FileName = filename; this.ContentType = contenttype; } } private static readonly Encoding encoding = Encoding.UTF8; /// <summary> /// MultipartForm请求 /// </summary> /// <param name="postUrl">服务地址</param> /// <param name="userAgent"></param> /// <param name="postParameters">参数</param> /// <returns></returns> public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters) { string text = string.Format("----------{0:N}", Guid.NewGuid()); string contentType = "multipart/form-data; boundary=" + text;//multipart类型 byte[] multipartFormData = HttpHelper4MultipartForm.GetMultipartFormData(postParameters, text); return HttpHelper4MultipartForm.PostForm(postUrl, userAgent, contentType, multipartFormData); } private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData) { HttpWebRequest httpWebRequest = WebRequest.Create(postUrl) as HttpWebRequest; if (httpWebRequest == null) { throw new NullReferenceException("request is not a http request"); } httpWebRequest.Method = "POST";//post方式 httpWebRequest.SendChunked = false; httpWebRequest.KeepAlive = true; httpWebRequest.Proxy = null; httpWebRequest.Timeout = Timeout.Infinite; httpWebRequest.ReadWriteTimeout = Timeout.Infinite; httpWebRequest.AllowWriteStreamBuffering = false; httpWebRequest.ProtocolVersion = HttpVersion.Version11; httpWebRequest.ContentType = contentType; httpWebRequest.CookieContainer = new CookieContainer(); httpWebRequest.ContentLength = (long)formData.Length; try { using (Stream requestStream = httpWebRequest.GetRequestStream()) { int bufferSize = 4096; int position = 0; while (position < formData.Length) { bufferSize = Math.Min(bufferSize, formData.Length - position); byte[] data = new byte[bufferSize]; Array.Copy(formData, position, data, 0, bufferSize); requestStream.Write(data, 0, data.Length); position += data.Length; } requestStream.Close(); } } catch (Exception ex) { return null; } HttpWebResponse result; try { result = (httpWebRequest.GetResponse() as HttpWebResponse); } catch (WebException arg_9C_0) { result = (arg_9C_0.Response as HttpWebResponse); } return result; } public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; } /// <summary> /// 从表单中获取数据 /// </summary> /// <param name="postParameters"></param> /// <param name="boundary"></param> /// <returns></returns> private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary) { Stream stream = new MemoryStream(); bool flag = false; foreach (KeyValuePair<string, object> current in postParameters) { if (flag) { stream.Write(HttpHelper4MultipartForm.encoding.GetBytes("\r\n"), 0, HttpHelper4MultipartForm.encoding.GetByteCount("\r\n")); } flag = true; if (current.Value is HttpHelper4MultipartForm.FileParameter) { HttpHelper4MultipartForm.FileParameter fileParameter = (HttpHelper4MultipartForm.FileParameter)current.Value; string s = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n", new object[] { boundary, current.Key, fileParameter.FileName ?? current.Key, fileParameter.ContentType ?? "application/octet-stream" }); stream.Write(HttpHelper4MultipartForm.encoding.GetBytes(s), 0, HttpHelper4MultipartForm.encoding.GetByteCount(s)); stream.Write(fileParameter.File, 0, fileParameter.File.Length); } else { string s2 = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}", boundary, current.Key, current.Value); stream.Write(HttpHelper4MultipartForm.encoding.GetBytes(s2), 0, HttpHelper4MultipartForm.encoding.GetByteCount(s2)); } } string s3 = "\r\n--" + boundary + "--\r\n"; stream.Write(HttpHelper4MultipartForm.encoding.GetBytes(s3), 0, HttpHelper4MultipartForm.encoding.GetByteCount(s3)); stream.Position = 0L; byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); stream.Close(); return array; } } }
二、接入AI功能
using System; using System.Collections.Generic; using System.Text; using System.Net; using System.IO; using UnityEngine; using MgLiveCOM_Eyes; /// <summary> /// Face++旷视 /// 人工智能开放平台 /// /// 面部特征分析文档 /// https://console.faceplusplus.com.cn/documents/118131136 /// </summary> public class FacePlusSDK : MonoBehaviour { //人脸检测请求地址 public const string FACE_DETECT_URL = "https://api-cn.faceplusplus.com/facepp/v3/detect"; //面部特征分析请求地址 public const string FACIAL_URL = "https://api-cn.faceplusplus.com/facepp/v1/facialfeatures"; public const string API_KEY = ""; public const string API_SECRET = ""; public Action<FaceDetectResult> OnFaceDetectResult; public Action<FacialFeaturesResult> OnFacialFeaturesResult; /// <summary> /// 人脸检测 /// 图片格式:JPG(JPEG),PNG /// 图片像素尺寸:最小 48*48 像素,最大 4096*4096 像素 /// 图片文件大小:2 MB /// 最小人脸像素尺寸: /// 系统能够检测到的人脸框为一个正方形, /// 正方形边长的最小值为图像短边长度的 48 分之一,最小值不低于 48 像素。 /// 例如图片为 4096*3200 像素,则最小人脸像素尺寸为 66*66 像素。 /// </summary> /// <param name="tex"></param> public void AsyncFaceDetect(Texture2D tex) { byte[] bytes = tex.EncodeToJPG(); var postParameters = new Dictionary<string, object>(); postParameters.Add("api_key", API_KEY); postParameters.Add("api_secret", API_SECRET); postParameters.Add("return_landmark", 0); string attributes = "gender,age,eyestatus,emotion,beauty,mouthstatus,skinstatus"; postParameters.Add("return_attributes", attributes); postParameters.Add("image_file", new HttpHelper4MultipartForm.FileParameter(bytes, "1.jpg", "application/octet-stream")); HttpWebResponse response = HttpHelper4MultipartForm.MultipartFormDataPost(FACE_DETECT_URL, string.Empty, postParameters); StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8); string result = reader.ReadToEnd(); File.WriteAllText("D:/face_detect.txt", result); var face = JsonUtility.FromJson<FaceDetectResult>(result); if (!string.IsNullOrEmpty(face.error_message)) { Debug.LogError(face.error_message); } OnFaceDetectResult?.Invoke(face); } /// <summary> /// 面部特征分析 /// 图片格式: JPG(JPEG) /// 图片像素尺寸: 最小200*200像素,最大4096*4096像素 /// 图片文件大小:最大 2 MB /// </summary> /// <param name="tex"></param> public void AsyncFacialFeatures(Texture2D tex) { byte[] bytes = tex.EncodeToJPG(); var postParameters = new Dictionary<string, object>(); postParameters.Add("api_key", API_KEY); postParameters.Add("api_secret", API_SECRET); postParameters.Add("image_file", new HttpHelper4MultipartForm.FileParameter(bytes, "1.jpg", "application/octet-stream")); HttpWebResponse response = HttpHelper4MultipartForm.MultipartFormDataPost(FACIAL_URL, string.Empty, postParameters); StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8); string result = reader.ReadToEnd(); //File.WriteAllText("D:/face_feature.txt", result); var facial = JsonUtility.FromJson<FacialFeaturesResult>(result); if (!string.IsNullOrEmpty(facial.error_message)) { Debug.LogError(facial.error_message); } OnFacialFeaturesResult?.Invoke(facial); } //人脸检测结果 public class FaceDetectResult { //用于区分每一次请求的唯一的字符串 public string request_id; //被检测出的人脸数组 public Face[] faces; //被检测的图片在系统中的标识 public string image_id; //整个请求所花费的时间,单位为毫秒 public int time_used; //当请求失败时才会返回此字符串 public string error_message; //检测出的人脸个数 public int face_num; [Serializable] public class Face { //人脸的标识 public string face_token; //人脸矩形框的位置 public FaceRectangle face_rectangle; //人脸的关键点坐标数组 public object landmark; //人脸属性特征 public FaceAttribute attributes; [Serializable] public class FaceRectangle { //矩形框左上角像素点的纵坐标 public int top; //矩形框左上角像素点的横坐标 public int left; //矩形框的宽度 public int width; //矩形框的高度 public int height; } [Serializable] public class FaceAttribute { //性别分析结果 Male:男, Female:女 public Gender gender; //年龄 public Age age; //笑容分析结果 public Smile smile; //人脸姿势分析结果 public Headpose headpose; //人脸模糊分析结果 public Blur blur; //眼睛状态信息 public EyeStatus eyestatus; //情绪识别结果 public Emotion emotion; //人脸质量判断结果 public Facequality facequality; //颜值识别结果 public Beauty beauty; //嘴部状态信息 public Mouthstatus mouthstatus; //眼球位置与视线方向信息 public Eyegaze eyegaze; //面部特征识别结果 public Skinstatus skinstatus; [Serializable] public class Gender { public string value; } [Serializable] public class Age { public int value; } [Serializable] public class Smile { //值为一个 [0,100] 的浮点数,小数点后3位有效数字。数值越大表示笑程度高。 public float value; //代表笑容的阈值,超过该阈值认为有笑容 public float threshold; } [Serializable] public class Headpose { //抬头 public float pitch_angle; //旋转(平面旋转) public float roll_angle; //摇头 public float yaw_angle; } [Serializable] public class Blur { //人脸模糊分析结果 public Blurness blurness; [Serializable] public class Blurness { //范围 [0,100],小数点后 3 位有效数字。值越大,越模糊。 public float value; //表示人脸模糊度是否影响辨识的阈值。 public float threshold; } } [Serializable] public class EyeStatus { //左眼状态 public EyeState left_eye_status; //右眼状态 public EyeState right_eye_status; [Serializable] public class EyeState { //眼睛被遮挡的置信度 public float occlusion; //不戴眼镜且睁眼的置信度 public float no_glass_eye_open; //佩戴普通眼镜且闭眼的置信度 public float normal_glass_eye_close; //佩戴普通眼镜且睁眼的置信度 public float normal_glass_eye_open; //佩戴墨镜的置信度 public float dark_glasses; //不戴眼镜且闭眼的置信度 public float no_glass_eye_close; //带眼镜的置信度 public float glass { get { return normal_glass_eye_close + normal_glass_eye_open + dark_glasses; } } } } [Serializable] public class Emotion { //愤怒 public float anger; //厌恶 public float disgust; //恐惧 public float fear; //高兴 public float happiness; //平静 public float neutral; //伤心 public float sadness; //惊讶 public float surprise; } [Serializable] public class Facequality { //值为人脸的质量判断的分数,是一个浮点数,范围 [0,100],小数点后 3 位有效数字 public float value; //表示人脸质量基本合格的一个阈值,超过该阈值的人脸适合用于人脸比对 public float threshold; } [Serializable] public class Beauty { //男性认为的此人脸颜值分数。值越大,颜值越高。 public float male_score; //女性认为的此人脸颜值分数。值越大,颜值越高。 public float female_score; } [Serializable] public class Mouthstatus { //嘴部被医用口罩或呼吸面罩遮挡的置信度 public float surgical_mask_or_respirator; //嘴部被其他物体遮挡的置信度 public float other_occlusion; //嘴部没有遮挡且闭上的置信度 public float close; //嘴部没有遮挡且张开的置信度 public float open; } [Serializable] public class Eyegaze { //左眼的位置与视线状态 public EyePosition left_eye_gaze; //右眼的位置与视线状态 public EyePosition right_eye_gaze; [Serializable] public class EyePosition { //眼球中心位置的 X 轴坐标 public float position_x_coordinate; //眼球中心位置的 Y 轴坐标 public float position_y_coordinate; //眼球视线方向向量的 X 轴分量 public float vector_x_component; //眼球视线方向向量的 Y 轴分量 public float vector_y_component; //眼球视线方向向量的 Z 轴分量 public float vector_z_component; } } [Serializable] public class Skinstatus { //健康 public float health; //色斑 public float stain; //青春痘 public float acne; //黑眼圈 public float dark_circle; } } } } //面部特征结果 public class FacialFeaturesResult { //用于区分每一次请求的唯一的字符串 public string request_id; //被检测的图片在系统中的标识 public string image_id; //人脸矫正后的图片,jpg格式。base64 编码的二进制图片数据 public string image_reset; //人脸特征分析的结果 public Result result; //人脸矩形框的位置 public FaceRectangle face_rectangle; //人脸姿势分析结果 public HeadPose headpose; //人脸五官及轮廓的关键点坐标数组 public DenseLandmark denselandmark; //整个请求所花费的时间,单位为毫秒 public int time_used; //当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在。 public string error_message; [Serializable] public class Result { //三庭 public ThreeParts three_parts; //五眼 public FiveEyes five_eyes; //黄金三角度数,单位为°,范围[0,180],保留至小数点后两位(若为0则返回null) public float golden_triangle; //脸型 public Face face; //下巴 public Jaw jaw; //眉型 public Eyebrow eyebrow; //眼型 public Eyes eyes; //鼻型 public Nose nose; //唇型 public Mouth mouth; [Serializable] public class ThreeParts { //返回三庭比例,保留至小数点后两位,若为0,则返回null public string parts_ratio; //返回上庭分析结果,距离单位为mm,保留至小数点后两位 public OnePart one_part; //返回中庭分析结果,距离单位为mm,保留至小数点后两位 public TwoPart two_part; //返回下庭分析结果,距离单位为mm,保留至小数点后两位 public ThreePart three_part; [Serializable] public class OnePart { //上庭长度(若为0或无法判断,则返回null) public float faceup_length; //上庭占比(若为0或无法判断,则返回null) public float faceup_ratio; //上庭判断结果(若为0或无法判断,则返回null) public FaceupResult faceup_result; //上庭判断结果枚举 [Serializable] public enum FaceupResult { //上庭标准 faceup_normal, //上庭偏长 faceup_long, //上庭偏短 faceup_short } } [Serializable] public class TwoPart { //中庭长度(若为0或无法判断,则返回null) public float facemid_length; //中庭占比(若为0或无法判断,则返回null) public float facemid_ratio; //中庭判断结果(若为0或无法判断,则返回null) public FacemidResult facemid_result; //中庭判断结果枚举 [Serializable] public enum FacemidResult { //中庭标准 facemid_normal, //中庭偏长 facemid_long, //中庭偏短 facemid_short } } [Serializable] public class ThreePart { //下庭长度(若为0或无法判断,则返回null) public float facedown_length; //下庭占比(若为0或无法判断,则返回null) public float facedown_ratio; //下庭判断结果(若为0或无法判断,则返回null) public FacedownResult facedown_result; //下庭判断结果枚举 [Serializable] public enum FacedownResult { //下庭标准 facedown_normal, //下庭偏长 facedown_long, //下庭偏短 facedown_short } } } [Serializable] public class FiveEyes { //返回五眼比例,保留至小数点后两位,若出现0,则返回null public string eyes_ratio; //返回五眼右侧分析结果,距离单位为mm,保留至小数点后两位 public OneEye one_eye; //返回右眼宽度分析结果,距离单位为mm,保留至小数点后两位,若为0,则返回null public float righteye; //返回内眼角间距分析结果,距离单位为mm,保留至小数点后两位 public ThreeEye three_eye; //返回左眼宽度分析结果,距离单位为mm,保留至小数点后两位,若为0,则返回null public float lefteye; //返回五眼左侧分析结果,距离单位为mm,保留至小数点后两位 public FiveEve five_eye; [Serializable] public class OneEye { //右外眼角颧弓留白距离(若为0或无法判断,则返回null) public float righteye_empty_length; //右外眼角颧弓留白占比(若为0或无法判断,则返回null) public float righteye_empty_ratio; //五眼右侧判断结果(若为0或无法判断,则返回null) public RighteyeEmptyResult righteye_empty_result; [Serializable] public enum RighteyeEmptyResult { //右眼外侧适中 righteye_empty_normal, //右眼外侧偏窄 righteye_empty_short, //右眼外侧偏宽 righteye_empty_long } } [Serializable] public class ThreeEye { //内眼角间距(若为0或无法判断,则返回null) public float eyein_length; //内眼角间距占比(若为0或无法判断,则返回null) public float eyein_ratio; //内眼角间距判断结果(若为0或无法判断,则返回null) public EyeinResult eyein_result; [Serializable] public enum EyeinResult { //内眼角间距适中 eyein_normal, //内眼角间距偏窄 eyein_short, //内眼角间距偏宽 eyein_long } } [Serializable] public class FiveEve { //左外眼角颧弓留白 (若为0或无法判断,则返回null) public float lefteye_empty_length; //左外眼角颧弓留白占比(若为0或无法判断,则返回null) public float lefteye_empty_ratio; //五眼左侧距判断结果(若为0或无法判断,则返回null) public LefteyeEmptyResult lefteye_empty_result; [Serializable] public enum LefteyeEmptyResult { //左眼外侧适中 lefteye_empty_normal, //左眼外侧偏窄 lefteye_empty_short, //左眼外侧偏宽 lefteye_empty_long } } } [Serializable] public class Face { //颞部宽度(若为0则返回null) public float tempus_length; //颧骨宽度(若为0则返回null) public float zygoma_length; //脸部长度(若为0则返回null) public float face_length; //下颌角宽度(若为0则返回null) public float mandible_length; //下颌角度数(若为0则返回null) public float E; //颞部宽度、颧部宽度(固定颧部为1)、下颌角宽度比(若为0则返回null) public float ABD_ratio; //脸型判断结果(若无法判断则返回null) public FaceType face_type; [Serializable] public enum FaceType { //瓜子脸 pointed_face, //椭圆脸 oval_face, //菱形脸 diamond_face, //圆形脸 round_face, //长形脸 long_face, //方形脸 square_face, //标准脸 normal_face } } [Serializable] public class Jaw { //下巴宽度(若为0或者无法判断,则返回null) public float jaw_width; //下巴长度(若为0或者无法判断,则返回null) public float jaw_length; //下巴角度(若为0则返回null) public float jaw_angle; //下巴判断结果(若为0或者无法判断,则返回null) public JawType jaw_type; [Serializable] public enum JawType { //圆下巴 flat_jaw, //尖下巴 sharp_jaw, //方下巴 square_jaw } } [Serializable] public class Eyebrow { //眉毛宽度(若为0则返回null) public float brow_width; //毛高度(若为0则返回null) public float brow_height; //眉毛挑度,若通过M2的水平线在M3的下方,则返回null public float brow_uptrend_angle; //眉毛弯度 public float brow_camber_angle; //眉毛粗细(若为0则返回null) public float brow_thick; //眉型判断结果(若无法判断则返回null) public EyebrowType eyebrow_type; //眉型判断结果枚举 [Serializable] public enum EyebrowType { //粗眉 bushy_eyebrows, //八字眉 eight_eyebrows, //上挑眉 raise_eyebrows, //一字眉 straight_eyebrows, //拱形眉 round_eyebrows, //柳叶眉 arch_eyebrows, //细眉 thin_eyebrows } } [Serializable] public class Eyes { //眼睛宽度(若为0或无法判断,则返回null) public float eye_width; //眼睛高度(若为0或无法判断,则返回null) public float eye_height; //内眦角度数(若为0或无法判断,则返回null) public float angulus_oculi_medialis; //眼型判断结果(若为0或无法判断,则返回null) public EyesType eyes_type; [Serializable] public enum EyesType { //圆眼 round_eyes, //细长眼 thin_eyes, //大眼 big_eyes, //小眼 small_eyes, //标准眼 normal_eyes } } [Serializable] public class Nose { //鼻翼宽度(若为0或无法判断,则返回null) public float nose_width; //鼻翼判断结果(若为0或无法判断,则返回null) public NoseType nose_type; [Serializable] public enum NoseType { //标准鼻 normal_nose, //宽鼻 thick_nose, //窄鼻 thin_nose } } [Serializable] public class Mouth { //嘴巴高度(若为0或无法判断,则返回null) public float mouth_height; //嘴巴宽度(若为0或无法判断,则返回null) public float mouth_width; //嘴唇厚度(若为0或无法判断,则返回null) public float lip_thickness; //嘴角弯曲度(若为0或无法判断,则返回null) public float angulus_oris; //唇型判断结果(若为0或无法判断,则返回null) public MouthType mouth_type; [Serializable] public enum MouthType { //薄唇 thin_lip, //厚唇 thick_lip, //微笑唇 smile_lip, //态度唇 upset_lip, //标准唇 normal_lip } } } [Serializable] public class FaceRectangle { public int top; public int left; public int width; public int height; } [Serializable] public class HeadPose { //抬头 public float pitch_angle; //旋转(平面旋转) public float roll_angle; //摇头 public float yaw_angle; } //数据结构文档 //https://console.faceplusplus.com.cn/documents/55107022 [Serializable] public class DenseLandmark { public object face; public object left_eyebrow; public object right_eyebrow; public object left_eye; public object left_eye_eyelid; public object right_eye; public object right_eye_eyelid; public object nose; public object mouth; } } }