Java票据识别实操案例
下面我将通过一个完整的实操案例,展示如何使用Java实现票据识别(OCR)功能。

我们将使用 Tesseract OCR 引擎实现票据文字识别,包括:
- 图片预处理(提高识别率)
- 文字提取
- 结构化数据解析
环境准备
安装Tesseract OCR引擎
Windows:
# 下载安装包 https://github.com/UB-Mannheim/tesseract/wiki # 安装后配置环境变量 # 添加 TESSDATA_PREFIX 环境变量 # 值: C:\Program Files\Tesseract-OCR\tessdata
Mac:
brew install tesseract brew install tesseract-lang # 中文语言包
Linux:
sudo apt-get install tesseract-ocr sudo apt-get install tesseract-ocr-chi-sim # 中文简体
创建Maven项目
pom.xml依赖配置:
<dependencies>
<!-- Tesseract OCR -->
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>5.4.0</version>
</dependency>
<!-- 图片处理 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.20</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
核心代码实现
票据识别服务类
package com.example.invoice;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class InvoiceOCRService {
private final ITesseract tesseract;
public InvoiceOCRService() {
this.tesseract = new Tesseract();
// 设置语言包目录(根据实际安装路径修改)
this.tesseract.setDatapath("D:/Program Files/Tesseract-OCR/tessdata");
// 设置识别语言:中文+英文
this.tesseract.setLanguage("chi_sim+eng");
// 设置OCR引擎模式
this.tesseract.setOcrEngineMode(1); // LSTM引擎
// 设置页面分割模式
this.tesseract.setPageSegMode(6); // 假定为统一的文本块
}
/**
* 识别票据图片
* @param imagePath 图片路径
* @return 识别出的原始文本
*/
public String recognizeInvoice(String imagePath) {
try {
File imageFile = new File(imagePath);
// 预处理图片
BufferedImage processedImage = preprocessImage(imageFile);
// 执行OCR识别
String result = tesseract.doOCR(processedImage);
return result;
} catch (TesseractException | IOException e) {
e.printStackTrace();
return "识别失败: " + e.getMessage();
}
}
/**
* 图片预处理(提高识别率)
*/
private BufferedImage preprocessImage(File imageFile) throws IOException {
BufferedImage originalImage = ImageIO.read(imageFile);
BufferedImage processedImage = new BufferedImage(
originalImage.getWidth(),
originalImage.getHeight(),
BufferedImage.TYPE_BYTE_GRAY // 转为灰度图
);
// 灰度化
Graphics2D graphics = processedImage.createGraphics();
graphics.drawImage(originalImage, 0, 0, null);
graphics.dispose();
// 二值化处理(简单的阈值方法)
for (int y = 0; y < processedImage.getHeight(); y++) {
for (int x = 0; x < processedImage.getWidth(); x++) {
int rgb = processedImage.getRGB(x, y);
int gray = (rgb >> 16) & 0xFF; // 获取灰度值
// 二值化阈值(可根据实际情况调整)
if (gray < 128) {
processedImage.setRGB(x, y, Color.BLACK.getRGB());
} else {
processedImage.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
return processedImage;
}
}
票据数据解析类
package com.example.invoice;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class InvoiceDataParser {
/**
* 解析发票信息
* @param rawText OCR识别的原始文本
* @return 解析后的发票信息
*/
public InvoiceInfo parseInvoiceInfo(String rawText) {
InvoiceInfo info = new InvoiceInfo();
// 提取发票号码
info.setInvoiceNo(extractByPattern(rawText, "发票号码[:: ]*(\\w+)"));
info.setInvoiceNo(extractByPattern(rawText, "发票代码[:: ]*(\\d+)"));
// 提取日期
info.setDate(extractByPattern(rawText, "开票日期[:: ]*(\\d{4}[-年]\\d{1,2}[-月]\\d{1,2})"));
// 提取金额
String amountStr = extractByPattern(rawText, "价税合计[:: ]*[¥¥]?(\\d+\\.?\\d*)");
if (amountStr.isEmpty()) {
amountStr = extractByPattern(rawText, "合计[:: ]*[¥¥]?(\\d+\\.?\\d*)");
}
if (!amountStr.isEmpty()) {
info.setTotalAmount(Double.parseDouble(amountStr));
}
// 提取购买方信息
info.setBuyerName(extractByPattern(rawText, "购买方[名称:: ]*([\\u4e00-\\u9fa5]+)"));
info.setBuyerTaxId(extractByPattern(rawText, "纳税人识别号[:: ]*(\\d{15,20})"));
// 提取商品/服务明细(简化版)
info.setGoodsDescription(extractByPattern(rawText, "货物或应税劳务、服务名称[:: ]*([^\\n]+)"));
return info;
}
/**
* 使用正则表达式提取信息
*/
private String extractByPattern(String text, String regex) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
return matcher.group(1).trim();
}
return "";
}
}
发票信息实体类
package com.example.invoice;
public class InvoiceInfo {
private String invoiceNo; // 发票号码
private String invoiceCode; // 发票代码
private String date; // 开票日期
private Double totalAmount; // 价税合计
private String buyerName; // 购买方名称
private String buyerTaxId; // 购买方税号
private String goodsDescription; // 商品描述
// Getters and Setters
public String getInvoiceNo() { return invoiceNo; }
public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; }
public String getInvoiceCode() { return invoiceCode; }
public void setInvoiceCode(String invoiceCode) { this.invoiceCode = invoiceCode; }
public String getDate() { return date; }
public void setDate(String date) { this.date = date; }
public Double getTotalAmount() { return totalAmount; }
public void setTotalAmount(Double totalAmount) { this.totalAmount = totalAmount; }
public String getBuyerName() { return buyerName; }
public void setBuyerName(String buyerName) { this.buyerName = buyerName; }
public String getBuyerTaxId() { return buyerTaxId; }
public void setBuyerTaxId(String buyerTaxId) { this.buyerTaxId = buyerTaxId; }
public String getGoodsDescription() { return goodsDescription; }
public void setGoodsDescription(String goodsDescription) { this.goodsDescription = goodsDescription; }
@Override
public String toString() {
return "InvoiceInfo{" +
"invoiceNo='" + invoiceNo + '\'' +
", invoiceCode='" + invoiceCode + '\'' +
", date='" + date + '\'' +
", totalAmount=" + totalAmount +
", buyerName='" + buyerName + '\'' +
", buyerTaxId='" + buyerTaxId + '\'' +
", goodsDescription='" + goodsDescription + '\'' +
'}';
}
}
主程序入口
package com.example.invoice;
public class InvoiceRecognitionApp {
public static void main(String[] args) {
// 票据图片路径
String imagePath = "D:/invoices/sample_invoice.jpg";
// 创建OCR服务
InvoiceOCRService ocrService = new InvoiceOCRService();
// 执行识别
System.out.println("开始识别票据...");
String rawText = ocrService.recognizeInvoice(imagePath);
System.out.println("原始识别结果:");
System.out.println(rawText);
System.out.println("==================================");
// 解析结构化数据
InvoiceDataParser parser = new InvoiceDataParser();
InvoiceInfo invoiceInfo = parser.parseInvoiceInfo(rawText);
// 输出结果
System.out.println("解析后的发票信息:");
System.out.println(invoiceInfo);
}
}
批量处理示例
package com.example.invoice;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class BatchInvoiceProcessor {
public static void main(String[] args) {
String invoiceDir = "D:/invoices/";
InvoiceOCRService ocrService = new InvoiceOCRService();
InvoiceDataParser parser = new InvoiceDataParser();
List<InvoiceInfo> results = new ArrayList<>();
File dir = new File(invoiceDir);
File[] files = dir.listFiles((d, name) ->
name.endsWith(".jpg") || name.endsWith(".png") || name.endsWith(".jpeg"));
if (files != null) {
for (File file : files) {
System.out.println("处理文件: " + file.getName());
String rawText = ocrService.recognizeInvoice(file.getAbsolutePath());
InvoiceInfo info = parser.parseInvoiceInfo(rawText);
results.add(info);
}
}
// 输出汇总结果
System.out.println("处理完成,共识别 " + results.size() + " 张票据");
results.forEach(System.out::println);
}
}
进阶优化建议
提高识别准确率的技巧
// 更精细的图片预处理
public BufferedImage advancedPreprocess(BufferedImage image) {
// 1. 去噪
BufferedImage denoised = applyMedianFilter(image);
// 2. 对比度增强
BufferedImage enhanced = enhanceContrast(denoised);
// 3. 倾斜校正
BufferedImage corrected = deskew(enhanced);
// 4. 自适应二值化
BufferedImage binary = adaptiveThreshold(corrected);
return binary;
}
使用OpenCV进行高级处理
<!-- 添加OpenCV依赖 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>
集成深度学习模型
对于更复杂的票据类型,建议使用专用的深度学习模型:
- 百度飞桨PaddleOCR:有Java调用接口
- 华为OCR:提供云服务API
- 自训练模型:使用TensorFlow/PyTorch训练自定义模型
测试用例
package com.example.invoice;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class InvoiceOCRTest {
@Test
public void testInvoiceRecognition() {
InvoiceOCRService ocrService = new InvoiceOCRService();
String result = ocrService.recognizeInvoice("src/test/resources/test_invoice.jpg");
assertNotNull(result);
assertFalse(result.contains("识别失败"));
System.out.println("识别结果:");
System.out.println(result);
}
@Test
public void testInvoiceDataParser() {
String mockText = "发票号码:12345678\n开票日期:2024-01-15\n价税合计:¥1560.00";
InvoiceDataParser parser = new InvoiceDataParser();
InvoiceInfo info = parser.parseInvoiceInfo(mockText);
assertEquals("12345678", info.getInvoiceNo());
assertEquals("2024-01-15", info.getDate());
assertEquals(1560.0, info.getTotalAmount(), 0.01);
}
}
常见问题解决
识别中文失败
- 确认已安装中文语言包:
tesseract --list-langs - 检查语言设置:
tesseract.setLanguage("chi_sim") - 图片文字方向:可以尝试旋转90度
识别率低
- 确保图片清晰,分辨率不低于300dpi
- 预处理时调整二值化阈值
- 尝试不同的PageSegMode值(3-13)
性能优化
- 使用多线程并行处理批量图片
- 调整Tesseract线程数:
tesseract.setThreadCount(4) - 对于清晰图片,可以跳过预处理步骤
项目结构建议
invoice-ocr/
├── src/
│ ├── main/java/com/example/invoice/
│ │ ├── InvoiceOCRService.java # OCR服务
│ │ ├── InvoiceDataParser.java # 数据解析
│ │ ├── InvoiceInfo.java # 数据实体
│ │ └── InvoiceRecognitionApp.java # 主程序
│ └── test/java/com/example/invoice/
│ └── InvoiceOCRTest.java # 测试类
├── resources/
│ └── test_invoice.jpg # 测试图片
├── tessdata/ # Tesseract语言包
└── pom.xml
这个案例提供了完整的票据识别解决方案,你可以根据实际业务需求进行调整和扩展。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播