IronOCR 和 Nanonets OCR 之间的比较
AWS Textract的按页定价模型在低容量时看起来不贵,但成本会在规模上无限增加。 您的应用程序处理的每个文档都会离开您的网络,传输到Amazon数据中心,由Amazon基础设施处理,账单会无限增加。 对于正在评估.NET中 OCR 选项的团队来说,问题不仅仅是 Textract 是否能产生准确的结果(它的确能),而是其按页计费模式、强制云传输以及多页文档的异步轮询架构是否符合应用程序的实际需求。
了解 AWS Textract
AWS Textract 是亚马逊的托管文档分析服务,可通过 .NET 的 AWSSDK.Textract NuGet 包访问。 它以云 API 的形式运行:您的应用程序将文档数据发送到亚马逊的基础设施并接收结构化结果。 每次 OCR 操作都需要 AWS 账户、具有 Textract 权限的 IAM 凭证和互联网连接。
Textract 提供几种不同的分析模式,每种模式单独定价:
- DetectDocumentText: 基础文本提取(查看 AWS Textract 定价 获取当前每页费率)
- AnalyzeDocument (Tables): 结构化表格提取的每页费率高于基础文本
- AnalyzeDocument (Forms): 关键价值表单提取的每页费率高于表格提取
- AnalyzeExpense:发票和收据解析,每页收费 0.01 美元
- AnalyzeID:身份文件提取,每页收费 0.025 美元
- StartDocumentTextDetection / StartDocumentAnalysis:任何多页 PDF 都需要异步 API,这要求使用 S3 暂存存储桶、作业轮询和结果分页。
结果模型使用平铺的 Block 对象列表,必须遍历关系ID以重建表格、表单或任何结构化输出。 简单的表格提取需要迭代 BlockType.TABLE 块,通过 RelationshipType.CHILD 关系IDS 查找子 BlockType.CELL 块,然后获取每个单元格文本的 BlockType.WORD 块。 这种关系图模型可以处理复杂的文档结构,但它并不轻量级。
S3异步管道
通过 DetectDocumentTextAsync 的单一图像 OCR 可以直接在请求中传递文档字节。多页 PDF 无法如此。 任何 PDF 文件都需要完整的异步流程:
// AWS Textract: 多页PDF requires S3 + async job
public async Task<string> ProcessPdfAsync(string pdfPath)
{
// Step 1: Upload to S3 — credentials for two services required
var key = $"uploads/{Guid.NewGuid()}.pdf";
using (var fileStream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = _bucketName,
Key = key,
InputStream = fileStream
});
}
try
{
// Step 2: Start async Textract job
var startResponse = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = _bucketName, Name = key }
}
});
var jobId = startResponse.JobId;
// Step 3: Poll every 5 seconds until complete
GetDocumentTextDetectionResponse getResponse;
do
{
await Task.Delay(5000);
getResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
} while (getResponse.JobStatus == JobStatus.IN_PROGRESS);
if (getResponse.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job failed: {getResponse.StatusMessage}");
// Step 4: Paginate through result blocks
var allText = new StringBuilder();
string nextToken = null;
do
{
var pageResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = jobId,
NextToken = nextToken
});
foreach (var block in pageResponse.Blocks.Where(b => b.BlockType == BlockType.LINE))
allText.AppendLine(block.Text);
nextToken = pageResponse.NextToken;
} while (nextToken != null);
return allText.ToString();
}
finally
{
// Step 5: 总是 clean up S3
await _s3Client.DeleteObjectAsync(_bucketName, key);
}
}
// AWS Textract: 多页PDF requires S3 + async job
public async Task<string> ProcessPdfAsync(string pdfPath)
{
// Step 1: Upload to S3 — credentials for two services required
var key = $"uploads/{Guid.NewGuid()}.pdf";
using (var fileStream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = _bucketName,
Key = key,
InputStream = fileStream
});
}
try
{
// Step 2: Start async Textract job
var startResponse = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = _bucketName, Name = key }
}
});
var jobId = startResponse.JobId;
// Step 3: Poll every 5 seconds until complete
GetDocumentTextDetectionResponse getResponse;
do
{
await Task.Delay(5000);
getResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
} while (getResponse.JobStatus == JobStatus.IN_PROGRESS);
if (getResponse.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job failed: {getResponse.StatusMessage}");
// Step 4: Paginate through result blocks
var allText = new StringBuilder();
string nextToken = null;
do
{
var pageResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = jobId,
NextToken = nextToken
});
foreach (var block in pageResponse.Blocks.Where(b => b.BlockType == BlockType.LINE))
allText.AppendLine(block.Text);
nextToken = pageResponse.NextToken;
} while (nextToken != null);
return allText.ToString();
}
finally
{
// Step 5: 总是 clean up S3
await _s3Client.DeleteObjectAsync(_bucketName, key);
}
}
Imports System
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
Imports Amazon.S3
Imports Amazon.Textract
Imports Amazon.Textract.Model
Public Class PdfProcessor
Private _s3Client As IAmazonS3
Private _textractClient As IAmazonTextract
Private _bucketName As String
Public Async Function ProcessPdfAsync(pdfPath As String) As Task(Of String)
' Step 1: Upload to S3 — credentials for two services required
Dim key = $"uploads/{Guid.NewGuid()}.pdf"
Using fileStream = File.OpenRead(pdfPath)
Await _s3Client.PutObjectAsync(New PutObjectRequest With {
.BucketName = _bucketName,
.Key = key,
.InputStream = fileStream
})
End Using
Try
' Step 2: Start async Textract job
Dim startResponse = Await _textractClient.StartDocumentTextDetectionAsync(
New StartDocumentTextDetectionRequest With {
.DocumentLocation = New DocumentLocation With {
.S3Object = New S3Object With {.Bucket = _bucketName, .Name = key}
}
})
Dim jobId = startResponse.JobId
' Step 3: Poll every 5 seconds until complete
Dim getResponse As GetDocumentTextDetectionResponse
Do
Await Task.Delay(5000)
getResponse = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {.JobId = jobId})
Loop While getResponse.JobStatus = JobStatus.IN_PROGRESS
If getResponse.JobStatus <> JobStatus.SUCCEEDED Then
Throw New Exception($"Textract job failed: {getResponse.StatusMessage}")
End If
' Step 4: Paginate through result blocks
Dim allText = New StringBuilder()
Dim nextToken As String = Nothing
Do
Dim pageResponse = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {
.JobId = jobId,
.NextToken = nextToken
})
For Each block In pageResponse.Blocks.Where(Function(b) b.BlockType = BlockType.LINE)
allText.AppendLine(block.Text)
Next
nextToken = pageResponse.NextToken
Loop While nextToken IsNot Nothing
Return allText.ToString()
Finally
' Step 5: 总是 clean up S3
Await _s3Client.DeleteObjectAsync(_bucketName, key)
End Try
End Function
End Class
这是可靠 PDF 处理的最低可行实现 —— 五个不同阶段,两个 AWS 服务客户端,以及 finally 块中的清理逻辑。 包含适当的错误处理、速率限制重试逻辑和超时管理的完整生产版本运行 150-300 行。
了解IronOCR
IronOCR是一个商业化的.NET OCR 库,完全运行在您自己的基础设施上。 它封装了一个优化的 Tesseract 5 引擎,具有自动图像预处理、原生 PDF 支持和同步 API,可以直接生成结果,无需外部服务调用或暂存步骤。
IronOCR架构的主要特点:
-仅限本地处理:任何文档数据都不会离开运行应用程序的计算机。
- 单一 NuGet 包:
dotnet add package IronOcr安装所有内容,包括本机二进制文件 -自动预处理:对低质量输入数据自动进行去斜、降噪、对比度增强、二值化和分辨率缩放。 -原生 PDF 支持:直接通过文件路径或流读取 PDF,无需 S3 暂存或异步作业 - 线程安全: 单个
IronTesseract实例处理跨线程的并发请求,无需争用 - 永久授权: $999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited — 一次性支付,没有每页收费,没有使用计量 -超过 125 个语言包:作为独立的NuGet包安装,本地加载,无需网络调用
功能对比
| 特征 | AWS Textract | IronOCR |
|---|---|---|
| 加工地点 | 亚马逊云(必填) | 本地/现场 |
| 多页PDF | 需要 S3 + 异步作业 | 直接同步调用 |
| 成本模型 | 按页收费(联系AWS获取当前定价) | 永久授权,无页数费用 |
| 需要互联网连接 | 总是 | 绝不 |
| 凭证设置 | IAM 用户/角色 + 可选的 S3 | 单个许可证密钥字符串 |
| 气隙部署 | 不可能 | 完全支持 |
| 支持加密 PDF | 不支持 | 内置(密码参数) |
详细功能对比
| 特征 | AWS Textract | IronOCR |
|---|---|---|
| 文本提取 | ||
| 基础OCR(图像) | 是 — DetectDocumentTextAsync |
是 — ocr.Read(path) |
| 多页PDF | 需要 S3 + 异步轮询 | 直接 input.LoadPdf(path) |
| 受密码保护的PDF | 不支持 | input.LoadPdf(path, Password: "x") |
| 流输入 | 是的(请求中包含字节数组) | 是 — input.LoadImage(stream) |
| 结构化萃取 | ||
| 表格提取 | AnalyzeDocument + 块图遍历 |
基于词位的重建 |
| 表单字段提取 | AnalyzeDocument + KEY_VALUE_SET 块 |
基于区域的 CropRectangle 区域 |
| 行级结果 | Block 通过 BlockType.LINE 过滤 |
result.Lines 直接收集 |
| 带坐标的词级 | Block 通过 BlockType.WORD 过滤 |
result.Words 与 .X, .Y, .Width |
| 置信度评分 | 每个区块的置信度 | 按词和整体的 result.Confidence |
| 处理模型 | ||
| 同步(图像) | 是的(仅限单页) | 是的(所有文档类型) |
| 异步 | PDF文件必需 | 可选 — Task.Run() 包装 |
| 批量处理 | 需要进行速率限制管理(默认 5 TPS) | 不受限的 Parallel.ForEach |
| 预处理 | ||
| 自动校正斜角 | 未暴露 | input.Deskew() |
| 降噪 | 内部(不可配置) | input.DeNoise() |
| 对比度增强 | 内部(不可配置) | input.Contrast() |
| 分辨率增强 | 内部(不可配置) | input.EnhanceResolution(300) |
| 二值化 | 内部的 | input.Binarize() |
| 输出格式 | ||
| 纯文本 | 是 | 是 |
| 可搜索的PDF | 否 | result.SaveAsSearchablePdf(path) |
| hHOCR | 否 | result.SaveAsHocrFile(path) |
| 结构化 JSON | 通过块序列化 | result.Words / result.Lines |
| 部署 | ||
| 本地部署 | 否 | 是 |
| 气隙式 | 否 | 是 |
| 多克 | 是的(已注入AWS凭证) | 是的(无需凭证) |
| AWS Lambda | 本地 | 支持 |
| 天域 | 是 | 是 |
| Linux | 是的(AWS托管) | 是 — get-started/linux/ |
| 合规性 | ||
| HIPAA | 需要与 AWS 签订 BAA 协议 | 没有外部处理器 |
| GDPR | 数据跨越到 AWS 区域 | 数据保持在边界内 |
| ITAR | 未经特别授权,禁止这样做。 | 完全本地部署 |
| 气隙式/CMMC 3级 | 不可能 | 支持 |
规模化成本
按页计费模式是AWS Textract的决定性结构限制。 看似微小的每页成本在实际文档工作流中显著累积。
AWS Textract方法
// Every call to this method costs money — per page, permanently
public async Task<string> DetectTextAsync(string imagePath)
{
var imageBytes = File.ReadAllBytes(imagePath); // Image leaves your network
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
var response = await _client.DetectDocumentTextAsync(request); // per-page charge
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
// Every call to this method costs money — per page, permanently
public async Task<string> DetectTextAsync(string imagePath)
{
var imageBytes = File.ReadAllBytes(imagePath); // Image leaves your network
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
var response = await _client.DetectDocumentTextAsync(request); // per-page charge
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
Imports System.IO
Imports System.Threading.Tasks
' Every call to this method costs money — per page, permanently
Public Async Function DetectTextAsync(imagePath As String) As Task(Of String)
Dim imageBytes = File.ReadAllBytes(imagePath) ' Image leaves your network
Dim request = New DetectDocumentTextRequest With {
.Document = New Document With {
.Bytes = New MemoryStream(imageBytes)
}
}
Dim response = Await _client.DetectDocumentTextAsync(request) ' per-page charge
Return String.Join(vbLf, response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
End Function
请查询AWS Textract定价页面了解当前每页费率。 不同的API功能(基本文本检测、表格提取、表单提取)有不同的费率。 包含表格和表单字段的文档收取的费用高于基本文本检测,且成本随着数量增加而增长,且无上限且无法提前支付。
在高页量情况下,三年总成本可能相当可观,计费继续进行。
IronOCR方法
// One license. 否 per-page cost. Same code handles 1 page or 1,000,000.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read("document.jpg").Text;
// One license. 否 per-page cost. Same code handles 1 page or 1,000,000.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read("document.jpg").Text;
Imports IronOcr
' One license. 否 per-page cost. Same code handles 1 page or 1,000,000.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read("document.jpg").Text
$2,999 Professional license 涵盖 10 个开发人员、无限项目和无限页面量。 第一年之后,页面处理的持续成本为零。 对于处理大量页量的团队,IronOCR许可与持续的每页云费用相比很快就能收回成本。
IronOCR许可页面涵盖了分级详情、基于使用量的计费方案的 SaaS 订阅选项以及 OEM 再分发条款。
数据主权与合规性
AWS Textract 的架构使得一项保证无法实现:即您的文档不会一直保留在您的基础设施内。 每次 OCR 操作都会将文档内容传输到亚马逊的服务器。
AWS Textract方法
// This code sends PHI, legal documents, financial records — whatever is in
// the file — to Amazon Web Services infrastructure
public async Task<string> ProcessSensitiveDocumentAsync(string documentPath)
{
var imageBytes = File.ReadAllBytes(documentPath);
// Data crosses your security perimeter here
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
// Amazon processes it; you receive text back
var response = await _client.DetectDocumentTextAsync(request);
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
// This code sends PHI, legal documents, financial records — whatever is in
// the file — to Amazon Web Services infrastructure
public async Task<string> ProcessSensitiveDocumentAsync(string documentPath)
{
var imageBytes = File.ReadAllBytes(documentPath);
// Data crosses your security perimeter here
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
// Amazon processes it; you receive text back
var response = await _client.DetectDocumentTextAsync(request);
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
Imports System.IO
Imports System.Threading.Tasks
Imports Amazon.Textract
Imports Amazon.Textract.Model
Public Class DocumentProcessor
Private _client As AmazonTextractClient
Public Sub New(client As AmazonTextractClient)
_client = client
End Sub
' This code sends PHI, legal documents, financial records — whatever is in
' the file — to Amazon Web Services infrastructure
Public Async Function ProcessSensitiveDocumentAsync(documentPath As String) As Task(Of String)
Dim imageBytes = File.ReadAllBytes(documentPath)
' Data crosses your security perimeter here
Dim request As New DetectDocumentTextRequest With {
.Document = New Document With {
.Bytes = New MemoryStream(imageBytes)
}
}
' Amazon processes it; you receive text back
Dim response = Await _client.DetectDocumentTextAsync(request)
Return String.Join(vbLf, response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
End Function
End Class
AWS 为受HIPAA保护的实体提供HIPAA业务伙伴协议,GovCloud 区域提供 FedRAMP 高级授权。 这些框架不会改变基本架构:每次操作都需要从基础架构中导出文档。 对于受ITAR管制的技术数据而言,这不是合规方面的细微差别,而是一项禁令。 对于包含 CUI 的 CMMC 3 级工作负载,云传输需要特定的授权,而大多数国防承包商并不具备这些授权。 对于物理隔离系统(例如研究网络、工业控制环境、机密设施),Textract 根本无法使用。
AWS Textract 在六个地区提供:us-east-1, us-west-2, eu-west-1, eu-west-2, ap-southeast-1, 和 ap-southeast-2。 对于数据驻留要求位于这些区域之外的组织而言,没有符合规定的选择。
IronOCR方法
// IronOCR: document bytes never leave this process
public string ProcessSensitiveDocument(string documentPath)
{
// Processes entirely on local hardware — no network call
var ocr = new IronTesseract();
return ocr.Read(documentPath).Text;
}
// IronOCR: document bytes never leave this process
public string ProcessSensitiveDocument(string documentPath)
{
// Processes entirely on local hardware — no network call
var ocr = new IronTesseract();
return ocr.Read(documentPath).Text;
}
' IronOCR: document bytes never leave this process
Public Function ProcessSensitiveDocument(documentPath As String) As String
' Processes entirely on local hardware — no network call
Dim ocr As New IronTesseract()
Return ocr.Read(documentPath).Text
End Function
由于IronOCR在本地执行,因此它自然而然地融入到处理 PHI 的医疗保健工作流程、处理特权通信的法律文档系统、处理支付卡图像的金融应用程序以及处理 CUI 的国防承包商流程中。 没有外部处理器需要审计,没有业务伙伴协议需要协商,也没有数据驻留限制需要满足。 合规范围是指贵组织自身的基础设施。
对于部署在 AWS 基础设施上但需要本地处理的团队, IronOCR可在 AWS EC2 和 Lambda 上运行,无需依赖 Textract – 处理在您自己的 AWS 账户边界内进行,而不是在 Amazon 的托管服务中进行。
异步轮询与同步处理
Textract 的同步(单图像)和异步(多页 PDF)API 之间的架构分离并非一个次要的 API 细节。 它决定了服务的构建方式、错误的处理方式,以及代码维护人员需要阅读和理解的代码量。
AWS Textract方法
// Full production-grade async processor for Textract PDF handling
public class TextractAsyncProcessor
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _bucketName;
private readonly TimeSpan _pollInterval = TimeSpan.FromSeconds(5);
private readonly TimeSpan _maxWaitTime = TimeSpan.FromMinutes(10);
public async Task<DocumentResult> ProcessDocumentAsync(
string localFilePath,
CancellationToken cancellationToken = default)
{
var s3Key = $"textract-uploads/{Guid.NewGuid()}{Path.GetExtension(localFilePath)}";
try
{
// Phase 1: Upload to S3
await UploadToS3Async(localFilePath, s3Key, cancellationToken);
// Phase 2: Start Textract job
var jobId = await StartTextractJobAsync(s3Key, cancellationToken);
// Phase 3: Poll until complete (up to 10 minutes)
var pollResult = await PollForCompletionAsync(jobId, cancellationToken);
if (!pollResult.Success)
throw new Exception($"Textract job failed: {pollResult.ErrorMessage}");
// Phase 4: Retrieve paginated results
return await GetAllResultsAsync(jobId, cancellationToken);
}
finally
{
// Phase 5: S3 cleanup — must succeed or storage costs accumulate
await DeleteFromS3Async(s3Key, cancellationToken);
}
}
private async Task<(bool Success, string ErrorMessage)> PollForCompletionAsync(
string jobId, CancellationToken cancellationToken)
{
var startTime = DateTime.UtcNow;
int pollCount = 0;
while (DateTime.UtcNow - startTime < _maxWaitTime)
{
cancellationToken.ThrowIfCancellationRequested();
var response = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId }, cancellationToken);
pollCount++;
switch (response.JobStatus)
{
case JobStatus.SUCCEEDED: return (true, null);
case JobStatus.FAILED: return (false, response.StatusMessage ?? "Unknown error");
case JobStatus.IN_PROGRESS:
await Task.Delay(_pollInterval, cancellationToken);
break;
default:
throw new Exception($"Unknown job status: {response.JobStatus}");
}
}
return (false, "Job timed out");
}
}
// Full production-grade async processor for Textract PDF handling
public class TextractAsyncProcessor
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _bucketName;
private readonly TimeSpan _pollInterval = TimeSpan.FromSeconds(5);
private readonly TimeSpan _maxWaitTime = TimeSpan.FromMinutes(10);
public async Task<DocumentResult> ProcessDocumentAsync(
string localFilePath,
CancellationToken cancellationToken = default)
{
var s3Key = $"textract-uploads/{Guid.NewGuid()}{Path.GetExtension(localFilePath)}";
try
{
// Phase 1: Upload to S3
await UploadToS3Async(localFilePath, s3Key, cancellationToken);
// Phase 2: Start Textract job
var jobId = await StartTextractJobAsync(s3Key, cancellationToken);
// Phase 3: Poll until complete (up to 10 minutes)
var pollResult = await PollForCompletionAsync(jobId, cancellationToken);
if (!pollResult.Success)
throw new Exception($"Textract job failed: {pollResult.ErrorMessage}");
// Phase 4: Retrieve paginated results
return await GetAllResultsAsync(jobId, cancellationToken);
}
finally
{
// Phase 5: S3 cleanup — must succeed or storage costs accumulate
await DeleteFromS3Async(s3Key, cancellationToken);
}
}
private async Task<(bool Success, string ErrorMessage)> PollForCompletionAsync(
string jobId, CancellationToken cancellationToken)
{
var startTime = DateTime.UtcNow;
int pollCount = 0;
while (DateTime.UtcNow - startTime < _maxWaitTime)
{
cancellationToken.ThrowIfCancellationRequested();
var response = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId }, cancellationToken);
pollCount++;
switch (response.JobStatus)
{
case JobStatus.SUCCEEDED: return (true, null);
case JobStatus.FAILED: return (false, response.StatusMessage ?? "Unknown error");
case JobStatus.IN_PROGRESS:
await Task.Delay(_pollInterval, cancellationToken);
break;
default:
throw new Exception($"Unknown job status: {response.JobStatus}");
}
}
return (false, "Job timed out");
}
}
Imports System
Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Imports Amazon.Textract
Imports Amazon.S3
Imports Amazon.Textract.Model
' Full production-grade async processor for Textract PDF handling
Public Class TextractAsyncProcessor
Private ReadOnly _textractClient As AmazonTextractClient
Private ReadOnly _s3Client As AmazonS3Client
Private ReadOnly _bucketName As String
Private ReadOnly _pollInterval As TimeSpan = TimeSpan.FromSeconds(5)
Private ReadOnly _maxWaitTime As TimeSpan = TimeSpan.FromMinutes(10)
Public Async Function ProcessDocumentAsync(localFilePath As String, Optional cancellationToken As CancellationToken = Nothing) As Task(Of DocumentResult)
Dim s3Key = $"textract-uploads/{Guid.NewGuid()}{Path.GetExtension(localFilePath)}"
Try
' Phase 1: Upload to S3
Await UploadToS3Async(localFilePath, s3Key, cancellationToken)
' Phase 2: Start Textract job
Dim jobId = Await StartTextractJobAsync(s3Key, cancellationToken)
' Phase 3: Poll until complete (up to 10 minutes)
Dim pollResult = Await PollForCompletionAsync(jobId, cancellationToken)
If Not pollResult.Success Then
Throw New Exception($"Textract job failed: {pollResult.ErrorMessage}")
End If
' Phase 4: Retrieve paginated results
Return Await GetAllResultsAsync(jobId, cancellationToken)
Finally
' Phase 5: S3 cleanup — must succeed or storage costs accumulate
Await DeleteFromS3Async(s3Key, cancellationToken)
End Try
End Function
Private Async Function PollForCompletionAsync(jobId As String, cancellationToken As CancellationToken) As Task(Of (Success As Boolean, ErrorMessage As String))
Dim startTime = DateTime.UtcNow
Dim pollCount As Integer = 0
While DateTime.UtcNow - startTime < _maxWaitTime
cancellationToken.ThrowIfCancellationRequested()
Dim response = Await _textractClient.GetDocumentTextDetectionAsync(New GetDocumentTextDetectionRequest With {.JobId = jobId}, cancellationToken)
pollCount += 1
Select Case response.JobStatus
Case JobStatus.SUCCEEDED
Return (True, Nothing)
Case JobStatus.FAILED
Return (False, If(response.StatusMessage, "Unknown error"))
Case JobStatus.IN_PROGRESS
Await Task.Delay(_pollInterval, cancellationToken)
Case Else
Throw New Exception($"Unknown job status: {response.JobStatus}")
End Select
End While
Return (False, "Job timed out")
End Function
End Class
这不是可以生成后就置之不理的模板代码。 当 Textract 作业在执行过程中失败时,S3 清理工作仍然必须运行。 当任务在 10 分钟后超时时,呼叫者需要收到清晰的错误信息。 当轮询期间网络中断时,重试策略不能创建重复作业。 每一种故障模式都需要明确处理——上面所示的结构是最低限度的负责任的实现。
批处理增加了另一层:Textract 的默认 StartDocumentTextDetection TPS 限制是每秒 5 个请求。 处理 100 个文档需要一个 SemaphoreSlim 节流、一个速率补充计时器,以及 ProvisionedThroughputExceededException 的重试逻辑。
IronOCR方法
// IronOCR: same synchronous API regardless of document type or size
public string ProcessDocument(string filePath)
{
using var input = new OcrInput();
if (Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase))
input.LoadPdf(filePath);
else
input.LoadImage(filePath);
return new IronTesseract().Read(input).Text;
}
// IronOCR: same synchronous API regardless of document type or size
public string ProcessDocument(string filePath)
{
using var input = new OcrInput();
if (Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase))
input.LoadPdf(filePath);
else
input.LoadImage(filePath);
return new IronTesseract().Read(input).Text;
}
Imports System.IO
' IronOCR: same synchronous API regardless of document type or size
Public Function ProcessDocument(filePath As String) As String
Using input As New OcrInput()
If Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase) Then
input.LoadPdf(filePath)
Else
input.LoadImage(filePath)
End If
Return New IronTesseract().Read(input).Text
End Using
End Function
没有轮询循环,没有作业 ID 跟踪,没有 S3 存储桶,没有结果分页。 同一段代码既可以处理单个 JPEG 文件,也可以处理 200 页的 PDF 文件。 处理要么完成,要么抛出异常——没有中间的"进行中"状态需要管理。 对于批处理,IronOCR 是线程安全的,单个 IronTesseract 实例处理 Parallel.ForEach,无锁或信号量。
IronTesseract 设置指南涵盖配置, PDF 输入指南记录了页面范围选择、密码保护的 PDF 以及从数据库或 HTTP 响应中检索的 PDF 的基于流的输入。
凭证管理开销
使用AWS Textract启动 OCR 操作需要先进行 IAM 配置,然后才能处理单个页面。
AWS Textract方法
调用 DetectDocumentTextAsync 之前,开发人员必须:
- 创建 AWS 账户或获取现有账户的访问权限
- 创建一个拥有
textract:DetectDocumentText和textract:AnalyzeDocument权限的 IAM 用户或角色 - 生成并安全存储访问密钥 ID 和秘密访问密钥
- 配置凭证解析——环境变量、AWS 凭证文件或 EC2 实例配置文件
- 如果处理 PDF:创建一个 S3 存储桶,配置存储桶策略,添加
s3:PutObject和s3:DeleteObject权限 - 实施凭证轮换策略以满足安全标准
- 在每个部署环境中安全地存储凭证——Docker Secrets、Kubernetes Secrets、AWS Secrets Manager 或 CI/CD 流水线变量
// Every environment needs these configured before this constructor succeeds
public TextractOcrService()
{
// Reads credentials from environment, ~/.aws/credentials, or IAM role
_client = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
}
// Every environment needs these configured before this constructor succeeds
public TextractOcrService()
{
// Reads credentials from environment, ~/.aws/credentials, or IAM role
_client = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
}
' Every environment needs these configured before this constructor succeeds
Public Sub New()
' Reads credentials from environment, ~/.aws/credentials, or IAM role
_client = New AmazonTextractClient(Amazon.RegionEndpoint.USEast1)
End Sub
当凭证过期、旋转或配置错误时,每个 OCR 调用都将失败,带有 AmazonTextractException ,携带 ErrorCode == "AccessDeniedException"。 在生产系统中,这意味着要实施针对凭证故障的特定捕获块,并监控 IAM 策略的偏差。
IronOCR方法
// One-time setup at application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment — recommended for deployments
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// One-time setup at application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment — recommended for deployments
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
' One-time setup at application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
' Or from environment — recommended for deployments
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
许可证密钥是一个静态字符串。 它不会在运行过程中过期,不需要轮换,也不需要任何管理权限。 处理文档的多克容器不需要注入 AWS 凭证、绑定到执行上下文的 IAM 角色,也不需要通过网络访问 AWS STS 来刷新令牌。
从 Textract 迁移到IronOCR时的完整凭证开销减少:三种 NuGet 包移除(AWSSDK.Textract, AWSSDK.S3, AWSSDK.Core),所有 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION 环境变量移除,IAM 角色和 S3 存储桶配置停用。 图像输入指南和流输入指南涵盖了取代 Textract 的字节数组和 S3 对象文档模型的全部输入方法。
API 映射参考
| AWS Textract API | IronOCR当量 |
|---|---|
AmazonTextractClient |
IronTesseract |
AmazonS3Client |
不要求 |
DetectDocumentTextRequest |
OcrInput |
DetectDocumentTextResponse |
OcrResult |
AnalyzeDocumentRequest |
OcrInput 使用 CropRectangle 进行区域 |
StartDocumentTextDetectionRequest |
OcrInput — 同步,无需启动 |
GetDocumentTextDetectionRequest |
无需操作——立即见效 |
Document.Bytes |
input.LoadImage(bytes) 或 input.LoadImage(stream) |
S3Object (文件暂存) |
文件路径字符串或流 |
Block (BlockType.LINE) |
result.Lines |
Block (BlockType.WORD) |
result.Words |
Block (BlockType.TABLE) |
通过 result.Words 进行单词位置分组 |
Block (BlockType.KEY_VALUE_SET) |
CropRectangle 区域提取 |
Block.Confidence |
word.Confidence / result.Confidence |
JobStatus.SUCCEEDED |
不适用 — 同步返回 |
JobStatus.IN_PROGRESS |
不适用——无异步状态 |
response.NextToken (分页) |
不适用——结果未分页 |
ProvisionedThroughputExceededException |
不适用——无TPS限制 |
client.DetectDocumentTextAsync(request) |
ocr.Read(path) |
client.AnalyzeDocumentAsync(request) |
ocr.Read(input) |
client.StartDocumentTextDetectionAsync(request) |
ocr.Read(input) |
client.GetDocumentTextDetectionAsync(request) |
不适用 |
当团队考虑从AWS Textract迁移到IronOCR时
当月度账单成为预算项目
最初使用 Textract 且业务量较小的团队经常会遇到这样一个情况:季度预算审查中出现了用于 OCR 处理的 AWS 账单,有人会问这笔费用是否固定。 它不是。 在高页量时,年度Textract成本可能相当可观 — 请查询AWS Textract定价页面了解当前费率。IronOCRProfessional license 在 $2,999 一次性快速收回成本,对于中等到高页量。
当合规性要求阻碍云处理时
医疗机构在实施文档数字化工作流程时,经常会在项目进行到一半时发现,如果没有 BAA 和额外的法律审查,HIPAA PHI 就无法通过云服务传输,或者他们的安全团队完全禁止云传输。 处理技术图纸、规范或任何受控非密信息的国防承包商面临ITAR和 CMMC 的限制,这使得AWS Textract无法被考虑。 处理特权通信的法律事务所也有类似的担忧。 这些并非理论上的合规性边缘案例——它们经常出现在采购审查、安全审计和合同谈判中。 IronOCR在本地进行处理,因此文档数据的合规性问题简化为您自己的基础设施是否在范围内,而不是亚马逊的基础设施是否在范围内。
当异步 PDF 复杂度超过其价值时
五阶段的 S3 异步管道——上传、启动作业、轮询、分页结果、清理——在技术上并不难实现。 它难以维护、测试和操作。 每个阶段都是故障点。 S3上传失败需要重试机制。 Textract作业失败需要区分暂时性错误和永久性错误。 轮询超时需要单独处理,与取消操作分开。 结果分页需要通过多次 API 调用来累积状态。 S3 清理失败需要发出警报,因为孤立对象会累积成本。 已经将此管道投入生产的团队,通常花费在维护它上的持续工程时间比构建它所花费的时间还要多。IronOCR等价物 — input.LoadPdf(path) 后接 ocr.Read(input) — 消除了所有五个阶段及其相关的故障模式。
当部署环境缺乏互联网接入时
在隔离的网络段中运行的多克容器、没有出站互联网的本地服务器、物理隔离的研究环境以及具有严格网络控制的工业系统都具有一个共同的特点:AWS Textract 不可用。 IronOCR作为标准NuGet包安装,安装后无需任何网络调用即可运行。 在这些环境中运行.NET应用程序的团队没有 Textract 选项,需要一个可以在本地处理的库。 Docker 部署指南和Linux 部署指南涵盖了容器化环境的具体配置。
当速率限制导致批处理工作流程中断时
默认 StartDocumentTextDetection TPS 限制是每秒 5 个请求。 DetectDocumentText 同步调用也有限制。 批量作业处理数百或数千个文档,必须实施 SemaphoreSlim 节流,在 ProvisionedThroughputExceededException 上进行指数退避,并设置速率补充计时器。 AWS 支持提高 TPS 限制的请求,但需要提供理由、进行审核,并且不能保证一定能够获得批准。IronOCR的处理速度取决于本地 CPU 的性能——一台 32 核服务器可以同时处理 32 个文档,无需进行限速配置或服务层级协商。
常见迁移注意事项
用直接集合替换块图
Textract 将所有结果表示为一个平铺的 List<Block>,其中行、单词、单元格、表格和键值对通过 BlockType 区分,并通过关系 ID 数组链接。 IronOCR提供直接的文本输入功能。
// Textract: filter flat block list by type
var lines = response.Blocks.Where(b => b.BlockType == BlockType.LINE);
var words = response.Blocks.Where(b => b.BlockType == BlockType.WORD);
// IronOCR: direct access to typed collections
var result = ocr.Read(imagePath);
var lines = result.Lines; // IEnumerable<OcrResult.OcrResultLine>
var words = result.Words; // IEnumerable<OcrResult.OcrResultWord>
foreach (var word in result.Words)
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) confidence {word.Confidence}%");
// Textract: filter flat block list by type
var lines = response.Blocks.Where(b => b.BlockType == BlockType.LINE);
var words = response.Blocks.Where(b => b.BlockType == BlockType.WORD);
// IronOCR: direct access to typed collections
var result = ocr.Read(imagePath);
var lines = result.Lines; // IEnumerable<OcrResult.OcrResultLine>
var words = result.Words; // IEnumerable<OcrResult.OcrResultWord>
foreach (var word in result.Words)
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) confidence {word.Confidence}%");
' Textract: filter flat block list by type
Dim lines = response.Blocks.Where(Function(b) b.BlockType = BlockType.LINE)
Dim words = response.Blocks.Where(Function(b) b.BlockType = BlockType.WORD)
' IronOCR: direct access to typed collections
Dim result = ocr.Read(imagePath)
Dim lines = result.Lines ' IEnumerable(Of OcrResult.OcrResultLine)
Dim words = result.Words ' IEnumerable(Of OcrResult.OcrResultWord)
For Each word In result.Words
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) confidence {word.Confidence}%")
Next
结构化结果指南覆盖 result.Pages, result.Paragraphs, result.Lines, result.Words,以及用于构建具有布局意识的文档处理的坐标访问。
用直接加载 PDF 取代 S3 分阶段 PDF 处理
任何在开始检测作业之前上传到 S3 的 Textract 代码都可以替换为直接加载 PDF 文件。 没有暂存桶,没有上传定时,没有清理逻辑。
// Textract: upload to S3 → start job → poll → paginate → cleanup (50+ lines)
//IronOCRequivalent:
public string ProcessPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return ocr.Read(input).Text;
}
// Specific page ranges (no Textract equivalent without async job per range)
public string ProcessPdfPages(string pdfPath, int startPage, int endPage)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdfPages(pdfPath, startPage, endPage);
return ocr.Read(input).Text;
}
// Textract: upload to S3 → start job → poll → paginate → cleanup (50+ lines)
//IronOCRequivalent:
public string ProcessPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return ocr.Read(input).Text;
}
// Specific page ranges (no Textract equivalent without async job per range)
public string ProcessPdfPages(string pdfPath, int startPage, int endPage)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdfPages(pdfPath, startPage, endPage);
return ocr.Read(input).Text;
}
Imports IronOcr
Public Class OcrProcessor
' Textract: upload to S3 → start job → poll → paginate → cleanup (50+ lines)
' IronOCRequivalent:
Public Function ProcessPdf(pdfPath As String) As String
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return ocr.Read(input).Text
End Using
End Function
' Specific page ranges (no Textract equivalent without async job per range)
Public Function ProcessPdfPages(pdfPath As String, startPage As Integer, endPage As Integer) As String
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadPdfPages(pdfPath, startPage, endPage)
Return ocr.Read(input).Text
End Using
End Function
End Class
对Textract中置信度较低的文档添加预处理
Textract 的预处理是内部的,不可配置。 当扫描文档产生不良结果时,唯一的选择是重试或接受低置信度的输出。 IronOCR直接公开了预处理流程。
// For documents that returned low-confidence results from Textract
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew(); // Fix rotation from scanner misalignment
input.DeNoise(); // Remove scanner noise artifacts
input.Contrast(); // Boost faint text
input.EnhanceResolution(300); // Scale to optimal OCR resolution
var result = new IronTesseract().Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
// For documents that returned low-confidence results from Textract
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew(); // Fix rotation from scanner misalignment
input.DeNoise(); // Remove scanner noise artifacts
input.Contrast(); // Boost faint text
input.EnhanceResolution(300); // Scale to optimal OCR resolution
var result = new IronTesseract().Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
Imports IronOcr
Dim input As New OcrInput()
input.LoadImage("low-quality-scan.jpg")
input.Deskew() ' Fix rotation from scanner misalignment
input.DeNoise() ' Remove scanner noise artifacts
input.Contrast() ' Boost faint text
input.EnhanceResolution(300) ' Scale to optimal OCR resolution
Dim result = New IronTesseract().Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%")
图像质量修正指南和图像过滤器教程记录了完整的预处理管道,以及针对特定文档类型效果最佳的组合。对于置信分解释及每个元素的置信访问,置信分指南涵盖 result.Confidence 属性和每个词的置信值。
处理异步到同步模式的转变
现有的 Textract 代码必然是 async Task<t> 的,因为 SDK 仅支持异步。 IronOCR操作是同步进行的。 对于已经有异步调用链的应用程序代码,将IronOCR调用包装在 Task.Run 中以保持异步边界。
// Preserves async call site for minimal refactoring
public async Task<string> ExtractTextAsync(string path)
{
return await Task.Run(() => new IronTesseract().Read(path).Text);
}
// Preserves async call site for minimal refactoring
public async Task<string> ExtractTextAsync(string path)
{
return await Task.Run(() => new IronTesseract().Read(path).Text);
}
Imports System.Threading.Tasks
' Preserves async call site for minimal refactoring
Public Async Function ExtractTextAsync(path As String) As Task(Of String)
Return Await Task.Run(Function() New IronTesseract().Read(path).Text)
End Function
这只是为了方便起见而添加的包装,并非必需品。 对于服务器端处理,如果调用代码已经在后台线程中运行,则最好直接进行同步调用。
其他IronOCR功能
除了以上比较点之外, IronOCR还提供了一些AWS Textract所不具备的功能:
- OCR 期间的条形码读取: 设置
ocr.Configuration.ReadBarCodes = true,文档中的条形码在一次传递中与文本一起提取 —— 无需单独的条形码扫描步骤 -长时间作业的进度跟踪:订阅多页面处理的进度事件,无需轮询外部服务 -扫描文档处理:针对典型办公扫描仪输出(包括双面扫描和混合方向页面)进行了优化的流程 - 多语言同时提取: 在读取时结合语言包 —
OcrLanguage.French + OcrLanguage.German— 无需 API 层更改 -护照和身份证读取:专用于身份证明文件上机器可读区域的流程,无需手动定义区域即可提取结构化字段。
.NET兼容性和未来准备情况
IronOCR 的目标平台是.NET 8 和.NET 9,同时积极兼容.NET Standard 2.0 项目和.NET Framework 4.6.2 至 4.8。该库通过单个NuGet包提供适用于 Windows x64、Windows x86、Linux x64 和 macOS 的原生二进制文件——无需运行时标识符切换或平台特定的包引用。AWS Textract的 AWSSDK.Textract 包支持相同的现代 .NET 目标,但部署模型附带完整的 AWS SDK 依赖树、IAM 凭证基础设施以及本文中记载的架构限制。 IronOCR持续进行积极开发,定期发布新版本,跟踪 Tesseract 5 引擎更新和.NET运行时进展,包括与.NET 10 的兼容性(发布时)。
结论
AWS Textract 和IronOCR解决的是同一个问题——从.NET应用程序的文档中提取文本——但它们的架构假设却从根本上不兼容。 Textract 假设文档可以离开您的网络,云服务成本随数量线性增长,并且多页 PDF 需要使用带有 S3 暂存的五阶段异步管道。 IronOCR假设文档保留在处理位置,许可费用应与处理量脱钩,并且 PDF 处理应与图像处理一样只需要三行代码。
成本算数是最明确的分界线。在低量时,Textract的每页费用是可控的。 随着量的增长,年度成本显著累积。 在高页量情况下进行表格提取,多年使用 Textract 的费用可能大大超过在 $5,999 下使用IronOCR的 Unlimited license。 开局算数成立:每页模式迅速累积,并且永不停止。
数据主权是第二个结构性制约因素。 对于医疗保健、法律、金融和政府工作而言,文件在哪里处理并不是一个偏好问题,而是一个合规要求。IronOCR的处理是按设计进行本地化,而不是按配置进行。 没有"本地模式"可供启用; 本地处理是唯一模式。 这就使得合规性的答案很简单:您的文档将保留在您的基础设施中,因为它们没有其他地方可以去。
对于正在大规模评估 OCR 或在文档数据不能离开内部基础设施的环境中运行的团队,IronOCR 的文档提供了完整的 API 参考、Docker、AWS、Azure 和Linux的部署指南,以及涵盖从基本图像读取到可搜索 PDF 生成和多语言提取等所有 OCR 用例的教程。
常见问题解答
什么是 Amazon Textract?
Amazon Textract 是一款 OCR 解决方案,开发者和企业使用它从图像和文档中提取文本。它是与 IronOCR 一起评估的几种用于 .NET 应用程序开发的 OCR 方案之一。
对于 .NET 开发人员来说,IronOCR 与 Amazon Textract 相比如何?
IronOCR 是一个基于 NuGet 的 .NET OCR 库,它使用 IronTesseract 作为核心引擎。与 Amazon Textract 相比,它提供更简单的部署方式(无需 SDK 安装程序)、统一的定价模式以及简洁的 C# API,无需 COM 互操作或云依赖。
IronOCR 比 Amazon Textract 更容易设置吗?
IronOCR 通过单个 NuGet 包进行安装。无需 SDK 安装程序、复制许可证文件、注册 COM 组件或管理单独的运行时二进制文件。整个 OCR 引擎都打包在包中。
Amazon Textract 和 IronOCR 在准确度方面存在哪些差异?
IronOCR 对标准商务文档、发票、收据和扫描表格的识别准确率很高。对于严重损坏的文档或不常见的文字,识别准确率会因源文件质量而异。IronOCR 包含图像预处理滤镜,可提高低质量输入文件的识别率。
IronOCR是否支持PDF文本提取?
是的。IronOCR只需一次调用即可从原生PDF和扫描的PDF图像中提取文本。它还支持多页TIFF文件、图像和流。对于扫描的PDF,OCR逐页进行处理,并为每个页面生成一个结果对象。
Amazon Textract 的授权方式与 IronOCR 相比有何不同?
IronOCR采用永久统一费率许可,不按页或扫描次数收费。处理大量文档的机构无论处理量多少,都只需支付相同的许可费用。详情及批量定价请访问IronOCR许可页面。
IronOCR支持哪些语言?
IronOCR 通过独立的 NuGet 语言包支持 127 种语言。添加语言只需一条命令“dotnet add package IronOcr.Languages.{Language}”。无需手动放置文件或配置路径。
如何在.NET项目中安装IronOCR ?
通过 NuGet 安装:在程序包管理器控制台中运行“Install-Package IronOcr”命令,或在命令行界面 (CLI) 中运行“dotnet add package IronOcr”命令。其他语言包的安装方式相同。无需使用原生 SDK 安装程序。
与 Amazon Textract 不同,IronOCR 是否适用于 Docker 和容器化部署?
是的。IronOCR 通过 NuGet 包在 Docker 容器中运行。许可证密钥通过环境变量设置。OCR 引擎本身不需要任何许可证文件、SDK 路径或卷挂载。
我可以在购买前试用 IronOCR,并将其与 Amazon Textract 进行比较吗?
是的。IronOCR 试用模式可以处理文档,并在输出结果上添加水印,从而生成 OCR 结果。您可以在购买许可证之前,先在自己的文档上验证其准确性。
IronOCR是否支持条形码读取和文本提取?
IronOCR专注于文本提取和OCR识别。对于条形码读取,Iron Software提供了配套库IronBarcode。两者都可单独购买,也可作为Iron Suite套装的一部分购买。
从 Amazon Textract 迁移到 IronOCR 容易吗?
从 Amazon Textract 迁移到 IronOCR 通常涉及将初始化序列替换为 IronTesseract 实例化、移除 COM 生命周期管理以及更新 API 调用。大多数迁移都能显著降低代码复杂度。

