创新实训2024.05.01日志:document-loaders

在建立易学知识库的过程中,仅仅有向量数据库以及词嵌入模型、分词器是不够的,因为我们有大量的非结构化文本(如doc,pdf)或者是图片需要上传(例如pdf里面有图片),此时词嵌入无法直接向向量数据库中嵌入图片,需要对图片内文字进行识别,转换为文本后才能继续嵌入。

1. 一切的基础:langchain.document_loaders

langchain为我们提供了一个基类:Unstructured File | 🦜️🔗 LangChain

from langchain.document_loaders.unstructured import UnstructuredFileLoader

即这个UnstructuredFileLoader,LangChain官方对其介绍是:

This notebook covers how to use Unstructured package to load files of many types. Unstructured currently supports loading of text files, powerpoints, html, pdfs, images, and more.

这个类专门用来加载文本、ppt、html、pdf、图片等文件。并且提供了一系列划分文本chunk分块分片的策略。

2. 对pdf的加载 

不过我们的任务可能会更复杂一点,因为我们有一些易学书籍是图片形式的(保存在pdf中),因此先需要进行ocr识别,然后再作为文本加载。

这里我注意到这些图片可能都是人为拍摄的(因为图片有些略有倾斜,不是规整的),因此就需要制定一套解决方案: 

  1. 先读取pdf中的每一页(这里使用了pyMuPDF里的fitz包)GitHub - pymupdf/PyMuPDF: PyMuPDF is a high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents.PyMuPDF is a high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents. - pymupdf/PyMuPDFicon-default.png?t=N7T8https://github.com/pymupdf/PyMuPDF.git
  2. 对于文本我们可以直接加载
  3. 对于图片
    1. 先把图片转正
    2. 然后利用ocr识别图片
  4. 将识别结果加载到内存,并嵌入转储到向量数据库

2.1. OCR识别实例

这一块我用了一个开源库:PaddleOCR

PaddlePaddle/PaddleOCR: Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices) (github.com)icon-default.png?t=N7T8https://github.com/PaddlePaddle/PaddleOCR可以通过:

pip install rapidocr_paddle

进行下载安装。

或者onnx格式的

pip install rapidocr_onnxruntime 

随后即可通过RapidOCR()进行实例初始化并返回该对象。

 2.2. 图像旋转

图像文本分离

首先我们要把图像和文本分离开来,因为文本可以直接加载,不需要ocr识别:

            for i, page in enumerate(doc):b_unit.set_description("RapidOCRPDFLoader context page index: {}".format(i))b_unit.refresh()text = page.get_text("")resp += text + "\n"img_list = page.get_image_info(xrefs=True)for img in img_list:

这里b_unit是tqdm这个进度条库的组件,用来显示pdf文件的文本加载到哪一页了。

b_unit = tqdm.tqdm(total=doc.page_count, desc="RapidOCRPDFLoader context page index: 0")

效果类似于下面的:

 图像尺寸检查

在处理图像的过程中,可能会因为一些小图像影响处理效率,因为小图像中包含的信息可能在视觉效果上很模糊,导致ocr难以识别出文字。因此我们要筛选掉那些过小的图像:

                    if xref := img.get("xref"):bbox = img["bbox"]# 检查图片尺寸是否超过设定的阈值if ((bbox[2] - bbox[0]) / (page.rect.width) < PDF_OCR_THRESHOLD[0]or (bbox[3] - bbox[1]) / (page.rect.height) < PDF_OCR_THRESHOLD[1]):continue

这里我们通过图像的包围盒(bounding box)与整个页面的长宽的比值判断这个图片是否“过小"。其中:

bbox = [ x0,y0,x1,y1 ]

即图像矩形边界框的坐标。这里如果长宽比值过小,我们就跳过这个图像,继续处理图像列表中的下一个。

获取图像像素矩阵以及图像对象

剩下的图像就都是当前页面中比较容易识别文字的图像了。不过正如我在上面提到过的,这些图片可能因为人为拍摄时的角度问题,不够”正“。为了让他们够正,我们要对图像的像素矩阵进行旋转。(注意是对像素进行旋转,包围盒是正的,里面的图像像素可能是歪的)第一步就是要先获取像素矩阵。

pix = fitz.Pixmap(doc, xref)
  1. 从打开的PDF文档(doc)中,使用给定的交叉引用(xref)来定位页面或页面的一部分。
  2. 创建一个 Pixmap 对象,它包含了定位到的页面区域的图像数据。

Pixmap 对象可以用于各种操作,比如图像处理、提取文本、转换格式等。例如,你可以对 pix 对象调用方法来获取图像的宽度、高度、像素数据,旋转角度等。

