OCR文字检测框的合并

项目的github地址:https://github.com/zcswdt/merge_text_boxs

在我们使用文字检测模型的对文本进行检测的时候,可能效果不能如愿以偿,如:某行文本会存在着多个检测框,这对于我们后续做OCR识别过程中语义连贯带来一系列的困扰,本文主要是解决针对文字检测得到的文字框,进行合并。

1.如下是一张原始图片:

2.使用常见的文字检测模型(craft,DBNet,PSENet)对该图片进行文字检测得到的效果如下:

  1. 使用如下代码对文字框进行x轴上的合并,得到的效果如下:

import numpy as npdef get_rect_points(text_boxes):x1 = np.min(text_boxes[:, 0])y1 = np.min(text_boxes[:, 1])x2 = np.max(text_boxes[:, 2])y2 = np.max(text_boxes[:, 3])return [x1, y1, x2, y2]class BoxesConnector(object):def __init__(self, rects, imageW, max_dist=None, overlap_threshold=None):print('max_dist',max_dist)print('overlap_threshold',overlap_threshold )self.rects = np.array(rects)self.imageW = imageWself.max_dist = max_dist  # x轴方向上合并框阈值self.overlap_threshold = overlap_threshold  # y轴方向上最大重合度self.graph = np.zeros((self.rects.shape[0], self.rects.shape[0]))  # 构建一个N*N的图 N等于rects的数量self.r_index = [[] for _ in range(imageW)]  # 构建imageW个空列表for index, rect in enumerate(rects):  # r_index第rect[0]个元素表示 第index个(数量可以是0/1/大于1)rect的x轴起始坐标等于rect[0]if int(rect[0]) < imageW:self.r_index[int(rect[0])].append(index)else:  # 边缘的框旋转后可能坐标越界self.r_index[imageW - 1].append(index)print(self.r_index)def calc_overlap_for_Yaxis(self, index1, index2):# 计算两个框在Y轴方向的重合度(Y轴错位程度)height1 = self.rects[index1][3] - self.rects[index1][1]height2 = self.rects[index2][3] - self.rects[index2][1]y0 = max(self.rects[index1][1], self.rects[index2][1])y1 = min(self.rects[index1][3], self.rects[index2][3])print('y1', y1)Yaxis_overlap = max(0, y1 - y0) / max(height1, height2)print('Yaxis_overlap', Yaxis_overlap)return Yaxis_overlapdef get_proposal(self, index):rect = self.rects[index]print('rect',rect)for left in range(rect[0] + 1, min(self.imageW - 1, rect[2] + self.max_dist)):#print('left',left)for idx in self.r_index[left]:print('58796402',idx)# index: 第index个rect(被比较rect)# idx: 第idx个rect的x轴起始坐标大于被比较rect的x轴起始坐标(+max_dist)且小于被比较rect的x轴终点坐标(+max_dist)if self.calc_overlap_for_Yaxis(index, idx) > self.overlap_threshold:return idxreturn -1def sub_graphs_connected(self):sub_graphs = []       #相当于一个堆栈for index in range(self.graph.shape[0]):# 第index列全为0且第index行存在非0if not self.graph[:, index].any() and self.graph[index, :].any(): #优先级是not > and > orv = indexprint('v',v)sub_graphs.append([v])print('sub_graphs', sub_graphs)# 级联多个框(大于等于2个)print('self.graph[v, :]', self.graph[v, :])while self.graph[v, :].any():v = np.where(self.graph[v, :])[0][0]          #np.where(self.graph[v, :]):(array([5], dtype=int64),)  np.where(self.graph[v, :])[0]:[5]print('v11',v)sub_graphs[-1].append(v)print('sub_graphs11', sub_graphs)return sub_graphsdef connect_boxes(self):for idx, _ in enumerate(self.rects):proposal = self.get_proposal(idx)print('idx11', idx)print('proposal',proposal)if proposal >= 0:self.graph[idx][proposal] = 1  # 第idx和proposal个框需要合并则置1sub_graphs = self.sub_graphs_connected() #sub_graphs [[0, 1], [3, 4, 5]]# 不参与合并的框单独存放一个子listset_element = set([y for x in sub_graphs for y in x])  #{0, 1, 3, 4, 5}for idx, _ in enumerate(self.rects):if idx not in set_element:sub_graphs.append([idx])            #[[0, 1], [3, 4, 5], [2]]result_rects = []for sub_graph in sub_graphs:rect_set = self.rects[list(sub_graph)]     #[[228  78 238 128],[240  78 258 128]].....print('1234', rect_set)rect_set = get_rect_points(rect_set)result_rects.append(rect_set)return np.array(result_rects)

