Unity3d C# 基于RokidAR眼镜扫码识别功能(含源码)

前言

增强现实(AR)技术正以前所未有的速度重塑我们与现实世界的交互方式,而工业、物流、医疗等专业领域对高效、无接触信息获取的需求尤为迫切。作为国内领先的AR硬件提供商,Rokid AR眼镜凭借其轻量化设计、卓越的显示效果和强大的交互能力,已成为众多企业实现智能化升级的关键工具。其中,快速、精准的扫码识别功能,作为连接物理世界与数字信息的核心桥梁,在资产追踪、设备点检、仓库管理、生产流程控制等场景中扮演着至关重要的角色。

为了助力开发者更高效地将这一核心能力集成到基于Unity引擎构建的Rokid AR应用中,我们特别整理并开源了这份Unity3D C# 实现的Rokid AR眼镜扫码识别功能解决方案。本资源不仅提供了清晰的技术实现思路,更包含了可直接集成、修改的完整C#源代码。

效果

在这里插入图片描述

关注并私信 RokidAR QRScan 免费获取体验安装包(底部公众号)。

实现过程

核心实现原理,是抓取眼镜端的摄像头画面,先通过CameraPreview类,在Unity中获取到眼镜摄像头的预览画面,再通过ZXing插件的解析出码的内容,识别到特定的内容做特定的处理逻辑。

UI搭建

基础的是一个预览画面+识别结果和扫描按钮:
在这里插入图片描述

开发

这里就不用自己启动摄像头截取画面,而是采用SDK内自带的CameraPreview类,它主要用于在Unity中实现摄像头预览功能。它依赖于设备原生接口(通过NativeInterface.NativeAPI)来获取摄像头数据,并将其实时显示在UI的RawImage组件上。通过一下代码可以获取画面(Texture2D):

CameraPreview.instance.previewTex;

完整的CameraPreview代码如下:

using UnityEngine;
using UnityEngine.UI;
using Rokid.UXR.Native;
using Rokid.UXR.Utility;

namespace Rokid.UXR.Demo
{
    public class CameraPreview : MonoBehaviour
    {
        private bool isInit;
        public Texture2D previewTex;
        public RawImage rawImage;
        private int width, height;

        public static CameraPreview instance = null;
        public void Init()
        {
            width = NativeInterface.NativeAPI.GetPreviewWidth();
            height = NativeInterface.NativeAPI.GetPreviewHeight();
            if (NativeInterface.NativeAPI.GetGlassName().Equals("Rokid Max Plus"))
            {
                NativeInterface.NativeAPI.SetCameraPreviewDataType(3);
                previewTex = new Texture2D(width, height, TextureFormat.RGBA32, false);
            }
            else
            {
                NativeInterface.NativeAPI.SetCameraPreviewDataType(1);
                previewTex = new Texture2D(width, height, TextureFormat.BGRA32, false);
            }
            NativeInterface.NativeAPI.OnCameraDataUpdate += OnCameraDataUpdate;
            isInit = true;
        }

        private void OnCameraDataUpdate(int width, int height, byte[] data, long timestamp)
        {
            Loom.QueueOnMainThread(() =>
            {
                previewTex.LoadRawTextureData(data);
                previewTex.Apply();
                rawImage.texture = previewTex;
            });
        }

        private void Awake()
        {
#if !UNITY_EDITOR
	        rawImage.color = Color.white;
#endif
            if (instance == null)
                instance = this;
            NativeInterface.NativeAPI.StartCameraPreview();
        }

        public void Release()
        {
            if (isInit)
            {
                NativeInterface.NativeAPI.OnCameraDataUpdate -= OnCameraDataUpdate;
                NativeInterface.NativeAPI.StopCameraPreview();
                NativeInterface.NativeAPI.ClearCameraDataUpdate();
                isInit = false;
            }
        }

        private void OnDestroy()
        {
            Release();
        }

        private void OnApplicationPause(bool pauseStatus)
        {
            if (pauseStatus)
            {
                Release();
            }
            else
            {
                NativeInterface.NativeAPI.StartCameraPreview();
            }
        }

        private void Update()
        {
            if (isInit == false && NativeInterface.NativeAPI.IsPreviewing())
            {
                Init();
            }
        }
    }
}

