python验证码识别教程之利用滴水算法分割图片


Posted in Python onJune 05, 2018

滴水算法概述

滴水算法是一种用于分割手写粘连字符的算法,与以往的直线式地分割不同 ,它模拟水滴的滚动,通过水滴的滚动路径来分割字符,可以解决直线切割造成的过分分割问题。

引言

之前提过对于有粘连的字符可以使用滴水算法来解决分割,但智商捉急的我实在是领悟不了这个算法的精髓,幸好有小伙伴已经实现相关代码。

我对上面的代码进行了一些小修改,同时升级为python3的代码。

还是以这张图片为例:

python验证码识别教程之利用滴水算法分割图片

在以前的我们已经知道这种简单的粘连可以通过控制阈值来实现分割,这里我们使用滴水算法。

首先使用之前文章中介绍的垂直投影或者连通域先进行一次切割处理,得到结果如下:

python验证码识别教程之利用滴水算法分割图片

针对于最后粘连情况来使用滴水算法处理:

from itertools import groupby

def binarizing(img,threshold):
 """传入image对象进行灰度、二值处理"""
 img = img.convert("L") # 转灰度
 pixdata = img.load()
 w, h = img.size
 # 遍历所有像素,大于阈值的为黑色
 for y in range(h):
  for x in range(w):
   if pixdata[x, y] < threshold:
    pixdata[x, y] = 0
   else:
    pixdata[x, y] = 255
 return img

def vertical(img):
 """传入二值化后的图片进行垂直投影"""
 pixdata = img.load()
 w,h = img.size
 result = []
 for x in range(w):
  black = 0
  for y in range(h):
   if pixdata[x,y] == 0:
    black += 1
  result.append(black)
 return result

def get_start_x(hist_width):
 """根据图片垂直投影的结果来确定起点
  hist_width中间值 前后取4个值 再这范围内取最小值
 """
 mid = len(hist_width) // 2 # 注意py3 除法和py2不同
 temp = hist_width[mid-4:mid+5]
 return mid - 4 + temp.index(min(temp))

def get_nearby_pix_value(img_pix,x,y,j):
 """获取临近5个点像素数据"""
 if j == 1:
  return 0 if img_pix[x-1,y+1] == 0 else 1
 elif j ==2:
  return 0 if img_pix[x,y+1] == 0 else 1
 elif j ==3:
  return 0 if img_pix[x+1,y+1] == 0 else 1
 elif j ==4:
  return 0 if img_pix[x+1,y] == 0 else 1
 elif j ==5:
  return 0 if img_pix[x-1,y] == 0 else 1
 else:
  raise Exception("get_nearby_pix_value error")


def get_end_route(img,start_x,height):
 """获取滴水路径"""
 left_limit = 0
 right_limit = img.size[0] - 1
 end_route = []
 cur_p = (start_x,0)
 last_p = cur_p
 end_route.append(cur_p)

 while cur_p[1] < (height-1):
  sum_n = 0
  max_w = 0
  next_x = cur_p[0]
  next_y = cur_p[1]
  pix_img = img.load()
  for i in range(1,6):
   cur_w = get_nearby_pix_value(pix_img,cur_p[0],cur_p[1],i) * (6-i)
   sum_n += cur_w
   if max_w < cur_w:
    max_w = cur_w
  if sum_n == 0:
   # 如果全黑则看惯性
   max_w = 4
  if sum_n == 15:
   max_w = 6

  if max_w == 1:
   next_x = cur_p[0] - 1
   next_y = cur_p[1]
  elif max_w == 2:
   next_x = cur_p[0] + 1
   next_y = cur_p[1]
  elif max_w == 3:
   next_x = cur_p[0] + 1
   next_y = cur_p[1] + 1
  elif max_w == 5:
   next_x = cur_p[0] - 1
   next_y = cur_p[1] + 1
  elif max_w == 6:
   next_x = cur_p[0]
   next_y = cur_p[1] + 1
  elif max_w == 4:
   if next_x > cur_p[0]:
    # 向右
    next_x = cur_p[0] + 1
    next_y = cur_p[1] + 1
   if next_x < cur_p[0]:
    next_x = cur_p[0]
    next_y = cur_p[1] + 1
   if sum_n == 0:
    next_x = cur_p[0]
    next_y = cur_p[1] + 1
  else:
   raise Exception("get end route error")

  if last_p[0] == next_x and last_p[1] == next_y:
   if next_x < cur_p[0]:
    max_w = 5
    next_x = cur_p[0] + 1
    next_y = cur_p[1] + 1
   else:
    max_w = 3
    next_x = cur_p[0] - 1
    next_y = cur_p[1] + 1
  last_p = cur_p

  if next_x > right_limit:
   next_x = right_limit
   next_y = cur_p[1] + 1
  if next_x < left_limit:
   next_x = left_limit
   next_y = cur_p[1] + 1
  cur_p = (next_x,next_y)
  end_route.append(cur_p)
 return end_route

