从 TesseractOcrMaui 迁移到 IronOCR
本指南将逐步介绍从TesseractOcrMaui到IronOCR 的完整迁移过程,并为每一步都提供了实际的迁移前后代码示例。它面向那些已经决定突破 MAUI 平台限制,并且需要系统地迁移到一个能够在移动应用、服务器端 API、后台工作进程和云函数中一致运行的库的开发者。 无需事先阅读对比文章。
为什么要从TesseractOcrMaui迁移
TesseractOcrMaui 的编写是为了解决一个真正的差距:现有的.NET Tesseract 封装器无法自行解决移动平台互操作性问题。 对于一个纯粹的、无需服务器的 MAUI 原型来说,它填补了这一空白。但一旦产品超出这个狭窄的范围,问题就出现了。
仅限MAUI的目标框架阻止代码共享。 TesseractOcrMaui为net8.0-windows——所有MAUI平台标识符提供目标。 该包不包含netstandard2.1,也没有服务器兼容的目标。 从类库、 ASP.NET Core项目或 Azure 函数中引用它会产生编译错误。 没有变通办法:该软件包的架构决定了它无法在 MAUI 主机之外运行。每次在非 MAUI 环境下需要 OCR 功能时,都必须引入并并行维护第二个库。
强制MAUI依赖注入耦合。 ITesseract连接到MAUI服务提供者。 该依赖注入图之外没有工厂方法、静态入口点和构造函数。 这意味着OCR逻辑无法提取到可移植类库中——每个在构造函数中使用ITesseract的类在其整个生命周期内都被锁定到MAUI应用程序主机。
任何级别均不接受PDF文件输入。PDF文档是扫描合同、发票和身份证件最常见的格式。 TesseractOcrMaui在任何PDF输入时抛出NotSupportedException。 处理 PDF 需要添加单独的 PDF 渲染库,编写逐页图像提取程序,管理设备缓存中的临时文件,并在每次调用后清理它们。 在执行一次 OCR 调用之前,需要编写 100 多行基础设施代码——而且它仍然只能在 MAUI 上运行。
没有针对真实世界图像的内置预处理功能。移动设备的摄像头拍摄的图像存在旋转、传感器噪声以及不同设备型号之间DPI不一致等问题。TesseractOcrMaui无需任何预处理即可将图像直接传递给 Tesseract 引擎。 需要更高准确度的团队必须添加 SkiaSharp 或 ImageSharp,手动实现去斜和降噪算法,编写临时文件管理,并在 iOS 和 Android 设备版本上进行测试。 大多数人都会跳过这一步。 因此,实际手机拍摄照片的准确率会受到影响。
单一开发者维护对生产依赖项构成风险。TesseractOcrMaui由一名开发者维护。 它背后没有公司支持,没有服务级别协议 (SLA),没有安全补丁承诺,除了GitHub问题之外也没有其他升级途径。 对于受监管行业(金融、医疗保健、法律)的生产应用而言,一个由志愿者维护、 NuGet总下载量约为 33,900 次的库是不可接受的依赖项。
基本问题
TesseractOcrMaui 只能在 MAUI 项目内部编译。 一旦其他类型的项目需要OCR功能,这种架构就会失效:
// TesseractOcrMaui: wired to MAUI host — cannot escape to a shared library
// This code compiles only inside a .NET MAUI application
public class OcrService
{
private readonly ITesseract _tesseract; // resolved from MAUI DI — no other source exists
public OcrService(ITesseract tesseract) { _tesseract = tesseract; }
public async Task<string> ReadAsync(string imagePath)
{
await _tesseract.InitAsync("eng"); // traineddata must be bundled as MauiAsset
var result = await _tesseract.RecognizeTextAsync(imagePath);
return result.Success ? result.RecognizedText : string.Empty;
}
// Cannot reference this class from ASP.NET Core, Azure Functions, or Docker
}Public Class OcrService
Private ReadOnly _tesseract As ITesseract ' resolved from MAUI DI — no other source exists
Public Sub New(tesseract As ITesseract)
_tesseract = tesseract
End Sub
Public Async Function ReadAsync(imagePath As String) As Task(Of String)
Await _tesseract.InitAsync("eng") ' traineddata must be bundled as MauiAsset
Dim result = Await _tesseract.RecognizeTextAsync(imagePath)
Return If(result.Success, result.RecognizedText, String.Empty)
End Function
' Cannot reference this class from ASP.NET Core, Azure Functions, or Docker
End Class// IronOCR: plain instantiable class — compiles in any .NET project type
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract(); // no DI, no MAUI host
public string Read(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
return _ocr.Read(input).Text;
}
// Place this in a netstandard2.1 library — reference from MAUI, API, and Functions together
}Public Class OcrService
Private ReadOnly _ocr As New IronTesseract() ' no DI, no MAUI host
Public Function Read(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
Return _ocr.Read(input).Text
End Using
End Function
' Place this in a netstandard2.1 library — reference from MAUI, API, and Functions together
End ClassIronOCR与 TesseractOcrMaui:功能对比
下表列出了与评估此迁移相关的团队的能力差异。
| 特征 | TesseractOcrMaui | IronOCR |
|---|---|---|
| .NET MAUI (iOS) | 是 | 是(IronOcr.iOS) |
| .NET MAUI (Android) | 是 | 是(IronOcr.Android) |
| .NET MAUI (Windows) | 是 | 是 |
| ASP.NET Core | 否 | 是 |
| Azure Functions | 否 | 是 |
| AWS Lambda | 否 | 是 |
| Docker/Linux容器 | 否 | 是 |
| 控制台应用程序 | 否 | 是 |
| WPF/WinForms | 否 | 是 |
| 共享的.NET类库 | 否 | 是 |
| PDF 输入(原生) | 否 | 是 |
| 受密码保护的 PDF 输入 | 否 | 是 |
| 流输入 | 否 | 是 |
| 字节数组输入 | 否 | 是 |
| 多页 TIFF 输入 | 否 | 是 |
| 可搜索的 PDF 输出 | 否 | 是 |
| hOCR导出 | 否 | 是 |
| 自动校正斜角 | 否 | 是 |
| 自动去噪 | 否 | 是 |
| 对比度增强 | 否 | 是 |
| 二值化 | 否 | 是 |
| 基于区域的OCR | 否 | 是 |
| OCR过程中的条形码读取 | 否 | 是 |
| 词级坐标 | 否 | 是 |
| 多语言同步 | 否 | 是 |
| 支持的语言 | 手动打包的训练数据 | 通过NuGet包提供 125 多个包 |
| 螺纹安全 | 手册 | 内置 |
| 商业支持 | 无(单个开发者) | 是的(Iron Software) |
| 许可 | Apache 2.0(免费) | 从$999永久有效 |
| NuGet下载 | 约33,900 | 530万+ |
快速入门:TesseractOcrMaui 到IronOCR 的迁移
步骤 1:替换 NuGet 软件包
从 MAUI 项目中移除 TesseractOcrMaui:
dotnet remove package TesseractOcrMaui
安装IronOCR。 对于 MAUI 项目,请在核心包旁边添加平台特定的包:
对于服务器端项目(ASP.NET Core、Azure Functions、控制台):
IronOCR NuGet包页面列出了所有可用的平台包。
步骤 2:更新命名空间
将TesseractOcrMaui命名空间替换为IronOCR命名空间:
// Before (TesseractOcrMaui)
using TesseractOcrMaui;
using TesseractOcrMaui.Results;
using Microsoft.Maui.Storage;
// After (IronOCR)
using IronOcr;' Before (TesseractOcrMaui)
Imports TesseractOcrMaui
Imports TesseractOcrMaui.Results
Imports Microsoft.Maui.Storage
' After (IronOCR)
Imports IronOcr步骤 3:初始化许可证
在应用程序启动时添加许可证初始化。在MAUI应用中,这个代码应放在MauiProgram.cs中; 在ASP.NET Core中,代码放在Program.cs中:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"代码迁移示例
替换 MAUI 依赖注入注册
TesseractOcrMaui 需要通过 MAUI 服务提供商注册 OCR 引擎。 移除该注册是架构上的第一步,因为它将所有后续的 OCR 代码锁定到 MAUI 主机。
TesseractOcrMaui 方法:
// MauiProgram.cs — OCR engine registered here; nowhere else resolves it
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
// Binds OCR to MAUI DI — no standalone path exists after this
builder.Services.AddTesseractOcr();
return builder.Build();
}
}
// Any class that needs OCR must receive ITesseract from the MAUI container
public class InvoicePageViewModel
{
private readonly ITesseract _tesseract;
public InvoicePageViewModel(ITesseract tesseract)
{
_tesseract = tesseract; // fails to construct outside MAUI host
}
public async Task<string> ScanInvoiceAsync(string imagePath)
{
await _tesseract.InitAsync("eng");
var result = await _tesseract.RecognizeTextAsync(imagePath);
return result.RecognizedText ?? string.Empty;
}
}' MauiProgram.vb — OCR engine registered here; nowhere else resolves it
Public Module MauiProgram
Public Function CreateMauiApp() As MauiApp
Dim builder = MauiApp.CreateBuilder()
builder.UseMauiApp(Of App)()
' Binds OCR to MAUI DI — no standalone path exists after this
builder.Services.AddTesseractOcr()
Return builder.Build()
End Function
End Module
' Any class that needs OCR must receive ITesseract from the MAUI container
Public Class InvoicePageViewModel
Private ReadOnly _tesseract As ITesseract
Public Sub New(tesseract As ITesseract)
_tesseract = tesseract ' fails to construct outside MAUI host
End Sub
Public Async Function ScanInvoiceAsync(imagePath As String) As Task(Of String)
Await _tesseract.InitAsync("eng")
Dim result = Await _tesseract.RecognizeTextAsync(imagePath)
Return If(result.RecognizedText, String.Empty)
End Function
End ClassIronOCR方法:
// MauiProgram.cs — license only; no DI registration needed
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
// One-line initialization — works for all project types
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
return builder.Build();
}
}
// 否 constructor injection needed — IronTesseract instantiates directly
public class InvoicePageViewModel
{
public string ScanInvoice(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
return ocr.Read(input).Text;
}
}' MauiProgram.vb — license only; no DI registration needed
Public Class MauiProgram
Public Shared Function CreateMauiApp() As MauiApp
Dim builder = MauiApp.CreateBuilder()
builder.UseMauiApp(Of App)()
' One-line initialization — works for all project types
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Return builder.Build()
End Function
End Class
' 否 constructor injection needed — IronTesseract instantiates directly
Public Class InvoicePageViewModel
Public Function ScanInvoice(imagePath As String) As String
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadImage(imagePath)
Return ocr.Read(input).Text
End Using
End Function
End Class删除AddTesseractOcr()可以消除MAUI DI耦合。 IronTesseract类有一个公共的无参数构造函数,没有平台依赖——可以在任何地方实例化。 有关初始化选项(包括引擎模式和语言配置),请参阅IronTesseract 设置指南。
将 OCR 逻辑迁移到共享类库
using TesseractOcrMaui,在结构上不可能跨项目类型共享 OCR 逻辑。 使用IronOCR,迁移路径很简单:将服务提取到net8.0类库,并从解决方案中的每个项目引用它。
TesseractOcrMaui 方法:
// This service CANNOT be extracted to a shared library.
// It compiles only in a project that references TesseractOcrMaui,
// which only has MAUI platform targets.
//
// Result: every non-MAUI project must use a different OCR library,
// duplicating language config, error handling, and accuracy tuning.
public class DocumentOcrService
{
private readonly ITesseract _tesseract; // MAUI DI only
public DocumentOcrService(ITesseract tesseract)
{
_tesseract = tesseract;
}
public async Task<string> ProcessDocumentAsync(string imagePath)
{
await _tesseract.InitAsync("eng");
var result = await _tesseract.RecognizeTextAsync(imagePath);
return result.Success ? result.RecognizedText : string.Empty;
}
// Server team writes their own version using a different library
// Two codebases, two accuracy profiles, two maintenance tracks
}Public Class DocumentOcrService
Private ReadOnly _tesseract As ITesseract ' MAUI DI only
Public Sub New(tesseract As ITesseract)
_tesseract = tesseract
End Sub
Public Async Function ProcessDocumentAsync(imagePath As String) As Task(Of String)
Await _tesseract.InitAsync("eng")
Dim result = Await _tesseract.RecognizeTextAsync(imagePath)
Return If(result.Success, result.RecognizedText, String.Empty)
End Function
' Server team writes their own version using a different library
' Two codebases, two accuracy profiles, two maintenance tracks
End ClassIronOCR方法:
// Place this in: MyCompany.OcrCore (net8.0 or netstandard2.1 class library)
// Reference from: MyCompany.MauiApp, MyCompany.Api, MyCompany.BatchWorker
using IronOcr;
namespace MyCompany.OcrCore
{
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
_ocr = new IronTesseract();
}
public string ProcessDocument(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();
input.DeNoise();
return _ocr.Read(input).Text;
}
public string ProcessDocumentFromBytes(byte[] imageData)
{
using var input = new OcrInput();
input.LoadImage(imageData);
input.Deskew();
input.DeNoise();
return _ocr.Read(input).Text;
}
public string ProcessDocumentFromStream(Stream imageStream)
{
using var input = new OcrInput();
input.LoadImage(imageStream);
return _ocr.Read(input).Text;
}
}
}Imports IronOcr
Namespace MyCompany.OcrCore
Public Class DocumentOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ProcessDocument(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
input.Deskew()
input.DeNoise()
Return _ocr.Read(input).Text
End Using
End Function
Public Function ProcessDocumentFromBytes(imageData As Byte()) As String
Using input As New OcrInput()
input.LoadImage(imageData)
input.Deskew()
input.DeNoise()
Return _ocr.Read(input).Text
End Using
End Function
Public Function ProcessDocumentFromStream(imageStream As Stream) As String
Using input As New OcrInput()
input.LoadImage(imageStream)
Return _ocr.Read(input).Text
End Using
End Function
End Class
End Namespace一个类库,一组测试,一份准确性配置文件。MAUI应用调用ProcessDocument(photoPath),ASP.NET Core API调用ProcessDocumentFromBytes(uploadedBytes),Azure Function调用ProcessDocumentFromStream(blobStream)——都有相同的实现支持。 流输入指南和图像输入指南记录了所有OcrInput加载变体。
在ASP.NET Core中启用服务器端 OCR
TesseractOcrMaui 无法从ASP.NET Core项目中引用。 添加文档上传端点的团队将被迫使用完全不同的库。 IronOCR可在ASP.NET Core中运行,除了许可证密钥之外无需任何配置更改。
TesseractOcrMaui 方法:
//ASP.NET CoreWeb API —TesseractOcrMauiCANNOT be used here.
// The package has no net8.0 or netstandard target.
// Referencing it produces: "The given project does not support targeting net8.0-ios/android/windows."
//
// Team is forced to add a second OCR library — Tesseract charlesw wrapper,
// a cloud API, or another solution — creating a split codebase.
[ApiController]
[Route("api/[controller]")]
public class DocumentsController : ControllerBase
{
// Cannot inject ITesseract here — no MAUI host, no MAUI DI container
// Must use a completely different OCR library for server-side processing
}'ASP.NET CoreWeb API —TesseractOcrMauiCANNOT be used here.
' The package has no net8.0 or netstandard target.
' Referencing it produces: "The given project does not support targeting net8.0-ios/android/windows."
'
' Team is forced to add a second OCR library — Tesseract charlesw wrapper,
' a cloud API, or another solution — creating a split codebase.
<ApiController>
<Route("api/[controller]")>
Public Class DocumentsController
Inherits ControllerBase
' Cannot inject ITesseract here — no MAUI host, no MAUI DI container
' Must use a completely different OCR library for server-side processing
End ClassIronOCR方法:
//ASP.NET Core—IronOCRworks without modification
using IronOcr;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class DocumentsController : ControllerBase
{
[HttpPost("extract-text")]
public async Task<IActionResult> ExtractText(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded.");
var ocr = new IronTesseract();
using var input = new OcrInput();
// Load directly from the upload stream — no temp files
using var stream = file.OpenReadStream();
if (file.ContentType == "application/pdf")
input.LoadPdf(stream);
else
input.LoadImage(stream);
input.Deskew();
input.DeNoise();
var result = ocr.Read(input);
return Ok(new
{
text = result.Text,
confidence = result.Confidence,
pageCount = result.Pages.Count()
});
}
[HttpPost("extract-text-batch")]
public async Task<IActionResult> ExtractTextBatch(List<IFormFile> files)
{
var results = new List<object>();
// Thread-safe: create one IronTesseract per thread
await Parallel.ForEachAsync(files, async (file, ct) =>
{
var ocr = new IronTesseract();
using var input = new OcrInput();
using var stream = file.OpenReadStream();
input.LoadImage(stream);
var result = ocr.Read(input);
lock (results)
{
results.Add(new { file = file.FileName, text = result.Text });
}
});
return Ok(results);
}
}'ASP.NET Core—IronOCRworks without modification
Imports IronOcr
Imports Microsoft.AspNetCore.Mvc
<ApiController>
<Route("api/[controller]")>
Public Class DocumentsController
Inherits ControllerBase
<HttpPost("extract-text")>
Public Async Function ExtractText(file As IFormFile) As Task(Of IActionResult)
If file Is Nothing OrElse file.Length = 0 Then
Return BadRequest("No file uploaded.")
End If
Dim ocr As New IronTesseract()
Using input As New OcrInput()
' Load directly from the upload stream — no temp files
Using stream = file.OpenReadStream()
If file.ContentType = "application/pdf" Then
input.LoadPdf(stream)
Else
input.LoadImage(stream)
End If
input.Deskew()
input.DeNoise()
Dim result = ocr.Read(input)
Return Ok(New With {
.text = result.Text,
.confidence = result.Confidence,
.pageCount = result.Pages.Count()
})
End Using
End Using
End Function
<HttpPost("extract-text-batch")>
Public Async Function ExtractTextBatch(files As List(Of IFormFile)) As Task(Of IActionResult)
Dim results As New List(Of Object)()
' Thread-safe: create one IronTesseract per thread
Await Task.Run(Sub()
Parallel.ForEach(files, Sub(file)
Dim ocr As New IronTesseract()
Using input As New OcrInput()
Using stream = file.OpenReadStream()
input.LoadImage(stream)
Dim result = ocr.Read(input)
SyncLock results
results.Add(New With {.file = file.FileName, .text = result.Text})
End SyncLock
End Using
End Using
End Sub)
End Sub)
Return Ok(results)
End Function
End Class相同的代码可以原封不动地部署到 IIS、Kestrel 或 Linux Docker 容器中。 ASP.NET OCR 指南涵盖中间件配置,而Docker 部署指南则记录了 Linux 容器的设置。
消除平台特定的处理程序代码
TesseractOcrMaui 仅支持 MAUI 目标的架构迫使开发人员在尝试将 OCR 集成到多目标解决方案中时编写平台条件代码。 IronOCR无需平台条件判断,因为同一个软件包在每个目标平台上都能正确解析。
TesseractOcrMaui 方法:
// Attempting to share OCR logic across MAUI and non-MAUI targets
// requires platform-conditional compilation — a maintenance hazard
#if ANDROID || IOS || WINDOWS
// Only compile this block in MAUI targets
// Non-MAUI targets cannot referenceTesseractOcrMauiat all
using TesseractOcrMaui;
public class PlatformOcrHandler
{
private readonly ITesseract _tesseract;
public PlatformOcrHandler(ITesseract tesseract)
{
_tesseract = tesseract;
}
public async Task<string> ProcessAsync(string imagePath)
{
await _tesseract.InitAsync("eng");
var r = await _tesseract.RecognizeTextAsync(imagePath);
return r.RecognizedText ?? string.Empty;
}
}
#else
// Server targets need a completely different implementation
public class PlatformOcrHandler
{
public string ProcessAsync(string imagePath)
{
// Duplicate logic using a different library
throw new PlatformNotSupportedException("Use server OCR library here");
}
}
#endif#If ANDROID Or IOS Or WINDOWS Then
' Only compile this block in MAUI targets
' Non-MAUI targets cannot reference TesseractOcrMaui at all
Imports TesseractOcrMaui
Public Class PlatformOcrHandler
Private ReadOnly _tesseract As ITesseract
Public Sub New(tesseract As ITesseract)
_tesseract = tesseract
End Sub
Public Async Function ProcessAsync(imagePath As String) As Task(Of String)
Await _tesseract.InitAsync("eng")
Dim r = Await _tesseract.RecognizeTextAsync(imagePath)
Return If(r.RecognizedText, String.Empty)
End Function
End Class
#Else
' Server targets need a completely different implementation
Public Class PlatformOcrHandler
Public Function ProcessAsync(imagePath As String) As String
' Duplicate logic using a different library
Throw New PlatformNotSupportedException("Use server OCR library here")
End Function
End Class
#End IfIronOCR方法:
// One implementation — no conditional compilation, no duplicate logic
using IronOcr;
public class PlatformOcrHandler
{
// This class compiles identically for:
// net8.0-android, net8.0-ios, net8.0-windows (MAUI targets)
// net8.0 (server targets)
// netstandard2.1 (shared library targets)
public string Process(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();
return ocr.Read(input).Text;
}
}
// Multi-target .csproj — no conditional package references needed
// <TargetFrameworks>net8.0;net8.0-android;net8.0-ios</TargetFrameworks>
// IronOcr resolves correctly for all three targets from one package referenceImports IronOcr
Public Class PlatformOcrHandler
' This class compiles identically for:
' net8.0-android, net8.0-ios, net8.0-windows (MAUI targets)
' net8.0 (server targets)
' netstandard2.1 (shared library targets)
Public Function Process(imagePath As String) As String
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadImage(imagePath)
input.Deskew()
Return ocr.Read(input).Text
End Using
End Function
End Class
' Multi-target .vbproj — no conditional package references needed
' <TargetFrameworks>net8.0;net8.0-android;net8.0-ios</TargetFrameworks>
' IronOcr resolves correctly for all three targets from one package referenceOCR处理代码中的平台条件表明架构存在分裂,而且这种分裂会随着时间的推移而加剧。每一次语言配置更改、每一次预处理调整、每一次置信度阈值微调都必须在两个分支中同时应用。 IronOCR使拆分变得不必要。 .NET OCR 库概述详细介绍了多目标项目结构。
基于词坐标的结构化数据提取
TesseractOcrMaui仅暴露result.RecognizedText和一个顶级信心分数。 提取带有边界框的单个单词(表单字段验证、文档解析或高亮显示需要此功能)是不可能的。 IronOCR公开了一个完整的文档对象模型:页面、段落、行、单词和字符,每个对象都有像素坐标。
TesseractOcrMaui 方法:
// TesseractOcrMaui: flat text string only — no structure, no coordinates
public class TesseractMauiFormParser
{
private readonly ITesseract _tesseract;
public TesseractMauiFormParser(ITesseract tesseract)
{
_tesseract = tesseract;
}
public async Task<Dictionary<string, string>> ParseFormAsync(string imagePath)
{
await _tesseract.InitAsync("eng");
var result = await _tesseract.RecognizeTextAsync(imagePath);
// result.RecognizedText is one flat string — no field positions
// Parsing requires fragile line-splitting and regex heuristics
var fields = new Dictionary<string, string>();
var lines = result.RecognizedText?.Split('\n') ?? Array.Empty<string>();
foreach (var line in lines)
{
// Hope the layout stays consistent enough to parse
var parts = line.Split(':');
if (parts.Length == 2)
fields[parts[0].Trim()] = parts[1].Trim();
}
return fields;
// 否 way to validate against expected field positions
// 否 confidence per word — only document-level confidence
}
}Public Class TesseractMauiFormParser
Private ReadOnly _tesseract As ITesseract
Public Sub New(tesseract As ITesseract)
_tesseract = tesseract
End Sub
Public Async Function ParseFormAsync(imagePath As String) As Task(Of Dictionary(Of String, String))
Await _tesseract.InitAsync("eng")
Dim result = Await _tesseract.RecognizeTextAsync(imagePath)
' result.RecognizedText is one flat string — no field positions
' Parsing requires fragile line-splitting and regex heuristics
Dim fields = New Dictionary(Of String, String)()
Dim lines = If(result.RecognizedText?.Split(ControlChars.Lf), Array.Empty(Of String)())
For Each line In lines
' Hope the layout stays consistent enough to parse
Dim parts = line.Split(":"c)
If parts.Length = 2 Then
fields(parts(0).Trim()) = parts(1).Trim()
End If
Next
Return fields
' 否 way to validate against expected field positions
' 否 confidence per word — only document-level confidence
End Function
End ClassIronOCR方法:
// IronOCR: full document structure with bounding boxes per word
using IronOcr;
public class IronOcrFormParser
{
public List<WordLocation> ExtractWordsWithPositions(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
var wordLocations = new List<WordLocation>();
foreach (var page in result.Pages)
{
foreach (var word in page.Words)
{
wordLocations.Add(new WordLocation
{
Text = word.Text,
Confidence = word.Confidence,
X = word.X,
Y = word.Y,
Width = word.Width,
Height = word.Height
});
}
}
return wordLocations;
}
public FormData ParseStructuredForm(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew();
var result = ocr.Read(input);
var form = new FormData();
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
// Use Y coordinate to identify form regions
if (paragraph.Y < 200)
form.HeaderText += paragraph.Text + " ";
else if (paragraph.Y > 800)
form.FooterText += paragraph.Text + " ";
else
form.BodyLines.Add(paragraph.Text);
}
}
form.OverallConfidence = result.Confidence;
return form;
}
}
public class WordLocation
{
public string Text { get; set; }
public float Confidence { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public class FormData
{
public string HeaderText { get; set; } = string.Empty;
public string FooterText { get; set; } = string.Empty;
public List<string> BodyLines { get; set; } = new();
public float OverallConfidence { get; set; }
}Imports IronOcr
Public Class IronOcrFormParser
Public Function ExtractWordsWithPositions(imagePath As String) As List(Of WordLocation)
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadImage(imagePath)
Dim result = ocr.Read(input)
Dim wordLocations = New List(Of WordLocation)()
For Each page In result.Pages
For Each word In page.Words
wordLocations.Add(New WordLocation With {
.Text = word.Text,
.Confidence = word.Confidence,
.X = word.X,
.Y = word.Y,
.Width = word.Width,
.Height = word.Height
})
Next
Next
Return wordLocations
End Using
End Function
Public Function ParseStructuredForm(imagePath As String) As FormData
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadImage(imagePath)
input.Deskew()
Dim result = ocr.Read(input)
Dim form = New FormData()
For Each page In result.Pages
For Each paragraph In page.Paragraphs
' Use Y coordinate to identify form regions
If paragraph.Y < 200 Then
form.HeaderText &= paragraph.Text & " "
ElseIf paragraph.Y > 800 Then
form.FooterText &= paragraph.Text & " "
Else
form.BodyLines.Add(paragraph.Text)
End If
Next
Next
form.OverallConfidence = result.Confidence
Return form
End Using
End Function
End Class
Public Class WordLocation
Public Property Text As String
Public Property Confidence As Single
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
End Class
Public Class FormData
Public Property HeaderText As String = String.Empty
Public Property FooterText As String = String.Empty
Public Property BodyLines As List(Of String) = New List(Of String)()
Public Property OverallConfidence As Single
End Class单词坐标可以针对已知的表单模板进行验证,基于置信度进行标记以供人工审核,并在文档查看器用户界面中突出显示叠加层。 结构化结果指南记录了完整的OcrResult对象模型,包括字符级访问,而信心分数指南涵盖每个单词的信心过滤模式。
使用原生异步和进度跟踪进行后台处理
TesseractOcrMaui在MAUI应用程序上下文中暴露了一个异步API(RecognizeTextAsync)。 长时间运行的批处理作业必须在后台服务、Azure 函数或工作进程中运行——而TesseractOcrMaui都无法针对这些服务或进程运行。 IronOCR提供原生异步支持,可在任何托管服务中使用。
TesseractOcrMaui 方法:
// Background processing is impossible with TesseractOcrMaui.
// IHostedService runs in a server context —TesseractOcrMauihas no server target.
// The MAUI async API exists, but there is nowhere to run it outside the MAUI app host.
public class DocumentBatchWorker : BackgroundService
{
// ITesseract cannot be injected here — no MAUI DI in a hosted service
// Attempting to referenceTesseractOcrMauiwill fail to compile:
// error: PackageTesseractOcrMauidoes not support target net8.0
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
throw new PlatformNotSupportedException(
"TesseractOcrMaui has no server target. Use a different OCR library.");
}
}Public Class DocumentBatchWorker
Inherits BackgroundService
' ITesseract cannot be injected here — no MAUI DI in a hosted service
' Attempting to referenceTesseractOcrMauiwill fail to compile:
' error: PackageTesseractOcrMauidoes not support target net8.0
Protected Overrides Function ExecuteAsync(stoppingToken As CancellationToken) As Task
Throw New PlatformNotSupportedException(
"TesseractOcrMaui has no server target. Use a different OCR library.")
End Function
End ClassIronOCR方法:
// IronOCR: hosted service background batch processor
using IronOcr;
using Microsoft.Extensions.Hosting;
public class DocumentBatchWorker : BackgroundService
{
private readonly ILogger<DocumentBatchWorker> _logger;
private readonly string _inputFolder;
private readonly string _outputFolder;
public DocumentBatchWorker(ILogger<DocumentBatchWorker> logger, IConfiguration config)
{
_logger = logger;
_inputFolder = config["Ocr:InputFolder"];
_outputFolder = config["Ocr:OutputFolder"];
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var pendingFiles = Directory.GetFiles(_inputFolder, "*.pdf")
.Concat(Directory.GetFiles(_inputFolder, "*.jpg"))
.ToList();
if (pendingFiles.Count > 0)
{
_logger.LogInformation("Processing {Count} documents.", pendingFiles.Count);
// Thread-safe parallel processing — one IronTesseract per thread
await Parallel.ForEachAsync(pendingFiles,
new ParallelOptions { MaxDegreeOfParallelism = 4, CancellationToken = stoppingToken },
async (filePath, ct) =>
{
await ProcessDocumentAsync(filePath, ct);
});
}
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
private async Task ProcessDocumentAsync(string filePath, CancellationToken ct)
{
try
{
var ocr = new IronTesseract();
using var input = new OcrInput();
if (Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase))
input.LoadPdf(filePath);
else
input.LoadImage(filePath);
input.Deskew();
input.DeNoise();
var result = await Task.Run(() => ocr.Read(input), ct);
// Produce searchable PDF from the same OCR pass
var outputPath = Path.Combine(_outputFolder,
Path.GetFileNameWithoutExtension(filePath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
File.Delete(filePath); // move from input queue
_logger.LogInformation("Processed {File}: {Confidence:F1}% confidence.", filePath, result.Confidence);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to process {File}.", filePath);
}
}
}' IronOCR: hosted service background batch processor
Imports IronOcr
Imports Microsoft.Extensions.Hosting
Public Class DocumentBatchWorker
Inherits BackgroundService
Private ReadOnly _logger As ILogger(Of DocumentBatchWorker)
Private ReadOnly _inputFolder As String
Private ReadOnly _outputFolder As String
Public Sub New(logger As ILogger(Of DocumentBatchWorker), config As IConfiguration)
_logger = logger
_inputFolder = config("Ocr:InputFolder")
_outputFolder = config("Ocr:OutputFolder")
End Sub
Protected Overrides Async Function ExecuteAsync(stoppingToken As CancellationToken) As Task
While Not stoppingToken.IsCancellationRequested
Dim pendingFiles = Directory.GetFiles(_inputFolder, "*.pdf").Concat(Directory.GetFiles(_inputFolder, "*.jpg")).ToList()
If pendingFiles.Count > 0 Then
_logger.LogInformation("Processing {Count} documents.", pendingFiles.Count)
' Thread-safe parallel processing — one IronTesseract per thread
Await Parallel.ForEachAsync(pendingFiles, New ParallelOptions With {.MaxDegreeOfParallelism = 4, .CancellationToken = stoppingToken}, Async Function(filePath, ct) As Task
Await ProcessDocumentAsync(filePath, ct)
End Function)
End If
Await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken)
End While
End Function
Private Async Function ProcessDocumentAsync(filePath As String, ct As CancellationToken) As Task
Try
Dim ocr = New IronTesseract()
Using input = New OcrInput()
If Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase) Then
input.LoadPdf(filePath)
Else
input.LoadImage(filePath)
End If
input.Deskew()
input.DeNoise()
Dim result = Await Task.Run(Function() ocr.Read(input), ct)
' Produce searchable PDF from the same OCR pass
Dim outputPath = Path.Combine(_outputFolder, Path.GetFileNameWithoutExtension(filePath) & "_searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
File.Delete(filePath) ' move from input queue
_logger.LogInformation("Processed {File}: {Confidence:F1}% confidence.", filePath, result.Confidence)
End Using
Catch ex As Exception
_logger.LogError(ex, "Failed to process {File}.", filePath)
End Try
End Function
End Class该工作者在builder.Services.AddHostedService<DocumentBatchWorker>(),并在任何.NET 8主机中运行——Windows服务,Linux systemd单元,Docker容器,或Azure容器应用。异步OCR指南涵盖了异步模式,而可搜索PDF指南记录了SaveAsSearchablePdf输出选项。
##TesseractOcrMauiAPI 到IronOCR映射参考
| TesseractOcrMaui | IronOCR当量 |
|---|---|
dotnet add package TesseractOcrMaui | dotnet add package IronOcr |
builder.Services.AddTesseractOcr() | 完全移除——无需注册 |
ITesseract(已注入) | new IronTesseract()(直接实例化) |
_tesseract.InitAsync("eng") | ocr.Language = OcrLanguage.English;(或省略以使用默认英语) |
_tesseract.RecognizeTextAsync(imagePath) | ocr.Read(input) |
result.RecognizedText | result.Text |
result.Success | 基于例外情况; 没有布尔标志 |
result.Status | catch (Exception ex)消息 |
result.Confidence | result.Confidence(也适用于每个单词) |
TesseractOcrMaui.Results.RecognitionResult | IronOcr.OcrResult |
<MauiAsset>训练数据包 | dotnet add package IronOcr.Languages.French |
Resources/Raw/tessdata/eng.traineddata | 移除——语言数据位于NuGet包内 |
FileSystem.OpenAppPackageFileAsync()(用于训练数据) | 删除——无需删除 |
| 不支持 PDF | input.LoadPdf(stream) |
| 无需预处理 | input.Deskew(), input.DeNoise(), input.Binarize(), input.Contrast() |
| 没有可搜索的 PDF 输出 | result.SaveAsSearchablePdf(outputPath) |
| 无词坐标 | result.Pages[0].Words[i].X, .Y, .Width, .Height |
| 不逐字自信 | result.Pages[0].Words[i].Confidence |
net8.0-ios仅限目标 | net8.0 + IronOcr.iOS包 |
net8.0-android仅限目标 | net8.0 + IronOcr.Android包 |
常见迁移问题和解决方案
问题1:移除AddTesseractOcr函数会破坏依赖类
TesseractOcrMaui: 每个执行OCR的类都通过构造函数注入收到ITesseract。 删除AddTesseractOcr()会立即导致这些构造函数出现DI解析异常。
解决方案: 删除构造函数参数,并将其替换为直接IronTesseract实例化。 如果项目使用DI容器,并且您想保持可注入模式,则需手动注册IronTesseract:
// Option A: Direct instantiation (recommended for most cases)
public class ScanPageViewModel
{
public string ScanDocument(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
return ocr.Read(input).Text;
}
}
// Option B: Register IronTesseract in DI if your architecture requires it
// In MauiProgram.cs or Program.cs:
builder.Services.AddSingleton<IronTesseract>();
// Then inject normally:
public class ScanPageViewModel
{
private readonly IronTesseract _ocr;
public ScanPageViewModel(IronTesseract ocr) { _ocr = ocr; }
public string ScanDocument(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
return _ocr.Read(input).Text;
}
}' Option A: Direct instantiation (recommended for most cases)
Public Class ScanPageViewModel
Public Function ScanDocument(ByVal imagePath As String) As String
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadImage(imagePath)
Return ocr.Read(input).Text
End Using
End Function
End Class
' Option B: Register IronTesseract in DI if your architecture requires it
' In MauiProgram.vb or Program.vb:
builder.Services.AddSingleton(Of IronTesseract)()
' Then inject normally:
Public Class ScanPageViewModel
Private ReadOnly _ocr As IronTesseract
Public Sub New(ByVal ocr As IronTesseract)
_ocr = ocr
End Sub
Public Function ScanDocument(ByVal imagePath As String) As String
Using input = New OcrInput()
input.LoadImage(imagePath)
Return _ocr.Read(input).Text
End Using
End Function
End Class问题 2:软件包移除后 Traineddata 文件丢失
TesseractOcrMaui: .csproj中的声明都需要被移除。 保留这些文件会导致构建警告,并使应用程序包中包含大量未使用的文件。
解决方案:删除tessdata文件夹,删除<MauiAsset>条目,并卸载任何手动下载的语言。 请改用等效的IronOCR语言包:
# Delete traineddata assets
rm -rf Resources/Raw/tessdata
# Remove from .csproj (delete the MauiAsset ItemGroup):
# <ItemGroup>
# <MauiAsset Include="Resources\Raw\tessdata\*.traineddata" />
# </ItemGroup>
# InstallIronOCRlanguage pack (if non-English language was needed)
dotnet add package IronOcr.Languages.French
dotnet add package IronOcr.Languages.German
IronOCR的语言包在构建时进行解析和打包,无需任何手动文件管理。 多语言指南记录了所有可用的软件包和同时进行多语言配置的方法。
问题 3:必须在每次调用 RecognizeTextAsync 之前调用 InitAsync
TesseractOcrMaui: RecognizeTextAsync调用之前。 团队通常会添加_isInitialized保护标志、双重检查锁定或信号量以避免重复初始化。 迁移后,所有这些代码都变成了无效代码。
解决方案: IronTesseract没有初始化步骤。语言在实例上只需设置一次。 移除所有_isInitialized标志,以及所有初始化保护逻辑:
// Before: initialization guard required before every OCR call
private bool _isInitialized = false;
private readonly SemaphoreSlim _initLock = new SemaphoreSlim(1, 1);
public async Task<string> GetTextAsync(string imagePath)
{
await _initLock.WaitAsync();
try
{
if (!_isInitialized)
{
await _tesseract.InitAsync("eng");
_isInitialized = true;
}
}
finally { _initLock.Release(); }
var result = await _tesseract.RecognizeTextAsync(imagePath);
return result.RecognizedText ?? string.Empty;
}
// After: no initialization, no guard, no semaphore
public string GetText(string imagePath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
return ocr.Read(input).Text;
}' Before: initialization guard required before every OCR call
Private _isInitialized As Boolean = False
Private ReadOnly _initLock As New SemaphoreSlim(1, 1)
Public Async Function GetTextAsync(imagePath As String) As Task(Of String)
Await _initLock.WaitAsync()
Try
If Not _isInitialized Then
Await _tesseract.InitAsync("eng")
_isInitialized = True
End If
Finally
_initLock.Release()
End Try
Dim result = Await _tesseract.RecognizeTextAsync(imagePath)
Return If(result.RecognizedText, String.Empty)
End Function
' After: no initialization, no guard, no semaphore
Public Function GetText(imagePath As String) As String
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage(imagePath)
Return ocr.Read(input).Text
End Using
End Function问题 4:结果。成功检查模式必须替换
TesseractOcrMaui: Status字符串。 检查result.Status以获取错误信息的代码需要重写。
解决方案: IronOCR使用标准的.NET异常语义。 用 try/catch 语句替换成功标志检查。 成功时,.Text始终被填充(如果未找到文本则为空字符串):
// Before: success-flag pattern
var result = await _tesseract.RecognizeTextAsync(imagePath);
if (!result.Success)
{
logger.LogError("OCR failed: {Status}", result.Status);
return string.Empty;
}
return result.RecognizedText ?? string.Empty;
// After: exception pattern
try
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
return result.Text; // empty string if no text found — never null
}
catch (Exception ex)
{
logger.LogError(ex, "OCR failed for {Path}.", imagePath);
return string.Empty;
}Try
Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadImage(imagePath)
Dim result = ocr.Read(input)
Return result.Text ' empty string if no text found — never null
End Using
Catch ex As Exception
logger.LogError(ex, "OCR failed for {Path}.", imagePath)
Return String.Empty
End Try问题 5:共享库项目中仅支持 MAUI 的目标框架
TesseractOcrMaui: 引用TesseractOcrMaui的类库自动继承其平台限制。 库的net8.0-windows),这使得它无法被服务器项目引用。
解决方案: 将类库目标更改为IronOcr。 现在任何使用该库的项目都能正确解析它:
<!-- Before: locked to MAUI target becauseTesseractOcrMauihas no net8.0 target -->
<TargetFramework>net8.0-android</TargetFramework>
<PackageReference Include="TesseractOcrMaui" Version="*" />
<!-- After: universal target — referenced from MAUI, API, worker, and Functions -->
<TargetFramework>net8.0</TargetFramework>
<PackageReference Include="IronOcr" Version="*" />
问题 6:PDF 处理需要移除第二个库
TesseractOcrMaui: 实现PDF支持的团队增加了第二个库(PDFium、PdfPig或云渲染器)以在传递给RecognizeTextAsync之前将PDF页面转换为图像。 迁移到IronOCR后,就可以删除第二个库及其所有页面渲染代码。
解决方案: 移除PDF渲染库,并用input.LoadPdf()替换整个页面提取管道:
// Before: PDF library + manual temp file management (50+ lines)
using var pdfDoc = PdfDocument.Open(pdfPath);
var results = new List<string>();
foreach (var page in pdfDoc.GetPages())
{
var tempImagePath = Path.Combine(FileSystem.CacheDirectory, $"page_{page.Number}.png");
RenderPageToImage(page, tempImagePath, dpi: 300);
await _tesseract.InitAsync("eng");
var r = await _tesseract.RecognizeTextAsync(tempImagePath);
results.Add(r.RecognizedText ?? string.Empty);
File.Delete(tempImagePath);
}
return string.Join("\n", results);
// After: native PDF support — 5 lines
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
var result = ocr.Read(input);
return result.Text;Dim ocr = New IronTesseract()
Using input = New OcrInput()
input.LoadPdf(pdfPath)
Dim result = ocr.Read(input)
Return result.Text
End UsingPDF 输入指南涵盖页面范围选择、密码保护的 PDF 和基于流的加载。
##TesseractOcrMaui迁移清单
迁移前
在更改任何代码之前,请审核代码库,清点所有TesseractOcrMaui的使用情况:
# Find all files that referenceTesseractOcrMauinamespaces
grep -r "TesseractOcrMaui" --include="*.cs" .
# Find all ITesseract injection points
grep -r "ITesseract" --include="*.cs" .
# Find all AddTesseractOcr registrations
grep -r "AddTesseractOcr" --include="*.cs" .
# Find all InitAsync calls
grep -r "InitAsync" --include="*.cs" .
# Find all RecognizeTextAsync calls
grep -r "RecognizeTextAsync" --include="*.cs" .
# Find traineddata asset declarations in project files
grep -r "tessdata" --include="*.csproj" .
# Find MauiAsset traineddata declarations
grep -r "MauiAsset" --include="*.csproj" .
# Identify projects with MAUI-only target frameworks that hold OCR logic
grep -r "net8.0-android\|net8.0-ios\|net8.0-windows" --include="*.csproj" .
注意每个在构造函数中使用ITesseract的类——这些构造函数会改变。 注意每个声明训练数据<MauiAsset>的项目文件——这些声明将被删除。 确定是否存在 PDF 渲染库,以及该库是否专门用于 OCR 预处理。
代码迁移
- 在引用它的每个项目中运行
dotnet remove package TesseractOcrMaui - 在将执行OCR的每个项目中运行
dotnet add package IronOcr - 在面向Android的MAUI项目中运行
dotnet add package IronOcr.Android - 在面向iOS的MAUI项目中运行
dotnet add package IronOcr.iOS - 对以前打包为训练数据的任何非英语语言运行
dotnet add package IronOcr.Languages.* - 在每个入口点项目中的应用程序启动时添加
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; - 从MAUI项目中删除
.traineddata文件 - 从
<MauiAsset Include="Resources\Raw\tessdata\*.traineddata" />行 - 从所有
builder.Services.AddTesseractOcr() - 用
using TesseractOcrMaui.Results; - 从所有服务和视图模型类中移除
ITesseract构造函数参数 - 如有需要,将
ocr.Language = OcrLanguage.English;(默认使用英语) - 使用
ocr.Read(input) - 将
result.Text - 将
if (!result.Success)检查替换为try/catch块 - 如果某PDF渲染库仅为支持TesseractOcrMaui而添加,删除它并替换页面提取代码
input.LoadPdf() - 将类库中持有OCR逻辑的仅限MAUI目标框架更改为
netstandard2.1
后迁移
- 验证 OCR 能否从 iOS 和 Android 目标设备上摄像头拍摄的 JPEG 图像中提取文本
- 验证OCR是否从通过服务器端API端点加载的相同图像生成文本
byte[] - 确认当从 MAUI 项目和ASP.NET Core项目中引用共享类库时,编译和运行结果完全相同
- 测试 PDF 输入是否能完整正常运行,无需创建任何临时文件。
- 验证
SaveAsSearchablePdf输出是否在PDF查看器中可索引 - 确认信心分数存在于
page.Words[i].Confidence上 - 测试 MAUI 应用是否生成无错误的启动日志,且不出现找不到训练数据文件的异常。
- 验证
Resources/Raw/tessdata/文件夹在发布构建的MAUI应用程序包中不存在 - 运行包含 10 个或更多文档的并行批处理作业,以验证线程安全性
- 确认移除
_isInitialized状态变量
迁移到IronOCR的主要优势
整个产品使用一个代码库。 迁移后,解决方案中的每个项目——MAUI移动应用程序、ASP.NET Core API、Azure Function、后台工作者——都调用相同的DocumentOcrService类,从同一个共享库中。 语言配置、预处理设置和准确率调优都在同一位置进行。 当新的文档类型需要新的预处理过滤器时,只需更改一次,即可在所有地方生效。
无需重写即可进行IronOCR端部署。IronOCR无需修改即可部署到 Linux 容器、Windows Server、Azure 应用服务、AWS Lambda 以及任何其他.NET 8 运行时目标平台。 相同的IronTesseract实例处理移动相机捕获和服务器端PDF上传。 Azure 部署指南和AWS 部署指南记录了特定于平台的配置步骤。
无需第二个库的PDF处理。 通过input.LoadPdf()的本地PDF输入消除了PDF渲染库、逐页图像提取循环、临时文件管理和TesseractOcrMaui架构所需的清理代码。 扫描的PDF合同、发票和身份文件在一行中加载。与提取文本相同的OCR过程可以生成一个可搜索的PDF,具有result.SaveAsSearchablePdf()——这是TesseractOcrMaui无法在任何级别提供的功能。
处理真实移动图像的预处理。 input.Sharpen()是单方法调用,它们在Tesseract引擎看到数据之前应用校准的图像校正。 以往在低光照条件下使用移动设备拍摄照片,未经预处理的准确率通常只有 40% 到 60%,而添加三层滤镜处理流程后,准确率一般能达到 85% 到 90% 以上。无需 SkiaSharp、ImageSharp 或任何算法实现。 图像质量校正指南详细记录了所有可用的滤镜及其应用时机。
提供完善的商业支持及升级流程。Iron Iron Software为所有IronOCR许可级别提供电子邮件支持,并为Professional和Enterprise版用户提供优先电话和在线聊天支持。 当平台更新破坏了特定 Android API 级别的原生库解析时——这种故障由TesseractOcrMaui的GitHub问题队列在志愿者时间中处理——确实有一个工程团队有义务做出响应。 永久许可证从Lite层的$999开始; 许可页面列出了所有级别及其包含的支持级别。
通过NuGet提供 125 多种语言,无需增加应用包大小。TesseractOcrMaui将训练数据文件打包在 MAUI 应用内——每种语言IronOCR应用下载大小增加 10-50 MB。IronOCR 语言包通过NuGet安装,并且仅包含在服务器端构建或明确引用它们的平台构建中。 移动应用包保持精简; 服务器端构建将获得完整的语言集。 添加新语言是一条dotnet add package命令,无需项目文件更改和文件管理。 完整的语言目录列出了所有 125 多个可用的语言包。
