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中apply函数的用法实例教程
Jul 31 Python
跟老齐学Python之使用Python查询更新数据库
Nov 25 Python
Python探索之爬取电商售卖信息代码示例
Oct 27 Python
Python pandas DataFrame操作的实现代码
Jun 21 Python
python爬虫添加请求头代码实例
Dec 28 Python
python 中的paramiko模块简介及安装过程
Feb 29 Python
解决Jupyter notebook中.py与.ipynb文件的import问题
Apr 21 Python
Python列表去重复项的N种方法(实例代码)
May 12 Python
keras模型保存为tensorflow的二进制模型方式
May 25 Python
Python中常见的数制转换有哪些
May 27 Python
matplotlib设置颜色、标记、线条,让你的图像更加丰富(推荐)
Sep 25 Python
Python中使用Lambda函数的5种用法
Apr 01 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
分页显示Oracle数据库记录的类之一
2006/10/09 PHP
PHP实现查询两个数组中不同元素的方法
2016/02/23 PHP
详解php实现页面静态化原理
2017/06/21 PHP
利用Homestead快速运行一个Laravel项目的方法详解
2017/11/14 PHP
laravel7学习之无限级分类的最新实现方法
2020/09/30 PHP
BOOM vs RR BO3 第一场2.13
2021/03/10 DOTA
JQuery中form验证出错信息的查看方法
2013/10/08 Javascript
详细分析单线程JS执行问题
2017/11/22 Javascript
jQuery EasyUI window窗口使用实例代码
2017/12/25 jQuery
使用jquery DataTable和ajax向页面显示数据列表的方法
2018/08/09 jQuery
微信小程序自定义轮播图
2018/11/04 Javascript
JavaScript定时器常见用法实例分析
2019/11/15 Javascript
webpack 动态批量加载文件的实现方法
2020/03/19 Javascript
Vue父子之间值传递的实例教程
2020/07/02 Javascript
js实现特别简单的钟表效果
2020/09/14 Javascript
如何利用nodejs自动定时发送邮件提醒(超实用)
2020/12/01 NodeJs
布同自制Python函数帮助查询小工具
2011/03/13 Python
详解Python 数据库 (sqlite3)应用
2016/12/07 Python
用python做一个搜索引擎(Pylucene)的实例代码
2017/07/05 Python
Django 根据数据模型models创建数据表的实例
2018/05/27 Python
Sanic框架异常处理与中间件操作实例分析
2018/07/16 Python
使用python 打开文件并做匹配处理的实例
2019/01/02 Python
python 根据时间来生成唯一的字符串方法
2019/01/14 Python
Python对wav文件的重采样实例
2020/02/25 Python
Python Switch Case三种实现方法代码实例
2020/06/18 Python
详解Open Folder as PyCharm Project怎么添加的方法
2020/12/29 Python
一款纯css3制作的2015年元旦雪人动画特效教程
2014/12/29 HTML / CSS
Pat McGrath Labs官网:世界上最有影响力的化妆师推出的彩妆品牌
2018/01/07 全球购物
初中女生自我鉴定
2013/12/19 职场文书
网上商城创业计划书范文
2014/01/31 职场文书
企业军训感想
2014/02/07 职场文书
2015年爱国卫生月活动总结
2015/03/26 职场文书
2016春季幼儿园开学寄语
2015/12/03 职场文书
团干部培训班心得体会
2016/01/06 职场文书
餐厅如何利用“营销策略”扭转亏本局面
2019/10/15 职场文书
详解在SQLPlus中实现上下键翻查历史命令的功能
2022/03/18 SQL Server