使用paddleocr识别图片文本的一种方案

pdf文本分为两种,一种是标准的pdf格式的文本,这种无需利用ocr识别,另外一种就是图片文本,这种需要进行ocr的识别。

OCR 识别文本和文本区域

ppstructure是paddleocr里面的一个子库,可以识别文档的页眉页脚、正文、标题等等。输出是json格式的数据,里面有文本type、文本内容、文本块区域bbox和每一行的文本区域text_region等信息。

版面恢复

agument-xy-cut

一个实现 code
其实这一步针对的是比较复杂的排版,比如表格类型的。
在这里插入图片描述
一般的论文什么的,排版都比较固定,单栏横版和双栏横版,基本上一句

 sorted_boxes = sorted(res, key=lambda x: (x['bbox'][1], x['bbox'][0]))

就可以解决了。一般的文本用不上 agument-xy-cut。我用 agument-xy-cut 来排版属于是杀鸡用了牛刀。

虽然ppstructure识别出来的文本块是无序的,但是它给出了文本块区域bbox和每一行的文本区域text_region等信息,我们可以利用文本块区域bbox的信息,来对文本块排序。
agument-xy-cut就是一种很好的算法。可以识别单栏、双栏、表格甚至更复杂的排版。
如果是一般的单栏横版的文章,我们可以跳过这一步,因为直接利用每一行的文本区域text_region进行下一步即可。

整合自然段

主要是整合自然段,因为ppstructure识别出来的文本有两个层次,第一层次是文本块区域,以bbox划分,利用上面的
agument-xy-cut算法可以进行排序。还有文本块区域内部的每一行的文本,这里我们要做的是将每一行的文本整合为自然段。
这里提供一种算法,code
不是特别精确,不过大概是能利用每一行的文本区域text_region对文本进行自然段的整合。