def get_split_seq(projection_x):
 split_seq = []
 start_x = 0
 length = 0
 for pos_x, val in enumerate(projection_x):
  if val == 0 and length == 0:
   continue
  elif val == 0 and length != 0:
   split_seq.append([start_x, length])
   length = 0
  elif val == 1:
   if length == 0:
    start_x = pos_x
   length += 1
  else:
   raise Exception('generating split sequence occurs error')
 # 循环结束时如果length不为0,说明还有一部分需要append
 if length != 0:
  split_seq.append([start_x, length])
 return split_seq


def do_split(source_image, starts, filter_ends):
 """
 具体实行切割
 : param starts: 每一行的起始点 tuple of list
 : param ends: 每一行的终止点
 """
 left = starts[0][0]
 top = starts[0][1]
 right = filter_ends[0][0]
 bottom = filter_ends[0][1]
 pixdata = source_image.load()
 for i in range(len(starts)):
  left = min(starts[i][0], left)
  top = min(starts[i][1], top)
  right = max(filter_ends[i][0], right)
  bottom = max(filter_ends[i][1], bottom)
 width = right - left + 1
 height = bottom - top + 1
 image = Image.new('RGB', (width, height), (255,255,255))
 for i in range(height):
  start = starts[i]
  end = filter_ends[i]
  for x in range(start[0], end[0]+1):
   if pixdata[x,start[1]] == 0:
    image.putpixel((x - left, start[1] - top), (0,0,0))
 return image

def drop_fall(img):
 """滴水分割"""
 width,height = img.size
 # 1 二值化
 b_img = binarizing(img,200)
 # 2 垂直投影
 hist_width = vertical(b_img)
 # 3 获取起点
 start_x = get_start_x(hist_width)

 # 4 开始滴水算法
 start_route = []
 for y in range(height):
  start_route.append((0,y))

 end_route = get_end_route(img,start_x,height)
 filter_end_route = [max(list(k)) for _,k in groupby(end_route,lambda x:x[1])] # 注意这里groupby
 img1 = do_split(img,start_route,filter_end_route)
 img1.save('cuts-d-1.png')

 start_route = list(map(lambda x : (x[0]+1,x[1]),filter_end_route)) # python3中map不返回list需要自己转换
 end_route = []
 for y in range(height):
  end_route.append((width-1,y))
 img2 = do_split(img,start_route,end_route)
 img2.save('cuts-d-2.png')

if __name__ == '__main__':
 p = Image.open("cuts-2.png")
 drop_fall(p)

执行后会得到切分后的2个照片:

python验证码识别教程之利用滴水算法分割图片

从这张图片来看,虽然切分成功但是效果比较一般。另外目前的代码只能对2个字符粘连的情况切分,参悟了滴水算法精髓的小伙伴可以试着改成多个字符粘连的情况。

总结

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

