unity 手写板 截取游戏画面 识别手写文字 全家桶

11 篇文章 0 订阅

要求离线识别,不能接外网
识别手写文字分为3步

  1. 第一步,做手写板
  2. 第二步,截取游戏画面
  3. 第三步,识别

第一步,做个手写板
我们可以使用LineRender实现手写板功能
具体思想为,读取鼠标位置,当鼠标的位移超过阈值时,为LineRender添加一个新点,听起来不靠谱,但实际上效果还不错

public class DrawLine : MonoBehaviour
{
    /// <summary>
    /// 线条的父物体
    /// </summary>
    private Transform lineParent;
    /// <summary>
    /// 线的材质 修改这个材质的颜色可以更改线的颜色
    /// </summary>
    private Material lineMaterial;
    /// <summary>
    /// 画笔的宽度
    /// </summary>
    public float paintSize = 3;
    /// <summary>
    /// 如果鼠标按下
    /// </summary>
    public bool isMouseDown = false;
    /// <summary>
    /// 当前的线条绘制器
    /// </summary>
    private LineRenderer currentLine;
    /// <summary>
    /// 当前的鼠标位置
    /// </summary>
    private Vector3 currentMousePostion;
    /// <summary>
    /// 上一个鼠标的位置
    /// </summary>
    private Vector3 lastMosuePostion;
    /// <summary>
    /// 当前笔记的所有点
    /// </summary>
    private List<Vector3> points = new List<Vector3>();

    private new void Awake()
    {
        base.Awake();
        lineParent = GameObject.Find("LineParent").transform;
        lineMaterial = Resources.Load<Material>("Material/LineMaterial");
    }
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            GameObject newLine = new GameObject();
            newLine.transform.parent = lineParent;
            currentLine = newLine.AddComponent<LineRenderer>();
            currentLine.materials[0] = lineMaterial;
            currentLine.startWidth = paintSize;
            currentLine.endWidth = paintSize;
            isMouseDown = true;            
        }

        if (isMouseDown)
        {
            //当前的鼠标的位置
            currentMousePostion = Input.mousePosition;
            currentMousePostion = new Vector3(currentMousePostion.x, currentMousePostion.y, 10);
            //超出阈值,添加一个新的点,阈值自己定
            if (Vector3.Distance(currentMousePostion, lastMosuePostion) >= 1f)
            {
                AddLinePoint(currentMousePostion);
                lastMosuePostion = currentMousePostion;
            }
        }

        if (Input.GetMouseButtonUp(0))
        {
            isMouseDown = false;
            currentLine = null;
            lastMosuePostion = Vector3.zero;
            points.Clear();
        }
    }

    /// <summary>
    /// 添加线的点
    /// </summary>
    private void AddLinePoint(Vector3 pos)
    {
        points.Add(pos);
        currentLine.positionCount = points.Count;
        currentLine.SetPositions(points.ToArray());
    }
    /// <summary>
    /// 清理已经画好的点
    /// </summary>
    public void ClearLine()
    {
        for (int i = 0; i < lineParent.childCount; i++)
        {
            Destroy(lineParent.GetChild(i).gameObject);
        }
    }

}

Unity中的相关物体:所有的线条都是这个物体的子物体
在这里苏偶有的图片描述
注意,我是用的分辨率是1920*1080,使用屏幕坐标画出的线非常巨大,每个像素都是1米,所以我将主相机放在x960y540的的位置,如果你觉得太大,可以使用射线检测一个背景Plane,用射线击中点,代替屏幕坐标,可以有效降低线的尺寸
效果:
请添加图片描述


第二步 截取游戏画面
有三种不同的截图方式可供我们选择
1 使用ScreenCapture

Texture2D texture2D = ScreenCapture.CaptureScreenshotAsTexture();

但只能全屏截图,会截到不需要的UI

2使用Texture2D读取屏幕像素

        Rect rect = new Rect(0, 0, Screen.width, Screen.height );        
        Texture2D resultTexture = new Texture2D(Screen.width, Screen.height);
        resultTexture.ReadPixels(rect, 0, 0);//将自动读取屏幕上的像素
        resultTexture.Apply();

