python:超简单的字符分割算法(车牌识别、仪表识别等)_python字符分割识别-程序员宅基地

技术标签: 算法  python  人工智能  python 学习  图像识别  opencv  

背景

在诸如车牌识别,数字仪表识别等问题中,最关键的就是将单个的字符分割开来再分别进行识别,如下图。最近刚好用到,就自己写了一个简单地算法进行字符分割,来记录一下。

在这里插入图片描述

图像预处理

彩图二值化以减小参数量,再进行腐蚀膨胀去除噪点。

image = cv2.imread('F://demo.jpg', 0)  # 读取为灰度图
_, image = cv2.threshold(image, 50, 255, cv2.THRESH_BINARY)  # 二值化
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))  # 腐蚀膨胀核
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))  # 腐蚀膨胀核
image = cv2.erode(image, kernel=kernel1)  # 腐蚀
image = cv2.dilate(image, kernel=kernel2)  # 膨胀

在这里插入图片描述

确定字符区域

考虑最理想的情况,图中的字符是端正没有倾斜歪曲的。将像素灰度矩阵分别进行列相加、行相加,则在得到的列和、行和数组中第一个非 0 元素索引到最后一个非 0 元素索引包裹的区间即就是字符区域。

h, w = image.shape  # 原图的高和宽
list1 = []  # 列和
list2 = []  # 行和

for i in range(w):
    list1.append(1 if image[:, i].sum() != 0 else 0)  # 列求和,不为0置1
for i in range(h):
    list2.append(1 if image[i, :].sum() != 0 else 0)  # 行求和,不为0置1

# 裁剪字符区域
# 求行的范围
flag = 0
for i, e in enumerate(list1):
    if e != 0:
        if flag == 0:  # 第一个不为0的位置记录
            start_w = i
            flag = 1
        else:  # 最后一个不为0的位置
            end_w = i
# 求列的范围
flag = 0
for i, e in enumerate(list2):
    if e != 0:
        if flag == 0:  # 第一个不为0的位置记录
            start_h = i
            flag = 1
        else:  # 最后一个不为0的位置
            end_h = i

print(start_w, end_w)  # 行索引范围
print(start_h, end_h)  # 列索引范围

在这里插入图片描述

分割单个字符

与分割全部字符区域同理,在行和数组中非 0 元素索引的范围即是单个字符的区域。

l = ([i for i, e in enumerate(list1) if e != 0])  # 列和列表中不为0的索引
img_list = []  # 分割数字图片存储列表
temp = []  # 存储某一个数字的所有行索引值
n = 0  # 数字图片数量

for x in l:
    temp.append(x)
    if x+1 not in l:  # 索引不连续的情况
        if len(temp) != 1:
            start_w = min(temp)  # 索引最小值
            end_w = max(temp)  # 索引最大值
            img_list.append(image[start_h:end_h, start_w:end_w])  # 对该索引包括数字切片
            n += 1
        temp = []

print(n)  # 字符数

在这里插入图片描述

完整源码

import cv2

start_h, end_h, start_w, end_w = 0, 0, 0, 0  # 字符区域的高和宽起止

image = cv2.imread('F://001_1.jpg', 0)  # 直接读取为灰度图
cv2.imshow('img_GRAY', image)

_, image = cv2.threshold(image, 50, 255, cv2.THRESH_BINARY)  # 二值化
cv2.imshow('img_BINARY', image)

# 去噪点
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))  # 简单腐蚀膨胀核
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))  # 简单腐蚀膨胀核
image = cv2.erode(image, kernel=kernel1)  # 腐蚀
image = cv2.dilate(image, kernel=kernel2)  # 膨胀
cv2.imshow('img_denoise', image)


h, w = image.shape  # 原图的高和宽
# print(h, w)

list1 = []  # 列和
list2 = []  # 行和

for i in range(w):
    list1.append(1 if image[:, i].sum() != 0 else 0)  # 列求和,不为0置1
for i in range(h):
    list2.append(1 if image[i, :].sum() != 0 else 0)  # 行求和,不为0置1

# print(len(list1))
# print(len(list2))