4.我们可以构造一些框,画在一张白纸上看看合并的效果如何,我编造了12个框,并且画在了一张白纸上,接下来是我自己构造并且合并的效果:


if __name__ == '__main__':import cv2rects = []rects = [[144, 5, 192, 25], [25, 6, 64, 25], [66, 6, 141, 25], [193, 5, 275, 33], [269, 30, 354, 50], [26, 30, 182, 52],[185, 28, 265, 55], [25, 56, 89, 76], [93, 56, 229, 78], [232, 56, 262, 76], [264, 52, 343, 81]]#创建一个白纸show_image = np.zeros([500, 500, 3], np.uint8) + 255connector = BoxesConnector(rects, 500, max_dist=15, overlap_threshold=0.2)new_rects = connector.connect_boxes()print(new_rects)for rect in rects:cv2.rectangle(show_image, (rect[0], rect[1]), (rect[2], rect[3]), (0, 0, 255), 1)for rect in new_rects:cv2.rectangle(show_image,(rect[0], rect[1]), (rect[2], rect[3]),(255,0,0),1)cv2.imshow('res', show_image)cv2.waitKey(0)

5.原始的坐标框一共有12个,画在白纸上如下图,使用该代码对文字检测模型检测的图片进行文字框进行x轴上的合并,得到的效果如下:


经过文字框合并之后仅仅有三个框,效果如下

  1. 当然我们有时候需要的是要将文字框进行按照区域进行合并,这样更能有利于提取该区域的文本内容的连贯性,不能说不同区域的文本在经过文字识别后不能组合到一起,形成语义信息。所以接下来我们需要对在x轴合并的文本框进行区域合并:
