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 27 Python
Python3之文件读写操作的实例讲解
Jan 23 Python
基于Django用户认证系统详解
Feb 21 Python
pycharm使用matplotlib.pyplot不显示图形的解决方法
Oct 28 Python
python简单验证码识别的实现方法
May 10 Python
python中open函数的基本用法示例
Sep 07 Python
Python函数的返回值、匿名函数lambda、filter函数、map函数、reduce函数用法实例分析
Dec 26 Python
Python urllib.request对象案例解析
May 11 Python
python小程序之4名牌手洗牌发牌问题解析
May 15 Python
Django自带用户认证系统使用方法解析
Nov 12 Python
python3实现无权最短路径的方法
May 12 Python
利用 Python 的 Pandas和 NumPy 库来清理数据
Apr 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
浅析is_writable的php实现
2013/06/18 PHP
PHP实现简单爬虫的方法
2015/07/29 PHP
thinkPHP实现MemCache分布式缓存功能
2016/03/23 PHP
php+html5+ajax实现上传图片的方法
2016/05/14 PHP
PHP Pipeline 实现中间件的示例代码
2020/04/26 PHP
json简单介绍
2008/06/10 Javascript
验证javascript中Object和Function的关系的三段简单代码
2010/06/27 Javascript
jQuery实现的图片分组切换焦点图插件
2015/01/06 Javascript
javascript模拟map输出与去除重复项的方法
2015/02/09 Javascript
jQuery自适应轮播图插件Swiper用法示例
2016/08/24 Javascript
使用Ajax生成的Excel文件并下载的实例
2016/11/21 Javascript
Node连接mysql数据库方法介绍
2017/02/07 Javascript
详谈angularjs中路由页面强制更新的问题
2017/04/24 Javascript
详解JavaScript按概率随机生成事件
2017/08/02 Javascript
Vue 滚动行为的具体使用方法
2017/09/13 Javascript
微信小程序实现换肤功能
2018/03/14 Javascript
vue实现购物车的小练习
2020/12/21 Vue.js
[03:03]DOTA2 2017国际邀请赛开幕战队入场仪式
2017/08/09 DOTA
Python2.7环境Flask框架安装简明教程【已测试】
2018/07/13 Python
解决Python3 被PHP程序调用执行返回乱码的问题
2019/02/16 Python
Python开发网站目录扫描器的实现
2019/02/21 Python
python mysql 字段与关键字冲突的解决方式
2020/03/02 Python
简单了解python shutil模块原理及使用方法
2020/04/28 Python
利用Bootstrap实现漂亮简洁的CSS3价格表实例源码
2017/03/02 HTML / CSS
AE美国鹰日本官方网站: American Eagle Outfitters
2016/12/10 全球购物
英国在线药房:Express Chemist
2019/03/28 全球购物
医大实习自我鉴定
2013/12/07 职场文书
挖掘机司机岗位职责
2014/02/12 职场文书
机电一体化应届生求职信
2014/08/09 职场文书
毕业生爱心捐书倡议书
2015/04/27 职场文书
农业项目合作意向书
2015/05/08 职场文书
乱世佳人观后感
2015/06/08 职场文书
法人身份证明书
2015/06/18 职场文书
青年文明号创建口号大全
2015/12/25 职场文书
python 中[0]*2与0*2的区别说明
2021/05/10 Python
java版 简单三子棋游戏
2022/05/04 Java/Android