java通过ocr实现识别pdf中的文字

需求:识别pdf文件中的中文

根据github项目mymonstercat 改造,先将pdf文件转为png文件存于临时文件夹,然后通过RapidOcr转为文字,最后删除临时文件夹

1、引入依赖

		
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>fontbox</artifactId>
                <version>3.0.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>pdfbox</artifactId>
                <version>3.0.3</version>
            </dependency>

            <!-- ocr图片识别 -->
            <dependency>
                <groupId>io.github.mymonstercat</groupId>
                <artifactId>rapidocr</artifactId>
                <version>0.0.7</version>
            </dependency>

            <dependency>
                <groupId>io.github.mymonstercat</groupId>
                <artifactId>rapidocr-onnx-platform</artifactId>
                <version>0.0.7</version>
            </dependency>
            
            <!-- 本地测试可不引 , 服务器部署linux x86架构 下引入 ,其他环境部署可搜maven -->
			<dependency>
                <groupId>io.github.mymonstercat</groupId>
                <artifactId>rapidocr-onnx-linux-x86_64</artifactId>
                <version>1.2.2</version>
            </dependency>

2、工具类



import org.springframework.util.StringUtils;
import com.benjaminwan.ocrlibrary.OcrResult;
import com.benjaminwan.ocrlibrary.TextBlock;


import io.github.mymonstercat.Model;
import io.github.mymonstercat.ocr.InferenceEngine;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
@Service
public class PdfOCRConverter {

	//临时输出png文件路径
    private static final String outputDirs = "D:/pdfToImg/temp/";

    public static void main(String[] args) throws IOException {
        List<String> fileNameList = getWords("D:/Download/123.pdf");
        for (String fileName : fileNameList) {
            System.out.println(fileName);
        }
    }


    public static List<String> getWords(String pdfFilePath) throws IOException {
        String outputDir =  outputDirs + UUID.randomUUID().toString().replace("-", "");

        List<String> fileNameList = convertPdfToImage(pdfFilePath, outputDir);

        List<String> wordsList = new ArrayList<>();
        for (String fileName : fileNameList) {
            System.out.println("识别图片:"+fileName);
            if (StringUtils.isEmpty(fileName)){break;}
            List<String> words = runOcr(fileName);
            for (String word : words) {
                System.out.println(word);
                wordsList.add(word);
            }
        }
        deleteDirectory(outputDir);
        return wordsList;
    }


    public static List<String> runOcr(String path) {
        List<String> results = new ArrayList<>();
        InferenceEngine engine = InferenceEngine.getInstance(Model.ONNX_PPOCR_V3);
        OcrResult ocrResult = engine.runOcr(path);
        for (TextBlock textBlock : ocrResult.getTextBlocks()) {
            results.add(textBlock.getText());
        }
        return results;
    }