# 合并:段落-横排-自然段from .merge_line import MergeLineclass MergePara(MergeLine):def __init__(self):super().__init__()self.tbpuName = "多行-自然段"self.mllhLine = 1.8  # 最大段间距def isSameColumn(self, A, B):  # 两个文块属于同一栏时,返回 True# 获取A、B行高if "lineHeight" in A:  # 已记录Ah = A["lineHeight"]else:  # 未记录,则写入记录Ah = A["lineHeight"] = A["box"][3][1] - A["box"][0][1]A["lineCount"] = 1  # 段落的行数Bh = B["box"][3][1] - B["box"][0][1]if abs(Bh - Ah) > Ah * self.mllhH:return False  # AB行高不符# 行高相符,判断垂直投影是否重叠ax1, ax2 = A["box"][0][0], A["box"][1][0]bx1, bx2 = B["box"][0][0], B["box"][1][0]if ax2 < bx1 or ax1 > bx2:return Falsereturn True  # AB垂直投影重叠def isSamePara(self, A, B):  # 两个文块属于同一段落时,返回 Trueah = A["lineHeight"]# 判断垂直距离ly = ah * self.mllhYlLine = ah * self.mllhLinea, b = A["box"], B["box"]ay, by = a[3][1], b[0][1]if by < ay - ly or by > ay + lLine:return False  # 垂直距离过大# 判断水平距离lx = ah * self.mllhXax, bx = a[0][0], b[0][0]if A["lineCount"] == 1:  # 首行允许缩进2格return ax - ah * 2.5 - lx <= bx <= ax + lxelse:return abs(ax - bx) <= lxdef merge2line(self, textBlocks, i1, i2):  # 合并2行ranges = [(0x4E00, 0x9FFF),  # 汉字(0x3040, 0x30FF),  # 日文(0xAC00, 0xD7AF),  # 韩文(0xFF01, 0xFF5E),  # 全角字符]# 判断两端文字的结尾和开头,是否属于汉藏语族# 汉藏语族:行间无需分割符。印欧语族:则两行之间需加空格。separator = " "ta, tb = textBlocks[i1]["text"][-1], textBlocks[i2]["text"][0]fa, fb = False, Falsefor l, r in ranges:if l <= ord(ta) <= r:fa = Trueif l <= ord(tb) <= r:fb = Trueif fa and fb:separator = ""#     print(f"【{ta}】与【{tb}】是汉字集。")# else:#     print(f"【{ta}】与【{tb}】是西文集。")self.merge2tb(textBlocks, i1, i2, separator)textBlocks[i1]["lineCount"] += 1  # 行数+1def mergePara(self, textBlocks):# 单行合并hList = self.mergeLine(textBlocks)# 按左上角y排序hList.sort(key=lambda tb: tb["box"][0][1])# 遍历每个行,寻找并合并属于同一段落的两个行listlen = len(hList)resList = []for i1 in range(listlen):tb1 = hList[i1]if not tb1:continuenum = 1  # 合并个数# 遍历后续文块for i2 in range(i1 + 1, listlen):tb2 = hList[i2]if not tb2:continue# 符合同一栏if self.isSameColumn(tb1, tb2):# 符合同一段,合并两行if self.isSamePara(tb1, tb2):self.merge2line(hList, i1, i2)num += 1# 同栏、不同段,说明到了下一段,则退出内循环else:breakif num > 1:tb1["score"] /= num  # 平均置信度resList.append(tb1)  # 装填入结果return resListdef run(self, textBlocks, imgInfo):# 段落合并resList = self.mergePara(textBlocks)# 返回新文块列表return resList
# 合并:单行-横排from .tbpu import Tbpufrom functools import cmp_to_keyclass MergeLine(Tbpu):def __init__(self):self.tbpuName = "单行-横排"# merge line limit multiple X/Y/H,单行合并时的水平/垂直/行高阈值系数,为行高的倍数self.mllhX = 2self.mllhY = 0.5self.mllhH = 0.5def isSameLine(self, A, B):  # 两个文块属于同一行时,返回 TrueAx, Ay = A[1][0], A[1][1]  # 块A右上角xyAh = A[3][1] - A[0][1]  # 块A行高Bx, By = B[0][0], B[0][1]  # 块B左上角xyBh = B[3][1] - B[0][1]  # 块B行高lx = Ah * self.mllhX  # 水平、垂直、行高 合并阈值ly = Ah * self.mllhYlh = Ah * self.mllhHif abs(Bx - Ax) < lx and abs(By - Ay) < ly and abs(Bh - Ah) < lh:return Truereturn Falsedef merge2tb(self, textBlocks, i1, i2, separator):  # 合并2个tb,将i2合并到i1中。tb1 = textBlocks[i1]tb2 = textBlocks[i2]b1 = tb1["box"]b2 = tb2["box"]# 合并两个文块boxyTop = min(b1[0][1], b1[1][1], b2[0][1], b2[1][1])yBottom = max(b1[2][1], b1[3][1], b2[2][1], b2[3][1])xLeft = min(b1[0][0], b1[3][0], b2[0][0], b2[3][0])xRight = max(b1[1][0], b1[2][0], b2[1][0], b2[2][0])b1[0][1] = b1[1][1] = yTop  # y上b1[2][1] = b1[3][1] = yBottom  # y下b1[0][0] = b1[3][0] = xLeft  # x左b1[1][0] = b1[2][0] = xRight  # x右# 合并内容tb1["score"] += tb2["score"]  # 合并置信度tb1["text"] = tb1["text"] + separator + tb2["text"]  # 合并文本textBlocks[i2] = None  # 置为空,标记删除def mergeLine(self, textBlocks):  # 单行合并# 所有文块,按左上角点的x坐标排序textBlocks.sort(key=lambda tb: tb["box"][0][0])# 遍历每个文块,寻找后续文块中与它接壤、且行高一致的项,合并两个文块resList = []listlen = len(textBlocks)for i1 in range(listlen):tb1 = textBlocks[i1]if not tb1:continueb1 = tb1["box"]num = 1  # 合并个数# 遍历后续文块for i2 in range(i1 + 1, listlen):tb2 = textBlocks[i2]if not tb2:continueb2 = tb2["box"]# 符合同一行,则合并if self.isSameLine(b1, b2):# 合并两个文块boxself.merge2tb(textBlocks, i1, i2, " ")num += 1if num > 1:tb1["score"] /= num  # 平均置信度resList.append(tb1)  # 装填入结果return resListdef sortLines(self, resList):  # 对文块排序,从上到下,从左到右def sortKey(A, B):# 先比较两个文块的水平投影是否重叠ay1, ay2 = A["box"][0][1], A["box"][3][1]by1, by2 = B["box"][0][1], B["box"][3][1]# 不重叠,则按左上角y排序if ay2 < by1 or ay1 > by2:return 0 if ay1 == by1 else (-1 if ay1 < by1 else 1)# 重叠,则按左上角x排序ax, bx = A["box"][0][0], B["box"][0][0]return 0 if ax == bx else (-1 if ax < bx else 1)resList.sort(key=cmp_to_key(sortKey))def run(self, textBlocks, imgInfo):# 单行合并resList = self.mergeLine(textBlocks)# 结果排序self.sortLines(resList)# 返回新文块列表return resList