然后我们从Pixmap中获取这个图像的像素矩阵(先用numpy库获取所有像素采样点,然后把buffer重塑为原始图像宽高的像素矩阵):

if int(page.rotation)!=0:  #如果Page有旋转角度,则旋转图片img_array = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.height, pix.width, -1)

随后我们利用Python Image Library(PIL)库,把这个矩阵转换为一个python图像(可能会疑惑怎么获取了像素矩阵还要转为Python图像,因为Pixmap那个里面的sample点不能直接用来做ocr识别,他接收的就是ndArray,然后ocr接收的是BGR三通道的图像,但我们获取的像素矩阵是RGB的,所以还得用PIL和OpenCV库转一下三通道背景,然后才能传到ocr函数里面去):

tmp_img = Image.fromarray(img_array)
ori_img = cv2.cvtColor(np.array(tmp_img),cv2.COLOR_RGB2BGR)

图像旋转 

接下来就是比较重要的一环,旋转图像:

        def rotate_img(img, angle):h, w = img.shape[:2]rotate_center = (w/2, h/2)#获取旋转矩阵# 参数1为旋转中心点;# 参数2为旋转角度,正值-逆时针旋转;负值-顺时针旋转# 参数3为各向同性的比例因子,1.0原图,2.0变成原来的2倍,0.5变成原来的0.5倍M = cv2.getRotationMatrix2D(rotate_center, angle, 1.0)#计算图像新边界new_w = int(h * np.abs(M[0, 1]) + w * np.abs(M[0, 0]))new_h = int(h * np.abs(M[0, 0]) + w * np.abs(M[0, 1]))#调整旋转矩阵以考虑平移M[0, 2] += (new_w - w) / 2M[1, 2] += (new_h - h) / 2rotated_img = cv2.warpAffine(img, M, (new_w, new_h))return rotated_img

首先我们先获取图像对象的宽和高,然后宽高中间的位置就是旋转中心(还是再强调一遍,图像本身不是歪的,是图像内容歪了!我们要旋转的是图像的像素点!)

OpenCV提供了一个矩阵旋转的函数,getRotationMatrix2D,第一个参数接收旋转中心,第二个参数接收旋转角度,第三个参数接收scale缩放因子,这里我们不缩放。

随后我们要用到计算机图形学/线性代数中的仿射变换

y = Ax+p \Leftrightarrow y = Mx

首先,我们先来推导一下旋转矩阵:假设二维向量(x,y)绕原点逆时针旋转了角度\Theta,求旋转后的二维向量。 

我们可以利用极坐标法来求解这个问题:

设:

r = \sqrt{x^2+y^2}

同时,向量(x,y)与x正半轴的夹角为\alpha。则

(x,y) = (r\cdot cos(\alpha ),r\cdot sin(\alpha )) \cdot \cdot \cdot \cdot \cdot \cdot (1)

则旋转后的坐标

(x',y') = (r\cdot cos(\alpha +\theta ),r\cdot sin(\alpha +\theta ))\cdot \cdot \cdot \cdot \cdot \cdot (2)

根据三角函数的和差公式:

cos(\alpha-\theta) = cos(\alpha)\cdot cos(\theta) - sin(\alpha)\cdot sin(\theta) \cdot \cdot \cdot \cdot \cdot \cdot (3)\\ sin(\alpha-\theta) = sin(\alpha)\cdot cos(\theta) + sin(\theta)\cdot cos(\alpha)\cdot \cdot \cdot \cdot \cdot \cdot (4)

 结合上述(1) (2) (3) (4)式可得: 

(x',y') = (x\cdot cos(\theta)-y\cdot sin(\theta),y\cdot cos(\theta)+x\cdot cos(\theta))

整理为仿射矩阵可得: 

M = \begin{bmatrix} \cos(\theta) & -\sin(\theta) & t_x \\ \sin(\theta) & \cos(\theta) & t_y \\ \end{bmatrix}

其中:

  • M 是仿射变换矩阵。
  • \Theta是旋转角度。
  • t_xty是旋转后的图像中心点在原始图像中的平移量。

则将M作用于任意一个像素点(x,y,1)得到(x',y'):(注意这里是齐次坐标)

x' = \cos(\theta) \cdot x - \sin(\theta) \cdot y + t_x \\ y' = \sin(\theta) \cdot x + \cos(\theta) \cdot y + t_y

同样,我们可以将原先的(width,height)视作(x,y)元组,计算旋转后图像的新边界。

