从 Windows.Media.OCR 迁移到 IronOCR
本指南为.NET开发人员从 Windows.Media.Ocr 迁移到IronOCR提供了分步迁移路径。 它涵盖了命名空间删除、项目文件更改、迁移过程中最常出现的模式的代码迁移示例,以及用于验证已完成过渡的实用清单。
为什么要从 Windows.Media.Ocr(UWP/WinRT OCR)迁移?
Windows.Media.Ocr 在其功能范围内运行良好。 这些界限很窄,项目经常会超出这些界限。 团队迁移的原因可以归纳为一些可预测的类别。
Windows TFM 阻止所有非Windows目标。 项目文件必须在 net*-windows* 命名空间之前声明一个目标框架标识符,才能在编译时解析。该声明不是运行时标志,而是一个构建约束,传播到引用您的项目的每个项目。 共享的 OCR 服务库、Web API、部署在 Linux 上的后台工作程序——它们都继承了这一约束。 删除它意味着删除 Windows.Media.Ocr。
语言可用性在运行时由操作系统确定,而不是在构建时由开发者确定。 当请求的语言包不在主机上时,OcrEngine.TryCreateFromLanguage 返回空值。开发者无法通过代码安装语言包、与应用程序二进制文件捆绑或提供备用模型。 在自动化环境中(构建代理、CI 运行器、最小云虚拟机、容器),很少安装语言包。 通过查看代码无法重现因缺少语言包而导致的生产故障; 需要检查目标机器的操作系统配置。
没有预处理意味着没有次优输入的恢复路径。 API 接受一个 SoftwareBitmap 并生成文本。 这两个点之间的图像质量改进完全由开发人员负责,他们使用单独的 Windows 图像组件 API,而这些 API 本身也仅适用于 Windows。 手机照片、平板扫描仪错位以及复印文件都会悄无声息地降低精度,而且没有内置机制来诊断或改善结果。
PDF 是Enterprise工作流程中最常用的文档格式。Windows.Media.Ocr没有 PDF 输入路径。 处理扫描的 PDF 文件需要外部渲染器、逐页光栅化和手动结果组装。 该渲染器增加了依赖项、许可方面的考虑以及单独的故障面——这正是"免费且内置"的库本应避免的复杂性。
服务器端部署在结构上不受支持。Windows.Media.Ocr面向客户端应用程序。 在 Windows Server 上运行需要桌面体验功能包,这会增加虚拟机成本和基础架构复杂性。 Docker部署是不可能的。 Azure Functions on Linux、AWS Lambda 以及任何基于 Linux 的容器工作负载都无法引用该 API。
WinRT 异步堆栈与标准 .NET 模式不兼容。 在读取单个字符之前,需要调用六个或更多链式的 await 调用——RecognizeAsync。 将该链集成到后台服务、Parallel.ForEach 循环或标准ASP.NET控制器中很麻烦。 WinRT IAsyncOperation 机械结构位于其下,并且与 .NET 的 Task 模型的交互在非UI上下文中创造了细微的边缘情况。
基本问题
Windows.Media.Ocr 中的语言可用性是一个运行时未知问题,无法在部署时解决:
// Windows.Media.Ocr: language availability decided by OS admin, not the developer
// Returns null on any machine without the language pack installed
var engine = OcrEngine.TryCreateFromLanguage(
new Windows.Globalization.Language("ja-JP"));
if (engine == null)
throw new InvalidOperationException(
"Japanese OCR unavailable — install the Japanese language pack in Windows Settings.");
// 否 recovery path. 否 bundled model. 否 fallback.' Windows.Media.Ocr: language availability decided by OS admin, not the developer
' Returns null on any machine without the language pack installed
Dim engine = OcrEngine.TryCreateFromLanguage(New Windows.Globalization.Language("ja-JP"))
If engine Is Nothing Then
Throw New InvalidOperationException("Japanese OCR unavailable — install the Japanese language pack in Windows Settings.")
End If
' 否 recovery path. 否 bundled model. 否 fallback.// IronOCR: language availability is a NuGet package, not an OS configuration
// dotnet add package IronOcr.Languages.Japanese
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.Japanese;
var result = ocr.Read("invoice.jpg"); // Works on any OS, any machine
Console.WriteLine(result.Text);' IronOCR: language availability is a NuGet package, not an OS configuration
' dotnet add package IronOcr.Languages.Japanese
Dim ocr = New IronTesseract()
ocr.Language = OcrLanguage.Japanese
Dim result = ocr.Read("invoice.jpg") ' Works on any OS, any machine
Console.WriteLine(result.Text)IronOCR与 Windows.Media.Ocr(UWP/WinRT OCR):功能对比
下表涵盖了与迁移决策相关的全部能力范围。
| 特征 | Windows.Media.Ocr | IronOCR |
|---|---|---|
| 平台:Windows 10/11 | 是 | 是 |
| 平台:Windows Server | 有限(需要桌面操作经验) | 是 |
| 平台:Linux | 否 | 是 |
| 平台:macOS | 否 | 是 |
| 平台:Docker容器 | 否 | 是 |
| 平台:Azure Functions(Linux) | 否 | 是 |
| 平台:AWS Lambda | 否 | 是 |
| 项目 TFM 要求 | net*-windows* 必需 | 无(标准 TFM) |
| 安装 | Windows 内置(无NuGet) | 单个 NuGet 包 (IronOcr) |
| 图片输入(JPG、PNG、BMP) | 是的(通过 WinRT 管道) | 是 |
| PDF 输入 | 否 | 是的(母语) |
| 多页 TIFF 输入 | 否 | 是 |
| 流和字节数组输入 | 否(仅限存储文件) | 是 |
| 语言来源 | 操作系统自带语言包 | 125 多个捆绑的NuGet包 |
| 语言可移植性 | 否(取决于机器) | 是的(随应用程序一起部署) |
| 多语言同步 | 否 | 是 |
| 预处理:去斜 | 否 | 是 (input.Deskew()) |
| 预处理:去噪 | 否 | 是 (input.DeNoise()) |
| 预处理:对比度 | 否 | 是 (input.Contrast()) |
| 预处理:二值化 | 否 | 是 (input.Binarize()) |
| 可搜索的 PDF 输出 | 否 | 是 (result.SaveAsSearchablePdf()) |
| 逐词置信度得分 | 否 | 是 (word.Confidence) |
| 结构化输出(段落、行、单词) | 仅线条 | 页数、段落数、行数、单词数、字符数 |
| OCR过程中的条形码读取 | 否 | 是 |
| 基于区域的OCR | 否 | 是 (CropRectangle) |
| 同步OCR路径 | 否 | 是 |
| 线程安全的并行处理 | 有限的 | 满的 |
| 商业支持 | 否(Windows平台团队) | 是 |
| 许可模式 | 免费(Windows 内置) | 永久 ($999 Lite, $1,499 Pro, $2,999 Enterprise) |
快速入门:Windows.Media.Ocr(UWP/WinRT OCR)到IronOCR 的迁移
步骤 1:替换 NuGet 软件包
Windows.Media.Ocr 没有NuGet包——它是 Windows 运行时的一部分,并通过 Windows TFM 解析。 删除它意味着删除特定于 Windows 的命名空间引用,并且尽可能从项目文件中删除 Windows TFM。
从所有源文件中移除 Windows.Media.Ocr 命名空间:
# Audit all files referencing Windows OCR namespaces
grep -r "Windows.Media.Ocr\|Windows.Graphics.Imaging\|Windows.Storage" --include="*.cs" .
安装IronOCR:
IronOCR NuGet 包 目标 net6.0, net7.0, net8.0, 和 net9.0,不带平台特定的 TFM。 在移除 Windows OCR 命名空间后,将项目文件中的 <TargetFramework> 从 net8.0-windows10.0.19041.0 更新到 net8.0(或适当的版本),前提是项目中没有其他 WinRT API。
步骤 2:更新命名空间
将三个 Windows OCR 命名空间替换为单个IronOCR命名空间:
// Before (Windows.Media.Ocr)
using Windows.Media.Ocr;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Globalization;
// After (IronOCR)
using IronOcr;// Before (Windows.Media.Ocr)
Imports Windows.Media.Ocr
Imports Windows.Graphics.Imaging
Imports Windows.Storage
Imports Windows.Globalization
// After (IronOCR)
Imports IronOcr步骤 3:初始化许可证
在应用程序启动时调用一次许可证初始化——在 Startup.cs,或应用程序主机构建器中:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"IronOCR许可页面提供免费试用密钥,可用于移除试用水印以进行评估。
代码迁移示例
在后台服务中替换 WinRT 异步链
Windows.Media.Ocr 至少需要六个链式异步操作才能开始识别。 在处理文档队列的后台服务中,该链在循环内运行——每次迭代都添加 SoftwareBitmap 处置、空检查和 WinRT IAsyncOperation 互操作的摩擦。
Windows.Media.Ocr 方法:
// Windows.Media.Ocr: full async chain required per document
// Requires net8.0-windows10.0.19041.0 TFM — cannot deploy to Linux workers
public async Task<List<string>> ProcessQueueAsync(IEnumerable<string> imagePaths)
{
var engine = OcrEngine.TryCreateFromUserProfileLanguages();
if (engine == null)
throw new InvalidOperationException("No OCR language pack installed on this machine.");
var results = new List<string>();
foreach (var path in imagePaths)
{
// Each document: 4 async steps before RecognizeAsync
var file = await StorageFile.GetFileFromPathAsync(path);
using var stream = await file.OpenAsync(FileAccessMode.Read);
var decoder = await BitmapDecoder.CreateAsync(stream);
var bitmap = await decoder.GetSoftwareBitmapAsync();
var ocrResult = await engine.RecognizeAsync(bitmap);
results.Add(ocrResult.Text);
bitmap.Dispose();
}
return results;
}Public Async Function ProcessQueueAsync(imagePaths As IEnumerable(Of String)) As Task(Of List(Of String))
Dim engine = OcrEngine.TryCreateFromUserProfileLanguages()
If engine Is Nothing Then
Throw New InvalidOperationException("No OCR language pack installed on this machine.")
End If
Dim results As New List(Of String)()
For Each path In imagePaths
' Each document: 4 async steps before RecognizeAsync
Dim file = Await StorageFile.GetFileFromPathAsync(path)
Using stream = Await file.OpenAsync(FileAccessMode.Read)
Dim decoder = Await BitmapDecoder.CreateAsync(stream)
Dim bitmap = Await decoder.GetSoftwareBitmapAsync()
Dim ocrResult = Await engine.RecognizeAsync(bitmap)
results.Add(ocrResult.Text)
bitmap.Dispose()
End Using
Next
Return results
End FunctionIronOCR方法:
// IronOCR: one call per document, no WinRT, no SoftwareBitmap, no null checks
// Runs on Windows, Linux, macOS, Docker — same binary, no TFM change
public List<string> ProcessQueue(IEnumerable<string> imagePaths)
{
var results = new List<string>();
foreach (var path in imagePaths)
{
var result = new IronTesseract().Read(path);
results.Add(result.Text);
}
return results;
}Public Function ProcessQueue(imagePaths As IEnumerable(Of String)) As List(Of String)
Dim results = New List(Of String)()
For Each path In imagePaths
Dim result = New IronTesseract().Read(path)
results.Add(result.Text)
Next
Return results
End FunctionIronOCR 版本消除了 StorageFile 往返,SoftwareBitmap 生命周期和空检查保护。 对于异步本地服务,IronOCR 提供了一个异步路径,可以干净地集成到基于 Task 的管道中,没有 WinRT 互操作开销。 IronTesseract 设置指南涵盖了高吞吐量队列场景的实例生命周期建议。
消除内存图像数据的软件位图转换
已经在内存中拥有图像数据的应用程序——从网络下载、数据库 Blob 或相机捕获回调——必须将数据转换为 SoftwareBitmap,才能让 Windows.Media.Ocr 处理。 转换路径通过 BitmapDecoder,需要一个流,这意味着将字节阵列复制到 MemoryStream。 IronOCR直接接受字节数组和字节流。
Windows.Media.Ocr 方法:
// Windows.Media.Ocr: byte array must travel through WinRT stream → BitmapDecoder → SoftwareBitmap
public async Task<string> RecognizeFromBytesAsync(byte[] imageBytes)
{
var engine = OcrEngine.TryCreateFromUserProfileLanguages();
if (engine == null)
throw new InvalidOperationException("No OCR language available.");
// Copy byte array into InMemoryRandomAccessStream (WinRT type)
using var ras = new Windows.Storage.Streams.InMemoryRandomAccessStream();
using var writer = new Windows.Storage.Streams.DataWriter(ras);
writer.WriteBytes(imageBytes);
await writer.StoreAsync();
ras.Seek(0);
var decoder = await BitmapDecoder.CreateAsync(ras);
var bitmap = await decoder.GetSoftwareBitmapAsync();
var result = await engine.RecognizeAsync(bitmap);
bitmap.Dispose();
return result.Text;
}Public Async Function RecognizeFromBytesAsync(imageBytes As Byte()) As Task(Of String)
Dim engine = OcrEngine.TryCreateFromUserProfileLanguages()
If engine Is Nothing Then
Throw New InvalidOperationException("No OCR language available.")
End If
' Copy byte array into InMemoryRandomAccessStream (WinRT type)
Using ras As New Windows.Storage.Streams.InMemoryRandomAccessStream()
Using writer As New Windows.Storage.Streams.DataWriter(ras)
writer.WriteBytes(imageBytes)
Await writer.StoreAsync()
ras.Seek(0)
End Using
Dim decoder = Await BitmapDecoder.CreateAsync(ras)
Dim bitmap = Await decoder.GetSoftwareBitmapAsync()
Dim result = Await engine.RecognizeAsync(bitmap)
bitmap.Dispose()
Return result.Text
End Using
End FunctionIronOCR方法:
// IronOCR: byte array loads directly into OcrInput — no conversion, no WinRT types
public string RecognizeFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // direct byte array load
var result = new IronTesseract().Read(input);
return result.Text;
}Public Function RecognizeFromBytes(imageBytes As Byte()) As String
Using input As New OcrInput()
input.LoadImage(imageBytes) ' direct byte array load
Dim result = New IronTesseract().Read(input)
Return result.Text
End Using
End FunctionWindows.Media.Ocr 路径需要 InMemoryRandomAccessStream —— 一个无法在 Windows 之外实例化的 WinRT 类型,以及 BitmapDecoder 和 SoftwareBitmap。IronOCR路径使用 OcrInput.LoadImage(byte[]) 并在两个代码行内产生结果。 查看 流输入指南 了解基于 Stream 的加载模式,其简便性与字节数组输入相同。
无需操作系统协调的多语言文档处理
一个必须在一次处理中识别英语、法语和德语文本的多语言发票处理流程,在使用 Windows.Media.Ocr 时遇到了架构上的死胡同。 API 只允许每个引擎实例使用一种语言。 处理混合语言文档需要使用最佳猜测的单语言引擎,或者运行三次识别并合并结果——但这两种方法都无法产生可靠的输出。
Windows.Media.Ocr 方法:
// Windows.Media.Ocr: one language per engine, no simultaneous multi-language support
// Each language requires a separate language pack installed on the machine
public async Task<string> RecognizeMultiLanguageAsync(SoftwareBitmap bitmap)
{
// Must pick ONE language — no simultaneous recognition
var engine = OcrEngine.TryCreateFromLanguage(
new Windows.Globalization.Language("en-US"));
if (engine == null)
throw new InvalidOperationException("English language pack not installed.");
// French and German text on the same document will be misrecognized
var result = await engine.RecognizeAsync(bitmap);
return result.Text;
}' Windows.Media.Ocr: one language per engine, no simultaneous multi-language support
' Each language requires a separate language pack installed on the machine
Public Async Function RecognizeMultiLanguageAsync(bitmap As SoftwareBitmap) As Task(Of String)
' Must pick ONE language — no simultaneous recognition
Dim engine = OcrEngine.TryCreateFromLanguage(New Windows.Globalization.Language("en-US"))
If engine Is Nothing Then
Throw New InvalidOperationException("English language pack not installed.")
End If
' French and German text on the same document will be misrecognized
Dim result = Await engine.RecognizeAsync(bitmap)
Return result.Text
End FunctionIronOCR方法:
// IronOCR: simultaneous multi-language recognition in a single pass
// Language packs are NuGet packages — no OS coordination required
// dotnet add package IronOcr.Languages.French
// dotnet add package IronOcr.Languages.German
public string RecognizeMultiLanguage(string documentPath)
{
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;
var result = ocr.Read(documentPath);
// Structured output: walk paragraphs with location data
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[{paragraph.X},{paragraph.Y}] {paragraph.Text}");
}
}
return result.Text;
}Public Function RecognizeMultiLanguage(documentPath As String) As String
Dim ocr = New IronTesseract()
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German
Dim result = ocr.Read(documentPath)
' Structured output: walk paragraphs with location data
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"[{paragraph.X},{paragraph.Y}] {paragraph.Text}")
Next
Next
Return result.Text
End FunctionIronOCR将多种语言模型组合在一个识别过程中,无需猜测特定地区使用的语言。 多语言 OCR 指南 涵盖了语言包安装以及 125+ 支持语言的 OcrLanguage 枚举值。 语言索引列出了完整的目录,包括中日韩文字、阿拉伯语、希伯来语、梵文和西里尔字母。
启用服务器端OCR和并行处理
Windows.Media.Ocr 无法在 Linux 的服务器上下文中运行,无法从跨平台主机上的标准ASP.NET Core控制器调用,并且在服务器场景中从非 UI 线程调用时具有未定义的行为。 一个团队将 OCR 端点从仅限 Windows 的桌面应用程序迁移到可扩展的 Web API 时,同时遇到了所有这三个限制。
Windows.Media.Ocr 方法:
// Windows.Media.Ocr: cannot run on Linux, Docker, or Azure Functions on Linux
// UWP/WinRT assumptions about thread context cause failures in ASP.NET pipelines
// The entire approach below is non-deployable outside Windows with Desktop Experience
[HttpPost("ocr")]
public async Task<IActionResult> RecognizeDocument(IFormFile file)
{
// WinRT requires STA thread context in some scenarios — not guaranteed in ASP.NET
// Cannot deploy this controller to a Linux App Service plan
using var stream = file.OpenReadStream();
// InMemoryRandomAccessStream is a WinRT type — does not exist on Linux
// var ras = new InMemoryRandomAccessStream(); // compile error on net8.0 TFM
return StatusCode(503, "Windows-only — cannot deploy cross-platform.");
}' Windows.Media.Ocr: cannot run on Linux, Docker, or Azure Functions on Linux
' UWP/WinRT assumptions about thread context cause failures in ASP.NET pipelines
' The entire approach below is non-deployable outside Windows with Desktop Experience
<HttpPost("ocr")>
Public Async Function RecognizeDocument(file As IFormFile) As Task(Of IActionResult)
' WinRT requires STA thread context in some scenarios — not guaranteed in ASP.NET
' Cannot deploy this controller to a Linux App Service plan
Using stream = file.OpenReadStream()
' InMemoryRandomAccessStream is a WinRT type — does not exist on Linux
' Dim ras As New InMemoryRandomAccessStream() ' compile error on net8.0 TFM
Return StatusCode(503, "Windows-only — cannot deploy cross-platform.")
End Using
End FunctionIronOCR方法:
// IronOCR: ASP.NET Core controller running on Linux, Docker, or Windows — same code
[HttpPost("ocr")]
public async Task<IActionResult> RecognizeDocument(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file provided.");
using var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
var imageBytes = memoryStream.ToArray();
using var input = new OcrInput();
input.LoadImage(imageBytes);
input.Deskew(); // straighten uploaded scans automatically
input.DeNoise(); // remove mobile camera noise
var result = new IronTesseract().Read(input);
return Ok(new
{
Text = result.Text,
Confidence = result.Confidence,
Pages = result.Pages.Count
});
}<HttpPost("ocr")> _
Public Async Function RecognizeDocument(file As IFormFile) As Task(Of IActionResult)
If file Is Nothing OrElse file.Length = 0 Then
Return BadRequest("No file provided.")
End If
Using memoryStream As New MemoryStream()
Await file.CopyToAsync(memoryStream)
Dim imageBytes = memoryStream.ToArray()
Using input As New OcrInput()
input.LoadImage(imageBytes)
input.Deskew() ' straighten uploaded scans automatically
input.DeNoise() ' remove mobile camera noise
Dim result = New IronTesseract().Read(input)
Return Ok(New With {
.Text = result.Text,
.Confidence = result.Confidence,
.Pages = result.Pages.Count
})
End Using
End Using
End Function该控制器无需修改即可部署到 Linux 应用服务、Docker 和 AWS Lambda。 Docker 部署指南 涵盖了 Linux 基础镜像所需的单一 apt-get 依赖。 Azure 部署指南和AWS 指南将逐步介绍特定于云的配置。
从扫描存档生成可搜索的 PDF 文件
Windows.Media.Ocr 生成的是纯文本字符串。 除了 OcrResult.Text 和 OcrResult.Lines 中的行几何外,没有输出格式。 将扫描的存档转换为可搜索的 PDF(这是文档管理系统和合规工作流程的常见要求)需要第三个库来构建 PDF 输出层。 IronOCR可直接生成可搜索的 PDF 文件。
Windows.Media.Ocr 方法:
// Windows.Media.Ocr: plain text output only
// Searchable PDF requires external PDF library + manual text layer construction
public async Task<string> GetTextOnlyAsync(SoftwareBitmap bitmap)
{
var engine = OcrEngine.TryCreateFromUserProfileLanguages();
if (engine == null)
throw new InvalidOperationException("No OCR language available.");
var result = await engine.RecognizeAsync(bitmap);
// result.Text is all you get
// Producing a searchable PDF requires an entirely separate library
return result.Text;
}' Windows.Media.Ocr: plain text output only
' Searchable PDF requires external PDF library + manual text layer construction
Public Async Function GetTextOnlyAsync(bitmap As SoftwareBitmap) As Task(Of String)
Dim engine = OcrEngine.TryCreateFromUserProfileLanguages()
If engine Is Nothing Then
Throw New InvalidOperationException("No OCR language available.")
End If
Dim result = Await engine.RecognizeAsync(bitmap)
' result.Text is all you get
' Producing a searchable PDF requires an entirely separate library
Return result.Text
End FunctionIronOCR方法:
// IronOCR: searchable PDF output is one method call on OcrResult
public void ProcessScannedArchive(IEnumerable<string> pdfPaths, string outputDirectory)
{
foreach (var sourcePdf in pdfPaths)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(sourcePdf); // native PDF input — no external renderer
input.Deskew(); // correct scan misalignment per page
input.DeNoise(); // remove scanner speckle
var result = ocr.Read(input);
var outputFileName = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(sourcePdf) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputFileName);
Console.WriteLine($"Processed: {sourcePdf} → {outputFileName} " +
$"({result.Pages.Count} pages, {result.Confidence:F1}% confidence)");
}
}Public Sub ProcessScannedArchive(pdfPaths As IEnumerable(Of String), outputDirectory As String)
For Each sourcePdf In pdfPaths
Dim ocr = New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(sourcePdf) ' native PDF input — no external renderer
input.Deskew() ' correct scan misalignment per page
input.DeNoise() ' remove scanner speckle
Dim result = ocr.Read(input)
Dim outputFileName = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(sourcePdf) & "-searchable.pdf")
result.SaveAsSearchablePdf(outputFileName)
Console.WriteLine($"Processed: {sourcePdf} → {outputFileName} ({result.Pages.Count} pages, {result.Confidence:F1}% confidence)")
End Using
Next
End SubSaveAsSearchablePdf 调用在原始扫描图像上嵌入了一个文本层,保留视觉准确性,同时在任何 PDF 查看器中启用全文搜索和 Ctrl+F。 这份可搜索的 PDF 操作指南涵盖了字体嵌入、文本图层定位和多页输出等选项。 PDF 输入指南涵盖了受密码保护的 PDF 和大型存档的页面范围选择。
利用词级坐标提取结构化数据
Windows.Media.Ocr 暴露 OcrResult.Lines,带有行级文本和边界矩形。 每个单词的几何形状在 OcrLine.Words 中存在,但没有段落、置信度得分和字符级数据。 对于表单字段提取或发票行项目解析,行几何形状是不够的——需要段落边界和单词置信度分数来区分结构化字段和周围的文本。
Windows.Media.Ocr 方法:
// Windows.Media.Ocr: line-level geometry, no paragraph grouping, no confidence scores
public async Task<List<string>> ExtractLineTextAsync(SoftwareBitmap bitmap)
{
var engine = OcrEngine.TryCreateFromUserProfileLanguages();
if (engine == null)
throw new InvalidOperationException("No OCR language available.");
var result = await engine.RecognizeAsync(bitmap);
var lineTexts = new List<string>();
foreach (var line in result.Lines)
{
// Line text + word bounding rects — no paragraph grouping, no confidence
lineTexts.Add(line.Text);
}
return lineTexts;
}Public Async Function ExtractLineTextAsync(bitmap As SoftwareBitmap) As Task(Of List(Of String))
Dim engine = OcrEngine.TryCreateFromUserProfileLanguages()
If engine Is Nothing Then
Throw New InvalidOperationException("No OCR language available.")
End If
Dim result = Await engine.RecognizeAsync(bitmap)
Dim lineTexts As New List(Of String)()
For Each line In result.Lines
' Line text + word bounding rects — no paragraph grouping, no confidence
lineTexts.Add(line.Text)
Next
Return lineTexts
End FunctionIronOCR方法:
// IronOCR: full hierarchy — pages, paragraphs, lines, words, characters
// Each element carries coordinates and confidence for downstream validation
public void ExtractStructuredData(string documentPath)
{
var result = new IronTesseract().Read(documentPath);
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%");
foreach (var page in result.Pages)
{
Console.WriteLine($"\n--- Page {page.PageNumber} ---");
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Paragraph at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
// Filter words below confidence threshold for validation workflows
var lowConfidence = paragraph.Words
.Where(w => w.Confidence < 70)
.ToList();
if (lowConfidence.Any())
{
Console.WriteLine($" Low-confidence words: " +
string.Join(", ", lowConfidence.Select(w => $"'{w.Text}' ({w.Confidence:F0}%)")));
}
}
}
}Public Sub ExtractStructuredData(documentPath As String)
Dim result = New IronTesseract().Read(documentPath)
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%")
For Each page In result.Pages
Console.WriteLine(vbCrLf & $"--- Page {page.PageNumber} ---")
For Each paragraph In page.Paragraphs
Console.WriteLine($"Paragraph at ({paragraph.X},{paragraph.Y}): {paragraph.Text}")
' Filter words below confidence threshold for validation workflows
Dim lowConfidence = paragraph.Words.Where(Function(w) w.Confidence < 70).ToList()
If lowConfidence.Any() Then
Console.WriteLine($" Low-confidence words: " & String.Join(", ", lowConfidence.Select(Function(w) $"'{w.Text}' ({w.Confidence:F0}%)")))
End If
Next
Next
End Sub结构化结果模型——Pages, Paragraphs, Lines, Words, Characters——提供了提取表单字段、发票解析和文档布局分析所需的坐标和置信度数据。 读取结果指南记录了完整的 OcrResult 对象图。 置信度评分指南解释了如何使用每个单词的置信度值来标记不确定的提取内容,以便进行人工审核。
Windows.Media.Ocr API 到IronOCR映射参考
| Windows.Media.Ocr | IronOCR |
|---|---|
OcrEngine.TryCreateFromLanguage(lang) | new IronTesseract() + ocr.Language = OcrLanguage.X |
OcrEngine.TryCreateFromUserProfileLanguages() | new IronTesseract()(默认英语; 无空返回值) |
engine.RecognizeAsync(softwareBitmap) | ocr.Read("image.jpg") 或 ocr.Read(ocrInput) |
StorageFile.GetFileFromPathAsync(path) | ocr.Read("path") 直接(不需要文件句柄) |
file.OpenAsync(FileAccessMode.Read) | 已消除——OcrInput 直接加载 |
BitmapDecoder.CreateAsync(stream) | input.LoadImage(stream) 通过 OcrInput |
decoder.GetSoftwareBitmapAsync() | 已消除——IronOCR 中没有 SoftwareBitmap |
SoftwareBitmap(WinRT 类型) | 已消除——OcrInput 接受字节、流、文件路径 |
InMemoryRandomAccessStream(WinRT 类型) | new MemoryStream() + input.LoadImage(stream) |
OcrResult.Text | OcrResult.Text |
OcrResult.Lines | OcrResult.Lines(也包括 Pages, Paragraphs, Words, Characters) |
OcrLine.Text | OcrResult.Lines[i].Text |
OcrLine.Words | OcrResult.Words 或 page.Paragraphs[i].Words |
OcrWord.BoundingRect | word.X, word.Y, word.Width, word.Height |
| 没有等效物 | result.Confidence(整体)/ word.Confidence(每个单词) |
| 没有等效物 | result.SaveAsSearchablePdf("output.pdf") |
| 没有等效物 | input.LoadPdf("document.pdf") |
| 没有等效物 | input.Deskew(), input.DeNoise(), input.Contrast() |
| 没有等效物 | ocr.Language = OcrLanguage.A + OcrLanguage.B(同时) |
| 没有等效物 | ocr.Configuration.ReadBarCodes = true |
| 没有等效物 | input.LoadImage(byteArray) |
常见迁移问题和解决方案
问题 1:迁移后项目文件仍然需要 Windows TFM
Windows.Media.Ocr: 需要 <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> 声明以解析 WinRT 类型。 如果删除 Windows.Media.Ocr 引用而不检查同一项目中的其他 WinRT 依赖项,则 TFM 可能会保留在原处,从而阻止跨平台构建。
解决方案:移除 Windows OCR 命名空间引用后,在更改 TFM 之前,请先在项目中搜索任何剩余的 WinRT API 使用情况:
# Find remaining WinRT API usage before removing the Windows TFM
grep -r "Windows\." --include="*.cs" .
grep -r "WinRT\|IAsyncOperation\|StorageFile\|SoftwareBitmap" --include="*.cs" .
如果所有 WinRT 引用都已移除,请更新项目文件:
<!-- Before -->
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<!-- After -->
<TargetFramework>net8.0</TargetFramework>
如果其他 WinRT 功能(Windows 通知、shell 集成、XAML)仍然在使用,则将 OCR 调用抽象到接口后面,并提供特定于平台的实现,而不是在整个项目中移除 TFM。
问题 2:空引擎检查没有IronOCR等效项
Windows.Media.Ocr: 每次调用 TryCreateFromLanguage 和 TryCreateFromUserProfileLanguages 都可能返回空值。 所有现有代码都包含空值检查保护子句,当引擎为空时会抛出异常或跳转。
解决方案: IronOCR在初始化失败时抛出结构化异常,而不是返回 null。 移除空值检查保护条款。 如果需要向调用者暴露初始化错误,请使用标准的 try/catch 语句进行包装:
// Before: null-check pattern
var engine = OcrEngine.TryCreateFromUserProfileLanguages();
if (engine == null)
throw new InvalidOperationException("OCR unavailable.");
// After: no null — IronTesseract throws if misconfigured
try
{
var result = new IronTesseract().Read("document.jpg");
}
catch (IronOcr.Exceptions.OcrException ex)
{
// structured exception with diagnostic message
logger.LogError("OCR failed: {Message}", ex.Message);
}' Before: null-check pattern
Dim engine = OcrEngine.TryCreateFromUserProfileLanguages()
If engine Is Nothing Then
Throw New InvalidOperationException("OCR unavailable.")
End If
' After: no null — IronTesseract throws if misconfigured
Try
Dim result = New IronTesseract().Read("document.jpg")
Catch ex As IronOcr.Exceptions.OcrException
' structured exception with diagnostic message
logger.LogError("OCR failed: {Message}", ex.Message)
End Try问题 3:现有方法签名中的 SoftwareBitmap 参数
Windows.Media.Ocr: 工具方法、服务和存储库类可以接受 SoftwareBitmap 作为参数类型。 移除 Windows TFM 后,这些方法签名将无法编译。
解决方案: 用 byte[] 或 Stream 替换 SoftwareBitmap 参数。IronOCR的 OcrInput 两者均直接接受。 以前构造 SoftwareBitmap 的调用点可以传递其底层数据:
// Before: SoftwareBitmap parameter — cannot compile cross-platform
public async Task<string> RecognizeAsync(SoftwareBitmap bitmap) { ... }
// After: byte array parameter — compiles on all platforms
public string Recognize(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes);
return new IronTesseract().Read(input).Text;
}Public Function Recognize(ByVal imageBytes As Byte()) As String
Using input = New OcrInput()
input.LoadImage(imageBytes)
Return New IronTesseract().Read(input).Text
End Using
End Function问题 4:仅异步调用者无法直接使用同步IronOCR
Windows.Media.Ocr: 每个识别调用都是 async。 代码库中的调用者使用 await 并返回 Task<string>。 在 async 方法中切换到IronOCR的同步 Read 方法有效,但可能会在原先架构使用了 async 的上下文中引入阻塞调用。
解决方案: IronOCR为需要的调用者提供异步路径。 在现有异步方法中使用 Task.Run 进行 CPU 绑定包装,或使用本地异步 API。
// Option A: wrap synchronous call in Task.Run for async callers
public async Task<string> RecognizeAsync(string imagePath)
{
return await Task.Run(() => new IronTesseract().Read(imagePath).Text);
}
// Option B:IronOCRasync path
// See: https://ironsoftware.com/csharp/ocr/how-to/async/' Option A: wrap synchronous call in Task.Run for async callers
Public Async Function RecognizeAsync(ByVal imagePath As String) As Task(Of String)
Return Await Task.Run(Function() New IronTesseract().Read(imagePath).Text)
End Function
' Option B: IronOCR async path
' See: https://ironsoftware.com/csharp/ocr/how-to/async/异步 OCR 指南记录了内置的异步 API,适用于需要"即发即弃"或"进度报告"模式的场景。
问题 5:Windows 语言标签格式无法直接映射
Windows.Media.Ocr: 使用传递给 Windows.Globalization.Language("fr-FR") 的 BCP-47 字符串标记指定语言。 这些字符串标签在IronOCR中没有直接对应的标签。
解决方案: 将 BCP-47 语言标记映射到 OcrLanguage 枚举。 对于常用语言来说,映射关系很简单:
// Before: BCP-47 string tags
var engine = OcrEngine.TryCreateFromLanguage(
new Windows.Globalization.Language("fr-FR"));
// After: OcrLanguage enum
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
// Also: OcrLanguage.German, OcrLanguage.Japanese, OcrLanguage.Arabic, etc.' Before: BCP-47 string tags
Dim engine = OcrEngine.TryCreateFromLanguage(
New Windows.Globalization.Language("fr-FR"))
' After: OcrLanguage enum
Dim ocr = New IronTesseract()
ocr.Language = OcrLanguage.French
' Also: OcrLanguage.German, OcrLanguage.Japanese, OcrLanguage.Arabic, etc.完整的映射关系可在IronOCR语言目录中找到。 对于未列在主枚举中的语言,自定义语言包支持涵盖直接加载 .traineddata 文件。
问题 6:FileAccessMode.Read 没有替代方案
Windows.Media.Ocr: file.OpenAsync(FileAccessMode.Read) 是一个 WinRT 专用的文件打开模式。 FileAccessMode 枚举在标准 .NET 中不存在。
解决方案: 用标准 System.IO.File.ReadAllBytes 或 FileStream 替换。 OcrInput 接受两者:
// Before: WinRT file access
using var stream = await file.OpenAsync(FileAccessMode.Read);
// After: standard .NET
var imageBytes = File.ReadAllBytes(imagePath);
using var input = new OcrInput();
input.LoadImage(imageBytes);Dim imageBytes = File.ReadAllBytes(imagePath)
Using input As New OcrInput()
input.LoadImage(imageBytes)
End UsingWindows.Media.Ocr(UWP/WinRT OCR)迁移清单
迁移前
在进行任何更改之前,请先审核代码库:
# Find all Windows OCR namespace usages
grep -rn "using Windows.Media.Ocr" --include="*.cs" .
grep -rn "using Windows.Graphics.Imaging" --include="*.cs" .
grep -rn "using Windows.Storage" --include="*.cs" .
grep -rn "using Windows.Globalization" --include="*.cs" .
# Find WinRT type usages
grep -rn "OcrEngine\|SoftwareBitmap\|BitmapDecoder\|StorageFile" --include="*.cs" .
grep -rn "TryCreateFromLanguage\|TryCreateFromUserProfileLanguages\|RecognizeAsync" --include="*.cs" .
grep -rn "InMemoryRandomAccessStream\|DataWriter\|FileAccessMode" --include="*.cs" .
# Find project files with Windows TFM
grep -rn "net.*-windows" --include="*.csproj" .
# Count files requiring changes
grep -rl "Windows.Media.Ocr\|Windows.Graphics.Imaging\|SoftwareBitmap" --include="*.cs" . | wc -l
记录受影响的文件数量、在用的语言标记("en-US", "fr-FR" 等)以及是否有 WinRT 类型出现在公共方法签名中(这些需要 API 表面更改,除了内部重写之外)。
代码迁移
- 安装
IronOcrNuGet 包:dotnet add package IronOcr - 在
Program.cs或Startup.cs中添加许可证初始化调用:IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; - 从所有源文件中删除
using Windows.Media.Ocr; - 从所有源文件中删除
using Windows.Graphics.Imaging; - 从所有源文件中删除
using Windows.Storage; - 从所有源文件中删除
using Windows.Globalization; - 向所有执行 OCR 的文件添加
using IronOcr; - 将每个
OcrEngine.TryCreateFromLanguage(new Language("xx-XX"))调用替换为new IronTesseract()并设置ocr.Language = OcrLanguage.X - 将每个
OcrEngine.TryCreateFromUserProfileLanguages()调用替换为new IronTesseract() - 删除引擎创建结果中的所有空值检查保护子句
- 在方法签名中用
byte[]或Stream替换SoftwareBitmap参数 - 将
StorageFile+BitmapDecoder+SoftwareBitmap构造链替换为OcrInput.LoadImage(path),OcrInput.LoadImage(bytes), 或OcrInput.LoadImage(stream) - 将
engine.RecognizeAsync(bitmap)替换为ocr.Read(path)或ocr.Read(input) - 将
InMemoryRandomAccessStream和DataWriter的用法替换为MemoryStream - 将 Windows BCP-47 语言标记字符串替换为
OcrLanguage枚举值; 安装所需的语言NuGet包 - 在
.csproj文件中更新<TargetFramework>,在没有其他 WinRT API 存在的情况下删除-windowsX.Y.Z后缀
后迁移
- 确保项目编译时目标
net8.0(或您的目标版本)没有 Windows TFM 后缀 - 确保项目在 Linux 环境或 Docker 容器中使用
mcr.microsoft.com/dotnet/aspnet:8.0编译和运行 - 验证测试Suite中每种文档类型的 OCR 输出文本是否与预期结果一致
- 使用IronOCR语言NuGet包验证所有先前支持的语言是否都能产生正确的输出。
- 验证多语言文档在单次识别过程中能否产生正确结果
- 确保在没有安装 Windows 语言包的机器上,
InvalidOperationException不会出现在引擎初始化时 - 验证
result.Confidence值是否在预期范围内,对应于清晰和低质量输入文档 - 如果应用程序生成文档,请验证
SaveAsSearchablePdf输出在 PDF 查看器中正常打开并支持文本搜索 - 运行所有现有的并行或多线程处理路径,并确认负载下的线程安全性
- 将程序部署到目标环境(Docker、Azure 应用服务、AWS、Linux 服务器),并执行至少一次完整的端到端 OCR 操作
迁移到IronOCR的主要优势
跨平台部署变成了一种配置决策,而非代码重写。迁移后,OCR 组件在 Windows、Linux、macOS、Docker 以及所有主流云服务提供商上都能完全正常运行。 将 OCR 工作负载从 Windows 虚拟机迁移到 Linux 容器以降低托管成本是一种部署操作。 Linux 部署指南和Docker 部署指南涵盖了在 Linux 基础镜像上添加一行依赖项所需的步骤。
语言支持随应用程序二进制文件一起传输。语言包以NuGet包的形式安装,并与IronOCR包版本绑定。 应用程序可识别的语言集在项目文件中定义,并且在所有机器上都相同——包括开发人员工作站、CI 运行器、预发布服务器和生产主机。无需操作系统管理员协调,无需组策略例外,也无需运行时空值检查。
OCR 准确度在没有外部工具的情况下提升。 预处理管道——Deskew, DeNoise, Contrast, Binarize, Sharpen, Scale —— 在识别引擎看到图像之前在IronOCR内部运行。 由于扫描错位或噪声,使用 Windows.Media.Ocr 识别文档时出现质量下降的情况,无需添加外部图像处理依赖项即可改善识别效果。 图像质量校正指南和滤镜向导可帮助确定每种文档类型的正确滤镜组合。
PDF 工作流程整合到一个单一库中。之前用于连接 Windows.Media.Ocr 和 PDF 输入的外部 PDF 渲染器已不再需要。 扫描的 PDF 存档通过与图像相同的 IronTesseract.Read 调用进行处理。 可搜索 PDF 输出是结果对象的一个方法。 双库架构及其版本管理、许可开销和部署界面都将消失。
结构化输出启用文档智能管道。 OcrResult 层次结构——Pages, Paragraphs, Lines, Words, Characters ——每个元素的坐标和置信度得分提供所需数据,用于发票字段提取、表单解析和文档分类。 Windows.Media.Ocr 的行级输出不足以满足这些工作流程的需求。 IronOCR具备置信度过滤的单词提取、段落边界检测和基于坐标的字段映射等一流功能,无需额外的库。
永久许可取代了对基础设施的无限依赖。在异构机器群中维护 Windows 语言包安装、Windows Server Desktop Experience 许可和仅限 Windows 的持续集成 (CI) 基础设施的成本是真实存在的,但却分散在各处——它体现在 IT 服务单和基础设施预算中,而不是作为 OCR 预算中的单独项目。 $999IronOCRLite 许可证为单一开发者项目消除了这种开销。 售价 1499 美元的Professional许可证可供 10 位开发人员使用。 两者均为一次性购买,并包含一年的更新服务。
