滴水算法概述
滴水算法是一种用于分割手写粘连字符的算法,与以往的直线式地分割不同 ,它模拟水滴的滚动,通过水滴的滚动路径来分割字符,可以解决直线切割造成的过分分割问题。
引言
之前提过对于有粘连的字符可以使用滴水算法来解决分割,但智商捉急的我实在是领悟不了这个算法的精髓,幸好有小伙伴已经实现相关代码。
我对上面的代码进行了一些小修改,同时升级为python3的代码。
还是以这张图片为例:
在以前的我们已经知道这种简单的粘连可以通过控制阈值来实现分割,这里我们使用滴水算法。
首先使用之前文章中介绍的垂直投影或者连通域先进行一次切割处理,得到结果如下:
针对于最后粘连情况来使用滴水算法处理:
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个照片:
从这张图片来看,虽然切分成功但是效果比较一般。另外目前的代码只能对2个字符粘连的情况切分,参悟了滴水算法精髓的小伙伴可以试着改成多个字符粘连的情况。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。
本文向大家介绍python入门教程之识别验证码,包括了python入门教程之识别验证码的使用技巧和注意事项,需要的朋友参考一下 前言 验证码?我也能破解? 关于验证码的介绍就不多说了,各种各样的验证码在人们生活中时不时就会冒出来,身为学生日常接触最多的就是教务处系统的验证码了,比如如下的验证码: 识别办法 模拟登陆有着复杂的步骤,在这里咱们不管其他操作,只负责根据输入的一张验证码图片返回一个答案字
本文向大家介绍python利用Tesseract识别验证码的方法示例,包括了python利用Tesseract识别验证码的方法示例的使用技巧和注意事项,需要的朋友参考一下 无论是是自动化登录还是爬虫,总绕不开验证码,这次就来谈谈python中光学识别验证码模块tesserocr和pytesseract。tesserocr和pytesseract是Python的一个OCR识别库,但其实是对tesse
本文向大家介绍Python验证码识别的方法,包括了Python验证码识别的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Python验证码识别的方法。分享给大家供大家参考。具体实现方法如下: 希望本文所述对大家的Python程序设计有所帮助。
本文向大家介绍Python网站验证码识别,包括了Python网站验证码识别的使用技巧和注意事项,需要的朋友参考一下 0x00 识别涉及技术 验证码识别涉及很多方面的内容。入手难度大,但是入手后,可拓展性又非常广泛,可玩性极强,成就感也很足。 验证码图像处理 验证码图像识别技术主要是操作图片内的像素点,通过对图片的像素点进行一系列的操作,最后输出验证码图像内的每个字符的文本矩阵。 读取图片 图片降噪
本文向大家介绍详解Python验证码识别,包括了详解Python验证码识别的使用技巧和注意事项,需要的朋友参考一下 以前写过一个刷校内网的人气的工具,Java的(以后再也不行Java程序了),里面用到了验证码识别,那段代码不是我自己写的:-) 校内的验证是完全单色没有任何干挠的验证码,识别起来比较容易,不过从那段代码中可以看到基本的验证码识别方式。这几天在写一个程序的时候需要识别验证码,因为程序是
目标 在本章中, 我们将学习使用分水岭算法实现基于标记的图像分割 我们将看到:cv.watershed() 理论 任何灰度图像都可以看作是一个地形表面,其中高强度表示山峰,低强度表示山谷。你开始用不同颜色的水(标签)填充每个孤立的山谷(局部最小值)。随着水位的上升,根据附近的山峰(坡度),来自不同山谷的水明显会开始合并,颜色也不同。为了避免这种情况,你要在水融合的地方建造屏障。你继续填满水,建造障