利用 Tesseract OCR 实现图片 Excel 识别与转换:从原理到实践

589 阅读3分钟

一、为什么选择 Tesseract 处理 Excel 图片?

在日常办公中,我们常遇到需要将扫描版 Excel、截图表格转换为可编辑文档的需求。Tesseract 作为开源 OCR(光学字符识别)引擎,具备以下优势:

  • 开源免费:无需商业授权,适合个人和中小团队使用;
  • 多语言支持:可识别中文、英文等多种语言文本;
  • 扩展性强:通过 Python 接口(pytesseract)可灵活集成到自定义流程中;
  • 社区活跃:丰富的文档和第三方优化方案(如自定义训练模型)。

二、环境搭建与依赖安装

因为我使用的腾讯云的centos8系统。本文实现基于centos8系统和Java语言实现。

1. 安装 Tesseract 引擎及语言包

CentOS 8 使用dnf包管理器安装,执行以下命令:


# 安装Tesseract核心引擎
sudo dnf install tesseract

# 安装中文(简体)和英文语言包
sudo dnf install tesseract-lang tesseract-lang-chi-sim tesseract-lang-eng

# 验证安装(输出版本号即成功)
tesseract --version

image.png

2. 安装图片处理依赖(可选)

若需预处理图片,建议安装 OpenCV(用于边缘检测、透视校正):

sudo dnf install opencv opencv-devel

二、Java 项目集成 Tesseract(基于 Tess4J 库)

1.加载依赖
<!-- Tesseract OCR -->
<dependency>
    <groupId>net.sourceforge.tess4j</groupId>
    <artifactId>tess4j</artifactId>
    <version>4.5.4</version>
</dependency>

<!-- Apache POI for Excel -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>


<!-- Apache POI用于生成Excel -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.3</version>
    </dependency>
    
    <!-- 图片处理库(可选) -->
    <dependency>
        <groupId>com.github.jai-imageio</groupId>
        <artifactId>jai-imageio-core</artifactId>
        <version>1.4.0</version>
    </dependency>
2. 核心代码实现:图片 Excel 识别
//记载解析容器,注意这个对象线程不安全
public Image2ExcelUtils() {
    this.tesseract = new Tesseract();
    try {
        // 设置Tesseract数据路径,需提前下载语言包
        tesseract.setDatapath("/usr/share/tesseract/tessdata");
        // 设置识别语言为中文
        tesseract.setLanguage("chi_sim");
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("初始化Tesseract失败", e);
    }
}


//解析图片
public List<String[]> recognizeTextFromImage(File imageFile, List<String> headerHolder) {
    List<String[]> result = new ArrayList<>();
    try {
        BufferedImage image = ImageIO.read(imageFile);
        String recognizedText = tesseract.doOCR(image);
        result = parseTextToTable(recognizedText,headerHolder);
    } catch (TesseractException | IOException e) {
        e.printStackTrace();
    }
    return result;
}




/**
 * 解析OCR识别出的文本,图片识别结果是一行一行的,我们需要提取表头和表格数据
 */
public List<String[]> parseTextToTable(String text, List<String> headerHolder) {
    List<String[]> tableData = new ArrayList<>();
    String[] lines = text.split("\n");

    for (String line : lines) {
        String trimmedLine = line.trim();

        if (trimmedLine.isEmpty()) {continue;}
        System.err.println("开始解析行: " + trimmedLine);
        List<String> row = extractColumns(trimmedLine);
        if (row.isEmpty()) {
            System.err.println("无法解析的行: " + trimmedLine);
            continue;
        }
        if (headerHolder.isEmpty()) {
            // 第一行作为表头
            headerHolder.addAll(row);
        } else {
            tableData.add(row.toArray(new String[0]));
        }
    }
    return tableData;
}


//列内容解析处理
private static List<String> extractColumns(String line) {
    
    // 统一替换特殊字符为|
    line = line.replaceAll("[\[“」]", "|")
            // 替换其他连续空格为|
            .replaceAll("\s{2,}", "|")
            // 合并连续|
            .replaceAll("\|+", "|")
            // 处理空列
            .replaceAll("\|\s*\|", "| |")
            .trim();
    log.info("row={}", line);
    return Arrays.stream(line.split("\|")).collect(Collectors.toList()); // 直接按竖线分割;
}

3. 有了数据后我们就可以把数据写入excel了
public void createExcelFromData(List<String[]> tableData, String outputPath, String[] headers) {
    try (Workbook workbook = new XSSFWorkbook();
         FileOutputStream fileOut = new FileOutputStream(outputPath)) {

        Sheet sheet = workbook.createSheet("ParsedData");
        Font headerFont = workbook.createFont();
        headerFont.setBold(true);
        CellStyle headerCellStyle = workbook.createCellStyle();
        headerCellStyle.setFont(headerFont);

        // 使用传入的动态表头
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerCellStyle);
        }

        // 创建数据行
        for (int i = 0; i < tableData.size(); i++) {
            Row row = sheet.createRow(i + 1);
            String[] data = tableData.get(i);
            for (int j = 0; j < data.length && j < headers.length; j++) {
                Cell cell = row.createCell(j);
                cell.setCellValue(data[j]);
            }
        }

        // 自动调整列宽(可选)
        for (int i = 0; i < headers.length; i++) {
            sheet.autoSizeColumn(i);
        }

        workbook.write(fileOut);
    } catch (IOException e) {
        throw new RuntimeException("写入Excel失败: " + outputPath, e);
    }
}
4. 写一个接口用于测试

@GetMapping(value="/excel")
public String dealImage(String imgUrl) throws ExecutionException, InterruptedException, TimeoutException {


    Image2ExcelUtils parser = new Image2ExcelUtils();
    File imageFile = new File("/data/app/demo.png");

    List<String> headers = new ArrayList<>();
    List<String[]> tableData = parser.recognizeTextFromImage(imageFile, headers);

    String[] headerArray = headers.toArray(new String[0]);
    parser.createExcelFromData(tableData, "/data/app/demo-all-5.xlsx", headerArray);
    
    return "Excel文件已生成";
}

三、总结

本文使用开源技术实现识别图片中的excel,但是还不是很精确,后面我要再结合opencv技术优化,提高精确度。