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的Django框架的视图中使用Session的方法
Jul 23 Python
python中利用Future对象回调别的函数示例代码
Sep 07 Python
Python爬虫实现全国失信被执行人名单查询功能示例
May 03 Python
python实现名片管理系统
Nov 29 Python
python执行CMD指令,并获取返回的方法
Dec 19 Python
python 调用钉钉机器人的方法
Feb 20 Python
Python循环中else,break和continue的用法实例详解
Jul 11 Python
Java byte数组操纵方式代码实例解析
Jul 22 Python
Python实现文件压缩和解压的示例代码
Aug 12 Python
Python计算矩阵的和积的实例详解
Sep 10 Python
anaconda安装pytorch1.7.1和torchvision0.8.2的方法(亲测可用)
Feb 01 Python
python中的None与NULL用法说明
May 25 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中$_REQUEST、$_POST、$_GET的区别和联系小结
2011/11/23 PHP
PHP中数字检测is_numeric与ctype_digit的区别介绍
2012/10/04 PHP
解析CI即CodeIgniter框架在Nginx下的重写规则
2013/06/03 PHP
php计算两个文件相对路径的方法
2015/03/14 PHP
ThinkPHP简单使用memcache缓存的方法
2016/11/15 PHP
PHP四种排序算法实现及效率分析【冒泡排序,插入排序,选择排序和快速排序】
2018/04/27 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
JS实现QQ图片一闪一闪的效果小例子
2013/07/31 Javascript
jQuery中:image选择器用法实例
2015/01/03 Javascript
浅谈javascript实现八大排序
2015/04/27 Javascript
JavaScript中innerHTML,innerText,outerHTML的用法及区别
2015/09/01 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
jQuery实现定位滚动条位置
2016/08/05 Javascript
jQuery实现div跟随鼠标移动
2020/08/20 jQuery
JS设计模式之数据访问对象模式的实例讲解
2017/09/30 Javascript
浅谈Vue2.0父子组件间事件派发机制
2018/01/08 Javascript
vue-cli下的vuex的简单Demo图解(实现加1减1操作)
2018/02/26 Javascript
vue中使用input[type="file"]实现文件上传功能
2018/09/10 Javascript
nodejs和react实现即时通讯简易聊天室功能
2019/08/21 NodeJs
Vuex的实战使用详解
2019/10/31 Javascript
[51:44]2018DOTA2亚洲邀请赛 4.3 突围赛 Optic vs iG 第二场
2018/04/04 DOTA
python线程锁(thread)学习示例
2013/12/04 Python
Python的Flask站点中集成xhEditor文本编辑器的教程
2016/06/13 Python
python模块之paramiko实例代码
2018/01/31 Python
python+selenium定时爬取丁香园的新型冠状病毒数据并制作出类似的地图(部署到云服务器)
2020/02/09 Python
Python unittest生成测试报告过程解析
2020/09/08 Python
详解win10下pytorch-gpu安装以及CUDA详细安装过程
2021/01/28 Python
凯特王妃父母建立的派对用品网站:Party Pieces
2017/05/28 全球购物
美国职棒大联盟官方网上商店:MLBShop.com
2017/11/12 全球购物
经贸专业毕业生求职信范文
2014/05/01 职场文书
大学新闻系求职信
2014/06/03 职场文书
天地会口号
2014/06/17 职场文书
简单租房协议书(范本)
2014/10/13 职场文书
离婚案件答辩状
2015/05/22 职场文书
nginx搭建图片服务器的过程详解(root和alias的区别)
2021/03/31 Servers
我收到了德劲DE1107
2022/04/05 无线电