创新实训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.mzph.cn/news/856898.shtml

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

相关文章

Uniapp获取具体地理位置

使用uniapp自带uni.getLocation获取当前定位经纬度 再调用高德逆地理编码API&#xff0c;查到具体位置信息 https://restapi.amap.com/v3/geocode/regeo?location${longitude},${latitude}&key${key}&extensionsall 但是个人申请的key&#xff0c;有配额限制 最多每…

LabVIEW程序闪退问题

LabVIEW程序出现闪退问题可能源于多个方面&#xff0c;包括软件兼容性、内存管理、代码质量、硬件兼容性和环境因素。本文将从这些角度进行详细分析&#xff0c;探讨可能的原因和解决方案&#xff0c;并提供预防措施&#xff0c;以帮助用户避免和解决LabVIEW程序闪退的问题。 1…

qmt量化交易策略小白学习笔记第44期【qmt编程之期货行情数据】

qmt编程之获取期货行情数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 获取行情数据 提示 使用该接口时&#xff0c;需要先订阅实时行情(subscribe_quote)或下载过历史行情(download_hi…

k8s中 docker和containerd 镜像相互导入导出

containerd镜像导出并导入docker 1 查看containerd 本地镜像列表 crictl images 2 containerd 导出本地镜像到当前目录下&#xff08;注意&#xff1a; 导出导入需要指定镜像平台类型 --platform&#xff09; ctr -n k8s.io images export nacos-server-24-06-30-13-02-…

openGauss开发者大会、华为云HDC大会举行; PostgreSQL中国技术大会7月杭州开启

重要更新 1. openGauss Developer Day本周五于北京举行&#xff0c;大会聚集了相关行业专家、用户、伙伴和开发者&#xff0c;分享给予openGauss的联合创新成果和实践案例。([2] ) &#xff1b;华为云 HDC 2024本周五于东莞松山湖举行&#xff0c;主题演讲主要覆盖鸿蒙、AI ([3…

Vue3 + Ant-Design 中 a-date-picke 实现选择切换年份 没有鼠标光标,输入框内自带‘年’

效果图&#xff1a; 效果图 <a-date-picker ref"datePicker" v-model:value"year" picker"year" value-format"YYYY年" format"YYYY年" :bordered"false" :allowClear"false" inputReadOnly change&…

【前端项目笔记】3 用户管理

用户管理相关功能实现 涉及表单、对话框、Ajax数据请求 基本页面 用户列表开发 在router.js中导入Users.vue 解决用户列表小问题 选中&#xff08;激活&#xff09;子菜单后刷新不显示高亮 给二级菜单绑定单击事件&#xff0c;点击链接时把对应的地址保存到sessionSto…

vlan技术--交换机实现局域网分割(Access模式trunk模式)

自作笔记... 目录 vlan技术--交换机连接pc实现局域网分割(Access模式) PC SW1 结果 vlan技术--交换机连接pc实现局域网分割(trunk模式) vlan技术--交换机连接pc实现局域网分割(Access模式) 交换机先创建vlan. 交换机分别进入接口 (配置好连接模式, 连接的vlan) PC SW1 …

Set集合系列——Set、HashSet、LinkedHashset、TreeSet

Set系列的公共特点&#xff1a;无重复、无索引&#xff0c;不可用普通for循环&#xff0c;API和Collection重复 HashSet&#xff1a;采取哈希表存取数据 哈希表组成&#xff1f; JDk8之前&#xff1a;数组链表&#xff0c; JDK8以后&#xff1a;数组链表红黑树 哈希值&#…

简单高效的盈利策略,昂首资本推荐价格行为交易

有没有这样一种简单高效的盈利策略&#xff0c;不仅易于新手掌握&#xff0c;也是专业人士的常用利器?当然有了&#xff0c;就是Anzo Capital昂首资本今天推荐的价格行为交易。价格行为交易以其透明清晰的市场视角受到交易员的青睐&#xff0c;它如实反映了市场的真实动态&…