# 裁剪字符区域
# 求行的范围
flag = 0
for i, e in enumerate(list1):
    if e != 0:
        if flag == 0:  # 第一个不为0的位置记录
            start_w = i
            flag = 1
        else:  # 最后一个不为0的位置
            end_w = i
# 求列的范围
flag = 0
for i, e in enumerate(list2):
    if e != 0:
        if flag == 0:  # 第一个不为0的位置记录
            start_h = i
            flag = 1
        else:  # 最后一个不为0的位置
            end_h = i

print(start_w, end_w)  # 行索引范围
print(start_h, end_h)  # 列索引范围

cv2.imshow('img_number', image[start_h:end_h, start_w:end_w])

l = ([i for i, e in enumerate(list1) if e != 0])  # 列和列表中不为0的索引
# print(l)

img_list = []  # 分割数字图片存储列表

temp = []  # 存储某一个数字的所有行索引值
n = 0  # 数字图片数量
for x in l:
    temp.append(x)
    if x+1 not in l:  # 索引不连续的情况
        if len(temp) != 1:
            start_w = min(temp)  # 索引最小值
            end_w = max(temp)  # 索引最大值
            img_list.append(image[start_h:end_h, start_w:end_w])  # 对该索引包括数字切片
            n += 1
            # print(temp)
        temp = []

print(n)  # 字符数

for i in range(n):  # 显示保存字符
    cv2.imshow('number'+str(i), img_list[i])
    cv2.imwrite('F://demo'+str(i+1).zfill(2)+'.jpg', img_list[i])

cv2.waitKey(0)

结语

利用列向和行向相加的方法简单分割字符的方法并不适用更加复杂的分割要求,另外算法中也没有考虑小数点分割问题,仅作为学习参考,欢迎有问题一起讨论交流。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Wenyuanbo/article/details/120432356

智能推荐

新能源汽车行业-某新能源汽车全国30个4S店全国sdwan组网-云直连介入阿里云_4s店 sdwan-程序员宅基地

文章浏览阅读1k次。客户需求分析:客户全国30个门店通过SDWAN线路访问阿里云服务器。 接入阿里云预计需求是100M带宽,每个门店10M带宽。 Telstra提供全国网络资源实现门店到阿里云的网络连通,搭建稳定可靠的SDWAN网络,客户SDWAN网络主要使用的应理类系统和销售应用系统。 解决方案说明1,客户共有阿里云和30个门店共31个节点的网络需求。2,Te..._4s店 sdwan

基本汇编指令_cmpl-程序员宅基地

文章浏览阅读6.8k次,点赞7次,收藏66次。eax寄存器eax,32位寄存器,ax代表十六位,al代表低八位,ah代表高八位,并且函数返回值一般使用eax存放,al的l是low低八位,ah的h是high高八位如图eip寄存器eip存放将要执行的指令的地址leave指令leave指令等价于movl %ebp , %esppopl %ebp起到退栈作用!!ret指令把返回地址放到eip寄存器中call指令保存下一条指令的地址,并转向被调用函数..._cmpl

开机启动时间计算机,Windows系统更改开机启动时间三种方法-程序员宅基地

文章浏览阅读5.9k次。Windows系统,无论是Windows XP、Windows7、Windows8/8.1还是Windows10各种版本的系统,都可以更改计算机的开机启动时间,具体方法如下:方法一:1、开始 - 运行,输入mscpnfig,确定或者回车,打开系统配置对话框;2、我们在打开的系统配置对话框,左键点击:引导;3、在引导标签下,我们可以看到右侧有个超时(T),在这个框里可以更改计算机的开机启动时间,这里..._电脑怎么设置开机时间

基于不确定混沌系统的无抖振模糊滑模变结构控制策略matlab仿真-程序员宅基地

文章浏览阅读140次。在非线性控制系统中,尤其是存在不确定性、时变性和复杂动力学特性的混沌系统,滑模变结构控制(Sliding Mode Control, SMC)因其强大的鲁棒性和对系统扰动的不敏感性而受到关注。然而,传统滑模控制在切换过程中会产生较大的抖振现象。为了解决这一问题,引入了模糊逻辑来设计一种无抖振模糊滑模控制器。

