简介:本资源是一套基于C#实现的离线OCR文字识别完整项目,面向Windows桌面应用开发者及图像处理初学者,解决图片中文字内容本地化、免网络依赖的自动提取需求,适用于发票识别、文档数字化、纸质资料归档等实际场景。压缩包共62个文件,含14个核心DLL库(支撑OCR引擎调用与图像处理)、12个XML配置与文档说明、6个关键CS源码文件(涵盖窗体逻辑、图像预处理、Tesseract集成与结果导出),以及EXE可执行程序、CSProj工程文件和NuGet包依赖项,整体大小为4.31MB。已有4523人学习下载,提供开箱即用的可视化界面与清晰分层代码结构——包括Form1.cs主交互逻辑、Program.cs入口控制、Properties资源配置及packages.config依赖管理,便于快速理解OCR流程:图像加载→灰度二值化预处理→多语言文本识别→结果校验与TXT输出,是掌握C#与开源OCR技术协同开发的实用入门范例。
1. 项目缘起:为什么我们需要一个离线的OCR工具?
最近在做一个内部工具,需要从大量的产品截图、扫描文档里自动提取文字信息。一开始图省事,直接调用了某云的在线OCR接口,跑起来确实方便,一个API调用就完事了。但很快就遇到了几个头疼的问题:一是网络依赖,内网环境或者网络波动的时候,整个流程就卡住了;二是数据安全,有些图片涉及内部信息,上传到第三方服务总让人心里不踏实;三是成本,量稍微大一点,API调用费用就上来了,对于长期运行的工具来说是个不小的负担。
于是,把目光投向了离线OCR方案。离线意味着部署一次,终身免费(不考虑电费),数据不出本地,完全可控。在.NET生态里,C#是我们的主力语言,所以目标很明确:用C#实现一个高性能、易集成的离线图片文字识别工具。经过一番调研和折腾,最终基于Tesseract引擎和AForge.NET(处理图像预处理)搞定了这个工具。今天就把整个实现思路、关键代码、以及踩过的那些坑,毫无保留地分享出来。如果你也在为C#环境下的本地文字识别发愁,这篇内容应该能帮你省下不少时间。
2. 核心武器库:Tesseract与AForge.NET的选型与部署
实现离线OCR,核心是OCR引擎。在C#/.NET的世界里,Tesseract几乎是开源离线OCR的不二之选。它由Google赞助开发,识别精度高,支持多种语言,并且有一个活跃的社区维护的C#封装库——Tesseract.NET SDK。另一个关键组件是AForge.NET(以及它的继任者Accord.NET),它是一个强大的计算机视觉和图像处理库,我们将用它来对图片进行预处理,这是提升Tesseract识别率的关键一步。
2.1 Tesseract引擎的本地化部署
Tesseract本身是一个用C++编写的命令行工具。在C#中使用,我们需要两样东西:引擎的核心库(.dll或.exe)和语言训练数据文件(.traineddata)。
1. 获取Tesseract引擎:对于Windows平台,最省事的方法是直接下载编译好的可执行文件和库。你可以从GitHub上的tesseract-ocr/tesseract官方仓库的Release页面,找到对应版本的Windows安装包(例如tesseract-ocr-w64-setup-5.3.3.20231005.exe)。安装后,你会得到tesseract.exe和相关的动态链接库。
但是,对于集成到C#项目里,我们更常用的是Tesseract.NET SDK这个NuGet包。它会处理与本地Tesseract库的交互。不过,这个包本身不包含引擎本体。因此,我们需要手动将Tesseract的库文件(主要是liblept-5.dll和libtesseract-5.dll)以及tessdata文件夹(存放语言包)放到我们程序的运行目录下,或者指定其路径。
一个更清晰的部署结构建议如下:
你的项目根目录/ ├── YourApp.exe ├── x64/ (或 x86/) │ ├── liblept-5.dll │ └── libtesseract-5.dll └── tessdata/ ├── eng.traineddata ├── chi_sim.traineddata (简体中文) └── chi_tra.traineddata (繁体中文)Tesseract.NET SDK会根据当前进程的位数(32位或64位)自动去对应的x86或x64子目录加载DLL。
2. 获取语言数据文件:这是识别准确度的基础。你需要从Tesseract的官方GitHub仓库(tesseract-ocr/tessdata或tesseract-ocr/tessdata_best)下载所需的.traineddata文件。对于中文识别,chi_sim(简体)和chi_tra(繁体)是必须的。tessdata_best里的模型通常精度更高,但体积也更大。对于大多数场景,标准版tessdata已经足够。
注意:国内下载GitHub资源可能较慢。你可以搜索“tesseract ocr 引擎国内镜像”来寻找更快的下载源,或者使用一些包管理器的镜像。确保下载的版本与你的Tesseract引擎版本大致匹配(主版本号相同即可,如5.x)。
3. 在C#项目中集成:在Visual Studio中,通过NuGet包管理器安装Tesseract包(作者:Charles Weld)。这是目前最流行和维护较好的封装。
Install-Package Tesseract安装后,你的项目引用中会增加Tesseract。
2.2 AForge.NET/Accord.NET用于图像预处理
原始图片直接扔给Tesseract,识别效果往往很差,尤其是对于背景复杂、光照不均、有噪声的图片。图像预处理的目的就是“净化”图片,让文字区域更突出,背景更干净,从而大幅提升OCR的识别率。
AForge.NET是一个经典的.NET图像处理库,虽然其核心开发已放缓,但其AForge.Imaging等模块依然稳定好用。而Accord.NET是在其基础上发展起来的更现代、功能更全面的框架,包含了AForge的许多功能并进行了扩展。
对于我们的OCR预处理任务,AForge.Imaging提供的功能已经足够。我们主要通过NuGet安装:
Install-Package AForge Install-Package AForge.Imaging如果希望使用更丰富的图像处理滤镜和机器学习功能,可以考虑安装Accord.Imaging等包。
预处理流程通常包括:灰度化、二值化、降噪、倾斜校正等。AForge.NET提供了丰富的滤镜类来轻松完成这些操作。
3. 从图片到文字:完整的识别流程拆解与实现
有了武器,我们来设计战斗流程。一个健壮的离线OCR工具,其核心流程可以概括为:加载图片 -> 图像预处理 -> 调用Tesseract识别 -> 输出文本。下面我们分步详解,并附上关键代码。
3.1 第一步:加载与解码图片
C#本身提供了System.Drawing来操作图片,但在跨平台和性能上有所局限。AForge.NET使用System.Drawing作为底层,所以我们暂时用它。确保项目引用了System.Drawing.CommonNuGet包(对于.NET Core/.NET 5+项目)。
using System.Drawing; using AForge.Imaging; public Bitmap LoadImage(string imagePath) { // 使用System.Drawing加载图片 Bitmap originalImage; try { originalImage = new Bitmap(imagePath); } catch (Exception ex) { throw new ArgumentException($"无法加载图片 '{imagePath}'。请检查文件路径和格式。", ex); } return originalImage; }这里直接返回了Bitmap对象,它是后续AForge.NET处理的常用格式。
3.2 第二步:至关重要的图像预处理
这是提升识别率的“魔法”环节。不同的图片需要不同的预处理组合,一个通用的强力流程如下:
1. 灰度化 (Grayscale):将彩色图转为灰度图,减少计算量,很多图像处理算法基于灰度。
public Bitmap ConvertToGrayscale(Bitmap originalImage) { // 使用AForge的灰度化滤镜 Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721); // BT709标准,符合人眼感知 Bitmap grayImage = filter.Apply(originalImage); // 注意:originalImage如果后续不再使用,应考虑释放(Dispose) return grayImage; }2. 二值化 (Thresholding):将灰度图转为黑白图,让文字(前景)和背景彻底分离。这是最关键的一步。简单的全局阈值可能不适用所有情况。我们可以尝试自适应阈值或大津法(Otsu)。
public Bitmap ApplyOtsuThreshold(Bitmap grayImage) { // OtsuThreshold 是AForge.Imaging.Filters里的一个滤镜 OtsuThreshold filter = new OtsuThreshold(); // Apply方法会直接修改传入的图片,如果想保留原图,可以先Clone一份 Bitmap binaryImage = AForge.Imaging.Image.Clone(grayImage); // 克隆 filter.ApplyInPlace(binaryImage); // 就地处理 return binaryImage; }如果图片光照不均,AdaptiveThresholdFilter(自适应阈值)可能效果更好,但它计算量更大。
3. 降噪 (Noise Reduction):去除二值化后图片中的孤立噪点。
public Bitmap RemoveNoise(Bitmap binaryImage) { // 使用中值滤波或腐蚀/膨胀等形态学操作 // 例如,一个简单的开运算(先腐蚀后膨胀)可以去除小白点 // AForge.Imaging.Filters.Morphology类提供了相关操作,但需要自己定义结构元素 // 这里用一个简单的滤波替代 Median filter = new Median(); // 中值滤波,对椒盐噪声效果好 // Median滤镜默认处理灰度图,对二值图也有效果 filter.ApplyInPlace(binaryImage); return binaryImage; }对于扫描文档,可能还需要倾斜校正(Deskew)。AForge.Imaging提供了DocumentSkewChecker类来检测倾斜角度,然后通过旋转进行校正。这部分逻辑稍复杂,但对于扫描件至关重要。
一个整合的预处理函数可能长这样:
public Bitmap PreprocessImage(Bitmap originalImage) { Bitmap processedImage = originalImage; // 1. 转为灰度图 processedImage = ConvertToGrayscale(processedImage); // 2. 尝试自动对比度拉伸,增强文字与背景的对比度(可选,对低对比度图片有效) ContrastStretch contrastFilter = new ContrastStretch(); contrastFilter.ApplyInPlace(processedImage); // 3. 使用大津法二值化 processedImage = ApplyOtsuThreshold(processedImage); // 4. 降噪 processedImage = RemoveNoise(processedImage); // 5. 可以考虑进行一次膨胀操作,让断开的笔画连接(针对某些字体) // Dilatation dilationFilter = new Dilatation(); // dilationFilter.ApplyInPlace(processedImage); return processedImage; }实操心得:预处理没有“银弹”。最好的方法是准备一批有代表性的测试图片,用不同的预处理组合进行试验,观察哪种组合对你这批图片的最终识别效果提升最大。可以写一个简单的测试程序来批量处理并对比结果。
3.3 第三步:调用Tesseract进行OCR识别
预处理后的Bitmap需要交给Tesseract。Tesseract.NET SDK的主要入口是TesseractEngine类。
using Tesseract; using System.Drawing.Imaging; using System.IO; public string PerformOcr(Bitmap processedImage, string language = "eng", string tessDataPath = @".\tessdata") { string resultText = string.Empty; // 检查语言数据路径是否存在 if (!Directory.Exists(tessDataPath)) { throw new DirectoryNotFoundException($"Tesseract语言数据目录未找到: {tessDataPath}。请确保已将'tessdata'文件夹(包含{language}.traineddata)放置于正确位置。"); } // 将Bitmap转换为Tesseract可接受的Pix格式 Pix pixImage; using (MemoryStream ms = new MemoryStream()) { processedImage.Save(ms, ImageFormat.Png); // 保存为PNG格式流 ms.Position = 0; pixImage = Pix.LoadFromMemory(ms.ToArray()); } using (var engine = new TesseractEngine(tessDataPath, language, EngineMode.Default)) { // 可以设置一些引擎参数,例如: // engine.SetVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); // 只识别特定字符 // engine.SetVariable("preserve_interword_spaces", "1"); // 保留单词间空格 using (var page = engine.Process(pixImage)) { resultText = page.GetText(); // 你还可以获取置信度、单词位置等信息 // var confidence = page.GetMeanConfidence(); // using (var iter = page.GetIterator()) // { ... 遍历单词 } } } // 释放资源 pixImage.Dispose(); // processedImage的释放由调用者负责 return resultText.Trim(); // 返回去除首尾空格的文本 }关键点解析:
TesseractEngine构造:第一个参数是tessdata文件夹的路径,第二个是语言代码(如"eng"、"chi_sim"),第三个是引擎模式。EngineMode.Default是平衡速度和精度的好选择。EngineMode.LstmOnly可能对某些新版模型有更好效果。- 图片格式转换:Tesseract处理的是Leptonica库的
Pix格式。我们将Bitmap通过内存流保存为PNG字节数组,再加载为Pix,这是一个可靠的方法。 - 引擎变量设置:通过
SetVariable可以精细控制Tesseract的行为。例如,tessedit_char_whitelist在识别验证码或固定格式文本时非常有用。 - 资源释放:
TesseractEngine、Page和Pix都实现了IDisposable,务必使用using语句确保及时释放,避免内存泄漏。
3.4 第四步:结果后处理与输出
Tesseract返回的文本可能包含多余的换行、空格,或者由于识别错误产生的乱码。根据你的应用场景,可能需要进行简单的后处理:
- 清理多余空白:使用正则表达式将多个连续空格或换行符替换为单个。
- 纠正常见错误:可以建立一个小型的常见错误映射表(例如,“0”和“O”,“1”和“l”),进行替换。但这需要针对你的具体内容。
- 结构化提取:如果识别的是表格、票据等有固定格式的文本,需要用正则表达式或简单的解析逻辑来提取关键字段。
public string PostProcessText(string rawOcrText) { if (string.IsNullOrEmpty(rawOcrText)) return rawOcrText; // 示例:将多个连续换行符替换为一个 string processed = System.Text.RegularExpressions.Regex.Replace(rawOcrText, @"\n\s*\n+", "\n"); // 示例:将多个连续空格替换为一个 processed = System.Text.RegularExpressions.Regex.Replace(processed, @"\s+", " "); return processed.Trim(); }4. 实战整合:封装成一个可复用的OCR工具类
将上述步骤整合,我们可以创建一个简单易用的OfflineOcrHelper类。
using System; using System.Drawing; using System.IO; using Tesseract; using AForge.Imaging; using AForge.Imaging.Filters; public class OfflineOcrHelper : IDisposable { private string _tessDataPath; public OfflineOcrHelper(string tessDataPath = @".\tessdata") { _tessDataPath = tessDataPath; if (!Directory.Exists(_tessDataPath)) { throw new ArgumentException($"Tesseract数据路径不存在: {_tessDataPath}"); } } public string RecognizeTextFromImage(string imagePath, string language = "chi_sim+eng", bool preprocess = true) { using (Bitmap originalBitmap = new Bitmap(imagePath)) { Bitmap imageToOcr = originalBitmap; try { if (preprocess) { imageToOcr = PreprocessImage(originalBitmap); } return PerformOcrCore(imageToOcr, language); } finally { // 确保预处理创建的新Bitmap被释放 if (preprocess && imageToOcr != null && imageToOcr != originalBitmap) { imageToOcr.Dispose(); } } } } public string RecognizeTextFromBitmap(Bitmap bitmap, string language = "chi_sim+eng", bool preprocess = true) { Bitmap imageToOcr = bitmap; Bitmap processedBitmap = null; try { if (preprocess) { processedBitmap = PreprocessImage(bitmap); imageToOcr = processedBitmap; } return PerformOcrCore(imageToOcr, language); } finally { if (processedBitmap != null && processedBitmap != bitmap) { processedBitmap.Dispose(); } } } private Bitmap PreprocessImage(Bitmap original) { // 这里调用前面章节实现的预处理流程 // 例如:灰度化 -> 对比度拉伸 -> 二值化 -> 降噪 Bitmap gray = new Grayscale(0.2125, 0.7154, 0.0721).Apply(original); new ContrastStretch().ApplyInPlace(gray); new OtsuThreshold().ApplyInPlace(gray); new Median().ApplyInPlace(gray); // 可选,根据图片情况 return gray; } private string PerformOcrCore(Bitmap image, string language) { string result; using (MemoryStream ms = new MemoryStream()) { image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); ms.Position = 0; using (Pix pix = Pix.LoadFromMemory(ms.ToArray())) using (var engine = new TesseractEngine(_tessDataPath, language, EngineMode.Default)) { // 可在此处设置引擎参数 // engine.SetVariable("user_defined_dpi", "300"); // 如果知道图片DPI可以设置 using (var page = engine.Process(pix)) { result = page.GetText(); // float confidence = page.GetMeanConfidence(); // 获取平均置信度 } } } return PostProcessText(result); } private string PostProcessText(string text) { // 简单的后处理:合并多余空行和空格 if (string.IsNullOrWhiteSpace(text)) return text; text = System.Text.RegularExpressions.Regex.Replace(text, @"(\r\n|\n)\s*(\r\n|\n)+", "\n"); text = System.Text.RegularExpressions.Regex.Replace(text, @"[ \t]+", " "); return text.Trim(); } public void Dispose() { // 目前没有需要显式释放的长期资源 } }使用示例:
class Program { static void Main(string[] args) { string tessDataPath = @"C:\MyApp\tessdata"; // 你的tessdata路径 string imagePath = @"C:\test.png"; using (var ocrHelper = new OfflineOcrHelper(tessDataPath)) { try { string recognizedText = ocrHelper.RecognizeTextFromImage(imagePath, "chi_sim"); Console.WriteLine("识别结果:"); Console.WriteLine(recognizedText); } catch (Exception ex) { Console.WriteLine($"OCR识别失败: {ex.Message}"); } } } }5. 避坑指南与性能优化实战经验
在实际开发和部署中,我遇到了不少坑。这里总结一下,希望你能绕过去。
5.1 部署与依赖的坑
坑1:Unable to load DLL 'liblept-5'或Unable to load DLL 'libtesseract-5'这是最常见的问题。根本原因是Tesseract.NET找不到原生的C++库。
- 解决方案:确保
liblept-5.dll和libtesseract-5.dll放在了正确的位置。对于Any CPU编译且Prefer 32-bit的项目,它会在x86子目录查找。最稳妥的方式是,在你的程序启动时,手动将DLL所在目录添加到PATH环境变量,或者使用NativeLibrary.SetDllImportResolver来指定加载路径。一个简单粗暴有效的方法:直接把这两个DLL复制到你的bin\Debug或bin\Release目录下(与你的exe同级),并确保tessdata文件夹也在同级或指定路径下。
坑2:语言包版本不匹配或缺失错误信息可能类似Error opening data file.../tessdata/eng.traineddata。
- 解决方案:首先检查路径是否正确。其次,确认下载的
.traineddata文件是完整的,并且与你的Tesseract引擎版本兼容(例如,Tesseract 5.x 尽量使用为5.x训练的数据)。可以从官方tessdata或tessdata_best仓库下载对应版本。
坑3:在Linux或Docker中运行在Linux上,你需要通过包管理器安装Tesseract的开发库,例如apt-get install libtesseract-dev,然后使用Tesseract.NET的Linux版本(它通过[DllImport]调用系统安装的.so库)。在Dockerfile中,记得安装相应的包并设置tessdata的环境变量TESSDATA_PREFIX。
5.2 识别精度提升的技巧
技巧1:针对性地预处理
- 扫描文档:重点做倾斜校正和去噪。AForge的
DocumentSkewChecker可以检测角度。 - 手机拍照:往往有透视变形和光照不均。可以尝试透视变换校正(需要检测四个角点,Accord.NET有相关功能)和自适应阈值二值化。
- 屏幕截图:通常比较干净,可能只需要简单的二值化,甚至可以直接识别。但要注意抗锯齿字体可能产生的灰度边缘。
技巧2:设置正确的DPI如果图片有较高的物理DPI(例如300 DPI的扫描件),告诉Tesseract可以提升精度。在调用engine.Process()之前,可以通过engine.SetVariable("user_defined_dpi", "300")来设置。
技巧3:使用多语言和自定义字典
- 多语言:如果图片中混有中英文,语言参数可以设为
"chi_sim+eng"。Tesseract会同时加载两个语言模型。 - 自定义字典/单词列表:对于特定领域的术语(如医药、法律),Tesseract允许你提供自定义的单词列表文件,可以显著提升该领域词汇的识别率。这需要创建
.wordlist或.user-words文件,并在初始化引擎时指定。
技巧4:分区域识别 (ROI)如果图片中只有特定区域有文字(如身份证的号码区域),可以先利用图像处理技术(轮廓检测、模板匹配)定位到该区域,然后只对该区域进行裁剪和识别,可以减少干扰,提高速度和精度。
5.3 性能考量与内存管理
性能瓶颈:OCR是计算密集型任务,尤其是预处理和高分辨率图片。图片越大,耗时越长。
- 优化建议:
- 缩放图片:如果原始图片分辨率远高于需要(例如,3000x4000的图片识别打印体文字),可以先按比例缩小到宽度1000-2000像素左右,能大幅减少处理时间,且对识别率影响不大。
- 异步处理:对于批量处理,一定要使用异步或并行(
Parallel.ForEach),避免阻塞UI或主线程。 - 对象复用:频繁创建和销毁
TesseractEngine开销较大。如果要在短时间内识别大量图片,考虑创建一个引擎实例(单例或池化)重复使用。但注意,TesseractEngine不是线程安全的,在多线程环境下需要加锁或使用线程本地存储。
内存泄漏:这是使用非托管库(Tesseract C++库)和System.Drawing时的常见问题。
- 关键点:确保所有实现了
IDisposable的对象都被妥善释放。包括Bitmap,Pix,TesseractEngine,Page。务必使用using语句或在finally块中Dispose。 - 在我们的
OfflineOcrHelper类中,Bitmap的创建和释放需要格外小心,特别是在预处理环节创建了新Bitmap时。
6. 进阶探索:超越基础OCR的应用场景
一个稳定的离线OCR引擎是基础,结合其他技术,可以玩出更多花样。
场景一:与AForge的摄像头结合,实现实时OCR还记得热词里的“c# aforge设置摄像头视频属性和控制属性”吗?AForge.NET本身有强大的视频采集能力。你可以:
- 使用
AForge.Video.DirectShow命名空间下的类捕获摄像头视频流。 - 从视频帧中获取
Bitmap。 - 应用运动检测或特定区域检测,当发现有文字区域变化或进入预设区域时,触发OCR识别。
- 将识别结果实时显示或保存。
这可以用来做简单的实时翻译机、信息录入工具等。
场景二:结构化信息提取(如发票、名片)单纯的OCR返回的是连续文本。结合正则表达式和简单的自然语言处理(NLP)规则,可以提取结构化信息。
- 模板匹配:对于格式固定的票据,可以先识别出整个文本。
- 关键词定位:通过寻找“日期:”、“总价:”等关键词,定位其后的文本位置。
- 规则提取:使用正则表达式匹配日期格式、金额格式、电话号码等。
- 输出JSON/对象:将提取的信息组装成结构化的数据对象。
场景三:集成到上位机或自动化工具中C#在工业上位机、自动化测试领域应用广泛。这个离线OCR工具可以作为一个组件集成进去。
- 质检报告读取:自动识别设备屏幕截图或拍摄的仪表盘读数。
- 文档自动化:自动从扫描的表格中提取数据并填入数据库。
- 配合OpenCV/Emgu CV:如果需要更复杂的图像定位和分割(如找出杂乱背景中的标签),可以考虑使用OpenCV的.NET封装(Emgu CV),进行更高级的图像处理,再将ROI送给Tesseract。
7. 源码结构与使用说明
为了让这个工具真正可用,我整理了一个简单的项目结构。你可以在文末找到源码仓库的链接(模拟)。这里先说明核心部分。
项目结构:
OfflineOcrTool/ ├── OfflineOcrTool.csproj # 项目文件 ├── Program.cs # 主程序/示例 ├── Ocr/ │ ├── OfflineOcrHelper.cs # 核心OCR工具类 │ └── ImagePreprocessor.cs # 图像预处理类(可分离) ├── Libs/ # 存放原生DLL (x86, x64) │ ├── x86/ │ │ ├── liblept-5.dll │ │ └── libtesseract-5.dll │ └── x64/ │ ├── liblept-5.dll │ └── libtesseract-5.dll ├── tessdata/ # 语言数据文件 │ ├── eng.traineddata │ └── chi_sim.traineddata └── Samples/ # 测试图片关键实现细节已在前面章节给出。使用这个工具,你只需要:
- 克隆或下载项目。
- 通过NuGet还原
Tesseract和AForge包。 - 确保
Libs目录下的DLL文件会根据你的项目平台(x86/x64)在编译时被复制到输出目录。这可以通过在.csproj文件中添加构建后事件或条件引用来实现。 - 将
tessdata文件夹复制到程序运行目录。 - 参考
Program.cs中的示例代码调用OfflineOcrHelper。
关于源码中的资源管理:在提供的OfflineOcrHelper类中,我已经特别注意了Bitmap和Pix对象的生命周期管理,使用了using和try-finally来确保即使在发生异常时资源也能被正确释放。这是在实际项目中稳定运行的基础。
最后,识别效果永远是第一位的。没有任何一套参数能通吃所有图片。这个工具类提供了基础的预处理流程,但最重要的还是你根据自己面对的图片类型,去调整PreprocessImage方法中的滤镜组合和参数。多试,多对比,积累针对你特定场景的“经验参数”,这才是离线OCR从“能用”到“好用”的关键。
本文还有配套的精品资源,点击获取