Ubuntu下安装docker

一、docker安装说明 解决官方源无法下载的问题 二、使用步骤 1.更新软件包索引 sudo apt-get update2.安装必要的软件包&#xff0c;以允许apt通过HTTPS使用仓库 sudo apt-get install apt-transport-https ca-certificates curl software-properties-common3.添加Docker的…

功能测试 之 单模块测试----购物车模块

1.需求分析 &#xff08;1&#xff09;购物车显示 1.若未登录&#xff0c;提示登录&#xff0c;提示文案“购物车内暂时没有商品&#xff0c;登录后将显示您之前加入的商品” 2.若已登录&#xff0c;购物车没有商品&#xff0c;提示去购物。 未登录状态 已登录状态 3.购物车有…

CVPR2024|UniPAD:一种自动驾驶的统一的预训练范式

本文章仅用于学术分享 论文标题丨 UniPAD: A Universal Pre-training Paradigm for Autonomous Driving 论文地址丨 https://arxiv.org/abs/2310.08370 代码地址 | https://github.com/Nightmare-n/UniPAD 关注「AI前沿速递」公众号&#xff0c;获取更多前沿资讯 01总览 这…

Spring Clude 是什么?

目录 认识微服务 单体架构 集群和分布式架构 集群和分布式 集群和分布式区别和联系 微服务架构 分布式架构&微服务架构 微服务的优势和带来的挑战 微服务解决方案- Spring Cloud 什么是 Spring Cloud Spring Cloud 版本 Spring Cloud 和 SpringBoot 的关系 Sp…

「51媒体」食品展览展会活动,媒体邀约资源有哪些?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 食品展览展会活动在媒体邀约方面拥有丰富的资源&#xff0c;可以吸引各类媒体的关注和报道。以下是一些常见的媒体邀约资源&#xff1a; 1. 行业媒体&#xff1a; 专业食品杂志&#xff…

可编程非线性RCD负载原理与应用

可编程非线性RCD负载&#xff08;Resistor-Capacitor-Diode&#xff09;是一种电子元件&#xff0c;其电阻、电容和二极管的特性可以通过编程进行控制和调整。这种负载广泛应用于电力系统、通信设备、电子设备等领域&#xff0c;具有很高的实用价值。 RCD负载的基本原理是利用电…

超声波清洗机的优势到底有哪些?四款精良爆品总结安利,质量放心

眼镜是现代人生活中的必备物品&#xff0c;但是很多人可能对于如何正确清洗眼镜感到困惑。传统的清洗方法可能会在清洗过程中对眼镜造成损坏&#xff0c;例如使用普通肥皂或清水清洗时容易划伤镜片。为了解决这个问题&#xff0c;家用眼镜超声波清洗机应运而生。超声波清洗机利…

[华为北向网管NCE开发教程(6)消息订阅

1.作用 之前介绍的都是我们向网管NCE发起请求获取数据&#xff0c;消息订阅则反过来&#xff0c;是网管NCE系统给我们推送信息。其原理和MQ&#xff0c;JMS这些差不多&#xff0c;这里不过多累述。 2.场景 所支持订阅的场景有如下&#xff0c;以告警通知为例&#xff0c;当我…

Talk|北京大学张嘉曌:NaVid - 视觉语言导航大模型

本期为TechBeat人工智能社区第602期线上Talk。 北京时间6月20日(周四)20:00&#xff0c;北京大学博士生—张嘉曌的Talk已经准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “NaVid - 视觉语言导航大模型”&#xff0c;NaVid是首个专为视觉语言导航&#xf…

深入理解Java并发锁

在Java中&#xff0c;并发锁是用来控制多个线程对共享资源的访问&#xff0c;确保数据的一致性和完整性。Java提供了多种并发锁机制&#xff0c;包括内置锁&#xff08;synchronized&#xff09;、显示锁&#xff08;如ReentrantLock&#xff09;、原子变量、并发容器以及一些高…