65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2022 年 4 月 19 日

CPOL

6分钟阅读

viewsIcon

4807

downloadIcon

72

在本文中,我们将实现文件上传功能并将其与 Azure Form Recognition 连接。

摘要

这个由三篇文章组成的系列演示了如何使用 Azure Form Recognizer 来构建一个实际的端到端表单识别应用程序,该应用程序使用了 Java 和 Spring Boot。

  • 第一部分 — 通过 Azure App Service 创建和部署应用
  • 第二部分 — 将图像上传添加到 Spring Boot 应用并通过 Form Recognizer 进行处理
  • 第三部分 — 实际利用 Form Recognizer 返回的数据

完整的代码 存储库 在 GitHub 上可用。

引言

我们在本系列文章的第一篇文章中创建了我们的项目和基础设施。

在第二篇文章中,我们将修改应用以处理图像上传。在我们的例子中,图像包含将发送到 Azure Form Recognizer 进行处理的收据。然后,我们将把识别出的三个字段(MerchantNameTransactionDateTotal)存储在我们为第一部分准备的 PostgreSQL 数据库中。

完成本文中的步骤后,我们的应用将能够识别收据并将选定的字段输出到终端。

我们的应用程序将能够应用机器学习来解决现实世界的业务问题。通常,商务旅客需要提供收据扫描件才能获得报销。通常,这些扫描件不会自动处理。相反,会计需要将数据输入到另一个系统中。通过自动化收据识别和数据摄取,我们缩短了这个过程。让我们继续我们的应用开发来实现这一点。

设置 Form Recognizer

首先,我们需要在 Azure 中预配 Form Recognizer。您可以使用 Azure CLI 或 Azure 门户来完成。我们将使用 Azure 门户。我们使用搜索框查找 Form Recognizers,然后在 Applied AI Services 视图的中心单击 **创建表单识别器** 按钮。它将打开另一个向导

在 **创建 Form Recognizer** 表单中,请按照以下步骤操作

  1. 选择 **DB-General** 作为您的订阅。
  2. 选择 **recognition** 作为您的资源组。
  3. 为您的实例选择一个 Azure 区域。我们将区域设置为 **East US**。
  4. 输入您在第一部分部署到 Azure App Service 时为应用设置的全局唯一名称。我们将应用名称设置为 db-receipt-recognizer-82
  5. 选择定价层。我们将定价层设置为 **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-bomazure-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 凭据,我们可以从 keyendpoint 字段中检索这些凭据。

然后,handleFileUpload 方法启动异步收据识别。为此,我们调用 FormRecognizerClient 类实例的 beginRecognizeReceiptsbeginRecognizeReceipts 方法接受两个参数:包含上传图像的输入流,以及文件大小。

在后端,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。我们使用此类来提取 MerchantNameTransactionDate 字段,并通过手动解析表单字段来提取 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 的实例,我们就会将其存储在数据库中。您可以参考配套代码中 FormRecognitionControllerhandleFileUpload 方法。

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 交付到云

© . All rights reserved.