opencv提取结构化文本总结
扫描文件表格识别
1.识别结构
situation1 有明确表格结构
1.纠正表格偏移角度(获取最大轮廓,计算最小的矩形,变换坐标截取矩形)
获取面积最大轮廓
_, contours, HIERARCHY = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
candidate_table = sorted(candidate_table, key=cv2.contourArea, reverse=True)
计算包含该轮廓的最小的矩形
rect = cv2.minAreaRect(candidate_table[0])
box = cv2.boxPoints(rect) # box是四个点的坐标
box = np.int0(box) # 取整
- 去除面积不符合要求的表格
candidate_table = [cnt for cnt in contours if cv2.contourArea(cnt) > min_table_area]
- 去除长宽比不符合要求的表格
box_width, box_height = cv2.minAreaRect(cnt)[1][0], cv2.minAreaRect(cnt)[1][1] # 长宽if cv2.minAreaRect(cnt)[2] > 45:box_width, box_height = box_height, box_width
坐标截取矩形
def get_sorted_rect(rect):'''获取矩阵排序的四个坐标,方便透视变换使用@param rect:@return:按照左上 右上 右下 左下排列返回'''mid_x = (max([x[1] for x in rect]) - min([x[1] for x in rect])) * 0.5 + min([x[1] for x in rect]) # 中间点坐标left_rect = [x for x in rect if x[1] < mid_x]left_rect.sort(key=lambda x: (x[0], x[1]))right_rect = [x for x in rect if x[1] > mid_x]right_rect.sort(key=lambda x: (x[0], x[1]))sorted_rect = left_rect[0], left_rect[1], right_rect[1], right_rect[0] # 左上 右上 右下 左下return sorted_rectdef perTran(image, rect):'''做透视变换image 图像rect 四个顶点位置:左上 右上 右下 左下'''tl, tr, br, bl = rect # 左下 右下 左上 右上 || topleft topright 左上 右上 右下 左下# 计算宽度widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA), int(widthB))# 计算高度heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))# 定义变换后新图像的尺寸dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype='float32')# 变换矩阵rect = np.array(rect, dtype=np.float32)dst = np.array(dst, dtype=np.float32)M = cv2.getPerspectiveTransform(rect, dst)# 透视变换warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warped
2. 计算每一条横线或总线的横坐标
def drop_duplicated_row_points(pos, max_span):'''获取去重后的 坐标点Args:sort_point:max_span: Returns: ''' sort_point = np.sort(list(set(pos)))point_arr = [sort_point[0]] # 每种类型数据max_span行都不一样for i in range(1, len(sort_point) - 1):if sort_point[i] - point_arr[-1] > max_span:point_arr.append(sort_point[i])return point_arrdef dilate_line(binary, type='vertical', x_scale=10, y_scale=5):'''获取竖线/横线腐蚀后的二值图@param binary:@param type:@return:'''rows_z, cols_z = binary.shapeif type == 'horizontal':size = (cols_z // x_scale, 1)else:size = (1, rows_z // y_scale)kernel = cv2.getStructuringElement(cv2.MORPH_RECT, size) eroded = cv2.erode(binary, kernel, iterations=1) # 腐蚀dilated = cv2.dilate(eroded, kernel, iterations=1) # 膨胀 return dilateddef get_page_rows_y_array(binary, row_height, x_scale=20):'''获取该图像行数 cv2.imwrite('dilated_col_z.jpg', dilated_col_z):param binary::return:''' dilated_col_z = dilate_line(binary, type='horizontal', x_scale=x_scale)ys, xs = np.where(dilated_col_z > 0)point_arr = drop_duplicated_row_points(ys, max_span=row_height)return point_arr
situation2 无明确表格结构
只有横线/纵线
1.纠正表格偏移角度( 消除除线条外的其他信息,计算横线或者纵向的起始点和坐标点,计算角度,纠正角度后,再通过坐标截取矩形 )
2. 回到situation1 第二步
计算坐标
lsd = cv2.createLineSegmentDetector(0, 1)
def get_pos_by_horizontal_line(lsd,binary ):'''获取横线的开始点和结束点'''dlines = lsd.detect(binary)[0]pos = [[x[0][0], x[0][1]] for x in dlines] + [[x[0][2], x[0][3]] for x in dlines]top_pos_list = [x for x in pos if x[0] == min([x[0] for x in pos])] # 最小的xbottom_pos_list = [x for x in pos if x[0] == max([x[0] for x in pos])] # 最大的xtop_pos = [x for x in top_pos_list if x[1] == min([x[1] for x in top_pos_list])][0] # 最小的ybottom_pos = [x for x in bottom_pos_list if x[1] == max([x[1] for x in bottom_pos_list])][0] # 最大的yx1, y1, x2, y2 = top_pos + bottom_posreturn int(x1), int(y1), int(x2), int(y2)
计算角度
def cal_angle(x1, y1, x2, y2, is_vertical=True):if x2 - x1 == 0:result = 90elif y2 - y1 == 0:result = 0else:# 计算斜率k = -(y2 - y1) / (x2 - x1)# 求反正切,再将得到的弧度转换为度result = np.arctan(k) * 57.29577 # 逆时针if is_vertical:if result < 0:result += 90elif result == 90:result = 0else:result -= 90print("通过竖线计算得到直线倾斜角度为:{} 度".format(result))else:print("通过横线计算得到直线倾斜角度为:{} 度".format(result))result = round(result, 3)return result
纠正角度
def rotate_image( image, angle):# dividing height and width by 2 to get the center of the imageheight, width = image.shape[:2]# get the center coordinates of the image to create the 2D rotation matrixcenter = (width / 2, height / 2)# using cv2.getRotationMatrix2D() to get the rotation matrixrotate_matrix = cv2.getRotationMatrix2D(center=center, angle=angle, scale=1)# rotate the image using cv2.warpAffinerotated_image = cv2.warpAffine(src=image, M=rotate_matrix, dsize=(width, height))return rotated_image
无横线/纵线
通过扫描时设置白边处理,图片转灰度图后,非255的像素值转成0,然后回到situation1注意扫描时如果有黑边会影响计算,需要考察扫描仪选项
binary[binary <= 254] = 1
binary[binary == 255] = 0
binary[binary == 1] = 255
tips
图片预处理,通过中值滤波和色差
def clean_gray(gray,ksize=3,difference=50):gray_copy = gray.copy()gray_copy[(gray_copy >= (255-difference))] = 255gray_copy = cv2.medianBlur(gray_copy, ksize)return gray_copy
如果线条有断裂的地方,可以先膨胀再腐蚀,把断裂线条补齐
opening = cv2.morphologyEx(binary_dilate, cv2.MORPH_OPEN, kernel, 1)
2.paddle ocr 识别文字
排序
ocr_result = ocr.ocr(padding_cell_child )
# 排序ocr_result = sorted(ocr_result, key=lambda x: (x[0][0][0]))# 横坐标排序
ocr_result = sorted(cell_result, key=lambda x: (x[0][3][1], x[0][3][0])) # 按照y 再按照x 排序
数字识别不到
检测到数字轮廓 ,截取数字,paddle det设置为False
长文本识别不到
描述:paddle超出25个字识别可能会识别不出来
- 改变图片比例为1:10
ratio = cell.shape[1] // cell.shape[0]
cell = cv2.resize(cell, (0, 0), fx=round(1 / (ratio / 10), 2), fy=1)
- 图片加padding (此处上下加5),少部分概率下会导致识别错误
cell = np.pad(cell, ((5, 5), (0, 0)), 'constant', constant_values=(255))
相关文章:
opencv提取结构化文本总结
扫描文件表格识别 1.识别结构 situation1 有明确表格结构 1.纠正表格偏移角度(获取最大轮廓,计算最小的矩形,变换坐标截取矩形) 获取面积最大轮廓 _, contours, HIERARCHY cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2…...

JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
文章目录前言一、概述二、案例二三、案例:方法区内存溢出1、代码:LambdaGC.java2、元空间内存溢出日志3、分析4、疑问*****四、案例:直接内存溢出问题(少见)(尽量不说)五、案例:栈内存溢出问题1…...

Redis的持久化方式
Redis支持两种方式的持久化,一种是RDB方式、另一种是AOF(append-only-file)方式,两种持久化方式可以单独使用其中一种,也可以将这两种方式结合使用。 •RDB:根据指定的规则“定时”将内存中的数据存储在硬…...

【unity游戏制作-mango的冒险】-4.场景二的镜头和法球特效跟随
👨💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:unity游戏制作 ⭐mango的冒险场景二——镜头和法球特效跟随⭐ 文章目录⭐mango的冒险场景二——镜…...

handwrite-1
-------------------- 实现防抖函数(debounce) 防抖函数原理:把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算…...
【一天一门编程语言】Pascal 语言程序设计极简教程
Pascal 语言程序设计极简教程 用 markdown 格式输出答案。 不少于3000字。细分到2级目录。 文章目录 Pascal 语言程序设计极简教程一、Pascal简介1.1 Pascal历史1.2 Pascal的特点1.3 Pascal的应用二、Pascal语言程序设计2.1 Pascal编程环境2.2 Pascal的基本语法2.3 Pascal程序…...

