0


Unity 百度AI实现无绿幕拍照抠像功能(详解版)

一、前言

1.抠像效果

抠像效果一般,边缘还是会生硬,然后用羽化来过渡。(如图1所示)

图1 抠像

这是网上找的图,侵权删。

2.去哪找百度ai抠图

在百度,这个功能叫人像分割,链接如下。

人像分割技术_人像分割算法_人像分割-百度AI开放平台

3.基础流程跳过

接下来注册账号之类的,咱就跳过了哈。

过程:
1.注册百度的账号

2.实名认证

3.领百度送的,对应功能的使用次数(我测试的时候送1w次,有效期一年)。

4.创建应用(这个不需要你有做好的程序,只需要走一下流程就行),这里走流程的意义就是,百度有很多功能,你需要什么功能选一下,然后我们给你一个账户和密码,这样你才能调用这些功能。

5.你就获得了一组账户和密码(如图2所示)

图2 账号和密码

api Key和Secret Key

api Key:api的英文是Application Programming Interface,应用程序编程接口的缩写,到这里就是我们获取了可以用来实现人像分割这个功能的用户名。

Secret Key:就是秘钥,上面用户名的密码。

上面两个Key,只要你自己不去更新,一般是不会改变的

但是,如果你的调用行为百度觉得很奇怪,也会暂时给你封了。

备注:百度ai有很多功能,假如百度ai是一个手机,手机里面装了很多个app,但是你登录每一个app都需要不同的用户名和密码。

这里就是,我们通过百度这个总的账号,去选择了自己想要的功能,然后百度给你生成一个用户名和密码,你通过这个可以使用你选好的功能。

二、获取AccessToken

1.什么是Token

不知道大家去过东北澡堂子没有,进去的时候,会被发一个手牌。(如图3所示)

图3 手牌

然后你只要带着这个手牌,就可以在澡堂子里吃饭,消费等,最后出来的时候,带着手牌去结账就行了。不同的手牌会有不同的权限,比如普通宾客,贵宾,vip,超级vip什么的。手牌上面还会有号码,号码就类似身份证号一样,大家都不一样,用来识别。

弄这个手牌的目的大致有两种:

1.澡堂子里有很多水,一直带着手机很麻烦,但不带手机,又会不知道你是哪一个宾客。

2.手机不能直观的看出来你是哪种宾客,但是牌子可以。

Token和这个手牌基本是一样的,你先把自己的账号和密码上交,根据你购买的套餐,会给你一串序列号,在一定的时间内,你只要提交序列号就可以用你购买的功能,百度也知道是哪个账号购买的。

2.为什么要获取Token

我们可以直接用账号和密码访问,我们也能使用token访问,但是,因为是用互联网传递消息,如果用账号和密码,那你每一次提出请求都要提供账户和密码,这样不是很安全,黑客一拦截一个准,所以,换成token,就减少了提交用户和密码的次数,就安全很多。

可以简单这么理解一下,至于细致的,大家可以自行百度,但总之,有这么一个流程。

3.如何获取token

这个可以在百度的官方文档中找到。在百度,这部分官方的名称叫做鉴权认证机制。

https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu

想看官方的可以看官方的,或者看我的也可以。

获取token需要三个东西:

1.往哪个网站发请求