import numpy as npdef get_rect_points(text_boxes):x1 = np.min(text_boxes[:, 0])y1 = np.min(text_boxes[:, 1])x2 = np.max(text_boxes[:, 2])y2 = np.max(text_boxes[:, 3])return [x1, y1, x2, y2]class BoxesConnector(object):def __init__(self, rects, imageW, max_dist=5, overlap_threshold=0.2):self.rects = np.array(rects)self.imageW = imageWself.max_dist = max_dist  # x轴方向上合并框阈值self.overlap_threshold = overlap_threshold  # y轴方向上最大重合度self.graph = np.zeros((self.rects.shape[0], self.rects.shape[0]))  # 构建一个N*N的图 N等于rects的数量self.r_index = [[] for _ in range(imageW)]  # 构建imageW个空列表for index, rect in enumerate(rects):  # r_index第rect[0]个元素表示 第index个(数量可以是0/1/大于1)rect的x轴起始坐标等于rect[0]if int(rect[1]) < imageW:self.r_index[int(rect[1])].append(index)else:  # 边缘的框旋转后可能坐标越界self.r_index[imageW - 1].append(index)print('self.r_index',self.r_index)print('len(self.r_index)', len(self.r_index))def calc_overlap_for_Yaxis(self, index1, index2):# 计算两个框在Y轴方向的重合度(Y轴错位程度)height1 = self.rects[index1][3] - self.rects[index1][1]height2 = self.rects[index2][3] - self.rects[index2][1]y0 = max(self.rects[index1][1], self.rects[index2][1])y1 = min(self.rects[index1][3], self.rects[index2][3])Yaxis_overlap = max(0, y1 - y0) / max(height1, height2)return Yaxis_overlapdef calc_overlap_for_Xaxis(self, index1, index2):# 计算两个框在Y轴方向的重合度(Y轴错位程度)width1 = self.rects[index1][2] - self.rects[index1][0]width2 = self.rects[index2][2] - self.rects[index2][0]x0 = max(self.rects[index1][0], self.rects[index2][0])x1 = min(self.rects[index1][2], self.rects[index2][2])Yaxis_overlap = max(0, x1 - x0) / max(width1, width2)print('Yaxis_overlap', Yaxis_overlap)return Yaxis_overlapdef get_proposal(self, index):rect = self.rects[index]for left in range(rect[1] + 1, min(self.imageW - 1, rect[3] + self.max_dist)):for idx in self.r_index[left]:print('56871',idx)# index: 第index个rect(被比较rect)# idx: 第idx个rect的x轴起始坐标大于被比较rect的x轴起始坐标(+max_dist)且小于被比较rect的x轴终点坐标(+max_dist)if self.calc_overlap_for_Xaxis(index, idx) > self.overlap_threshold:return idxreturn -1def sub_graphs_connected(self):sub_graphs = []       #相当于一个堆栈for index in range(self.graph.shape[0]):# 第index列全为0且第index行存在非0if not self.graph[:, index].any() and self.graph[index, :].any(): #优先级是not > and > orv = indexprint('v',v)sub_graphs.append([v])print('sub_graphs', sub_graphs)# 级联多个框(大于等于2个)print('self.graph[v, :]', self.graph[v, :])while self.graph[v, :].any():v = np.where(self.graph[v, :])[0][0]          #np.where(self.graph[v, :]):(array([5], dtype=int64),)  np.where(self.graph[v, :])[0]:[5]print('v11',v)sub_graphs[-1].append(v)print('sub_graphs11', sub_graphs)return sub_graphsdef connect_boxes(self):for idx, _ in enumerate(self.rects):print('idx', idx)proposal = self.get_proposal(idx)print('proposal',proposal)if proposal > 0:self.graph[idx][proposal] = 1  # 第idx和proposal个框需要合并则置1sub_graphs = self.sub_graphs_connected() #sub_graphs [[0, 1], [3, 4, 5]]# 不参与合并的框单独存放一个子listset_element = set([y for x in sub_graphs for y in x])  #{0, 1, 3, 4, 5}for idx, _ in enumerate(self.rects):if idx not in set_element:sub_graphs.append([idx])            #[[0, 1], [3, 4, 5], [2]]result_rects = []for sub_graph in sub_graphs:rect_set = self.rects[list(sub_graph)]     #[[228  78 238 128],[240  78 258 128]].....print('1234', rect_set)rect_set = get_rect_points(rect_set)result_rects.append(rect_set)return np.array(result_rects)

接下来构建一张白纸来演示一下代码的合并效果:

if __name__ == '__main__':import cv2rects = []#在y轴上的合并rects.append(np.array([50, 50, 130, 80]))rects.append(np.array([50, 90, 100, 125]))rects.append(np.array([50, 135, 150, 167]))rects.append(np.array([180, 50, 270, 82]))rects.append(np.array([180, 92, 270, 125]))rects.append(np.array([180, 135, 210, 170]))rects.append(np.array([50, 220, 110, 251]))rects.append(np.array([50, 261, 130, 289]))rects.append(np.array([130, 190, 210, 210]))#创建一个白纸show_image = np.zeros([400, 400, 3], np.uint8) + 255connector = BoxesConnector(rects, 400, max_dist=15, overlap_threshold=0.3)new_rects = connector.connect_boxes()print(new_rects)for rect in rects:cv2.rectangle(show_image, (rect[0], rect[1]), (rect[2], rect[3]), (0, 0, 255), 1)# for rect in new_rects:#     cv2.rectangle(show_image,(rect[0], rect[1]), (rect[2], rect[3]),(255,0,0),1)cv2.imshow('res', show_image)cv2.waitKey(0)

res是使用了9个文本框来演示:

res_c中的蓝色文本框是经过文本区域合并以后的文字框:

真实效果如下:绿色框是我们在沿着x轴进行的文字框合并,而蓝色的文本框才是经过区域合并的效果:

使用文本x轴方向的合并,和文本区域合并,有利用将大块区域的文本提取到一起,这样更有利于OCR文字识别时的组合语义之间的连贯性。

