Unity 3D中Tesseract导入异常致OCR调用报错的解决求助
Unity中Tesseract OCR调用异常的解决思路
问题概述
通过Visual Studio NuGet安装Tesseract后,将相关文件复制到Unity项目Assets/Plugins目录,并放置eng.traineddata资源,运行拍照识别代码时抛出异常:
OCR Error: Exception has been thrown by the target of an invocation.
原示例代码
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; using Newtonsoft.Json.Linq; using Tesseract; public class CaptureCards : MonoBehaviour { public RawImage cameraFeed; public Button captureButton; public Text cardDetailsText; private WebCamTexture webCamTexture; private HttpClient httpClient; void Start() { StartCameraFeed(); httpClient = new HttpClient(); captureButton.onClick.AddListener(CaptureAndProcess); } void StartCameraFeed() { WebCamDevice[] devices = WebCamTexture.devices; if (devices.Length == 0) { Debug.LogError("No camera devices found."); return; } Debug.Log("Available cameras:"); foreach (var device in devices) { Debug.Log(device.name); } string selectedCamera = devices[0].name; Debug.Log("Using camera: " + selectedCamera); webCamTexture = new WebCamTexture(selectedCamera); cameraFeed.texture = webCamTexture; cameraFeed.material.mainTexture = webCamTexture; webCamTexture.Play(); } async void CaptureAndProcess() { if (webCamTexture == null || !webCamTexture.isPlaying) { cardDetailsText.text = "Camera feed not available."; return; } Texture2D captureImage = new Texture2D(webCamTexture.width, webCamTexture.height); captureImage.SetPixels(webCamTexture.GetPixels()); captureImage.Apply(); string tempPath = Path.Combine(Application.persistentDataPath, "captureCard.png"); Texture2D preprocessedImage = PreprocessImage(captureImage); File.WriteAllBytes(tempPath, preprocessedImage.EncodeToPNG()); Debug.Log("Image saved at: " + tempPath); string cardName = await RecognizeText(tempPath); if (!string.IsNullOrEmpty(cardName)) { JObject cardData = await FetchCardDetails(cardName); if (cardData != null) { DisplayCardDetails(cardData); SaveCardDetails(cardData); } else { cardDetailsText.text = "Card not Found"; } } else { cardDetailsText.text = "Failed to recognize card name"; } } Texture2D PreprocessImage(Texture2D original) { int targetWidth = 800; int targetHeight = (int)(original.height * (800.0f / original.width)); Texture2D resizedImage = new Texture2D(targetWidth, targetHeight); for (int y = 0; y < targetHeight; y++) { for (int x = 0; x < targetWidth; x++) { Color pixel = original.GetPixelBilinear((float)x / targetWidth, (float)y / targetHeight); resizedImage.SetPixel(x, y, pixel); } } resizedImage.Apply(); Color[] pixels = resizedImage.GetPixels(); for (int i = 0; i < pixels.Length; i++) { float gray = pixels[i].grayscale; pixels[i] = new Color(gray, gray, gray); } resizedImage.SetPixels(pixels); resizedImage.Apply(); return resizedImage; } async Task<string> RecognizeText(string imagePath) { string resultText = string.Empty; string tessDataPath = Path.Combine(Application.streamingAssetsPath, "tessdata"); Debug.Log($"StreamingAssets Path: {Application.streamingAssetsPath}"); Debug.Log($"File Exists: {File.Exists(Path.Combine(Application.streamingAssetsPath, "tessdata", "eng.traineddata"))}"); await Task.Run(() => { try { if (!Directory.Exists(tessDataPath)) throw new DirectoryNotFoundException($"Tesseract data directory not found: {tessDataPath}"); if (!File.Exists(Path.Combine(tessDataPath, "eng.traineddata"))) throw new FileNotFoundException("Tesseract trained data file not found (eng.traineddata)."); if (!File.Exists(imagePath)) throw new FileNotFoundException($"Image file not found: {imagePath}"); using (var engine = new TesseractEngine(tessDataPath, "eng", EngineMode.Default)) { using (var img = Pix.LoadFromFile(imagePath)) { using (var page = engine.Process(img, PageSegMode.SingleBlock)) { resultText = page.GetText().Trim(); } } } } catch (Exception ex) { Debug.LogError($"OCR Error: {ex.Message}\n{ex.StackTrace}"); } }); return resultText; } async Task<JObject> FetchCardDetails(string cardName) { try { string apiUrl = $"https://api.scryfall.com/cards/named?exact={UnityWebRequest.EscapeURL(cardName)}"; string response = await httpClient.GetStringAsync(apiUrl); return JObject.Parse(response); } catch { return null; } } void DisplayCardDetails(JObject cardData) { string name = cardData["name"].ToString(); string type = cardData["type_line"].ToString(); string manaCost = cardData["mana_cost"].ToString(); string description = cardData["oracle_text"].ToString(); cardDetailsText.text = $"Name: {name}\nType: {type}\nMana Cost: {manaCost}\nDescription: {description}"; } void SaveCardDetails(JObject cardData) { string savePath = Path.Combine(Application.persistentDataPath, "savedCards.json"); JArray savedCards = File.Exists(savePath) ? JArray.Parse(File.ReadAllText(savePath)) : new JArray(); savedCards.Add(cardData); File.WriteAllText(savePath, savedCards.ToString()); } void OnDestroy() { webCamTexture.Stop(); httpClient.Dispose(); } }
解决步骤
1. 修正Tesseract库导入方式
- 避免直接从NuGet复制文件,Unity对.NET运行时环境有特定要求,建议使用Unity兼容的预编译Tesseract版本,或者通过Unity Package Manager安装适配包
- 手动导入时,需将库文件放置到对应平台目录(如
Plugins/x86_64、Plugins/Android),并在Inspector面板设置正确的平台兼容选项(取消勾选无关平台) - 删除NuGet导入的冗余依赖文件,避免版本冲突
2. 修复tessdata路径与访问权限
- 移动端无法直接通过
File类访问StreamingAssets目录,需先将eng.traineddata复制到persistentDataPath再使用,修改RecognizeText方法:async Task<string> RecognizeText(string imagePath) { string resultText = string.Empty; string targetTessPath = Path.Combine(Application.persistentDataPath, "tessdata"); string trainedDataPath = Path.Combine(targetTessPath, "eng.traineddata"); // 复制tessdata到持久化目录 if (!Directory.Exists(targetTessPath)) { Directory.CreateDirectory(targetTessPath); string sourcePath = Path.Combine(Application.streamingAssetsPath, "tessdata/eng.traineddata"); #if UNITY_EDITOR || UNITY_STANDALONE File.Copy(sourcePath, trainedDataPath, overwrite: true); #else UnityWebRequest request = UnityWebRequest.Get(sourcePath); await request.SendWebRequest(); File.WriteAllBytes(trainedDataPath, request.downloadHandler.data); #endif } await Task.Run(() => { try { using (var engine = new TesseractEngine(targetTessPath, "eng", EngineMode.Default)) { using (var img = Pix.LoadFromFile(imagePath)) { using (var page = engine.Process(img, PageSegMode.SingleBlock)) { resultText = page.GetText().Trim(); } } } } catch (TargetInvocationException ex) { Debug.LogError($"OCR Inner Error: {ex.InnerException.Message}\n{ex.InnerException.StackTrace}"); } catch (Exception ex) { Debug.LogError($"OCR Error: {ex.Message}\n{ex.StackTrace}"); } }); return resultText; }
3. 捕获深层异常信息
- 原异常是包装后的
TargetInvocationException,需捕获内部异常才能看到真实错误原因(如库文件缺失、架构不兼容),在catch块中添加专门处理逻辑
4. 优化图像预处理
- 在灰度化基础上添加二值化处理,强化文本与背景对比度,提升OCR准确率:
// 替换原灰度化代码 float threshold = 0.6f; for (int i = 0; i < pixels.Length; i++) { float gray = pixels[i].grayscale; pixels[i] = gray >= threshold ? Color.white : Color.black; }
内容的提问来源于stack exchange,提问作者Kayle Brown
相关产品推荐
相关产品推荐

