python验证码识别教程之利用投影法、连通域法分割图片


Posted in Python onJune 04, 2018

前言

今天这篇文章主要记录一下如何切分验证码,用到的主要库就是Pillow和Linux下的图像处理工具GIMP。首先假设一个固定位置和宽度、无粘连、无干扰的例子学习一下如何使用Pillow来切割图片。

使用GIMP打开图片后,按 加号 放大图片,然后点击View->Show Grid来显示网格线:

python验证码识别教程之利用投影法、连通域法分割图片

其中,每个正方形边长为10像素,所以数字1切割坐标为左20、上20、右40、下70。以此类推可以知道剩下3个数字的切割位置。

代码如下:

from PIL import Image
p = Image.open("1.png")
# 注意位置顺序为左、上、右、下
cuts = [(20,20,40,70),(60,20,90,70),(100,10,130,60),(140,20,170,50)]
for i,n in enumerate(cuts,1):
 temp = p.crop(n) # 调用crop函数进行切割
 temp.save("cut%s.png" % i)

切割后得到4张图片:

python验证码识别教程之利用投影法、连通域法分割图片

那么,如果字符位置不固定怎么办呢?现在假设一种随机位置宽度、无粘连、无干扰线的情况。

第一种方法,也是最简单的方法叫做”投影法”。原理就是将二值化后的图片在竖直方向进行投影,根据投影后的极值来判断分割边界。这里我依然使用上面的验证码图片来进行演示:

def vertical(img):
 """传入二值化后的图片进行垂直投影"""
 pixdata = img.load()
 w,h = img.size
 ver_list = []
 # 开始投影
 for x in range(w):
 black = 0
 for y in range(h):
  if pixdata[x,y] == 0:
  black += 1
 ver_list.append(black)
 # 判断边界
 l,r = 0,0
 flag = False
 cuts = []
 for i,count in enumerate(ver_list):
 # 阈值这里为0
 if flag is False and count > 0:
  l = i
  flag = True
 if flag and count == 0:
  r = i-1
  flag = False
  cuts.append((l,r))
 return cuts

p = Image.open('1.png')
b_img = binarizing(p,200)
v = vertical(b_img)

通过vertical函数我们就得到了一个包含所有黑色像素在X轴上投影后左右边界的位置。由于验证码没有任何干扰,所以我的阈值设定为0。 关于binarizing函数可以参考上一篇文章

输出如下:

[(21, 37), (62, 89), (100, 122), (146, 164)]

可以看到,投影法给出左右边界和我们手工查看得到很接近。对于上下边界,偷懒的可以直接使用0和图片的高度,也可以在水平方向进行投影,这里有兴趣的小伙伴可以自己尝试。

但是,对于字符间有粘连的情况,投影法就会出现拆分错误,比如上篇文章中的:

python验证码识别教程之利用投影法、连通域法分割图片

修改阈值为5后,投影法给出的左右边界是:

[(5, 27), (33, 53), (59, 108)]

明显最后的6和9数字没有切割。

修改阈值为7,结果则是:

[(5, 27), (33, 53), (60, 79), (83, 108)]

所以对于简单粘连的情况,调整阈值也是可以解决的。

第二种方法,叫做CFS连通域分割法。原理就是假定每个字符都由一个单独的连通域组成,换言之就是无粘连,找到一个黑色像素并开始判断,直到所有相连的黑色像素都被遍历标记过后即可判断出这个字符的分割位置。算法如下:

  • 将二值化后的图片进行从左到右、从上到下的遍历,如果遇到黑色像素并且这个像素没有没访问过,就将这个像素入栈并标记为已经访问。
  • 如果栈不为空,则继续探测周围8个像素,并执行第2步;如果栈空,则代表探测完了一个字符块。
  • 探测结束,这样就确定了若干字符。

代码如下:

import queue

def cfs(img):
 """传入二值化后的图片进行连通域分割"""
 pixdata = img.load()
 w,h = img.size
 visited = set()
 q = queue.Queue()
 offset = [(-1,-1),(0,-1),(1,-1),(-1,0),(1,0),(-1,1),(0,1),(1,1)]
 cuts = []
 for x in range(w):
  for y in range(h):
   x_axis = []
   #y_axis = []
   if pixdata[x,y] == 0 and (x,y) not in visited:
    q.put((x,y))
    visited.add((x,y))
   while not q.empty():
    x_p,y_p = q.get()
    for x_offset,y_offset in offset:
     x_c,y_c = x_p+x_offset,y_p+y_offset
     if (x_c,y_c) in visited:
      continue
     visited.add((x_c,y_c))
     try:
      if pixdata[x_c,y_c] == 0:
       q.put((x_c,y_c))
       x_axis.append(x_c)
       #y_axis.append(y_c)
     except:
      pass
   if x_axis:
    min_x,max_x = min(x_axis),max(x_axis)
    if max_x - min_x > 3:
     # 宽度小于3的认为是噪点,根据需要修改
     cuts.append((min_x,max_x))
 return cuts

