從Windows.Media.Ocr遷移至IronOCR
本指南提供 .NET 開發人員從 Windows.Media.Ocr 遷移到 IronOCR 的逐步遷移路徑。 它涵蓋了名稱空間移除、項目文件更改、遷移過程中最常出現的模式的程式碼遷移範例,以及驗證完成過渡的實際清單。
為什麼要從 Windows.Media.Ocr (UWP/WinRT OCR) 遷移
Windows.Media.Ocr 在其邊界內運作良好。 那些邊界很狹窄,項目通常會超出這些邊界。 團隊遷移的原因可預測地分為幾類。
Windows TFM 阻止所有非 Windows 目標。 項目文件必須聲明一個 net*-windows* 目標框架標識符,才能在編譯時解析 Windows.Media.Ocr 名稱空間。該聲明不是運行時標誌,而是構建約束,傳遞給引用您的每個項目。 共享的 OCR 服務庫、Web API、部署到 Linux 的後台工作程式——它們都繼承了這一約束。 移除它意味著要移除 Windows.Media.Ocr。
語言可用性在運行時由操作系統決定,而不是在編譯時由開發人員決定。 當主機機器上缺少所需的語言包時,OcrEngine.TryCreateFromLanguage 返回空值。開發人員無法從程式碼中安裝語言包,無法將其捆綁到應用程式二進位檔案中,也無法提供後備模式。 在自動化環境中——構建代理、CI 運行器、最小化的雲 VM、容器——很少安裝語言包。 因缺少語言包而導致的生產故障無法通過查看程式碼復現; 它們需要檢查目標機器的操作系統配置。
無預處理意味著對於次優輸入沒有恢復路徑。 API 接受 SoftwareBitmap 並生成文字。 從那兩個點改善影像品質完全是開發人員的責任,使用的是Windows Imaging Component APIs ,這些 API 本身也是僅限於 Windows 使用。 手機照片、不對齊的平板掃描和影印文件無聲地降低準確性,沒有內建機制來診斷或改進結果。
PDF 是企業工作流程中最常見的文件格式。 Windows.Media.Ocr 沒有 PDF 輸入路徑。 要處理掃描的 PDF 需要一個外部渲染器、按頁的光柵化,以及手動結果組合。 渲染器增加了一個依賴性、許可考量和一個獨立的故障表面——這正是 "免費內建的" 庫要避免的複雜性。
伺服器端部署在結構上不支持。 Windows.Media.Ocr 針對客戶端應用程式。 在 Windows Server 上運行它需要桌面體驗功能包,這會增加 VM 成本和基礎設施複雜性。 Docker 部署是不可能的。 基於 Linux 的 Azure Functions、AWS Lambda 和任何基於 Linux 的容器負載無法引用 API。
WinRT 非同步堆疊與標準 .NET 模式不相容。 在開始讀取單個字元之前,需要六個或更多鏈式的 await 呼叫——RecognizeAsync。 將該鏈整合到後台服務中、平行.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 Nothing 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 pipeline) | 是 |
| PDF輸入 | 不是 | 是(本地) |
| 多頁 TIFF 輸入 | 不是 | 是 |
| 流和字節陣列輸入 | 否 (僅限 StorageFile) | 是 |
| 語言來源 | 系統安裝的語言包 | 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 Runtime 的一部分,通過 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 套件 針對 net8.0 和 net9.0,沒有特定於平台的 TFM。 在移除 Windows OCR 名稱空間後,將項目文件中的 <TargetFramework> 從 net8.0-windows10.0.19041.0 更新為 net8.0 (或適當的版本),前提是項目中沒有其他 WinRT API。
步驟2:更新命名空間
用單個IronOCR名稱空間替換三個 Windows OCR 名稱空間:
// 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 枚舉值。 語言索引列表包括 CJK 脚本、阿拉伯語、希伯來語、天城文和西里爾文。
透過平行處理啟用伺服器端 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 和 OcrWord.BoundingRect 的 Pages 中,但沒有段落、信心分數,也沒有字元級資料。 對於表單字段提取或發票行項解析,行幾何是不足的——需要段落邊界和字信心分數來區分結構化字段與周圍的文字。
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結構化結果模型——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 型別。 不檢查同一項目中的其他 WinRT 相依性而移除 Windows.Media.Ocr 引用,可能會使 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在初始化失敗時拋出結構化異常,而不是返回空值。 刪除空值檢查保護語句。 如果您需要將初始化錯誤呈現給調用者,請包裹在標準的 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: 使用 BCP-47 字串標籤傳遞到 Windows.Globalization.Language("fr-FR") 來指定語言。 這些字串標籤在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; - 用
new IronTesseract()替換每個OcrEngine.TryCreateFromLanguage(new Language("xx-XX"))呼叫並設置ocr.Language = OcrLanguage.X - 用
new IronTesseract()替換每個OcrEngine.TryCreateFromUserProfileLanguages()呼叫 - 刪除引擎建立結果上的所有空值檢查保護語句
- 用
byte[]或Stream替換方法簽名中的SoftwareBitmap參數 - 用
OcrInput.LoadImage(bytes)或OcrInput.LoadImage(stream)替換BitmapDecoder和SoftwareBitmap構造鏈 - 用
ocr.Read(path)或ocr.Read(input)替換engine.RecognizeAsync(bitmap) - 用
MemoryStream替換InMemoryRandomAccessStream和DataWriter使用 - 用
OcrLanguage規範值替換 Windows 的 BCP-47 語言標籤字串; 安裝所需的語言 NuGet 套件 - 更新
<TargetFramework>,從.csproj文件中移除-windowsX.Y.Z後綴,無其他 WinRT API 留存
遷移後
- 確認項目在沒有 Windows TFM 後綴的情況下針對
net8.0(或您目標版本) 編譯 - 確認項目在 Linux 環境或 Docker 容器中使用
mcr.microsoft.com/dotnet/aspnet:8.0編譯和運行 - 驗證 OCR 輸出文字是否符合測試套件中每種文件型別的預期結果
- 檢查所有先前支援的語言是否使用IronOCR語言 NuGet 套件產生正確的輸出
- 驗證多語言檔案是否在單次識別中產生正確的結果
- 確保在沒有安裝 Windows 語言包的機器上引擎初始化不會出現
NullReferenceException或InvalidOperationException - 驗證
result.Confidence值是否在乾淨和低質量輸入文件的預期範圍內 - 如果應用程式產生文件,驗證
SaveAsSearchablePdf輸出在 PDF 檢視器中正確打開並支持文字搜尋 - 運行任何現有的平行或多執行緒處理通道,並在負載下確認執行緒安全性
- 部署到目標環境 (Docker, Azure 應用服務, AWS, Linux 伺服器) 並執行至少一次完整的端到端 OCR 操作
遷移至IronOCR的主要好處
跨平台部署成為配置決定,而不是程式碼重寫。 遷移之後,OCR 組件在 Windows、Linux、macOS、Docker 和每個主要雲供應商上運行相同。 將 OCR 工作量從 Windows VM 移動到 Linux 容器以降低託管成本是一個部署操作。 Linux 部署指南 和 Docker 部署指南 涵蓋了在 Linux 基本映像上所需的一行依賴性新增。
語言支持隨著應用程式二進位文件移動。 語言包安裝為 NuGet 套件並與IronOCR包版本鎖定。 您應用程式可識別的語言集在項目文件中定義,並且在每台機器上都是相同的——開發者工作站、CI 運行器、預發伺服器和生產主機。 無需操作系統管理員協調,無需求例政策例外,無運行時空值檢查。
OCR 準確性在沒有外部工具的情況下得到提升。 在識別引擎看到影像之前,預處理流水線——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 桌面體驗許可,以及僅供 Windows 使用的 CI 基礎設施是實際的但分散的代價——它會出現在 IT 工單和基礎設施預算中,而不是 OCR 預算中的行項。 一個 $999IronOCRLite 許可消除了單一開發者項目的常見管理成本。 $1,499 的專業許可覆蓋了十個開發人員。 兩者都是一次性購買,包含一年更新。
