Python+OpenCV竖版古籍文字分割
在做图片文字分割的时候,常用的方法有两种。一种是投影法,适用于排版工整,字间距行间距比较宽裕的图像;还有一种是用OpenCV的轮廓检测,适用于文字不规则排列的图像。
1. 思路
一开始想偷个懒,直接用OpenCV的模型,结果发现效果不佳。文字出现了过度分割的问题,部分文字甚至没有被识别:

于是只好使用传统方法,投影法。对文字图片作横向和纵向投影,即通过统计出每一行像素个数,和每一列像素个数,来分割文字。代码参考https://www.cnblogs.com/zxy-joy/p/10687152.html,但是对于古籍来说,需要做一些修改。比如,古籍文字书写在习惯是从上到下的,所以说在扫描的时候应该扫描列投影,在扫描行投影,搞定这次简单的操作顺序修改以后,分割结果如下:

很显然,虽然说没有出现过度分割的问题,但是由于字体有大有小,有的地方两个字被合起来识别成了一个字。那么很显然,只要把这些地方再进行一次列投影,把它们再度拆分成两个字,问题不就解决了么。添加代码:
# 再进行一次列扫描
DcropImg = cropImg[H_start[pos]:H_end[pos], 0:w]
d_h, d_w = DcropImg.shape
# cv2.imshow("dcrop", DcropImg)
sec_V = getVProjection(DcropImg)
c1, c2 = scan(sec_V, 0)
if len(c1) > len(c2):
c2.append(d_w) # cv2.waitKey(0)
if len(c1) == 1:
Position.append([V_start[i],H_start[pos],V_end[i],H_end[pos]])
else:
for x in range(len(c1)):
Position.append([V_start[i]+c1[x], H_start[pos],V_start[i]+c2[x], H_end[pos]])
2. 优化
对单行文本做列扫描,很容易出现过度分割的问题。因为只有一行,会扫描到很多没有像素点的列,最终就会出现这种情况:

为了避免这种过度分割的情况,可以添加一个检测两个分割之间距离的代码,使距离较近的分割进行合并。
x = 1
while x < len(c1):
if c1[x] - c2[x-1] < 12:
c2.pop(x-1)
c1.pop(x)
x -= 1
x += 1
3. 代码
再通过添加一些属性来限制一个字的最大长度宽度、两个字之间的最小间距,来避免过度分割,最终效果如下:

虽然仍然存在一些小瑕疵,但是总体效果还算不错。
详细代码如下:
import cv2
import numpy as np HIOG = 50
VIOG = 3
Position = [] '''水平投影'''
def getHProjection(image):
hProjection = np.zeros(image.shape,np.uint8)
# 获取图像大小
(h,w)=image.shape
# 统计像素个数
h_ = [0]*h
for y in range(h):
for x in range(w):
if image[y,x] == 255:
h_[y]+=1
#绘制水平投影图像
for y in range(h):
for x in range(h_[y]):
hProjection[y,x] = 255
# cv2.imshow('hProjection2',cv2.resize(hProjection, None, fx=0.3, fy=0.5, interpolation=cv2.INTER_AREA))
# cv2.waitKey(0)
return h_ def getVProjection(image):
vProjection = np.zeros(image.shape,np.uint8);
(h,w) = image.shape
w_ = [0]*w
for x in range(w):
for y in range(h):
if image[y,x] == 255:
w_[x]+=1
for x in range(w):
for y in range(h-w_[x],h):
vProjection[y,x] = 255
# cv2.imshow('vProjection',cv2.resize(vProjection, None, fx=1, fy=0.1, interpolation=cv2.INTER_AREA))
# cv2.waitKey(0)
return w_ def scan(vProjection, iog, pos = 0):
start = 0
V_start = []
V_end = [] for i in range(len(vProjection)):
if vProjection[i] > iog and start == 0:
V_start.append(i)
start = 1
if vProjection[i] <= iog and start == 1:
if i - V_start[-1] < pos:
continue
V_end.append(i)
start = 0
return V_start, V_end def checkSingle(image):
h = getHProjection(image)
start = 0
end = 0 for i in range(h):
pass if __name__ == "__main__":
# 读入原始图像
origineImage = cv2.imread('test_data/test2.jpg')
# 图像灰度化
#image = cv2.imread('test.jpg',0)
image = cv2.cvtColor(origineImage,cv2.COLOR_BGR2GRAY) # cv2.imshow('gray',image)
# 将图片二值化
retval, img = cv2.threshold(image,127,255,cv2.THRESH_BINARY_INV)
# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# img = cv2.erode(img, kernel)
# cv2.imshow('binary',cv2.resize(img, None, fx=0.3, fy=0.3, interpolation=cv2.INTER_AREA))
#图像高与宽
(h,w)=img.shape
#垂直投影
V = getVProjection(img) start = 0
V_start = []
V_end = [] # 对垂直投影水平分割
V_start, V_end = scan(V, HIOG)
if len(V_start) > len(V_end):
V_end.append(w-5) # 分割行,分割之后再进行列分割并保存分割位置
for i in range(len(V_end)):
#获取行图像
if V_end[i] - V_start[i] < 30:
continue cropImg = img[0:h, V_start[i]:V_end[i]]
# cv2.imshow('cropImg',cropImg)
# cv2.waitKey(0)
#对行图像进行垂直投影
H = getHProjection(cropImg)
H_start, H_end = scan(H, VIOG, 40) if len(H_start) > len(H_end):
H_end.append(h-5) for pos in range(len(H_start)):
# 再进行一次列扫描
DcropImg = cropImg[H_start[pos]:H_end[pos], 0:w]
d_h, d_w = DcropImg.shape
# cv2.imshow("dcrop", DcropImg)
sec_V = getVProjection(DcropImg)
c1, c2 = scan(sec_V, 0)
if len(c1) > len(c2):
c2.append(d_w) x = 1
while x < len(c1):
if c1[x] - c2[x-1] < 12:
c2.pop(x-1)
c1.pop(x)
x -= 1
x += 1 # cv2.waitKey(0)
if len(c1) == 1:
Position.append([V_start[i],H_start[pos],V_end[i],H_end[pos]])
else:
for x in range(len(c1)):
Position.append([V_start[i]+c1[x], H_start[pos],V_start[i]+c2[x], H_end[pos]]) #根据确定的位置分割字符
for m in range(len(Position)):
cv2.rectangle(origineImage, (Position[m][0]-5,Position[m][1]-5), (Position[m][2]+5,Position[m][3]+5), (0 ,0 ,255), 2)
cv2.imshow('image',cv2.resize(origineImage, None, fx=0.6, fy=0.6, interpolation=cv2.INTER_AREA))
cv2.waitKey(0)
4. 总结
果然,在面对具体问题时,一个再优秀的普适模型往往都不如优化的比较好的传统方法。就像调参得当的网络,再具体问题上往往比一些十分优秀的网络模型效果还要好一样。
参考文献:https://www.cnblogs.com/zxy-joy/p/10687152.html
Python+OpenCV竖版古籍文字分割的更多相关文章
- Python + opencv 实现图片文字的分割
实现步骤: 1.通过水平投影对图形进行水平分割,获取每一行的图像: 2.通过垂直投影对分割的每一行图像进行垂直分割,最终确定每一个字符的坐标位置,分割出每一个字符: 先简单介绍一下投影法:分别在水平和 ...
- 【Python | opencv+PIL】常见操作(创建、添加帧、绘图、读取等)的效率对比及其优化
一.背景 本人准备用python做图像和视频编辑的操作,却发现opencv和PIL的效率并不是很理想,并且同样的需求有多种不同的写法并有着不同的效率.见全网并无较完整的效率对比文档,遂决定自己丰衣足食 ...
- python opencv识别蓝牌车牌号 之 取出车牌号 (1/3)
概述 车牌识别是计算机视频图像识别技术在车辆牌照识别中的一种应用,通常来讲如果结合opencv进行车牌识别主要分为四个大步骤,分别为: 图像采集 车牌定位 分割车牌字符 字符识别 当然,如果结合了机器 ...
- Python+opencv打开修图的正确方式get
先逼逼两句: 图像是 Web 应用中除文字外最普遍的媒体格式. 流行的 Web 静态图片有 JPEG.PNG.ICO.BMP 等.动态图片主要是 GIF 格式.为了节省图片传输流量,大型互联网公司还会 ...
- 搭建基于python +opencv+Beautifulsoup+Neurolab机器学习平台
搭建基于python +opencv+Beautifulsoup+Neurolab机器学习平台 By 子敬叔叔 最近在学习麦好的<机器学习实践指南案例应用解析第二版>,在安装学习环境的时候 ...
- .NET + OpenCV & Python + OpenCV 配置
最近需要做一个图像识别的GUI应用,权衡了Opencv+ 1)QT,2)Python GUI,3).NET后选择了.NET... 本文给出C#+Opencv和Python+Opencv的相应参考,节省 ...
- RPi 2B python opencv camera demo example
/************************************************************************************** * RPi 2B pyt ...
- OpenCV学习(20) grabcut分割算法
http://www.cnblogs.com/mikewolf2002/p/3330390.html OpenCV学习(20) grabcut分割算法 在OpenCV中,实现了grabcut分割算法, ...
- Python+OpenCV图像处理(一)
Python+OpenCV图像处理(一): 读取,写入和展示图片 调用摄像头拍照 调用摄像头录制视频 1. 读取.写入和展示图片 图像读入:cv2.imread() 使用函数cv2.imread() ...
随机推荐
- Nacos(八):Nacos持久化
参考和感谢 Spring Cloud Alibaba基础教程:Nacos的数据持久化 前言 前景回顾: Nacos(七):Nacos共享配置 Nacos(六):多环境下如何"管理" ...
- 替代Flume——Kafka Connect简介
我们知道过去对于Kafka的定义是分布式,分区化的,带备份机制的日志提交服务.也就是一个分布式的消息队列,这也是他最常见的用法.但是Kafka不止于此,打开最新的官网. 我们看到Kafka最新的定义是 ...
- C#开发BIMFACE系列9 服务端API之获取应用支持的文件类型
系列目录 [已更新最新开发文章,点击查看详细] BIMFACE最核心能力之一是工程文件格式转换.无需安装插件,支持数十种工程文件格式在云端转换,完整保留原始文件信息.开发者将告别原始文件解析烦 ...
- zabbix设置钉钉报警
1 添加机器人 在钉钉群里面添加一个机器人 会获取到一个URL: 'https://oapi.dingtalk.com/robot/send?access_token=62be1ea97b4653b8 ...
- 【Linux命令】lsmod命令
lsmod(list modules)命令 lsmod命令用来显示已被内核加载的模块的状态 1)语法:lsmod 2)功能: lsmod命令可以美观地显示/prco/module中的内容,这些内容是被 ...
- P1073 最优贸易 建立分层图 + spfa
P1073 最优贸易:https://www.luogu.org/problemnew/show/P1073 题意: 有n个城市,每个城市对A商品有不同的定价,问从1号城市走到n号城市可以最多赚多少差 ...
- P1415 拆分数列 DP
传送门: 题意: 将一个数字串分成许多不同的小串,使得这些小串代表的数字严格递增,要求最后一个数字尽可能地小. 然后满足字典序尽可能大. 思路: 由于最后一个数字要尽可能地小,所以先处理出每个数的L[ ...
- Codeforces Technocup 2017 - Elimination Round 2 E Subordinates(贪心)
题目链接 http://codeforces.com/contest/729/problem/E 题意:给你n个人,主管id为s,然后给你n个id,每个id上对应一个数字表示比这个人大的有几个. 最后 ...
- 2015 JSOI冬令营训练 彩色格子 题解
解析 棋盘上黑白格染色.曼哈顿距离偶数:奇偶性相同. 枚举有几种颜色分到白格,组合数计算即可. 注意预处理,时间还是比较宽裕的. 为了不重复计数,考虑枚举严格用了i种颜色,我们再枚举分配j种给白集合. ...
- 相同类中方法间调用时日志Aop失效处理
本篇分享的内容是在相同类中方法间调用时Aop失效处理方案,该问题我看有很多文章描述了,不过大多是从事务角度分享的,本篇打算从日志aop方面分享(当然都是aop,失效和处理方案都是一样),以下都是基于s ...