#计算图像新边界
new_w = int(h * np.abs(M[0, 1]) + w * np.abs(M[0, 0]))
new_h = int(h * np.abs(M[0, 0]) + w * np.abs(M[0, 1]))

 随后将旋转得到的图像像素平移至包围盒中心。

#调整旋转矩阵以考虑平移
M[0, 2] += (new_w - w) / 2
M[1, 2] += (new_h - h) / 2

 最后我们通过OpenCV提供的仿射变换函数,将该矩阵作用到图像上:

rotated_img = cv2.warpAffine(img, M, (new_w, new_h))

至此,图像旋转完毕。

在具体使用这个rotate_img时,传入的旋转角为:  

angle = 360 - page.rotation 

例如,一开始这个图像顺时针旋转了45°,那么我们转360-45相当于让他转了一圈,再往回倒45°,就转正了。

rot_img = rotate_img(img=ori_img, angle=360-page.rotation)

 2.3. 案例

这里可以看一个例子,展示一下ocr识别的效果。(这里我就不用那个易学书籍的例子了,因为之前跑的时候没截图,跑一次要很久很久,一本书200页)

 识别效果如下:

可以看到都是可以识别出来的。而且结果也很精准(只要图片不是太糊)。 

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

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

相关文章

自学鸿蒙HarmonyOS的ArkTS语言<三>路由跳转及传参

【官方文档传送门】 一、导入模块 import router from ohos.router二、新增页面配置 三、常用api 1、跳转到应用内的指定页面 build() {Row() {Button(下一页).onClick(() > {router.pushUrl({url: pages/Index2,params: {name: test}})})}.height(100%)}2、用应用内的某…

SpringBoot配置第三方专业缓存技术jetcache方法缓存方案

jetcache方法缓存 我们可以给每个方法配置缓存方案 JetCache 是一个基于 Java 的缓存库&#xff0c;支持多种缓存方案和缓存策略&#xff0c;主要用于提升应用程序的性能和响应速度。它提供了多种缓存模式和特性&#xff0c;可以根据需求选择合适的缓存方案。 JetCache 的主…

在WordPress上添加亚马逊联盟链接的三种方法

在互联网快速发展的今天&#xff0c;很多人都希望通过网络来增加收入&#xff0c;而加入亚马逊联盟计划&#xff08;Amazon Associates&#xff09;无疑是一个不错的选择。如果你有一个WordPress网站&#xff0c;那么在文章中添加亚马逊联盟链接是个很好的变现方式。今天&#…

钡铼BL110在智慧气象站实现Modbus转MQTT无线接入主流云

随着物联网&#xff08;IoT&#xff09;技术的发展&#xff0c;各行各业都在积极探索将智能设备与云平台相结合&#xff0c;以提升系统的智能化和自动化水平。智慧气象站作为其中重要的一环&#xff0c;通过实时监测环境数据&#xff0c;为农业、交通、航空等行业提供精准的气象…

Idea Git中 unversioned files的处理

项目中&#xff0c;使用git commit命令可以查看当前所在的分支&#xff0c;以及当前改动的文件&#xff0c;可以使用快捷键Alt 0打开/关闭&#xff1b;如下图所示&#xff0c; 可以看到分成了两个不同的区域&#xff0c; Changes 表示有改动的文件&#xff0c;包括修改、新增…

apache activeMq

https://blog.csdn.net/qq_29651203/article/details/108487924 游览器输入地址: http://127.0.0.1:8161/admin/ 访问activemq管理台 账号和密码默认为: admin/admin# yml配置的密码也是如下的密码 activemq:url: failover:(tcp://localhost:61616)username: adminpassword: ad…

哪个充电宝牌子好?性价比高与质量好并存!热门充电宝推荐!

随着科技的不断进步&#xff0c;我们的日常生活越来越依赖于便携式电子设备。然而&#xff0c;电池续航问题始终是这些设备的一大软肋。为了确保我们的智能手机、平板电脑、甚至是智能手表在忙碌的日子里始终有电&#xff0c;一个可靠的充电宝成为了我们的必备之选。面对市场上…

VoIP Hopper一键分析VoIP 网络信息(KALI工具系列二十九)

目录 1、KALI LINUX 简介 2、VoIP Hopper工具简介 3、信息收集 3.1 目标主机IP 3.2kali的IP 4、操作实例 4.1 VLAN 跳跃攻击 4.2 指定MAC地址 4.3 设置参数 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发行版 &#xff0c;广泛用于网络…

flask实战之模板实现公共导航