Python 相关文章推荐
Python模块学习 datetime介绍
Aug 27 Python
Python使用urllib模块的urlopen超时问题解决方法
Nov 08 Python
Python常用小技巧总结
Jun 01 Python
教你用python3根据关键词爬取百度百科的内容
Aug 18 Python
Python正则替换字符串函数re.sub用法示例
Jan 19 Python
python opencv 图像尺寸变换方法
Apr 02 Python
Python 简单计算要求形状面积的实例
Jan 18 Python
Python图像处理库PIL的ImageFont模块使用介绍
Feb 26 Python
pycharm设置python文件模板信息过程图解
Mar 10 Python
python 常见的排序算法实现汇总
Aug 21 Python
Python常用数字处理基本操作汇总
Sep 10 Python
Python包管理工具pip的15 个使用小技巧
May 17 Python
django反向解析URL和URL命名空间的方法
Jun 05 #Python
python topN 取最大的N个数或最小的N个数方法
Jun 04 #Python
pytorch + visdom 处理简单分类问题的示例
Jun 04 #Python
numpy中以文本的方式存储以及读取数据方法
Jun 04 #Python
浅谈python中np.array的shape( ,)与( ,1)的区别
Jun 04 #Python
Numpy array数据的增、删、改、查实例
Jun 04 #Python
python实现判断一个字符串是否是合法IP地址的示例
Jun 04 #Python
You might like
PHP数据集构建JSON格式及新数组的方法
2012/11/07 PHP
一个完整的PHP类包含的七种语法说明
2015/06/04 PHP
9个比较实用的php代码片段
2016/03/15 PHP
php 生成签名及验证签名详解
2016/10/26 PHP
PHP中字符串长度的截取用法示例
2017/01/12 PHP
ImageFlow可鼠标控制图片滚动
2008/01/30 Javascript
ymPrompt的doHandler方法来实现获取子窗口返回值的方法
2010/06/25 Javascript
node在两个div之间移动,用ztree实现
2013/03/06 Javascript
JS 获取滚动条高度示例代码
2013/10/24 Javascript
js实现两点之间画线的方法
2015/05/12 Javascript
javaScript中slice函数用法实例分析
2015/06/08 Javascript
javascript中Date对象应用之简易日历实现
2016/07/12 Javascript
js实现拖拽功能
2017/03/01 Javascript
深入理解AngularJs-scope的脏检查(一)
2017/06/19 Javascript
Angular通过指令动态添加组件问题
2018/07/09 Javascript
详解Vue组件之间通信的七种方式
2019/04/14 Javascript
[38:41]2014 DOTA2国际邀请赛中国区预选赛 LGD VS CNB
2014/05/22 DOTA
[56:48]FNATIC vs EG 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
Python的函数嵌套的使用方法
2014/01/24 Python
python网络编程之UDP通信实例(含服务器端、客户端、UDP广播例子)
2014/04/25 Python
解决PyCharm中光标变粗的问题
2017/08/05 Python
TensorFlow入门使用 tf.train.Saver()保存模型
2018/04/24 Python
Python实现性能自动化测试竟然如此简单
2019/07/30 Python
python matplotlib工具栏源码探析三之添加、删除自定义工具项的案例详解
2021/02/25 Python
HTML5 History API 实现无刷新跳转
2016/01/11 HTML / CSS
英国街头品牌:Bee Inspired Clothing
2018/02/12 全球购物
护士个人自我鉴定
2014/03/24 职场文书
团结就是力量演讲稿
2014/05/21 职场文书
企业总经理任命书
2014/06/05 职场文书
名人演讲稿范文
2014/09/16 职场文书
毕业证代领委托书
2014/09/26 职场文书
个人总结格式范文
2015/03/09 职场文书
2015新教师教学工作总结
2015/07/22 职场文书
小学一年级班主任工作经验交流材料
2015/11/02 职场文书
2019通用版新员工入职培训方案!
2019/07/11 职场文书
手写实现JS中的new
2021/11/07 Javascript