Java 中的 AI 表单识别,第二部分:将图像上传添加到 Spring Boot 应用并通过 Form Recognizer 进行处理





5.00/5 (1投票)
在本文中,我们将实现文件上传功能并将其与 Azure Form Recognition 连接。
摘要
这个由三篇文章组成的系列演示了如何使用 Azure Form Recognizer 来构建一个实际的端到端表单识别应用程序,该应用程序使用了 Java 和 Spring Boot。
- 第一部分 — 通过 Azure App Service 创建和部署应用
- 第二部分 — 将图像上传添加到 Spring Boot 应用并通过 Form Recognizer 进行处理
- 第三部分 — 实际利用 Form Recognizer 返回的数据
完整的代码 存储库 在 GitHub 上可用。
引言
我们在本系列文章的第一篇文章中创建了我们的项目和基础设施。
在第二篇文章中,我们将修改应用以处理图像上传。在我们的例子中,图像包含将发送到 Azure Form Recognizer 进行处理的收据。然后,我们将把识别出的三个字段(MerchantName
、TransactionDate
和 Total
)存储在我们为第一部分准备的 PostgreSQL 数据库中。
完成本文中的步骤后,我们的应用将能够识别收据并将选定的字段输出到终端。
我们的应用程序将能够应用机器学习来解决现实世界的业务问题。通常,商务旅客需要提供收据扫描件才能获得报销。通常,这些扫描件不会自动处理。相反,会计需要将数据输入到另一个系统中。通过自动化收据识别和数据摄取,我们缩短了这个过程。让我们继续我们的应用开发来实现这一点。
设置 Form Recognizer
首先,我们需要在 Azure 中预配 Form Recognizer。您可以使用 Azure CLI 或 Azure 门户来完成。我们将使用 Azure 门户。我们使用搜索框查找 Form Recognizers,然后在 Applied AI Services 视图的中心单击 **创建表单识别器** 按钮。它将打开另一个向导
在 **创建 Form Recognizer** 表单中,请按照以下步骤操作
- 选择 **DB-General** 作为您的订阅。
- 选择 **recognition** 作为您的资源组。
- 为您的实例选择一个 Azure 区域。我们将区域设置为 **East US**。
- 输入您在第一部分部署到 Azure App Service 时为应用设置的全局唯一名称。我们将应用名称设置为
db-receipt-recognizer-82
。 - 选择定价层。我们将定价层设置为 **Free F0**。
Free F0 定价层使我们能够免费使用 Azure Form Recognizer,但每月有 500 页的限制。这对于我们的概念验证来说绰绰有余。设置 Form Recognizer 时,请确保您可以从任何网络访问它。
服务预配完成后,导航到 **资源管理** 下的 **密钥和终结点**。
然后,打开 application.properties
并添加以下示例代码的最后两行
logging.level.org.springframework.jdbc.core=DEBUG
spring.datasource.url=<YOUR_POSTGRES_URL>
spring.datasource.username=<YOUR_USERNAME>
spring.datasource.password=<YOUR_PASSWORD>
spring.jpa.hibernate.ddl-auto=update
server.port=80
azure.form.recognizer.key=<YOUR_FORM_RECOGNIZER_KEY>
azure.form.recognizer.endpoint=<YOUR_FORM_RECOGNIZER_ENDPOINT>
这会存储您的终结点和两个密钥中的一个。
最后,在 pom.xml 文件的依赖项组中添加 azure-sdk-bom
和 azure-ai-formrecognizer
包
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-sdk-bom</artifactId>
<version>1.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-ai-formrecognizer</artifactId>
<version>3.1.8</version>
</dependency>
图像上传视图
现在让我们修改 upload.html 视图。
在 resources/templates/upload.html 中,在以下示例的 <!-- comment -->
标签之间添加代码,以创建一个允许用户上传图像的表单
<body class="w3-black">
<header class="w3-container w3-padding-32 w3-center w3-black">
<h1 class="w3-jumbo">Form recognizer</h1>
<p>Upload a new file</p>
</header>
<!-- insert code starting here -->
<form class="w3-container w3-padding-32"
method="POST"
enctype="multipart/form-data"
action="/upload"
style="margin:auto;width:40%">
<div class="w3-container" >
<input type="file" name="file" />
</div>
<div class="w3-container">
<input type="submit" value="Upload"/>
</div>
</form>
<!-- end of code to insert -->
</body>
</html>
渲染后,上传视图看起来像这样
在这里,我们只有一个表单元素,它允许我们上传图像。在实际场景中,我们可能会在表单中添加其他元素,例如用于提交用户姓名的字段,或适当的验证来限制图像大小、图像格式等。
处理图像上传
接下来,我们需要在控制器端实现处理图像上传的逻辑。这要求我们在 Azure Form Recognizer 服务中进行授权后将图像发送进行识别。
为了使我们的代码更通用,让我们使用 application.properties
中的 Azure Form Recognizer 密钥和终结点。为了在运行时获取这些值,我们将修改 FormRecognitionController
并添加两个字段。您可以参考配套代码查看完整的类。
在 FormRecognitionController
类中,添加以下代码
@Value("${azure.form.recognizer.key}")
private String key;
@Value("${azure.form.recognizer.endpoint}")
private String endpoint;
这两个字段使用 @Value
属性从 application.properties
中检索相应的值。请注意,我们只需要在 @Value
属性的参数中提供属性名称。
然后,我们需要实现控制器的 handleFileUpload
方法,每当用户提交图像上传表单时都会调用该方法。
将以下代码添加到 FormRecognitionController
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
// Create FormRecognizerClient
FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
.credential(new AzureKeyCredential(key))
.endpoint(endpoint)
.buildClient();
try (InputStream receiptImage = file.getInputStream()) {
SyncPoller<FormRecognizerOperationResult, List<RecognizedForm>> syncPoller =
formRecognizerClient.beginRecognizeReceipts(receiptImage, file.getSize());
List<RecognizedForm> recognizedForms = syncPoller.getFinalResult();
// Check if we have at least one form
if(recognizedForms.size() >= 1) {
// Get recognized form
final RecognizedForm recognizedForm = recognizedForms.get(0);
// Extract fields
RecognitionResult recognitionResult = ExtractFormFields(file, recognizedForm);
// Store result
resultsRepository.save(recognitionResult);
// Debut results
System.out.println("\n\n--== Recognition result ==--\n\n"
+ recognitionResult.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
return "index";
}
首先,该方法使用 FormRecognizerClientBuilder
创建一个 FormRecognizerClient
实例。客户端构建器需要我们的 Azure Form Recognizer 凭据,我们可以从 key
和 endpoint
字段中检索这些凭据。
然后,handleFileUpload
方法启动异步收据识别。为此,我们调用 FormRecognizerClient
类实例的 beginRecognizeReceipts
。beginRecognizeReceipts
方法接受两个参数:包含上传图像的输入流,以及文件大小。
在后端,beginRecognizeReceipts
将我们的图像发送到 Azure Form Recognizer 进行处理。底层进程使用默认的预训练机器学习模型,该模型可以识别收据图像的特定元素。在这种情况下,我们只传递一张图像,但您可以一次发送多张图像。当 beginRecognizeResults
完成后,我们需要检索并解释识别结果。
检索识别结果
beginRecognizeResults
方法返回一个 RecognizedForm
类实例的集合。RecognizedForm
类有几个属性,但我们主要对 fields
成员感兴趣
private final Map<String, FormField> fields;
该成员将识别元素的名称及其值存储为 FormField 类的实例。FormField
类有几个用于解释我们收据中识别元素的成员
public final class FormField {
private final float confidence;
private final FieldData labelData;
private final String name;
private final FieldValue value;
private final FieldData valueData;
// Constructor, getters and setters
}
正如我们所见,我们可以选择性地检索字段名称、值,甚至预测置信度。当收据图像质量较低并导致预测不佳或不可靠时,这一点尤其重要。在这种情况下,我们可能希望拒绝置信度低于某个阈值的识别,然后通知提交者重新上传图像。
要提取已识别字段的实际值,您可以按照以下两种方式之一进行。
第一种方法是使用强类型 Receipt 类。其构造函数接受一个 RecognizedForm 实例,并创建一个具有映射到收据图像中相应元素的属性的对象
public final class Receipt {
/**
* List of recognized field items.
*/
private List<ReceiptItem> receiptItems;
/**
* Recognized receipt type information.
*/
private ReceiptType receiptType;
/**
* Recognized field merchant name.
*/
private TypedFormField<String> merchantName;
// …
}
另一种方法是手动解析表单字段
// Retrieve total
Map<String, FormField> recognizedFields = recognizedForm.getFields();
FormField totalField = recognizedFields.get("Total");
if (totalField != null) {
if (FieldValueType.FLOAT == totalField.getValue().getValueType()) {
recognitionResult.setTotal(totalField.getValue().asFloat());
}
}
在本教程中,我们将在 handleFileUpload
中结合使用这两种方法,即使用 ExtractFormFields
方法。要做到这一点,我们需要为项目添加 Receipt.java
。我们使用此类来提取 MerchantName
和 TransactionDate
字段,并通过手动解析表单字段来提取 Total
字段。
ExtractFormFields
方法如下所示
private RecognitionResult ExtractFormFields(MultipartFile file,
final RecognizedForm recognizedForm) {
RecognitionResult recognitionResult = new RecognitionResult();
Receipt receipt = new Receipt(recognizedForm);
// Set receipt file name based on the upload image name
recognitionResult.setReceiptFileName(file.getOriginalFilename());
// Get Merchant name and transaction date
recognitionResult.setMerchantName(receipt.getMerchantName().getValue());
recognitionResult.setTransactionDate(receipt.getTransactionDate().getValue());
// Retrieve total
Map<String, FormField> recognizedFields = recognizedForm.getFields();
FormField totalField = recognizedFields.get("Total");
if (totalField != null) {
if (FieldValueType.FLOAT == totalField.getValue().getValueType()) {
recognitionResult.setTotal(totalField.getValue().asFloat());
}
}
return recognitionResult;
}
ExtractFormFields
辅助方法返回一个 RecognitionResult
类的实例,该类是我们在此教程第一部分中实现的。一旦我们有了 RecognitionResult
的实例,我们就会将其存储在数据库中。您可以参考配套代码中 FormRecognitionController
的 handleFileUpload
方法。
在 FormRecognitionController
中,添加以下代码
resultsRepository.save(recognitionResult);
要测试该解决方案,请使用 mvn clean install
编译您的应用。然后,使用 mvn spring-boot:run
运行您的应用。
接下来,转到上传视图以识别收据图像。我们使用的是配套 GitHub 存储库中 images/ 文件夹中的示例图像。识别结果会出现在 IDE 的输出窗口中。
摘要
在本教程的这一部分,我们学习了如何使用图像上传扩展 Spring Java 应用。我们为该功能添加了所需的表单和控制器方法。
此外,我们使用了两种不同的方法上传收据图像,这些图像被发送到 Azure Form Recognizer 实例进行识别。识别结果被存储在部署到 Azure 的 PostgreSQL 数据库中。
在本教程的最后一部分,我们将利用识别结果的显示。
要了解有关将 Java 代码交付到 Azure 和其他云的最简单方法的更多技巧,请查看网络研讨会 Azure 网络研讨会系列 - 使用 Azure 和 GitHub 将 Java 交付到云。