要求离线识别,不能接外网 识别手写文字分为3步
- 第一步,做手写板
- 第二步,截取游戏画面
- 第三步,识别
第一步,做个手写板 我们可以使用LineRender实现手写板功能 具体思想为,读取鼠标位置,当鼠标的位移超过阈值时,为LineRender添加一个新点,听起来不靠谱,但实际上效果还不错
public class DrawLine : MonoBehaviour
{
private Transform lineParent;
private Material lineMaterial;
public float paintSize = 3;
public bool isMouseDown = false;
private LineRenderer currentLine;
private Vector3 currentMousePostion;
private Vector3 lastMosuePostion;
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();
}
}
private void AddLinePoint(Vector3 pos)
{
points.Add(pos);
currentLine.positionCount = points.Count;
currentLine.SetPositions(points.ToArray());
}
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
RenderTexture renderTexture = new RenderTexture(Screen.width,Screen.height,0);
uiCamera_2.targetTexture = renderTexture;
uiCamera_2.Render();
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;
这么做的好处是,截到的画面只和相机拍到的画面有关,因此可以屏蔽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程序用于展示代码
免积分下载 待补充
|