基础实现 目标 在Flask中&#xff0c;使用模板继承和块&#xff08;blocks&#xff09;可以方便地提取公共导航菜单&#xff0c;使得您可以在多个页面上重用相同的导航结构。以下是一个基本示例&#xff0c;展示如何创建一个包含公共导航菜单的模板&#xff1a; 创建基础模板…

前端菜鸡学习日记 -- 关于pnpm

哈咯哇大家&#xff0c;我又来了&#xff0c;最近稍微悠闲一些&#xff0c;所以就趁着这个机会学习一些新的知识&#xff0c;今天就是碰巧遇到了pnm&#xff0c;这个可以看作是npm的升级版本&#xff0c;比npm要快&#xff0c;用起来也更得劲更迅速 官网地址&#xff1a;https…

【CT】LeetCode手撕—103. 二叉树的锯齿形层序遍历

目录 题目1- 思路2- 实现⭐103. 二叉树的锯齿形层序遍历——题解思路 2- ACM实现 题目 原题连接&#xff1a;103. 二叉树的锯齿形层序遍历 1- 思路 二叉树的层序遍历&#xff0c;遇到奇数时&#xff0c;利用 Collections.reverse() 翻转即可 2- 实现 ⭐103. 二叉树的锯齿形层…

优惠卷秒杀(并发问题)

Redis实战篇 | Kyles Blog (cyborg2077.github.io) 目录 一、Redis实现全局唯一id 二、添加优惠卷 三、实现秒杀下单 四、解决超卖问题&#xff08;库存为负&#xff09; 乐观锁解决超卖问题&#xff08;CAS法&#xff09; 五、实现一人一单 ​编辑 悲观锁解决一人一单问题…

QT绘画仪表盘

代码一步一步讲&#xff0c;就不写用啥之类的了&#xff0c;暗部走来&#xff0c;自己找使用的类以及使用方法 1、创建工程 2、重载paintEvent #include <QMainWindow> #include <QPainter> #include <QPaintEvent> protected:virtual void paintEvent(QP…

QT自定义标题栏窗口其一:实现拖动及可拉伸效果

1、效果 2、核心代码 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(paren

前端新手小白的Vue3入坑指南

昨天有同学说想暑假在家学一学Vue3&#xff0c;问我有没有什么好的文档&#xff0c;我给他找了一些&#xff0c;然后顺带着&#xff0c;自己也写一篇吧&#xff0c;希望可以给新手小白们一些指引&#xff0c;Vue3欢迎你。 目录 1 项目安装 1.1 初始化项目 1.2 安装初始化依…

redis哨兵模式下业务代码连接实现

目录 一&#xff1a;背景 二&#xff1a;实现过程 三&#xff1a;总结 一&#xff1a;背景 在哨兵模式下&#xff0c;真实的redis服务地址由一个固定ip转变为可以变化的ip,这样我们业务代码在连接redis的时候&#xff0c;就需要判断哪个主redis服务地址&#xff0c;哪个是从…

火绒安全删除explorer.exe文件造成windows系统异常的问题

问题 过程是这样的&#xff0c;电脑在使用过程中突然就变成了黑色的&#xff0c;任务栏、桌面等都消失了&#xff0c;只有部分程序的窗口。具体如下&#xff1a; 因为&#xff0c;在变化的时候&#xff0c;我有瞟到一眼有个火绒的气泡消息&#xff0c;就感觉是火绒错误的删除…

适用于Mac的免费外置硬盘数据恢复软件

“我有一个 1 TB 的外置硬盘&#xff0c;它被意外格式化了。我尝试从中恢复丢失的数据。我把它连接到我的Mac&#xff0c;但里面什么也没找到。我正在寻找适用于Mac的免费外置硬盘数据恢复软件&#xff0c;例如奇客数据恢复Mac版或其他Mac数据恢复免费软件来扫描它并恢复数据。…

如何有效地进行机台数据管理,让数据发挥更大的价值?

机台数据管理是一个涉及收集、存储、分析和保护与机台&#xff08;可能是机械设备、生产线设备、医疗设备等&#xff09;相关数据的过程。通常包括以下几个方面&#xff1a; 1.数据收集&#xff1a;使用传感器和数据采集系统来收集机台的性能数据&#xff0c;如温度、压力、速…

Python轻松设置Excel单元格数字显示格式

Excel作为强大的数据处理与分析工具&#xff0c;不仅能够存储大量数据&#xff0c;还支持复杂的数据处理与可视化功能。而如何恰当地展示Excel表格中的数据是Excel文件制作的关键之一。这便涉及到Excel单元格数字格式的设置。数字格式不仅关乎数据的美学呈现&#xff0c;如货币…