You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Tesseract与EmguCV(C#)的文本位置提取及标注等需求咨询

嘿,我之前在项目里刚好实现过类似的需求,用EmguCV + Tesseract完全能搞定!下面给你一套具体的分步实现方案,代码都给你写好啦:

1. 先搞定环境依赖

首先得把需要的NuGet包装上:

  • Emgu.CV(选最新稳定版,比如4.8.x或者5.x都可以)
  • Emgu.CV.runtime.windows(必须装,不然运行会报错)
  • Tesseract(NuGet上的官方包,版本选5.x,和EmguCV兼容就行)

另外还要下载Tesseract的语言训练数据,比如英文的eng.traineddata,放在项目的tessdata文件夹里,记得设置文件属性为“复制到输出目录”。

2. 核心代码实现

2.1 加载图像并初始化Tesseract引擎

先把图像加载进来,转成灰度图(Tesseract对灰度图识别效果更好),然后初始化Tesseract引擎,设置合适的页面分割模式和字符白名单:

using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Tesseract;

// 加载目标图像
var image = new Image<Bgr, byte>("your_image_path.jpg");
var grayImage = image.Convert<Gray, byte>();

// 初始化Tesseract引擎
using var engine = new TesseractEngine("./tessdata", "eng", EngineMode.Default);
// 可选:只识别字母,过滤其他字符
engine.SetVariable("tessedit_char_whitelist", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
// 自动分割页面,适合大多数通用场景
engine.DefaultPageSegMode = PageSegMode.Auto;

2.2 获取字符和单词的位置并绘制矩形

用ResultIterator遍历每个单词和字符,再调用EmguCV的矩形绘制API,给字母画蓝色框,单词画红色框:

// 把EmguCV图像转成Bitmap给Tesseract处理
using var page = engine.Process(grayImage.ToBitmap());
using var iterator = page.GetIterator();

// 遍历所有单词,绘制红色矩形
iterator.Begin();
do
{
    if (iterator.TryGetBoundingBox(PageIteratorLevel.Word, out var wordRect))
    {
        // 转换Tesseract的Rect格式到EmguCV的Rectangle
        var emguWordRect = new Rectangle(wordRect.X1, wordRect.Y1, wordRect.Width, wordRect.Height);
        // 红色框,线宽2
        CvInvoke.Rectangle(image, emguWordRect, new Bgr(0, 0, 255).MCvScalar, 2);
    }

    // 遍历当前单词内的每个字符,绘制蓝色矩形
    if (iterator.TryGetBoundingBox(PageIteratorLevel.Symbol, out _))
    {
        do
        {
            if (iterator.TryGetBoundingBox(PageIteratorLevel.Symbol, out var charRect))
            {
                var emguCharRect = new Rectangle(charRect.X1, charRect.Y1, charRect.Width, charRect.Height);
                // 蓝色框,线宽1
                CvInvoke.Rectangle(image, emguCharRect, new Bgr(255, 0, 0).MCvScalar, 1);
            }
        } while (iterator.Next(PageIteratorLevel.Symbol));
    }
} while (iterator.Next(PageIteratorLevel.Word));

2.3 获取单词的行列位置

通过判断单词的Y坐标范围区分行,同一行内按X坐标排序得到列顺序,这样就能给每个单词标记行列号,方便后续添加3D对象:

// 定义辅助类存储单词的完整信息
public class WordInfo
{
    public string Text { get; set; }
    public Rectangle Rect { get; set; }
    public int LineNumber { get; set; }
    public int ColumnNumber { get; set; }
}

// 收集所有单词的基础信息
var wordInfos = new List<WordInfo>();
iterator.Begin();
int currentLine = 0;
int prevY = -1;
// 两行之间的Y差阈值,根据图像字体大小调整
int lineThreshold = 10;

do
{
    if (iterator.TryGetBoundingBox(PageIteratorLevel.Word, out var wordRect) && iterator.GetText(PageIteratorLevel.Word) is string wordText)
    {
        // 判断是否进入新行
        if (prevY == -1 || Math.Abs(wordRect.Y1 - prevY) > lineThreshold)
        {
            currentLine++;
            prevY = wordRect.Y1;
        }

        wordInfos.Add(new WordInfo
        {
            Text = wordText,
            Rect = new Rectangle(wordRect.X1, wordRect.Y1, wordRect.Width, wordRect.Height),
            LineNumber = currentLine
        });
    }
} while (iterator.Next(PageIteratorLevel.Word));

// 按行分组,每行内按X坐标排序,设置列号
var groupedByLine = wordInfos.GroupBy(w => w.LineNumber).OrderBy(g => g.Key);
foreach (var lineGroup in groupedByLine)
{
    var sortedWords = lineGroup.OrderBy(w => w.Rect.X).ToList();
    for (int i = 0; i < sortedWords.Count; i++)
    {
        sortedWords[i].ColumnNumber = i + 1; // 列号从1开始计数
    }
}

// 现在每个单词都有明确的行列位置了,可用于3D对象定位
foreach (var wordInfo in wordInfos)
{
    Console.WriteLine($"单词: {wordInfo.Text}, 行: {wordInfo.LineNumber}, 列: {wordInfo.ColumnNumber}, 位置: {wordInfo.Rect}");
    // 这里可以根据行列号和2D坐标,转换为3D空间坐标来添加3D对象
}

2.4 显示或保存结果图像

最后把处理好的图像展示出来或者保存:

// 显示结果图像
CvInvoke.Imshow("字符&单词框选结果", image);
CvInvoke.WaitKey(0);
CvInvoke.DestroyAllWindows();

// 保存结果到本地
image.Save("result.jpg");
一些实用提示
  • 如果图像有噪声或模糊,建议先做预处理:比如阈值化CvInvoke.Threshold(grayImage, grayImage, 127, 255, ThresholdType.Binary);,或者膨胀腐蚀操作
  • 若图像是单行/单列文本,可以修改PageSegMode为SingleLine/SingleColumn,提升识别准确率
  • lineThreshold的数值要根据实际字体大小调整,字体越大阈值需要设得越高

内容的提问来源于stack exchange,提问作者soroush Saidy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:11:34