这里的 key 需要自己手动更改,‘box’、‘score’分别对应的是paddleocr识别出来的’text_region’、‘confidence’。
(其实paddleocr里面应该也有对应的算法,here,这里面也是根据文本块区域bbox进行的排序,先y后x,也挺合适的。不过没有整合自然段的功能。就是粗暴的把文本块区域都弄在一起了。)

总结

步骤就是这样,先ocr识别文本和区域,后面根据区域进行版面恢复。版面恢复部分根据自己的需要,可以省略。

PS:突然有个问题,我发现wps+python-word处理的应该也还行,段落什么的也都分的很好,表格也识别对了。之前是觉得wps对生僻字识别的不好,所以没用,而且wps要钱hh。不过工程上的方法就是很多,只有达到效果就行,科研就不行,都是精益求精的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从手工测试进阶中高级测试?如何突破职业瓶颈...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、手工测试如何进…

Linux:gdb的简单使用

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、前置理解二、使用总结 前言 gdb是Linux中的调试代码的工具 一、前置理解 我们都知道要调试一份代码&#xff0c;这份代码的发布模式必须是debug。那你知道在li…

jquery实现省市区三级联动

一、技术: 前端采用的是jsp页面 后端采用springmvc+mybatis+mysql8 效果图 二、cascadeSelect.jsp页面 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%String path = request.getContextPath();String basePath = r…

YOLOv8改进 | 2023主干篇 | 利用RT-DETR特征提取网络PPHGNetV2改进YOLOv8(超级轻量化精度更高)

一、本文介绍 本文给大家带来利用RT-DETR模型主干HGNet去替换YOLOv8的主干&#xff0c;RT-DETR是今年由百度推出的第一款实时的ViT模型&#xff0c;其在实时检测的领域上号称是打败了YOLO系列&#xff0c;其利用两个主干一个是HGNet一个是ResNet&#xff0c;其中HGNet就是我们…

Mybatis映射接口的动态代理实现原理

Mybatis映射接口的动态代理实现原理 在上一节中&#xff0c;我们介绍了MyBatis的核心配置文件加载流程&#xff0c;Mybatis核心配置文件加载流程详解 在文中&#xff0c;我们介绍了MyBatis在加载配置文件的过程中会针对每个接口类都生成一个相应的MapperProxyFactory动态代理工…

【上海大学数字逻辑实验报告】六、时序电路

一、 实验目的 掌握同步二进制计数器和移位寄存器的原理。学会用分立元件构成2位同步二进制加计数器。学会在Quartus II上设计单向移位寄存器。学会在Quartus II上设计环形计数器。 二、 实验原理 同步计数器是指计数器中的各触发器的时钟脉冲输入端连接在一起&#xff0c;接…

FL Studio Producer Edition 21.2.2.3914中文汉化破解版新功能介绍及下载安装教程

FL Studio Producer Edition 21.2.2.3914中文汉化破解版 也就是 Image-Line 出品的一款功能强大的编曲软件&#xff0c;全名 Fruity Loops Studio 简称“FL Studio”今天突然的发现我们经常使用的水果音乐制作软件 FL STUDIO 居然从FL STUDIO 21.1.1 一下子跨越了版本号到了FL …

【产品经理】需求池和版本树

在这个人人都是产品经理的时代&#xff0c;每位入行的产品人进阶速度与到达高度各有不同。本文作者结合自身三年产品行业的经历&#xff0c;根据案例拆解产品行业的极简研发过程、需求池、版本树、产品自我优化等相关具体方法论。 一、产品研发的极简过程 1. 产品概述 产品就…

Server check fail, please check server xxx.xxx.xxx.xxx,port 9848 is available

记录一次服务调用中的错误 背景&#xff1a;我使用了nacos2.x的版本&#xff0c;同时在同一台服务器的三个docker容器中部署了nacos1、2、3&#xff0c;并将它们连接到了同一个docker网络 错误&#xff1a;Server check fail, please check server xxx.xxx.xxx.xxx,port 9848 …