可以自定义读取的矩形区域,Rect的四个参数分别为 起始坐标X,起始坐标Y,从起始坐标向右读多少像素,从起始坐标向上读多少相许 ,例子中的起始坐标(0,0),是屏幕的左下角

但是这么做的问题在于,依然会截到UI,而且如果是多屏幕,他截取的那个屏幕不一定,根据实验应该是最后激活的屏幕

3使用相机的RenderTexture
本方法基于方法2,只不过Textrue2D不再读取屏幕像素,而是读取RenderTexture

        //声明一个临时的渲染纹理,如果不用临时纹理,也可以在Assets里新建一个RenderTexure,拖给相机的targetTexure属性,但是注意有RenderTexture的相机将不会向game窗口输出画面
        RenderTexture renderTexture = new RenderTexture(Screen.width,Screen.height,0);
        uiCamera_2.targetTexture = renderTexture;
        uiCamera_2.Render();//让相机渲染一帧
        //激活渲染纹理,Textrue2D将读取这个纹理的像素,而不读取屏幕,如果不激活,将读取屏幕
        RenderTexture.active = renderTexture; 

        Rect rect = new Rect(0, 0, Screen.width, Screen.height );        
        Texture2D resultTexture = new Texture2D(Screen.width, Screen.height);
        resultTexture.ReadPixels(rect, 0, 0);
        resultTexture.Apply();

        uiCamera_2.targetTexture = null;//将相机画面重新输出到game窗口

这么做的好处是,截到的画面只和相机拍到的画面有关,因此可以屏蔽UI,并且不受Display窗口限制


第三步,文字识别
研究了一些方法,发现只用unity是不行的(主要是因为识别文字的程序包和Untiy冲突),需要外接c#程序,我使用的是winform程序,通过Unity调用winform程序,通过udp通信完成文字识别

首先新建一个winform程序
右键解决方案,打开Nuget包管理
在这里插入图片描述
切换到浏览,搜索PaddleOCRSharp,并安装
在这里插入图片描述
引入命名空间

using PaddleOCRSharp;

编写代码

 private void GetOCR()
        {
            Bitmap bitmap = new Bitmap("d://ocr.png");
            //启动引擎  引擎启动比较耗时,可以提前启动,待程序完成再释放
            OCRModelConfig config = null;
            OCRParameter oCRParameter = new OCRParameter();
            OCRResult oCRResult = new OCRResult();
            PaddleOCREngine engine = new PaddleOCREngine(config,oCRParameter);
            
            oCRResult = engine.DetectText(bitmap);

            Console.WriteLine(oCRResult.Text);
            //释放引擎
            engine.Dispose();
			//释放图片
            bitmap.Dispose();
        }

此处只是识别演示代码,可以实现最基本的识别

注意:字不可以太大,也不可以太小,会影响结果,不能识别连笔

工程完整代码
winform已经放入untiy,Untiy工程可以直接单独运行,无需启动winform
winform程序用于展示代码