嵌入式C语言编程——.h文件与.c文件-程序员宅基地

文章浏览阅读2.1k次。.h文件与.c文件的关系参考高手的程序时,发现别人写的严格的程序都带有一个“KEY.H”,里面定义了.C文件里用到的自己写的函数,如Keyhit()、Keyscan()等。.H文件..._c语言 编写.c和.h文件示例

人脸数据集——亚洲人脸数据集-程序员宅基地

文章浏览阅读3.6w次,点赞30次,收藏126次。大规模亚洲人脸数据的制作 在这次大规模亚洲人脸数据制作主要是亚洲明星人脸数据集,此次我爬取了大概20万张亚洲人脸图像,可以修改爬取每位明星图片的数量来获取更多的图片,过程中主要分以下几步: 获取明星名字列表 (1)、首先从百度搜索栏中搜索“明星”,显示出明星栏目,地区包括内地、香港、台湾、韩国和日本,如下图:(2)、使用python爬虫将这些明星的名字爬取下来,代码如下所...

随便推点

终于搞懂了 VUE 的代理和 NGINX 的代理区别了!_vue nginx.conf 和kong的优先级-程序员宅基地

文章浏览阅读5.5k次,点赞4次,收藏28次。前端小白一枚,在开发一个 vue 项目,之前开发的时候遇到了跨域问题,通过网上冲浪自己配置了 vue 代理,美滋滋的认为一劳永逸了。 vue 代理配置如下:在 vue.config.js 中配置proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, p..._vue nginx.conf 和kong的优先级

数据结构-单链表基本操作实现(含全部代码)_单链表的基本操作代码-程序员宅基地

文章浏览阅读5.6w次,点赞168次,收藏842次。今天是单链表的实现,主要实现函数如下: InitList(LinkList &L) 参数:单链表L 功能:初始化 时间复杂度 O(1) ListLength(LinkList L) 参数:单链表L 功能:获得单链表长度 时间复杂度O(n) ListInsert(LinkList &L,int i,Elem..._单链表的基本操作代码

md5算法原理_md5常量-程序员宅基地

文章浏览阅读2.4k次。1、MD5算法是对输入的数据进行补位,使得如果数据位长度LEN对512求余的结果是448。即数据扩展至K*512+448位。即K*64+56个字节,K为整数。具体补位操作:补一个1,然后补0至满足上述要求 2、补数据长度:用一个64位的数字表示数据的原始长度B,把B用两个32位数表示。这时,数据就被填补成长度为512位的倍数。 3. 初始化MD5参数四个32位整数 (A,B,C,D) 用来_md5常量

ORACLE ORA-28545: 连接代理时 Net8 诊断到错误 解决办法-程序员宅基地

文章浏览阅读1.9w次,点赞3次,收藏2次。oralce透明网关很好很强大,可以把多种数据库当成一个数据库来使用,比如在你的oracle数据库里面想和db2里面的数据一起使用,通过透明网关就可以将db2连接到oracle中,具体怎么配置可以参见官网文档,这里记录一下一个错误的处理办法ORA-28545: 连接代理时 Net8 诊断到错误Unable to retrieve text of NETWORK/NCR message 655..._ora-28545

如何在pycharm中安装pygame_pycharm安装pygame-程序员宅基地

文章浏览阅读2.3w次,点赞24次,收藏69次。一、场景描述:在开发python小游戏前,需要安装pygame的插件,如何在pycharm软件中安装pygame的插件呢,本文将重点讲解如何安装pygame这个插件。二、安装步骤:1、在pycharm软件中定位到file–settings2、定位到:project(自己的项目中)–python interpreter,选择右边的pip3、双击pip,进入查询插件界面,输入pygame,进行查询这个插件,最后点击install package4、安装成功后,会在pycharm软件的下方提示安_pycharm安装pygame

SAP ABAP程序性能优化 2-程序员宅基地

文章浏览阅读5.7k次。以下是以前看到的一些关于SAP ABAP程序性能优化的东西 As you all know, it is important to use as many key fields as possible in WHERE clauses of SELECT statements. Sometimes you are not sure about the value of some key field

推荐文章

热门文章

相关标签