(上面的官方链接里有写,往https://aip.baidubce.com/oauth/2.0/token?发请求)

2.用户名(前言里获取过)

3.密码(前言里获取过)

在代码里,我们可以先把他们写出来。

我们的目的是获取token,我们可以再建一个字符串用来接token。(如图4所示)

图4 常用字符

  1. void Start()
  2. {
  3. GetToken();
  4. }
  5. public void GetToken()
  6. {
  7. //声明一个客户端,就是自己
  8. HttpClient client = new HttpClient();
  9. //建立一个字典,把我们需要的信息都放进去
  10. //但是建立字典的类型是KeyValuePair,因为我们后面要传递数据
  11. List<KeyValuePair<String, String>> paraList = new List<KeyValuePair<string, string>>();
  12. paraList.Add(new KeyValuePair<string, string>("client_id", client_id));
  13. paraList.Add(new KeyValuePair<string, string>("client_secret", client_secret));
  14. paraList.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
  15. //这里本来要放httpContent,但是这个方法可以把
  16. //接受网络的回复 //keyValuePair转换成httpContent
  17. HttpResponseMessage response = client.PostAsync(token_url, new FormUrlEncodedContent(paraList)).Result;
  18. //最后接受回复,转换成string
  19. string resultJson = response.Content.ReadAsStringAsync().Result;
  20. //打印一下string
  21. Debug.Log(resultJson);
  22. }

打印结果为:

图5 打印结果

你会得到一大堆数据,其中有一个部分是access_token,我们要的就是这部分,这部分就是token。

4.解析json

得到数据后,这个数据是json格式的数据,如果你会解析json数据,就可以自己解决了。

如果解决不了,你也可以采用string的手法直接弄出来。

up很懒,用了一个叫LitJson.dll,里面直接有解析的方法。

  1. //先读取json格式的文本
  2. JsonReader json = new JsonReader(resultJson);
  3. //把上面读取的文本转换成可以直接调用的数据格式
  4. JsonData jsonData = JsonMapper.ToObject<JsonData>(json);
  5. //直接获取名称是access_token后面的数据
  6. jsonData["access_token"].ToString();
5.完整代码
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Net.Http;
  4. using LitJson;
  5. using UnityEngine;
  6. public class GetAccessToken : MonoBehaviour
  7. {
  8. //网址
  9. string token_url = "https://aip.baidubce.com/oauth/2.0/token?";
  10. //用户名
  11. string client_id = "填自己的id";
  12. //密码
  13. string client_secret = "填自己的密码";
  14. //token
  15. public string token;
  16. void Start()
  17. {
  18. token = GetToken();
  19. Debug.Log("获得token:" + token);
  20. }
  21. public string GetToken()
  22. {
  23. HttpClient client = new HttpClient();
  24. List<KeyValuePair<String, String>> paraList = new List<KeyValuePair<string, string>>();
  25. paraList.Add(new KeyValuePair<string, string>("client_id", client_id));
  26. paraList.Add(new KeyValuePair<string, string>("client_secret", client_secret));
  27. paraList.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
  28. HttpResponseMessage response = client.PostAsync(token_url, new FormUrlEncodedContent(paraList)).Result;
  29. string resultJson = response.Content.ReadAsStringAsync().Result;
  30. //Debug.Log(resultJson);
  31. JsonReader json = new JsonReader(resultJson);
  32. JsonData jsonData = JsonMapper.ToObject<JsonData>(json);
  33. return jsonData["access_token"].ToString();
  34. }
  35. }

三、抠像

官方文档:https://ai.baidu.com/ai-doc/BODY/Fk3cpyxua

可以自己去研究,也可以往下看我的。

首先从官方那里拿到网址:

然后准备一下中途要用的方法(这些方法均不是重点,都不讲了)

方法1:图片从Texture转换到Texture2D

  1. //Texture转Texture2D
  2. private Texture2D TextureToTexture2D(Texture texture)
  3. {
  4. width = texture.width;
  5. height = texture.height;
  6. Texture2D texture2D = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
  7. RenderTexture currentRT = RenderTexture.active;
  8. RenderTexture renderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 32);
  9. Graphics.Blit(texture, renderTexture);
  10. RenderTexture.active = renderTexture;
  11. texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
  12. texture2D.Apply();
  13. RenderTexture.active = currentRT;
  14. RenderTexture.ReleaseTemporary(renderTexture);
  15. return texture2D;
  16. }

方法2:图片从Texture2D转换为Base64

  1. //texture2D转base64
  2. public string TextureToBase64(Texture2D texture2D)
  3. {
  4. byte[] bytes;
  5. bytes = texture2D.EncodeToPNG();
  6. return Convert.ToBase64String(bytes);
  7. }

方法3:Base64转texture2D

  1. //base转texture2D
  2. public Texture2D Base64ToTexture2d(string base64)
  3. {
  4. byte[] bytes = Convert.FromBase64String(base64);
  5. Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
  6. tex.LoadImage(bytes);
  7. return tex;
  8. }
1.准备地址

在前面给出地址的基础上,加上我们前面获取的token

  1. //host地址
  2. string host = "https://aip.baidubce.com/rest/2.0/image-classify/v1/body_seg?access_token=" + token;
2.建立链接,和基本配置
  1. //设置编码格式是默认
  2. Encoding encoding = Encoding.Default;
  3. //发送请求到前面配好的网址
  4. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
  5. //这是前面要求的
  6. request.Method = "post";
  7. //一直保持链接
  8. request.KeepAlive = true;