免积分下载

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 实现举手识别的具体代码,可以使用Unity和人机交互的技术来完成。下面给出一个简单的示例代码,可以作为参考: 1. 首先,在Unity中创建一个新的空物体,命名为"HandRecognition"。 2. 在"HandRecognition"物体上添加一个脚本组件,命名为"HandRecognitionScript"。 3. 在脚本中导入所需的命名空间: ``` using UnityEngine; using UnityEngine.XR.WSA.Input; ``` 4. 在脚本中声明一个手势识别器对象: ``` private GestureRecognizer gestureRecognizer; ``` 5. 在脚本的Start方法中初始化手势识别器对象,并注册识别到举手手势时的回调函数: ``` void Start() { gestureRecognizer = new GestureRecognizer(); gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap | GestureSettings.Hold | GestureSettings.HoldWithTimeout); gestureRecognizer.Tapped += OnTapped; gestureRecognizer.HoldStarted += OnHoldStarted; gestureRecognizer.HoldCompleted += OnHoldCompleted; gestureRecognizer.StartCapturingGestures(); } ``` 6. 在脚本的相应回调函数中实现举手识别的具体逻辑。例如,当识别到举起一只手时,执行相关操作: ``` private void OnHoldStarted(HoldStartedEventArgs obj) { Debug.Log("Hand raised"); // 做一些处理,比如显示相关提示或触发事件等 } ``` 以上是一个简单的实现举手识别的示例代码,可以根据具体需求进行扩展和优化。本示例使用了Unity自带的手势识别库,并通过注册回调函数来实现相应的逻辑。在实际使用过程中,还需要确保设备支持相应的手势识别功能,以及对代码进行调试、优化等工作。 ### 回答2: 要使用Unity实现举手识别,可以按照以下步骤编写代码: 首先,需要借助Unity的AR技术库或者使用第三方插件,如AR Foundation或Vuforia,来获取相机画面和处理图像。 接下来,需要使用计算机视觉算法来检测举手动作。可以使用OpenCV或其他图像处理库来进行手势检测。以下是一个简单的基于OpenCV的例子: 1. 导入OpenCV库,并初始化摄像头。 ```csharp using UnityEngine; using OpenCvSharp; public class HandDetection : MonoBehaviour { private VideoCapture capture; private Mat frame; void Start() { capture = new VideoCapture(0); // 根据实际摄像头索引进行设置 frame = new Mat(); } void Update() { capture.Read(frame); // 读取当前摄像头帧 // 对frame进行处理,例如转为灰度图、滤波等预处理 // 执行手势检测算法,例如使用肤色检测或其他方法 // 通过跟踪算法,标记手和手指的位置 // 基于手的位置,判断是否举手 // 根据识别结果进行相应逻辑处理或输出 } void OnApplicationQuit() { capture.Release(); frame.Dispose(); } } ``` 2. 在Update函数中实现手势检测的算法,如肤色检测、阈值分割等。这些算法需要根据实际需求和场景进行调整和优化。 3. 根据手的位置,可以通过追踪算法或其他手部特征提取方法获取手指的位置,并据此判断是否举手。 4. 最后,根据识别结果进行相应的逻辑处理或输出,例如显示提示信息或执行相应动作。 需要注意的是,这只是一个简单的示例代码,实际实现中可能需要更多的预处理和后处理步骤,以及对算法的优化和调整。此外,还需要综合考虑性能、灵敏度和误判率等因素,以提供更好的用户体验。 ### 回答3: 要实现Unity中的举手识别,可以使用各种技术和方法。下面是一种可能的实现方式的示例代码供参考: 1. 首先,需要使用Unity中的手势识别库,例如Leap Motion或Microsoft Kinect等。这些库可以在Unity Asset Store中获得。 2. 在场景中创建一个空对象,并将手势识别库的脚本组件添加到该对象上。 3. 在脚本中,设置一个变量来存储手势识别的结果,例如"handRaised",默认值为false。 4. 使用手势识别库的方法,检测手势是否为举手的手势。例如,在每一帧中,使用手势识别库提供的方法获取当前帧中的所有手势。然后,遍历检查是否有符合举手条件的手势。 5. 如果检测到有举手的手势,则将"handRaised"设置为true,否则设置为false。 6. 在脚本中添加一个方法,用于检查"handRaised"变量的值,并根据其结果执行相应的操作。例如,当"handRaised"为true时,可以触发某个事件或在游戏中播放相应的动画。 示例代码如下: ```csharp using UnityEngine; using System.Collections; using Leap; public class HandGestureRecognition : MonoBehaviour { private bool handRaised = false; private Controller leapController; void Start() { leapController = new Controller(); } void Update() { Frame frame = leapController.Frame(); HandList hands = frame.Hands; foreach (Hand hand in hands) { if (hand.IsRaised()) { handRaised = true; break; } } if (handRaised) { // 执行举手后的操作,例如触发事件或播放动画 } } } ``` 请注意,这只是一个简单的示例代码,实际实现中可能需要根据具体需求进行适当的修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值