调用后输出结果和使用投影法是一样的。另外我看网上还有一种叫做“泛洪填充(Flood Fill)”的方法,似乎和连通域是一样的。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python实现的数据结构与算法之基本搜索详解
Apr 22 Python
简单介绍Python中用于求最小值的min()方法
May 15 Python
实例讲解Python中函数的调用与定义
Mar 14 Python
最大K个数问题的Python版解法总结
Jun 16 Python
pandas.DataFrame选取/排除特定行的方法
Jul 03 Python
Pycharm无法显示动态图片的解决方法
Oct 28 Python
Python实现多进程的四种方式
Feb 22 Python
Python安装selenium包详细过程
Jul 23 Python
python tkinter库实现气泡屏保和锁屏
Jul 29 Python
Python 3 判断2个字典相同
Aug 06 Python
kafka-python 获取topic lag值方式
Dec 23 Python
Django model.py表单设置默认值允许为空的操作
May 19 Python
python验证码识别教程之灰度处理、二值化、降噪与tesserocr识别
Jun 04 #Python
实用自动化运维Python脚本分享
Jun 04 #Python
python中验证码连通域分割的方法详解
Jun 04 #Python
python 匹配url中是否存在IP地址的方法
Jun 04 #Python
Python实现ping指定IP的示例
Jun 04 #Python
用Python3创建httpServer的简单方法
Jun 04 #Python
Python3之简单搭建自带服务器的实例讲解
Jun 04 #Python
You might like
基于PHP遍历数组的方法汇总分析
2013/06/08 PHP
php实现多城市切换特效
2015/08/09 PHP
php preg_match的匹配不同国家语言实例
2016/12/29 PHP
PHP实现图片的等比缩放和Logo水印功能示例
2017/05/04 PHP
php使用redis的有序集合zset实现延迟队列应用示例
2020/02/20 PHP
通过jquery实现tab标签浏览效果
2007/02/20 Javascript
javascript 从if else 到 switch case 再到抽象
2010/07/17 Javascript
jQuery的实现原理的模拟代码 -5 Ajax
2010/08/07 Javascript
javascript 基础篇3 类,回调函数,内置对象,事件处理
2012/03/14 Javascript
简单js代码实现selece二级联动(推荐)
2014/02/18 Javascript
JavaScript中的toDateString()方法使用详解
2015/06/12 Javascript
AngularJS控制器controller正确的通信的方法
2016/01/25 Javascript
浅谈在js传递参数中含加号(+)的处理方式
2016/10/11 Javascript
使用JS读取XML文件的方法
2016/11/25 Javascript
js实现控制文件拖拽并获取拖拽内容功能
2018/02/17 Javascript
angularjs使用gulp-uglify压缩后执行报错的解决方法
2018/03/07 Javascript
浅谈Node.js 沙箱环境
2018/05/15 Javascript
深入理解Promise.all
2018/08/08 Javascript
简述vue状态管理模式之vuex
2018/08/29 Javascript
Python迭代用法实例教程
2014/09/08 Python
详解从Django Rest Framework响应中删除空字段
2019/01/11 Python
pytorch 在sequential中使用view来reshape的例子
2019/08/20 Python
500行代码使用python写个微信小游戏飞机大战游戏
2019/10/16 Python
Python3如何在Windows和Linux上打包
2020/02/25 Python
Python如何读取、写入CSV数据
2020/07/28 Python
巧用CSS3的calc()宽度计算做响应模式布局的方法
2018/03/22 HTML / CSS
美国存储和组织商店:The Container Store
2017/08/16 全球购物
Blank NYC官网:夹克、牛仔裤等
2020/12/16 全球购物
大课间体育活动方案
2014/03/12 职场文书
会计试用期自我评价
2014/09/19 职场文书
2014年司法所工作总结
2014/11/22 职场文书
2014年测量员工作总结
2014/12/12 职场文书
高三英语教学计划
2015/01/23 职场文书
2015年上半年信访工作总结
2015/03/30 职场文书
python实现会员信息管理系统(List)
2022/03/18 Python
golang定时器
2022/04/14 Golang