【基础篇0】Linux下ANACONDA与TF-LITE环境配置
0 写在前面:一些摸索与总结 对于Linux系统,我发现不管是电脑x86的Ubuntu还是树莓派arm的raspberry系统,在系统安装完毕后,总是自带一个特定版本的python. 例如我的ubuntu22.04自带的python版本是3.10,而高版本的py…...

TCP协议原理二
文章目录四、滑动窗口二、流量窗口三、拥塞控制四、滑动窗口 前面我们学习了 确认应答,超时重传,连接管理,这些机制都为我们TCP的可靠性提供了保证,当然在保证TCP的可靠性的同时,传输效率也受到了一定的影响ÿ…...

电子科技大学网络协议(TCP/IP作业答案)--网工(五次作业汇总)
目录 作业1:OSI/RM、TCP/IP编址和底层网络技术 作业2:IP地址规划与路由选择 作业3:ARP、IP、ICMP 作业4:UDP、Routing Protocol 作业五 作业1:OSI/RM、TCP/IP编址和底层网络技术 物理地址属于OSI/RM的哪一层&…...
Kubernetes集群声明式文件YAML
一、YAML介绍 YAML 的意思是:仍是一种标记语言,但为了强调这种语言以数据做为中心,而不是以标记语言为重点。是一个可读性高,用来表达数据序列的格式。 二、基本语法 1.低版本缩进时不允许使用Tab键,只允许使用空格…...

为赋能,创共赢~ 〖TFS_CLUB社区〗-〖星荐官计划〗来袭~ 期待各位小伙伴的加入~
文章目录❤️🔥 TFS社区介绍❤️🔥 星荐官计划在直播结束之后,有几位小伙伴跟我说,想法是好的,但是会很难搞。试想一下如果真的是很容易做的事情,那岂不是人人都可以做?正因为难做ÿ…...

【华为OD机试模拟题】用 C++ 实现 - 水仙花数(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 获得完美走位(2023.Q1) 文章目录 最近更新的博客使用说明水仙花数题目输入输出描述示例一输入输出说明示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。…...

Windows作为操作系统的典型特征和主要功能
我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows这个我们熟悉的不能再熟悉的系统。我们每天都在用Windows操作系统,但是其实我们每天直接在打交道的并不是Windows操作系统的内核,而是Windows操作系统的…...

【Linux】-- 多线程安全
目录 进程互斥 计算 -> 时序问题 加锁保护 pthread_mutex_lock pthread_mutex_unlock 使用init与destory pthread_mutex_init phtread_mutex_destory 锁的实现原理 图 可重入VS线程安全 死锁 Linux线程同步 条件变量 系统调用 进程互斥 进程线程间的互斥相关…...

Unity Avatar Camera Controller 第一、第三人称相机控制
文章目录简介Variables实现Target PositionTarget RotationOthers简介 本文介绍如何实现用于Avatar角色的相机控制脚本,支持第一人称、第三人称以及两种模式之间的切换,工具已上传至SKFramework框架的Package Manager中: Variables Avatar&…...

SRE中 的SLO,SLI等知识 归纳
SLA Service Level Agreement 服务质量/水平协议SLO Service Level Objective 服务质量/水平目标SLI Services Level Indicator 服务质量/水平指标下面用人、事、物的逻辑进行阐释。人和事用从上到下,从左到右的顺序。客户 - 每 1 个客户在使用产品服务时&…...

MS9123是一款单芯片USB投屏器,内部集成了USB2 0控制器和数据收发模块、视频DAC和音视频处理模块,MS9123可以通过USB接口显示或者扩展PC、
MS9123是一款单芯片USB投屏器,内部集成了USB2.0控制器和数据收发模块、视频DAC和音视频处理模块,MS9123可以通过USB接口显示或者扩展PC、智能手机、平板电脑的显示信息到更大尺寸的显示设备上,支持CVBS、S-Video视频接口。 主要功能特征 C…...

针孔成像模型零基础入门(三)
2020年爆火的Nerf(神经辐射场)横空出世,据说只要用手机拍照,然后喂给模型,就可以生成3D模型了,我试过了,确有此事! 那我们有想过,为什么可以从二维的图片里面获取物体三…...

你真的了解环形队列吗?(学习数据结构必须掌握的模型)
目录 0.前言 1. 什么是环形队列 2. 如何使用数组结构 / 链表结构 对环形队列封装 3. 代码手撕环形队列各个接口 3.1 代表封装一个环形队列 3.2 环形队列的初始化 3.3 环形队列的插入 3.4环形队列的删除 3.5环形队列的判空 3.6环形队列的判满 3.7环形队列的队头 3.8环…...

《痞子衡嵌入式半月刊》 第 72 期
痞子衡嵌入式半月刊: 第 72 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...