引言:
随着AI人工智能的发展,物联网技术逐渐进入平常百姓家,而人脸融合技术更能体现AI人工智能的发展,人脸融合技术算是比较“老”的技术,在众多的如美图秀秀等修图软件面前此技术很是渺茫,本博客为什么要写关于人脸融合呢?因为在项目中用到了,也是学习新技术,用此博客来记录,方便后期再次使用。
给定一个模板图,根据“人脸位置检测”接口获取到脸部准确位置( “top”, “left”,“width”,“height”),将要融合的图通过发送请求给第三方接口,反馈一个json格式的字符串,从字符串中提取出“result”对应的字符串,即融合成的图片base64格式的字符串,再将base64格式字符串图片格式转成图片。
在Face++网站注册个人Key和Secert
Web API:https://console.faceplusplus.com.cn/documents/5671787
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Web;
namespace Coommon
{
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;
///
/// MultipartForm请求
///
/// 服务地址
///
/// 参数
///
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;
}
///
/// 从表单中获取数据
///
///
///
///
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;
}
}
}
此处我创建了一个方法,给定两个参数:模板图路径和融合图的路径,返回第三方接口反馈的结果字符串
///
/// 调用三方接口实现图片合成
///
/// 人脸融合的模板图
/// 人脸融合的融合图
[HttpPost]
public string PostSend(string tempImg,string mergeImg) {
try
{
//用来存储接口参数值
Dictionary<string, object> verifyPostParameters = new Dictionary<string, object>();
var key = "xxxxxxxxxxxxxxxxx";//注册后得到的key
var secret = "xxxxxxxxxxxxxxxxxxxxx";//注册后得到的secret
//接口必须参数一
verifyPostParameters.Add("api_key", key);
//接口必须参数二
verifyPostParameters.Add("api_secret", secret);
//(模板图)
Bitmap bmp = new Bitmap(tempImg); // 图片地址
byte[] fileImage;
using (Stream stream1 = new MemoryStream())
{
bmp.Save(stream1, ImageFormat.Jpeg);
byte[] arr = new byte[stream1.Length];
stream1.Position = 0;
stream1.Read(arr, 0, (int)stream1.Length);
stream1.Close();
fileImage = arr;
}
//接口必须参数三 模板图的路径地址,必须以post multipart/form-data 的方式上传(此处通过调用HTTPHelper4MultipartForm的方法已实现)
verifyPostParameters.Add("template_file", new HttpHelper4MultipartForm.FileParameter(fileImage, "1.jpg", "application/octet-stream"));
//接口可选参数 脸部坐标位置
verifyPostParameters.Add("template_rectangle", "415,225,402,402");
//(融合图)
Bitmap bmp2 = new Bitmap(mergeImg); // 图片地址
byte[] fileImage2;
using (Stream stream2 = new MemoryStream())
{
bmp2.Save(stream2, ImageFormat.Jpeg);
byte[] arr2 = new byte[stream2.Length];
stream2.Position = 0;
stream2.Read(arr2, 0, (int)stream2.Length);
stream2.Close();
fileImage2 = arr2;
}
//接口必须参数四 融合图的路径地址,必须以post multipart/form-data 的方式上传(此处通过调用HTTPHelper4MultipartForm的方法已实现)
verifyPostParameters.Add("merge_file", new HttpHelper4MultipartForm.FileParameter(fileImage2, "2.jpg", "application/octet-stream"));
//调用HttpHelper4MultipartForm的MultipartFormDataPost方法实现post提交并反馈第三方提供的返回结果
HttpWebResponse verifyResponse = HttpHelper4MultipartForm.MultipartFormDataPost("https://api-cn.faceplusplus.com/imagepp/v1/mergeface", "", verifyPostParameters);
//此处获取反馈的流来读取json格式的字符串,因为图片的字符串非常的“大”,必须循环获取,此操作在HttpHelper4MultipartForm类的MultipartFormDataPost方法中已经实现,所以在调用反馈的字符串是这个地方只需将它全部读取。
var res = verifyResponse.GetResponseStream();
StreamReader sr = new StreamReader(res, Encoding.ASCII);
string reslut= sr.ReadToEnd();
sr.Close();
res.Close();
verifyResponse.Close();
return reslut;
}
catch (WebException ex)
{
HttpWebResponse res = (HttpWebResponse)ex.Response;
Stream myResponseStream = res.GetResponseStream();
StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.Default);
string retString = myStreamReader.ReadToEnd();
return retString;
}
}
实现步骤:
(1)创建 Dictionary
用来存储第三方接口所需的参数和值
(2)赋值 给所需的参数赋值:
var key = “xxxxxxxxxxxxxxxxx”;//注册后得到的key
var secret = “xxxxxxxxxxxxxxxxxxxxx”;//注册后得到的secret
verifyPostParameters.Add(“api_key”, key);//必须参数
verifyPostParameters.Add(“api_secret”, secret);//必须参数
verifyPostParameters.Add(“template_file”, new HttpHelper4MultipartForm.FileParameter(fileImage, “1.jpg”, “application/octet-stream”));//必须参数(模板图片文件) 需要post multipart/form-data 的方式上传
verifyPostParameters.Add(“template_rectangle”, “415,225,402,402”);//接口可选参数 脸部坐标位置
可以通过 接口"人脸位置检测"获取到脸部位置 https://www.faceplusplus.com.cn/face-detection/
verifyPostParameters.Add(“merge_file”, new HttpHelper4MultipartForm.FileParameter(fileImage2, “2.jpg”, “application/octet-stream”));//必须参数(融合图片文件) 需要post multipart/form-data 的方式上传
(3)调用 HttpHelper4MultipartForm类的MultipartFormDataPost
实现发送请求和反馈结果
HttpWebResponse verifyResponse = HttpHelper4MultipartForm.MultipartFormDataPost("https://api-cn.faceplusplus.com/imagepp/v1/mergeface", "", verifyPostParameters);
(4)获取 读取反馈的流,得到json格式字符串,其中包含base64格式的图片字符串。
此处需注意要通过循环读取反馈的字符串,不然读取到的字符串不完整,出现一系列的报错。(此处已在HttpHelper4MultipartForm类中循环)
var res = verifyResponse.GetResponseStream();
StreamReader sr = new StreamReader(res, Encoding.ASCII);
string reslut= sr.ReadToEnd();
//模板图
var tempImg = Server.MapPath(@"~\Image\Template\1.jpg");
//融合图
var mergeImg = system.SourceFolder + "\\" + imgname + ".jpg";
//调用三方接口生成字符串
string postset = PostSend(tempImg, mergeImg);
//将得到的json格式字符串转换得到图片字符串
JObject read = JsonConvert.DeserializeObject<JObject>(postset);
string resul = read["result"].ToString();//图片字符串
string err = read["error_message"].ToString();//错误信息
string id = read["request_id"].ToString();//所用时间
Bitmap Phon = ToImage(resul);//生成的图片
///
/// 将Base64字符串转换为图片
///
/// base64格式图片字符串
private Bitmap ToImage(string base64)
{
try
{
byte[] bytes = Convert.FromBase64String(base64);
MemoryStream ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
Bitmap bmp = new Bitmap(ms);
return bmp;
}
catch (Exception ex)
{
throw ex;
}
}
实现步骤:
(1)得到 模板图和融合图的图片文件路径地址;
(2)调用 生成反馈的字符串方法;
string postset = PostSend(tempImg, mergeImg);
(3)转换 将得到的json格式字符串转换成json,得到图片字符串和错误信息字符串,及其他反馈参数字符串;
JObject read = JsonConvert.DeserializeObject<JObject>(postset);
string resul = read["result"].ToString();//图片字符串
string err = read["error_message"].ToString();//错误信息
string id = read["request_id"].ToString();//所用时间
(4)调用 转换成图片的方法;
Bitmap Phon = ToImage(resul);//生成的图片
帮助文档:
Merge Face API (V1): https://console.faceplusplus.com.cn/documents/20813963
注册帮助文档:https://console.faceplusplus.com.cn/documents/5671787
人脸位置检测: https://www.faceplusplus.com.cn/face-detection/
HttpHelper4MultipartForm类参考:https://console.faceplusplus.com.cn/documents/6329752