C/C++,动态 DP 问题的计算方法与源程序

1 文本格式 #include <bits/stdc.h> using namespace std; typedef long long LL; const int maxn 500010; const int INF 0x3f3f3f3f; int Begin[maxn], Next[maxn], To[maxn], e, n, m; int size[maxn], son[maxn], top[maxn], fa[maxn], dis[maxn], p[maxn], i…

AI PC行业深度研究报告:AI PC革新端侧AI交互体验

今天分享的人工智能系列深度研究报告&#xff1a;《AI PC行业深度研究报告&#xff1a;AI PC革新端侧AI交互体验》。 &#xff08;报告出品方&#xff1a;华创证券&#xff09; 报告共计&#xff1a;28页 点击添加图片描述&#xff08;最多60个字&#xff09;编辑 一、硬件端…

12.字符串拼接【2023.12.4】

1.问题描述 我们在编程过程中经常会遇到把不同字符串拼接在一起的情况&#xff0c;从而更直观地展示给用户我们所要表达的信息。本题将给出两个字符串&#xff0c;请依次将这两个字符串拼接在一起。 2.解决思路 用字符串拼接符 进行连接两个字符串 3.代码实现 str1input(…

XSS漏洞 深度解析 XSS_labs靶场

XSS漏洞 深度解析 XSS_labs靶场 0x01 简介 XSS原名为Cross-site Sciprting(跨站脚本攻击)&#xff0c;因简写与层叠样式表(Cascading style sheets)重名&#xff0c;为了区分所以取名为XSS。 这个漏洞主要存在于HTML页面中进行动态渲染输出的参数中&#xff0c;利用了脚本语…

Apollo配置发布原理解析

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

P1单片机定时器配置及定时器中断——C51(超详细)

目录 1. 简介 1.1 概念解读 1.2 定时器怎么定时 1.什么是晶振 2.什么是时钟周期 3.什么是机器周期 4.加1经过了多少时间 1.3 定时器编程 1.如何算出10ms定时器的初值(TL0 TH0) 2.关于TCON ,怎么知道爆表 3.怎么开始计时(TR0) 4.定时器使用是有很多种模式的&#xf…

深入了解基础故障编排:提升系统故障应对能力的关键

在当今高度数字化的世界中&#xff0c;系统的稳定性和可用性对于业务的成功至关重要。然而&#xff0c;在复杂的软件和硬件环境中&#xff0c;故障不可避免地会发生。为了更有效地应对这些故障&#xff0c;基础故障编排成为一项关键的技术。本文将探讨基础故障编排的概念及作用…

「PPT 下载」Google DevFest Keynote | 复杂的海外网络环境下,如何提升连接质量

&#xff08;全网都在找的《社交泛娱乐出海作战地图》&#xff0c;点击获取&#x1f446;&#xff09; 12 月 10 日&#xff0c;“Google DevFest 2023 上海站”大会如期在上海市东方万国宴会中心举办。延续过往的技术交流碰撞、前沿技术学习基调传统&#xff0c;本届大会聚焦行…

基于导数Zernike多项式拟合技术的干涉测量二维相位展开算法(原文翻译)

Zixin Zhao1&#xff0c;Hong Zhao1、Lu Zhang 1&#xff0c;Fen Gao2&#xff0c;Yuwei Qin3&#xff0c;Hubing Du 摘要: 我们提出了一种适用于一般干涉测量应用的相位展开方法。所提出的方法依赖于导数泽尼克多项式拟合&#xff08;DZPF&#xff09;技术&#xff0c;其中相…

淡化了技术指标 还能做现货黄金交易?

技术指标是分析和预测现货黄金走势的其中一种方法&#xff0c;普通投资者多数依赖技术指标为自己的交易做判断。然而&#xff0c;近几年有一种观点认为&#xff0c;我们应该淡化技术指标&#xff0c;少使用或者不用技术分析来服务我们的交易。这个观点引起了不少投资者的思考&a…

现代密码学复习

密码学总结 目录 密码学总结 第一章——只因础模型与概念 1.1 密码学五元组&#xff08;结合&#x1f40f;皮卷&#xff09; 1.2 Dolev-Yao威胁模型 1.3 攻击类型 1.4 柯克霍夫原则&#xff08;Kerckhoffss principle&#xff09; 1.5 对称、非对称加密 1.6 密码的目标…