3.图片格式转换

这里假设我们有一个RawImage,名字叫photo,最后转换成buffer

  1. //获取图片并转换成texture2D
  2. Texture2D texture2D = TextureToTexture2D(photo.texture);
  3. //再转换成base64
  4. string base64 = TextureToBase64(texture2D);
  5. //转换成url格式
  6. string str = "image=" + HttpUtility.UrlEncode(base64);
  7. //转换成byte格式
  8. byte[] buffer = encoding.GetBytes(str);
4.开始上传
  1. //获取长度
  2. request.ContentLength = buffer.Length;
  3. //发送请求
  4. request.GetRequestStream().Write(buffer, 0, buffer.Length);
5.获取回复
  1. HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  2. StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
  3. string result = reader.ReadToEnd();
  4. Debug.Log("人像分割:" + result);
6.解析json
  1. JsonReader jr = new JsonReader(result);
  2. JsonData data = JsonMapper.ToObject<JsonData>(jr);
  3. //拿到的扣好的数据
  4. string picData = data["foreground"].ToString();
  5. Texture2D tex = Base64ToTexture2d(picData);
7.纯净代码
  1. IEnumerator IBody_seg()
  2. {
  3. string host = "https://aip.baidubce.com/rest/2.0/image-classify/v1/body_seg?access_token=" + token;
  4. Encoding encoding = Encoding.Default;
  5. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
  6. request.Method = "post";
  7. request.KeepAlive = true;
  8. yield return null;
  9. Texture2D texture2D = TextureToTexture2D(photo.texture);
  10. string base64 = TextureToBase64(texture2D);
  11. string str = "image=" + HttpUtility.UrlEncode(base64);
  12. byte[] buffer = encoding.GetBytes(str);
  13. request.ContentLength = buffer.Length;
  14. request.GetRequestStream().Write(buffer, 0, buffer.Length);
  15. yield return null;
  16. HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  17. StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
  18. string result = reader.ReadToEnd();
  19. Debug.Log("人像分割:" + result);
  20. yield return null;
  21. JsonReader jr = new JsonReader(result);
  22. JsonData data = JsonMapper.ToObject<JsonData>(jr);
  23. string picData = data["foreground"].ToString();
  24. Texture2D tex = Base64ToTexture2d(picData);
  25. //如果你有个image,可以使用图片
  26. pic.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0, 0));
  27. pic2.sprite = pic.sprite;
  28. }

四、作者的碎碎念

如果有什么疑问,可以在评论区发出来讨论一下,我看见了会回复。

照片是直接在网上找的,侵权立删。

五、常见问题

1.WWWForm写法

以上写法来自百度官方写法,但还有一种常用写法叫做WWWForm,具体讲解在:

Unity C# 网络学习—WWWForm-CSDN博客

针对本篇文章,WWWForm的写法为:

由于是备用写法,就不细讲了

  1. IEnumerator IBody_seg()
  2. {
  3. //host地址
  4. string host = "https://aip.baidubce.com/rest/2.0/image-classify/v1/body_seg?access_token=" + token;
  5. Texture2D texture2D = TextureToTexture2D(photo.texture);
  6. string base64 = TextureToBase64(texture2D);
  7. //Form写法
  8. WWWForm form = new WWWForm();
  9. form.AddField("image", base64);
  10. form.AddField("type", "foreground");
  11. UnityWebRequest request = UnityWebRequest.Post(host, form);
  12. yield return request.SendWebRequest();
  13. if (request.result == UnityWebRequest.Result.ProtocolError
  14. || request.result == UnityWebRequest.Result.ConnectionError
  15. || request.result == UnityWebRequest.Result.DataProcessingError)
  16. {
  17. Debug.LogError(request.error);
  18. }
  19. else
  20. {
  21. var result = request.downloadHandler.text;
  22. resultbody = JsonUtility.FromJson<ResultBody>(result);
  23. resultbody.t2d = Base64ToTexture2d(resultbody.foreground);
  24. }
  25. }

本文转载自: https://blog.csdn.net/weixin_49427945/article/details/141752875
版权归原作者 菌菌巧乐兹 所有, 如有侵权,请联系我们删除。

“Unity 百度AI实现无绿幕拍照抠像功能(详解版)”的评论:

还没有评论