private String extractImageText(MultipartFile file) { try { // 创建临时文件 Path tempFile = Files.createTempFile("ocr_", getFileExtension(file.getOriginalFilename())); Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING); // 执行OCR识别 File imageFile = tempFile.toFile(); String result = tesseract.doOCR(imageFile) .replaceAll("\\s+", " ").trim(); // 清理临时文件 Files.deleteIfExists(tempFile); return result; } catch (Exception e) { return "OCR处理失败: " + e.getMessage(); } }他也把内容给读取出来了但是没有办法比较和校验啊
时间: 2025-07-24 21:36:06 浏览: 2
<think>我们之前讨论的是如何从图片或PDF中提取文本并与JSON内容进行严格比较。现在,问题聚焦在`extractImageText`方法虽然能提取文本,但如何与JSON内容进行比较和校验。
实际上,我们需要将提取的文本与JSON中的预期内容进行比对。之前已经给出了一个完整的校验服务,但用户可能希望看到更直接的比较部分。
因此,我将提供一个完整的流程,包括:
1. 提取图片/PDF文本(使用OCR或PDFBox)
2. 解析JSON配置文件(包含预期的字段和值)
3. 将提取的文本与JSON中的每个字段值进行严格比较
下面是一个整合了提取和比较的完整服务类,并特别关注比较逻辑。</think>### 图像文本提取与严格校验解决方案
您指出了一个关键问题:`extractImageText` 方法只负责提取文本,但缺少与 JSON 内容比较的校验逻辑。我将提供一个完整的端到端解决方案,包含文本提取、JSON 解析和严格校验的全流程。
```java
package com.luxsan.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
@Slf4j
@Service
public class DocumentValidationService {
private final ObjectMapper objectMapper = new ObjectMapper();
private final Map<String, Pattern> patternCache = new ConcurrentHashMap<>();
private Tesseract tesseract;
public DocumentValidationService() {
initOcrEngine();
}
private void initOcrEngine() {
try {
tesseract = new Tesseract();
tesseract.setDatapath("/path/to/tessdata");
tesseract.setLanguage("eng+chi_sim");
tesseract.setPageSegMode(1);
tesseract.setOcrEngineMode(1);
} catch (Exception e) {
log.error("OCR引擎初始化失败", e);
throw new RuntimeException("OCR引擎初始化失败", e);
}
}
/**
* 提取图像文本内容
*/
public String extractImageText(MultipartFile file) {
try {
// 创建临时文件
Path tempFile = Files.createTempFile("ocr_", getFileExtension(file.getOriginalFilename()));
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
// 执行OCR识别
File imageFile = tempFile.toFile();
String result = tesseract.doOCR(imageFile)
.replaceAll("\\s+", " ").trim();
// 清理临时文件
Files.deleteIfExists(tempFile);
return result;
} catch (Exception e) {
log.error("OCR处理失败", e);
return "OCR_ERROR: " + e.getMessage();
}
}
/**
* 提取PDF文本内容
*/
public String extractPdfText(MultipartFile file) {
try (InputStream pdfStream = file.getInputStream();
PDDocument doc = PDDocument.load(pdfStream)) {
PDFTextStripper stripper = new PDFTextStripper();
return stripper.getText(doc).replaceAll("\\s+", " ").trim();
} catch (Exception e) {
log.error("PDF提取失败", e);
return "PDF_ERROR: " + e.getMessage();
}
}
/**
* 解析JSON配置
*/
public JsonNode parseJson(String jsonContent) {
try {
return objectMapper.readTree(jsonContent);
} catch (Exception e) {
log.error("JSON解析失败", e);
return null;
}
}
/**
* 严格内容校验
*/
public List<ValidationResult> validateContent(String documentText, JsonNode jsonConfig) {
List<ValidationResult> results = new ArrayList<>();
if (documentText.startsWith("OCR_ERROR") || documentText.startsWith("PDF_ERROR")) {
results.add(new ValidationResult("SYSTEM", "ERROR", "文档提取失败: " + documentText, false));
return results;
}
if (jsonConfig == null) {
results.add(new ValidationResult("SYSTEM", "ERROR", "JSON解析失败", false));
return results;
}
// 标准化文档文本(保留原始大小写)
String normalizedDocText = normalizeText(documentText);
log.info("标准化文档文本: \n{}", normalizedDocText);
// 遍历JSON字段并进行校验
jsonConfig.fields().forEachRemaining(entry -> {
String fieldName = entry.getKey();
JsonNode valueNode = entry.getValue();
if (!valueNode.isValueNode()) return;
String expectedValue = valueNode.asText().trim();
if (expectedValue.isEmpty()) return;
// 执行严格匹配检查
boolean isExactMatch = isExactMatch(normalizedDocText, expectedValue);
results.add(new ValidationResult(
fieldName,
expectedValue,
isExactMatch ? "完全匹配" : "不匹配",
isExactMatch
));
});
return results;
}
/**
* 完整校验流程
*/
public List<ValidationResult> fullValidation(MultipartFile documentFile, String jsonContent) {
String documentText;
if (documentFile.getOriginalFilename().toLowerCase().endsWith(".pdf")) {
documentText = extractPdfText(documentFile);
} else {
documentText = extractImageText(documentFile);
}
JsonNode jsonConfig = parseJson(jsonContent);
return validateContent(documentText, jsonConfig);
}
// ============== 辅助方法 ==============
private String normalizeText(String text) {
if (text == null) return "";
return text.replaceAll("\\s{2,}", " ").trim();
}
private boolean isExactMatch(String document, String expected) {
// 方法1:直接包含检查
if (document.contains(expected)) {
return true;
}
// 方法2:边界匹配(考虑单词边界)
String regex = "\\b" + Pattern.quote(expected) + "\\b";
Pattern pattern = patternCache.computeIfAbsent(regex, Pattern::compile);
if (pattern.matcher(document).find()) {
return true;
}
// 方法3:容错匹配(处理常见OCR错误)
return matchWithOcrTolerance(document, expected);
}
private boolean matchWithOcrTolerance(String document, String expected) {
// 构建容错正则表达式
StringBuilder regexBuilder = new StringBuilder();
for (char c : expected.toCharArray()) {
switch (c) {
case '0': regexBuilder.append("[0Oo]"); break;
case 'O': case 'o': regexBuilder.append("[Oo0]"); break;
case '1': regexBuilder.append("[1Il]"); break;
case 'I': case 'l': regexBuilder.append("[Il1]"); break;
case '5': regexBuilder.append("[5Ss]"); break;
case 'S': case 's': regexBuilder.append("[Ss5]"); break;
case '8': regexBuilder.append("[8B]"); break;
case 'B': regexBuilder.append("[B8]"); break;
default: regexBuilder.append(Pattern.quote(String.valueOf(c)));
}
}
Pattern pattern = patternCache.computeIfAbsent(regexBuilder.toString(), Pattern::compile);
return pattern.matcher(document).find();
}
private String getFileExtension(String filename) {
if (filename == null) return ".tmp";
int dotIndex = filename.lastIndexOf('.');
return (dotIndex == -1) ? ".tmp" : filename.substring(dotIndex);
}
/**
* 验证结果类
*/
public static class ValidationResult {
private final String fieldName;
private final String expectedValue;
private final String actualStatus;
private final boolean isValid;
public ValidationResult(String fieldName, String expectedValue, String actualStatus, boolean isValid) {
this.fieldName = fieldName;
this.expectedValue = expectedValue;
this.actualStatus = actualStatus;
this.isValid = isValid;
}
// Getters
public String getFieldName() { return fieldName; }
public String getExpectedValue() { return expectedValue; }
public String getActualStatus() { return actualStatus; }
public boolean isValid() { return isValid; }
@Override
public String toString() {
return String.format("%s: 期望值='%s', 状态='%s', 有效=%b",
fieldName, expectedValue, actualStatus, isValid);
}
}
}
```
### 核心校验逻辑详解
#### 1. 完整校验流程 (`fullValidation` 方法)
```java
public List<ValidationResult> fullValidation(MultipartFile documentFile, String jsonContent) {
// 步骤1: 提取文档文本
String documentText = extractText(documentFile);
// 步骤2: 解析JSON配置
JsonNode jsonConfig = parseJson(jsonContent);
// 步骤3: 执行内容校验
return validateContent(documentText, jsonConfig);
}
```
这个方法提供了端到端的校验流程:
1. 自动识别文件类型(PDF或图片)
2. 调用相应的提取方法
3. 解析JSON配置
4. 执行严格匹配校验
#### 2. 严格匹配校验 (`isExactMatch` 方法)
```java
private boolean isExactMatch(String document, String expected) {
// 直接包含检查
if (document.contains(expected)) return true;
// 边界匹配
Pattern pattern = patternCache.computeIfAbsent("\\b" + Pattern.quote(expected) + "\\b", Pattern::compile);
if (pattern.matcher(document).find()) return true;
// OCR容错匹配
return matchWithOcrTolerance(document, expected);
}
```
三级匹配策略:
1. **直接匹配**:最快速的基础检查
2. **边界匹配**:确保匹配的是完整单词
3. **OCR容错**:处理常见识别错误
#### 3. OCR容错机制 (`matchWithOcrTolerance` 方法)
```java
private boolean matchWithOcrTolerance(String document, String expected) {
StringBuilder regexBuilder = new StringBuilder();
for (char c : expected.toCharArray()) {
switch (c) {
case '0': regexBuilder.append("[0Oo]"); break; // 0/O/o 混淆
case 'O': case 'o': regexBuilder.append("[Oo0]"); break;
case '1': regexBuilder.append("[1Il]"); break; // 1/I/l 混淆
// ...其他字符处理
}
}
Pattern pattern = Pattern.compile(regexBuilder.toString());
return pattern.matcher(document).find();
}
```
处理常见OCR错误:
- 数字 `0` 与字母 `O`/`o` 混淆
- 数字 `1` 与字母 `I`/`l` 混淆
- 数字 `5` 与字母 `S`/`s` 混淆
- 数字 `8` 与字母 `B` 混淆
### 使用示例
```java
// 初始化服务
DocumentValidationService validator = new DocumentValidationService();
// 准备测试数据
MultipartFile imageFile = ... // 获取上传的图像文件
String jsonContent = "{\"invoiceNumber\":\"INV-2023-001\", \"totalAmount\":\"$1,250.00\"}";
// 执行完整校验
List<ValidationResult> results = validator.fullValidation(imageFile, jsonContent);
// 打印结果
System.out.println("===== 校验结果 =====");
for (ValidationResult result : results) {
System.out.println(result);
}
```
#### 可能的输出:
```
===== 校验结果 =====
invoiceNumber: 期望值='INV-2023-001', 状态='完全匹配', 有效=true
totalAmount: 期望值='$1,250.00', 状态='完全匹配', 有效=true
```
或当存在OCR错误时:
```
===== 校验结果 =====
invoiceNumber: 期望值='INV-2023-001', 状态='完全匹配', 有效=true
totalAmount: 期望值='$1,250.00', 状态='不匹配', 有效=false
```
### 错误处理增强
#### 1. 提取错误处理
```java
public String extractImageText(MultipartFile file) {
try {
// ...提取逻辑...
} catch (Exception e) {
log.error("OCR处理失败", e);
return "OCR_ERROR: " + e.getMessage(); // 特殊错误标识
}
}
```
#### 2. 校验错误处理
```java
public List<ValidationResult> validateContent(String documentText, JsonNode jsonConfig) {
if (documentText.startsWith("OCR_ERROR") || documentText.startsWith("PDF_ERROR")) {
results.add(new ValidationResult("SYSTEM", "ERROR", "文档提取失败: " + documentText, false));
return results;
}
if (jsonConfig == null) {
results.add(new ValidationResult("SYSTEM", "ERROR", "JSON解析失败", false));
return results;
}
// ...正常校验逻辑...
}
```
### 性能优化技巧
1. **并行处理多个字段**:
```java
public List<ValidationResult> parallelValidate(String document, JsonNode jsonConfig) {
List<ValidationResult> results = Collections.synchronizedList(new ArrayList<>());
jsonConfig.fields().forEachRemaining(entry -> {
CompletableFuture.runAsync(() -> {
String field = entry.getKey();
String value = entry.getValue().asText();
boolean match = isExactMatch(document, value);
results.add(new ValidationResult(field, value, match ? "匹配" : "不匹配", match));
});
});
// 等待所有任务完成
CompletableFuture.allOf(
jsonConfig.fieldNames().map(name -> CompletableFuture.completedFuture(name))
.toArray(CompletableFuture[]::new)
).join();
return results;
}
```
2. **文本预索引**:
```java
private Map<String, Boolean> createTextIndex(String document) {
Map<String, Boolean> index = new HashMap<>();
String[] words = document.split("\\s+");
for (String word : words) {
index.put(word, true);
// 添加常见OCR变体
if (word.contains("0")) index.put(word.replace('0', 'O'), true);
if (word.contains("1")) index.put(word.replace('1', 'I'), true);
// ...其他变体
}
return index;
}
```
### 增强功能建议
1. **相似度评分**:
```java
private double calculateSimilarity(String actual, String expected) {
int maxLen = Math.max(actual.length(), expected.length());
if (maxLen == 0) return 1.0;
int[][] dp = new int[actual.length() + 1][expected.length() + 1];
for (int i = 0; i <= actual.length(); i++) {
for (int j = 0; j <= expected.length(); j++) {
if (i == 0) dp[i][j] = j;
else if (j == 0) dp[i][j] = i;
else {
int cost = actual.charAt(i-1) == expected.charAt(j-1) ? 0 : 1;
dp[i][j] = Math.min(Math.min(
dp[i-1][j] + 1,
dp[i][j-1] + 1),
dp[i-1][j-1] + cost);
}
}
}
return 1.0 - (double) dp[actual.length()][expected.length()] / maxLen;
}
```
2. **上下文验证**:
```java
private boolean validateWithContext(String document, String expected, String context) {
String pattern = Pattern.quote(context) + "\\s*[:=]?\\s*" + Pattern.quote(expected);
return Pattern.compile(pattern).matcher(document).find();
}
```
阅读全文
相关推荐