    public static List<String> convertPdfToImage(String pdfFilePath, String outputDir) {

        // 设置DPI(越高图片越清晰,但文件也会更大)
        int dpi = 300;
        List<String> fileNameList = new ArrayList<>();
        File file = new File(pdfFilePath);
        try (PDDocument document = Loader.loadPDF(file)) {
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            String pdfFileName = file.getName().replace(".pdf", "");
            String name = pdfFileName;
            for (int page = 0; page < document.getNumberOfPages(); page++) {
                BufferedImage bim = pdfRenderer.renderImageWithDPI(page, dpi);
                String folder = createFolder(outputDir + "/" + name);
                String fileName = folder + "/" + pdfFileName + "_page_" + (page + 1) + ".png";
                ImageIO.write(bim, "png", new File(fileName));
                fileNameList.add(fileName);
                System.out.println("生成图片:"+fileName);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileNameList;
    }


    public static void deleteDirectory(String path) throws IOException {
        // 如果路径不指向一个目录,则抛出异常
        Path directory = Paths.get(path);
        if (!Files.isDirectory(directory)) {
            throw new IOException("The provided path is not a directory.");
        }

        // 遍历目录中的所有文件和子目录
        Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                // 删除文件
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                // 所有内容被删除后删除目录本身
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                // 如果访问文件失败,则抛出异常
                throw exc;
            }
        });
    }

    public static String createFolder(String folderPath) {
        String txt = folderPath;
        try {
            File myFilePath = new File(txt);
            txt = folderPath;
            if (!myFilePath.exists()) {
                myFilePath.mkdirs();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return txt;
    }


    public static List<String> getWordsByBase64(String base64) throws IOException {
        List<String> words = new ArrayList<>();

        if (StringUtils.isEmpty(base64)) {
            return null;
        }
        String outputDir = outputDirs + UUID.randomUUID().toString().replace("-", "");

        
        // 解码Base64字符串
        byte[] decodedBytes = Base64.getDecoder().decode(base64);
        createFolder(outputDir);
        // 输出的PDF文件名
        String outputFilePath = outputDir+"/output.pdf";
        try (FileOutputStream fos = new FileOutputStream(outputFilePath)) {
            // 将解码后的字节数组写入文件
            fos.write(decodedBytes);
            System.out.println("PDF文件已成功生成: " + outputFilePath);

            words = getWords(outputFilePath);
        } catch (Exception e) {
            e.printStackTrace();
        }

        deleteDirectory(outputDir);
        return words;
    }
    
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/951830.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Maui】动态菜单实现(绑定数据视图)

前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移动和桌面应用。 使用 .NET MAUI&#xff0c;可从单个共享代码库开发可在 Android、iOS、macOS 和 Windows 上运行的应用。 .NET MAUI 是一款开放源代码应用&#xff0c;是 X…

【json】

JSON JSON是一种轻量级的,按照指定的格式去组织和封装数据的数据交互格式。 本质上是一个带有特定格式的字符串(py打印json时认定为str类型) 在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互,类似于计算机普通话 python与json关系及相互转换…

51单片机——中断(重点)

学习51单片机的重点及难点主要有中断、定时器、串口等内容&#xff0c;这部分内容一定要认真掌握&#xff0c;这部分没有学好就不能说学会了51单片机 1、中断系统 1.1 概念 中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的&#xff0c;中断功能的存在&#…

易支付二次元网站源码及部署教程

易支付二次元网站源码及部署教程 引言 在当今数字化时代&#xff0c;二次元文化逐渐成为年轻人生活中不可或缺的一部分。为了满足这一庞大用户群体的需求&#xff0c;搭建一个二次元主题网站显得尤为重要。本文将为您详细介绍易支付二次元网站源码的特点及其部署教程&#xf…

开源生成式物理引擎Genesis,可模拟世界万物

这是生成大模型时代 —— 它们能生成文本、图像、音频、视频、3D 对象…… 而如果将所有这些组合到一起&#xff0c;我们可能会得到一个世界&#xff01; 现在&#xff0c;不管是 LeCun 正在探索的世界模型&#xff0c;还是李飞飞想要攻克的空间智能&#xff0c;又或是其他研究…

【fly-iot飞凡物联】(19):开源飞凡物联项目重启,使用go重写后端代码,感兴趣的小伙伴可以一起参加,使用apache协议开源,招募感兴趣的小伙伴!!

目录 前言fly-iot飞凡物联&#xff0c;感兴趣的小伙伴可以一起参加&#xff0c;使用apache协议开源使用go重写后端代码 前言 fly-iot飞凡物联专栏&#xff1a; https://blog.csdn.net/freewebsys/category_12219758.html fly-iot飞凡物联&#xff0c;感兴趣的小伙伴可以一起参…

用于与多个数据库聊天的智能 SQL 代理问答和 RAG 系统(3) —— 基于 LangChain 框架的文档检索与问答功能以及RAG Tool的使用

介绍基于 LangChain 框架的文档检索与问答功能&#xff0c;目标是通过查询存储的向量数据库&#xff08;VectorDB&#xff09;&#xff0c;为用户的问题检索相关内容&#xff0c;并生成自然语言的答案。以下是代码逻辑的详细解析&#xff1a; 代码结构与功能 初始化环境与加载…

消息中间件类型介绍

消息中间件是一种在分布式系统中用于实现消息传递的软件架构模式。它能够在不同的系统或应用之间异步地传输数据&#xff0c;实现系统的解耦、提高系统的可扩展性和可靠性。以下是几种常见的消息中间件类型及其介绍&#xff1a; 1.RabbitMQ 特点&#xff1a; • 基于AMQP&#…

uniapp使用scss mixin抽离css常用的公共样式

1、编写通用scss样式文件 // 通用 Flex Mixin mixin flex($direction: row, $justify: flex-start, $align: stretch, $wrap: nowrap) {display: flex;flex-direction: $direction;justify-content: $justify;align-items: $align;flex-wrap: $wrap; }// 水平居中 mixin flex-…

Matlab Steger算法提取条纹中心线(亚像素位置)

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 Steger 算法是一种常用的图像边缘检测算法,可以用于提取图像中的中心线或边缘信息。它的理论假设是:条纹的亮度是按照高斯分布呈现的,即中心亮两侧渐暗。 其计算过程如下所述: 1、首先,我们需要计算每个点Hess…

PySide6 Qt for Python Qt Quick参考网址

Qt QML BOOK&#xff1a; 《Qt for Python》 -Building an Application https://www.qt.io/product/qt6/qml-book/ch19-python-build-app#signals-and-slots Qt for Python&#xff1a;与C版本的差异即BUG处理&#xff08;常见的DLL文件确实的问题等&#xff09; Qt for Pyt…

【大数据】Apache Superset:可视化开源架构

Apache Superset是什么 Apache Superset 是一个开源的现代化数据可视化和数据探索平台&#xff0c;主要用于帮助用户以交互式的方式分析和展示数据。有不少丰富的可视化组件&#xff0c;可以将数据从多种数据源&#xff08;如 SQL 数据库、数据仓库、NoSQL 数据库等&#xff0…

ELK实战(最详细)

一、什么是ELK ELK是三个产品的简称&#xff1a;ElasticSearch(简称ES) 、Logstash 、Kibana 。其中&#xff1a; ElasticSearch&#xff1a;是一个开源分布式搜索引擎Logstash &#xff1a;是一个数据收集引擎&#xff0c;支持日志搜集、分析、过滤&#xff0c;支持大量数据…

汽车物资拍卖系统架构与功能分析

2015工作至今&#xff0c;10年资深全栈工程师&#xff0c;CTO&#xff0c;擅长带团队、攻克各种技术难题、研发各类软件产品&#xff0c;我的代码态度&#xff1a;代码虐我千百遍&#xff0c;我待代码如初恋&#xff0c;我的工作态度&#xff1a;极致&#xff0c;责任&#xff…

利用 Python 爬虫从义乌购根据关键词获取商品列表

在当今数字化商业时代&#xff0c;数据是企业获取竞争优势的关键。对于从事国际贸易的商家而言&#xff0c;能够及时、准确地获取商品信息至关重要。义乌购作为知名的国际贸易批发平台&#xff0c;汇集了海量的商品资源。通过 Python 爬虫技术&#xff0c;我们可以高效地从义乌…

HDFS编程 - 使用HDFS Java API进行文件操作

文章目录 前言一、创建hdfs-demo项目1. 在idea上创建maven项目2. 导入hadoop相关依赖 二、常用 HDFS Java API1. 简介2. 获取文件系统实例3. 创建目录4. 创建文件4.1 创建文件并写入数据4.2 创建新空白文件 5. 查看文件内容6. 查看目录下的文件或目录信息6.1 查看指定目录下的文…

直流无刷电机控制(FOC):电流模式

目录 概述 1 系统框架结构 1.1 硬件模块介绍 1.2 硬件实物图 1.3 引脚接口定义 2 代码实现 2.1 软件架构 2.2 电流检测函数 3 电流环功能实现 3.1 代码实现 3.2 测试代码实现 4 测试 概述 本文主要介绍基于DengFOC的库函数&#xff0c;实现直流无刷电机控制&#x…

51单片机——串口通信(重点)

1、通信 通信的方式可以分为多种&#xff0c;按照数据传送方式可分为串行通信和并行通信&#xff1b; 按照通信的数据同步方式&#xff0c;可分为异步通信和同步通信&#xff1b; 按照数据的传输方向又可分为单工、半双工和全双工通信 1.1 通信速率 衡量通信性能的一个非常…

如何在 Linux、MacOS 以及 Windows 中打开控制面板

控制面板不仅仅是一系列图标和菜单的集合&#xff1b;它是通往优化个人计算体验的大门。通过它&#xff0c;用户可以轻松调整从外观到性能的各种参数&#xff0c;确保他们的电脑能够完美地适应自己的需求。无论是想要提升系统安全性、管理硬件设备&#xff0c;还是简单地改变桌…

浅谈弱电系统RVVP和RVSP电缆的区别(

1、RVVP 1.1RVVP电缆定义&#xff1f; RVVP电缆抗干扰软电缆、屏蔽电缆、信号电缆、控制电缆&#xff08;名字很多&#xff09;&#xff0c;学名&#xff1a;铜芯-聚氯乙烯绝缘-屏蔽聚氯乙烯护套-软电缆。 1.2RVVP执行标准 主要执行标准为JB/T8734.5-2016&#xff0c;部…