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 相关文章推荐
Windows下Python使用Pandas模块操作Excel文件的教程
May 31 Python
numpy使用技巧之数组过滤实例代码
Feb 03 Python
python 实现对文件夹内的文件排序编号
Apr 12 Python
python 上下文管理器及自定义原理解析
Nov 19 Python
python实现简易淘宝购物
Nov 22 Python
Python实现搜索算法的实例代码
Jan 02 Python
使用Tensorflow将自己的数据分割成batch训练实例
Jan 20 Python
Django实现将一个字典传到前端显示出来
Apr 03 Python
Scrapy 配置动态代理IP的实现
Sep 28 Python
python logging模块的使用详解
Oct 23 Python
Python 中Operator模块的使用
Jan 30 Python
忆童年!用Python实现愤怒的小鸟游戏
Jun 07 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网站在线人数统计
2008/04/09 PHP
PHP用函数嵌入网站访问量计数器
2017/10/27 PHP
jquery焦点图片切换(数字标注/手动/自动播放/横向滚动)
2013/01/24 Javascript
Jquery事件的连接使用示例
2013/06/18 Javascript
javascript获取xml节点的最大值(实现代码)
2013/12/11 Javascript
利用JS解决ie6不支持max-width,max-height问题的方法
2014/01/02 Javascript
Node.js中AES加密和其它语言不一致问题解决办法
2014/03/10 Javascript
js贪吃蛇网页版游戏特效代码分享(挑战十关)
2015/08/24 Javascript
深入探讨Vue.js组件和组件通信
2016/09/12 Javascript
vue mixins组件复用的几种方式(小结)
2017/09/06 Javascript
详解基于vue-router的动态权限控制实现方案
2017/09/28 Javascript
解决IE11 vue +webpack 项目中数据更新后页面没有刷新的问题
2018/09/25 Javascript
浅谈webpack devtool里的7种SourceMap模式
2019/01/14 Javascript
浅谈对于“不用setInterval,用setTimeout”的理解
2019/08/28 Javascript
VUE动态生成word的实现
2020/07/26 Javascript
python判断给定的字符串是否是有效日期的方法
2015/05/13 Python
python实现计算倒数的方法
2015/07/11 Python
django连接mysql配置方法总结(推荐)
2018/08/18 Python
python反编译学习之字节码详解
2019/05/19 Python
python单例模式的多种实现方法
2019/07/26 Python
Selenium 滚动页面至元素可见的方法
2020/03/18 Python
Django修改app名称和数据表迁移方案实现
2020/09/17 Python
Python爬虫之Selenium鼠标事件的实现
2020/12/04 Python
美国著名首饰网站:BaubleBar
2016/08/29 全球购物
微软英国官方网站:Microsoft英国
2016/10/15 全球购物
亚洲颇具影响力的男性在线购物零售商:His
2019/11/24 全球购物
台湾全方位线上课程与职能学习平台:TibaMe
2019/12/04 全球购物
化工工艺专业求职信
2013/09/22 职场文书
生产现场工艺工程师岗位职责
2013/11/28 职场文书
承办会议欢迎词
2014/01/17 职场文书
保护动物倡议书
2014/04/15 职场文书
合同权益转让协议书模板
2014/11/18 职场文书
大学生求职自荐信范文
2015/03/04 职场文书
2015年实习生工作总结报告
2015/04/28 职场文书
奖金申请报告模板
2015/05/15 职场文书
Django中celery的使用项目实例
2022/07/07 Python