一、前言
我目前实现了拍照保存到手机的功能
我想进一步优化,实现通过手机摄像头实时识别眼前的物体,显示对应的英文的功能。
1.项目技术栈:Unity 2022.3.53+ Vuforia 11+ 百度物体识别API + 百度翻译API
2.功能目标:使用手机摄像头识别现实中的物体,返回物体识别结果和物体英文翻译,UI 实时展示翻译结果。
二、实现思路
用户点击按钮 → 截图当前画面 → 上传到百度物体识别API → 获取中文物体名称 →
调用百度翻译API → 得到英文单词 → 在 UI 显示英文 + 中文。
三、实现代码
脚本名 | 功能说明 | 应该挂在哪个物体上 |
---|---|---|
CameraCapture.cs |
采集 AR 摄像头画面截图 | ARCamera(Vuforia 默认相机) |
BaiduAuth.cs |
获取百度 Access Token | 新建空物体 BaiduManager |
BaiduObjectRecognition.cs |
调用百度物体识别 API | 和 BaiduAuth.cs 放同一个物体即可 |
BaiduTranslate.cs |
调用百度翻译 API | 也放在同一个 BaiduManager 上 |
ARRecognitionController.cs |
管理完整识别逻辑和 UI 更新 | 新建空物体挂载 RecognitionController |
1.CameraCapture.cs
using UnityEngine;
public class CameraCapture : MonoBehaviour
{
public Camera arCamera;
public Texture2D CaptureImage()
{
int captureWidth = 1280;
int captureHeight = 720;
RenderTexture rt = new RenderTexture(captureWidth, captureHeight, 24);
arCamera.targetTexture = rt;
arCamera.Render();
RenderTexture.active = rt;
Texture2D screenShot = new Texture2D(captureWidth, captureHeight, TextureFormat.RGB24, false);
screenShot.ReadPixels(new Rect(0, 0, captureWidth, captureHeight), 0, 0);
screenShot.Apply();
arCamera.targetTexture = null;
RenderTexture.active = null;
Destroy(rt);
return screenShot;
}
}
2.BaiduAuth.cs
public class BaiduAuth : MonoBehaviour
{
public string clientId = "你的API Key";
public string clientSecret = "你的Secret Key";
public string accessToken;
public IEnumerator GetAccessToken(System.Action onSuccess)
{
string url = $"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}";
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if (!www.result.Equals(UnityWebRequest.Result.ConnectionError))
{
var response = JsonUtility.FromJson<TokenResponse>(www.downloadHandler.text);
accessToken = response.access_token;
onSuccess?.Invoke();
}
else
{
Debug.LogError(www.error);
}
}
[System.Serializable]
public class TokenResponse { public string access_token; }
}
3.BaiduObjectRecognition.cs
public class BaiduObjectRecognition : MonoBehaviour
{
public string accessToken;
public IEnumerator RecognizeObject(Texture2D image, System.Action<string> callback)
{
byte[] imageData = image.EncodeToJPG();
string imageBase64 = System.Convert.ToBase64String(imageData);
string url = $"https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general?access_token={accessToken}";
WWWForm form = new WWWForm();
form.AddField("image", imageBase64);
UnityWebRequest www = UnityWebRequest.Post(url, form);
www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
yield return www.SendWebRequest();
if (!www.result.Equals(UnityWebRequest.Result.ConnectionError))
{
callback?.Invoke(www.downloadHandler.text);
}
else
{
Debug.LogError(www.error);
}
}
}
4.BaiduTranslate.cs
public class BaiduTranslate : MonoBehaviour
{
public string appId = "你的翻译AppId";
public string secretKey = "你的翻译密钥";
public IEnumerator Translate(string query, System.Action<string> callback)
{
string salt = Random.Range(10000, 99999).ToString();
string sign = GetMD5(appId + query + salt + secretKey);
string url = $"https://fanyi-api.baidu.com/api/trans/vip/translate?q={UnityWebRequest.EscapeURL(query)}&from=zh&to=en&appid={appId}&salt={salt}&sign={sign}";
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if (!www.result.Equals(UnityWebRequest.Result.ConnectionError))
{
callback?.Invoke(www.downloadHandler.text);
}
else
{
Debug.LogError(www.error);
}
}
string GetMD5(string str)
{
using var md5 = System.Security.Cryptography.MD5.Create();
byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(str);
byte[] hashBytes = md5.ComputeHash(inputBytes);
return System.BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
5.ARRecognitionController.cs
public class ARRecognitionController : MonoBehaviour
{
public CameraCapture cameraCapture;
public BaiduAuth auth;
public BaiduObjectRecognition recognition;
public BaiduTranslate translator;
public TextMeshProUGUI resultText;
public void StartRecognition()
{
StartCoroutine(auth.GetAccessToken(() =>
{
StartCoroutine(StartRecognitionFlow());
}));
}
IEnumerator StartRecognitionFlow()
{
yield return new WaitForSeconds(0.3f);
Texture2D img = cameraCapture.CaptureImage();
if (img == null)
{
resultText.text = "无法获取图像";
yield break;
}
yield return recognition.RecognizeObject(img, json =>
{
var result = JObject.Parse(json);
string chineseName = result["result"]?[0]?["keyword"]?.ToString();
if (!string.IsNullOrEmpty(chineseName))
{
StartCoroutine(translator.Translate(chineseName, transJson =>
{
var transResult = JObject.Parse(transJson);
string english = transResult["trans_result"]?[0]?["dst"]?.ToString();
resultText.text = $"{english}\n({chineseName})";
}));
}
else
{
resultText.text = "未识别出物体";
}
});
}
}
四、unity中具体实现
1.最终ARRecognitionController上的内容参考如下:
2.UI设计(最基本的就是一个按钮,一个text)
(1)TextMeshProUGUI
a.在 Canvas 里创建一个 TextMeshPro - Text
组件
命名为 ResultText
。设置一个大点的字体、颜色醒目一些。
b.在 Canvas 创建一个 Button
命名为识别按钮
按钮点击事件绑定 RecognitionController.StartRecognition()
方法。
五、最终实现效果
操作 | 系统行为 |
---|---|
用户点击“识别”按钮 | 调用百度授权,获取 access token(如果已有可跳过) |
系统截图 AR 相机当前画面 | 使用 RenderTexture 稳定截图 |
后端自动处理,上传截图至百度物体识别 API | 返回识别出的中文关键词 |
中文词汇传给翻译 API | 得到对应英文单词 |
显示 UI 结果 | “Monitor screen 显示器屏幕” 这样显示在界面上 |
六、探索过程的一些问题
整个流程的串通其实实现的比较顺利。
一直卡在拍照的图片效果不好,百度识别不出来或者不准确上。
一个就是要处理好ar camera的摄像头像素帧,之前博客提到的gallery screenshot我没有用,直接新写了脚本。
第二个报错是百度返回 access token 无效,需要每次识别前先动态获取 access token。
第三个是照片图像老是重复,每次点击时重新采图,延时 0.3 秒防止摄像头卡帧。