OCR文字检测框的合并相关推荐

  1. OCR文字检测主要算法

    转载:https://www.mayi888.com/archives/60604 文字检测是文字识别过程中的一个非常重要的环节,文字检测的主要目标是将图片中的文字区域位置检测出来,以便于进行后面的文 ...

  2. 基础 | OCR文字检测与识别

    作者|Gidi Shperber   编译|AI公园 导读 OCR中的研究,工具和挑战,都在这儿了. 介绍 我喜欢OCR(光学字符识别).对我来说,它代表了数据科学,尤其是计算机视觉的真正挑战.这是一 ...

  3. 【OCR】文字检测:传统算法、CTPN、EAST

    我的east和ctpn速度差不多,east正确率高4% http://xiaofengshi.com/2019/01/23/深度学习-TextDetection/ https://codeload.g ...

  4. OpenCV OCR实战 文档扫描与文字检测

    本文讲述使用OpenCV- python以及easyocr库实现文档扫描与文字检测的思路和具体实现过程. 目录 知识准备 项目概述 实现过程 代码讲解 1.读入图片并进行预处理(灰度转换,高斯滤波) ...

  5. OCR文字识别—文本检测的算法

    一.文字检测:通用目标检测(一阶段.两阶段) 二.Faster R-CNN 这个是通用目标检测的典型算法. 框架分为四大部分: 三.CTPN:两阶段的文本检测 四.FCN:一阶段 区分前景和背景. 五 ...

  6. 商汤联手华科:提出文字检测模型GNNets,新颖模块可解决几何分布难题

    加入「公开课」交流群,获取更多学习资料.课程及热招岗位等信息 编辑 | Jane 出品 | AI科技大本营(ID:rgznai100) [导读]今年的ICCV,商汤科技及联合实验室共有57篇论文入选I ...

  7. 【项目实践】中英文文字检测与识别项目(CTPN+CRNN+CTC Loss原理讲解)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自:opencv学堂 OCR--简介 文字识别也是图像领域一 ...

  8. 自然场景下的文字检测:从多方向迈向任意形状

    点击我爱计算机视觉标星,更快获取CVML新技术 本文经作者授权转载自知乎旷视Detection组专栏: https://zhuanlan.zhihu.com/p/68058851 旷视检测组在刚刚结束 ...

  9. 文字检测与识别1-MSER

    导语 文字识别在现实场景中的用途非常广泛,现在已经有很多公司将这项技术用于实际中.比如车牌识别,图片转换成文档,拍照搜题,拍照翻译等.这让很多人有了错觉,感觉文字识别的技术已经炉火纯青,可以广泛应用. ...

最新文章

  1. 【青少年编程】【蓝桥杯】绘制莲花图形
  2. 自动驾驶定位技术-粒子滤波实践
  3. 大三前端实习生2018总结
  4. girton college map
  5. 数据连接java面试题
  6. SecureCRT无法登录Ubuntu
  7. ARToolKit从图片生成特征点数据
  8. [转] js对象监听实现
  9. 拆分是解决大规模应用问题的本质
  10. windows下cfree5中%d输出浮点数的问题
  11. 【软件测试】软考-2022软件评测师考试心得
  12. Aurora8B10B IP使用 -01- 简介与端口描述
  13. Sequence operation HDU - 3397
  14. xgboost.fit函数
  15. 2021年6月27日 root Redmi Note 8
  16. KOA 2.0 新一代WEB开发框架-姜威-专题视频课程
  17. 我已经是糖尿病患者将近9年了。
  18. python random库生成伯努利随机数的方法_详解Python基础random模块随机数的生成
  19. Ubuntu16.04、18.04换清华源
  20. 微软enchange服务器安装,安装 Exchange Server 2010

热门文章

  1. MySQL报错1055解决办法
  2. Notepad++强大的代码补全和代码提示功能的方法
  3. oracle数据库行号,数据库的行号函数
  4. 《Imperfect C++》译序[已出版]
  5. 将普通函数当中的arguments(类数组转换为数组以及类数组调用数组方法)
  6. 仿站和模板建站的区别_建站方式多种,哪个比较适合?
  7. HDU 6072 Logical Chain
  8. 最标准的UML(活动图)画法
  9. SketchUp Pro 2018 剖面填充功能讲解
  10. 小新打印机固件升级包Lenovo M7268W-01.11.01