扫描启动代码:

 if (cor != null) return;     
barcodeReader = new BarcodeReader {
    Options = new DecodingOptions { TryHarder = true },
        AutoRotate = true
    };
    
    isScan = true;
ScanObj?.SetActive(true);
cor = StartCoroutine(ScanQRCode()); 

这里的启动有防止重复启动处理,初始化ZXing解码器(启用增强识别和自动旋转),并启动识别协程。

二维码识别核心的处理如下:

 try {
            Texture2D previewTex = CameraPreview.instance.previewTex;
            Color32[] pixels = previewTex.GetPixels32();
            
            Result result = barcodeReader.Decode(
                pixels, 
                previewTex.width, 
                previewTex.height
            );

            if (result != null) {
                resultText.text = "扫描结果: " + result.Text;                 
                if (result.Text == "特定内容") { 
                    resultText.text = "识别结果: " + result.Text;
                    StopScan(); 
                    yield break; 
                }
            }
        } catch (System.Exception ex) {
            Debug.LogError(ex.Message);
        }

从CameraPreview(外部摄像头管理模块)获取一帧图像,解码流程:获取纹理像素数据(GetPixels32())和调用ZXing的Decode()方法解析二维码。
停止识别扫描:

public void StopScan() {
    if (cor != null) StopCoroutine(cor);
    ScanObj?.SetActive(false); // 隐藏扫描UI
    isScan = false;
    cor = null;
}

其他尝试

上面的开发实现过程是最终的版本,不过在开发过程中,踩过了很多的坑,这里也分享出来。

刚开始时,是打算直接启动摄像头来通过webCamTexture截取画面像素进识别:

    void FixedUpdate()
    {
        if (isScan && webCamTexture.isPlaying && webCamTexture.didUpdateThisFrame)
        {
            // 获取摄像头图像数据
            Color32[] color32 = webCamTexture.GetPixels32();
            var result = barcodeReader.Decode(color32, webCamTexture.width, webCamTexture.height);

            if (result != null)
            {
                // Debug.Log("识别结果: " + result.Text);
                resultText.text = "识别结果: " + result.Text;
                // 在这里处理识别结果,例如触发事件或更新UI
                //webCamTexture.Stop(); // 可选:识别成功后停止摄像头
            }
        }
}

但运行起来效果不理想,而且画面有卡顿问题。

还尝试了异步GPU读取、多线程解码、帧同步和主线程调度的处理:

IEnumerator ScanQRCode()
        {
            while (true)
            {
                yield return new WaitForEndOfFrame();
                if (CameraPreview.instance?.previewTex == null) continue;

                System.Threading.ManualResetEvent threadSignal = null;
                System.Exception threadException = null;
                bool success = false;

                try
                {

                    var pixelArray = CameraPreview.instance.previewTex.GetPixels32();
                    int width = CameraPreview.instance.previewTex.width;
                    int height = CameraPreview.instance.previewTex.height;

                    threadSignal = new System.Threading.ManualResetEvent(false);
                    threadException = null;
                    success = false;

                    ThreadPool.QueueUserWorkItem(_ =>
                    {
                        try
                        {
                            Result result = barcodeReader.Decode(pixelArray, width, height);
                            if (result?.Text == "rokidar.shaxiguzheng.com")
                            {
                                success = true;
                            }
                        }
                        catch (System.Exception ex)
                        {
                            threadException = ex;
                        }
                        finally
                        {
                            threadSignal.Set();
                        }
                    });
                }
                catch (System.Exception ex)
                {
                    Debug.LogError(ex);
                    continue; 
                }

               if (threadSignal == null) continue;

              while (!threadSignal.WaitOne(0))
                {
                    yield return null;
                }
                try
                {
                    if (threadException != null)
                    {
                        throw threadException;
                    }

                    if (success)
                    {
                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
                        {
                            StopScan();
                        });
                        yield break;
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.LogError(ex);
                }
            }
        }

不过最终因为插件依赖和平台兼容问题,没有采用此方法。

源码

https://download.csdn.net/download/qq_33789001/91579018

关注并私信 RokidAR QRScan 免费获取体验安装包(底部公众号)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十幺卜入

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值