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中类型检查的详细介绍
Feb 13 Python
django rest framework之请求与响应(详解)
Nov 06 Python
解决PyCharm控制台输出乱码的问题
Jan 16 Python
selenium+python截图不成功的解决方法
Jan 30 Python
python实现在函数中修改变量值的方法
Jul 16 Python
python Gunicorn服务器使用方法详解
Jul 22 Python
Tensorflow矩阵运算实例(矩阵相乘,点乘,行/列累加)
Feb 05 Python
python实现简单飞行棋
Feb 06 Python
Python flask框架实现浏览器点击自定义跳转页面
Jun 04 Python
浅析python中的del用法
Sep 02 Python
python画图时设置分辨率和画布大小的实现(plt.figure())
Jan 08 Python
python自动化测试之Selenium详解
Mar 13 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中convert_uuencode()与convert_uuencode函数用法实例
2014/11/22 PHP
php+ajax实现文章自动保存的方法
2014/12/30 PHP
PHP实现mysqli批量执行多条语句的方法示例
2017/07/22 PHP
ThinkPHP5.1框架数据库链接和增删改查操作示例
2019/08/03 PHP
半角全角相互转换的js函数
2009/10/16 Javascript
javascript 多浏览器 事件大全
2010/03/23 Javascript
js 幻灯片的实现
2011/12/06 Javascript
JavaScript生成GUID的多种算法小结
2013/08/18 Javascript
JS匀速运动演示示例代码
2013/11/26 Javascript
js jquery分别实现动态的文件上传操作按钮的添加和删除
2014/01/13 Javascript
Javascript中Array.prototype.map()详解
2014/10/22 Javascript
详解Javascript动态操作CSS
2014/12/08 Javascript
jquery判断至少有一个checkbox被选中的方法
2015/06/05 Javascript
js行号显示的文本框实现效果(兼容多种浏览器 )
2015/10/23 Javascript
jQuery+PHP+MySQL实现无限级联下拉框效果
2016/02/19 Javascript
IOS中safari下的select下拉菜单文字过长不换行的解决方法
2016/09/26 Javascript
微信小程序promsie.all和promise顺序执行
2017/10/27 Javascript
js实现微信/QQ直接跳转到支付宝APP打开口令领红包功能
2018/01/09 Javascript
实例详解Node.js 函数
2018/06/10 Javascript
vue 2.8.2版本配置刚进入时候的默认页面方法
2018/09/21 Javascript
Vue核心概念Action的总结
2019/01/18 Javascript
通过图带你深入了解vue的响应式原理
2019/06/21 Javascript
webpack4 optimization使用总结
2019/11/10 Javascript
uni-app从安装到卸载的入门教程
2020/05/15 Javascript
python让图片按照exif信息里的创建时间进行排序的方法
2015/03/16 Python
用Python编写web API的教程
2015/04/30 Python
Python时间的精准正则匹配方法分析
2017/08/17 Python
Linux下python3.6.1环境配置教程
2018/09/26 Python
Python程序暂停的正常处理方法
2019/11/07 Python
基于python实现雪花算法过程详解
2019/11/16 Python
利用python实现AR教程
2019/11/20 Python
Python3.7将普通图片(png)转换为SVG图片格式(网站logo图标)动起来
2020/04/21 Python
移动端rem布局的两种实现方法
2018/01/03 HTML / CSS
Sofmap官网:日本著名的数码电器专卖店
2017/05/19 全球购物
I.T中国官网:精选时尚设计师单品网购平台
2018/03/26 全球购物
电子商务专员岗位职责
2013/12/11 职场文书