Skip to content

识别错误 乱码 #178

@anan1213095357

Description

@anan1213095357

Describe the bug

`using BlazorBot.Core;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
// 👇 引入 PaddleOCR 相关命名空间
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleOCR.Models.Local;

namespace BlazorBot.Funcs
{
[FunctionalDescription("提供基于摄像头的文字识别(OCR)能力,支持读取文档、标签、屏幕截图中的文字内容")]
public class OcrVision : BaseSkillSet
{
// 这里使用 LocalFullModels.ChineseV3,它是目前效果最好的轻量级中文模型之一
private readonly FullOcrModel _ocrModel = LocalFullModels.ChineseV3;

    [KernelFunction("识别图片中的所有文字信息。支持传入本地路径或网络URL,如果不传路径,将自动调用摄像头拍照识别。")]
    public async Task<string> RecognizeText(
        [KernelParam("待识别的图片路径(可以是本地文件路径,也可以是 http 开头的网络 URL)")] string imageSource = "")
    {
        try
        {
            Mat srcImage = null;
            string sourceDescription = "";

            // =========================================================================
            // 1. 获取图片源 (网络URL / 本地路径 / 摄像头)
            // =========================================================================
            if (!string.IsNullOrWhiteSpace(imageSource) && (imageSource.StartsWith("http://") || imageSource.StartsWith("https://")))
            {
                // A. 网络图片模式
                Console.WriteLine($"🌐 正在下载网络图片: {imageSource}");
                using (HttpClient http = new HttpClient())
                {
                    byte[] imageData = await http.GetByteArrayAsync(imageSource);
                    srcImage = Cv2.ImDecode(imageData, ImreadModes.Color);
                    sourceDescription = "网络图片";
                }
            }
            else if (!string.IsNullOrWhiteSpace(imageSource) && File.Exists(imageSource))
            {
                // B. 本地图片模式
                Console.WriteLine($"📂 正在加载本地图片: {imageSource}");
                srcImage = Cv2.ImRead(imageSource, ImreadModes.Color);
                sourceDescription = "本地文件";
            }
            else
            {
                // C. 摄像头模式 (当路径为空或文件不存在时)
                Console.WriteLine("📷 未提供有效图片,正在打开摄像头拍摄当前画面...");
                string capturedPath = await CaptureImageFromCameraAsync("OcrScan");

                if (string.IsNullOrEmpty(capturedPath))
                {
                    return "❌ OCR 失败:无法获取图片或摄像头画面。";
                }

                srcImage = Cv2.ImRead(capturedPath, ImreadModes.Color);
                sourceDescription = "摄像头拍摄";
            }

            if (srcImage == null || srcImage.Empty())
            {
                return "❌ 图片加载失败,数据为空。";
            }

            // =========================================================================
            // 2. 初始化 PaddleOCR 引擎并执行推理
            // =========================================================================

            Console.WriteLine("🧠 正在加载 PaddleOCR 模型并提取文字...");

            // 使用 Mkldnn 加速 (CPU)
            using var all = new PaddleOcrAll(_ocrModel, PaddleDevice.Mkldnn())
            {
                AllowRotateDetection = true,      // 允许识别有角度的文字
                Enable180Classification = false,  // 是否检测文字翻转
            };

            // 执行识别
            // 注意:PaddleOCR 也是计算密集型,建议放入 Task.Run
            PaddleOcrResult result = await Task.Run(() => all.Run(srcImage));

            // 释放图片资源
            srcImage.Dispose();

            if (result == null || result.Regions.Count() == 0)
            {
                return "👀 OCR 完成,但图片中未检测到任何可读文字。";
            }

            // =========================================================================
            // 3. 数据解析与文本/JSON混合返回
            // =========================================================================
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"✅ OCR 识别完成 ({sourceDescription})!共发现 {result.Regions.Count()} 处文本区域。");
            sb.AppendLine("---");

            // 3.1 生成自然语言摘要 (直接拼接文字,方便 LLM 阅读语意)
            sb.AppendLine("📄 **识别到的完整文本内容**:");
            sb.AppendLine("```text");
            sb.AppendLine(result.Text);
            sb.AppendLine("```");
            sb.AppendLine("---");

            // 3.2 生成结构化 JSON 数据 (包含位置信息和置信度)
            // 这对于 LLM 需要知道“文字在哪里”或者“是标题还是正文”时非常有用
            sb.AppendLine("🎯 **详细数据清单** (JSON格式,含坐标与置信度):");
            sb.AppendLine("```json");
            sb.AppendLine("[");

            var regions = result.Regions.ToList();
            for (int i = 0; i < regions.Count; i++)
            {
                var r = regions[i];
                // 简化坐标:将旋转矩形转换为中心点和宽高,保留2位小数
                string jsonLine = $@"  {{ ""id"": {i + 1}, ""text"": ""{JsonEscape(r.Text)}"", ""score"": {r.Score:F2}, ""center"": {{ ""x"": {r.Rect.Center.X:F0}, ""y"": {r.Rect.Center.Y:F0} }}, ""size"": {{ ""w"": {r.Rect.Size.Width:F0}, ""h"": {r.Rect.Size.Height:F0} }} }}";

                sb.Append(jsonLine);
                if (i < regions.Count - 1) sb.Append(",");
                sb.AppendLine();
            }

            sb.AppendLine("]");
            sb.AppendLine("```");

            return sb.ToString();
        }
        catch (Exception ex)
        {
            return $"❌ OCR 识别过程发生异常: {ex.Message} \n {ex.StackTrace}";
        }
    }

    // =========================================================================
    // 🛠️ 辅助方法:JSON 字符转义
    // =========================================================================
    private string JsonEscape(string original)
    {
        if (string.IsNullOrEmpty(original)) return "";
        return original
            .Replace("\\", "\\\\")
            .Replace("\"", "\\\"")
            .Replace("\n", "\\n")
            .Replace("\r", "\\r")
            .Replace("\t", "\\t");
    }

    // =========================================================================
    // 🛠️ 辅助方法:跨平台摄像头拍照 (复用自 YoloVision)
    // =========================================================================
    private Task<string> CaptureImageFromCameraAsync(string prefix)
    {
        return Task.Run(() =>
        {
            try
            {
                // 0 号摄像头通常是默认摄像头
                using var capture = new VideoCapture(0);
                if (!capture.IsOpened())
                {
                    Console.WriteLine("⚠️ 无法连接到摄像头。");
                    return string.Empty;
                }

                using var frame = new Mat();
                // 预热几帧,让摄像头自动对焦和调整白平衡
                for (int i = 0; i < 5; i++) capture.Read(frame);
                capture.Read(frame);

                if (frame.Empty())
                {
                    Console.WriteLine("⚠️ 摄像头捕获到了空帧。");
                    return string.Empty;
                }

                string folderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "OcrCaptures");
                if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);

                string fileName = $"{prefix}_{DateTime.Now:yyyyMMddHHmmss}.jpg";
                string fullPath = Path.Combine(folderPath, fileName);

                // 保存图片以便后续读取
                Cv2.ImWrite(fullPath, frame);
                return fullPath;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"📸 摄像头调用异常: {ex.Message}");
                return string.Empty;
            }
        });
    }
}

}`

Steps to reproduce the bug

代码执行到 PaddleOcrResult result = await Task.Run(() => all.Run(srcImage)); 这行时候 直接 看到的返回结果是 乱码

Expected behavior

No response

Screenshots

No response

Release version

No response

IDE

No response

OS version

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions