前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >结合连通块平均分割以及投影矫正的验证码分割算法

结合连通块平均分割以及投影矫正的验证码分割算法

作者头像
mythsman
发布2022-11-14 14:11:53
2490
发布2022-11-14 14:11:53
举报
文章被收录于专栏:mythsman的个人博客

上一节 中记录了基于投影的验证码矫正算法的实现。通过矫正,我们可以比较好的将倾斜的字符归一成较为规整的字符,接下来我们需要对矫正后的字符进行分割。简单的方法大概是投影法了,但是很明显,这样做的可靠性并不够。我们也可以找到整张图的最左端和最右端然后平均分割,但是在字符大小不一样的情况下效果也太好。还有个朴素的方法就是找连通块,但是由于存在字符粘连问题,连通块也不能完全区分字符。那么我这里就结合后两种方法,先进行连通块分割,对于能分割的字符直接进行后续处理,对于不能分割的字符再用平均分割的方法分割处理。实践表明这种方法对于那些干扰线不明显的验证码(比如新浪微博的验证码)的分割效果还是不错的。

代码

代码语言:javascript
复制
import cv2,os,sys,math
import numpy as np
from pylab import *
%matplotlib inline

def getBinary(path):
    '''
    输入:图片路径名称
    输出:黑底二值化的图片

    '''
    im=cv2.imread(path,0)
    thresh,im=cv2.threshold(255-im,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    return im

def shadow(im,angel):
    '''
    输入:灰度图片,投影角度
    输出:投影大小

    '''
    x=[]
    height , width = im.shape
    for i in xrange(height):
        for j in xrange(width):
            if im[i][j]==255:
                x.append(j-1.0*i/math.tan(math.radians(angel)))
    x=np.array(x)
    return x.max()-x.min()

def shadowTest():
    '''
    测试shadow函数
    '''
    directory='weibo'
    pics=os.listdir(directory)
    for pic in pics[:1]:
        im=getBinary(os.path.join(directory,pic))
        print shadow(im,50)
        figure()
        gray()
        imshow(im)

def getAngle(im):
    '''
    输入:灰度图
    输出:最优的投影角度
    '''
    minShadow=500
    ans=90
    for angle in np.linspace(45,135,91):
        thisShadow=shadow(im,angle)
        if minShadow>thisShadow:
            ans=angle
            minShadow=thisShadow
    return ans

def getAngleTest():
    '''
    测试getAngle函数
    '''
    directory='weibo'
    pics=os.listdir(directory)
    for pic in pics[:5]:
        im=getBinary(os.path.join(directory,pic))
        print getAngle(im)
        gray()
        figure()
        imshow(im)

def affine(im,angle=None):
    '''
    输入:灰度图,仿射变换角度(默认为自适应最优角度)
    输出:旋转后的灰度图
    '''
    height,width=im.shape
    if angle==None:
        angle=getAngle(im)
    pts1=np.float32([[width/2,height/2],[width/2,0],[0,height/2]])
    pts2=np.float32([[width/2,height/2],[width/2+height/2/math.tan(math.radians(angle)),0],[0,height/2]])
    M=cv2.getAffineTransform(pts1,pts2)
    dst=cv2.warpAffine(im,M,(width,height))
    return dst

def affineTest():
    '''
    测试affine函数
    '''
    directory='weibo'
    pics=os.listdir(directory)
    for pic in pics[:50]:
        im=getBinary(os.path.join(directory,pic))
        dst=affine(im)
        gray() 
        figure()
        imshow(np.hstack([im,dst]))
        axis('off')

def splitImg(im,num):
    '''
    输入:灰度图,图中包含的字符个数
    输出:分割后的图片数组
    '''
    height,width=im.shape
    maxPos=0
    minPos=width
    for i in xrange(height):
        for j in xrange(width):
            if im[i][j]==255:
                maxPos=max(maxPos,j)
                minPos=min(minPos,j)
    points=[]
    ans=[]
    for i in xrange(num+1):
        points.append(minPos+(maxPos-minPos)/num*i)
    for i in xrange(num):
        left=points[i]
        right=points[i+1]
        p1=np.zeros((height,left))
        p2=np.ones((height,right-left+1))*255
        p3=np.zeros((height,width-right))
        new_im=np.hstack([p1,p2,p3])
        for i in xrange(height):
            for j in xrange(width):
                if im[i][j]==0:
                    new_im[i][j]=0
        ans.append(new_im)
    return ans

def seedSplit(im):
    '''
    输入:灰度图
    输出:从图中高亮提取出的字符图片数组
    '''
    angle=getAngle(im)
    height,width = im.shape
    vis=np.zeros((height,width))
    vis2=np.zeros((height,width))
    def mark(i,j):
        vis2[i][j]=1
        points=[]
        points.append((i,j))
        for dx in [-1,0,1]:
            for dy in [-1,0,1]:
                if i+dx>=0 and i+dx<height and j+dy>=0 and j+dy<width and im[i+dx][j+dy]==255 and vis2[i+dx][j+dy]==0:
                     points.extend(mark(i+dx,j+dy))
        return points

    def delete(i,j):
        vis[i][j]=1
        ans=1
        for dx in [-1,0,1]:
            for dy in [-1,0,1]:
                if i+dx>=0 and i+dx<height and j+dy>=0 and j+dy<width and im[i+dx][j+dy]==255 and vis[i+dx][j+dy]==0:
                    ans+=delete(i+dx,j+dy)
        return ans

    def getImg(points):
        new_im=np.zeros((height,width))
        for point in points:
            i,j=point         
            new_im[i][j]=255
        return new_im,len(points)

    imgs=[]
    for i in xrange(height):
        for j in xrange(width):
            if vis[i][j]==1:
                continue
            if im[i][j]==255:
                num=delete(i,j)   
                if num<=50:
                    continue
                points=mark(i,j)
                imgs.append(getImg(points))
            vis[i][j]=1

    imgs.sort(lambda left,right:cmp(left[1],right[1]))
    new_imgs=[]
    for i,j in imgs:
        new_imgs.append((affine(i,angle),j))
    imgs=new_imgs
    ans=[]
    if len(imgs)>4:
        imgs=imgs[len(imgs)-4:]
        for i in imgs[len(imgs)-4:]:
            ans.append(i[0])
    elif len(imgs)==4:
        for i in imgs:
            ans.append(i[0])
    elif len(imgs)==3:
        ans.append(imgs[0][0])
        ans.append(imgs[1][0])
        ans.extend(splitImg(imgs[2][0],2))
    elif len(imgs)==2:
        rate=imgs[0][1]*1.0/imgs[1][1]
        if rate>0.6:
            ans.extend(splitImg(imgs[0][0],2))
            ans.extend(splitImg(imgs[1][0],2))
        else:
            ans.append(imgs[0][0])
            ans.extend(splitImg(imgs[1][0],3))
    else:
        ans.extend(splitImg(imgs[0][0],4))
    return ans

def seedSplitTest():
    '''
    测试seedSplit函数
    '''
    directory='weibo'
    pics=os.listdir(directory)
    gray()
    for pic in pics[5:15]:
        im=getBinary(os.path.join(directory,pic))
        figure()
        imshow(im)
        imgs=seedSplit(im)   
        for i in imgs:
            figure()
            imshow(i)

def getTrainPic(im):
    '''
    输入:图片
    输出:依次输出图片中包含的字符,并归一化成统一大小
    '''
    def getCenterx(img):
        height,width=im.shape
        points=[]
        for i in xrange(height):
            for j in xrange(width):
                if img[i][j]==255:
                    points.append(j)
        return np.average(points)

    def getResult(im,row=30,column=30):
        height,width=im.shape
        maxX=0
        maxY=0
        minX=99999
        minY=99999
        for i in xrange(height):
            for j in xrange(width):
                if im[i][j]==255:
                    maxX=max(maxX,j)
                    minX=min(minX,j)
                    maxY=max(maxY,i)
                    minY=min(minY,i)
        im=im[minY:maxY+1].T[minX:maxX+1].T
        im=cv2.copyMakeBorder(im,3,3,3,3,cv2.BORDER_CONSTANT)
        im=cv2.resize(im,(30,30))
        return im

    imgs=seedSplit(im)
    for i in xrange(len(imgs)):
        imgs[i]=(imgs[i],getCenterx(imgs[i]))
    imgs.sort(lambda x,y:cmp(x[1],y[1]))
    ans=[]
    for i,j in imgs:
        ans.append(getResult(i))
    return ans

def getTrainPicTest():
    '''
    测试getTrain函数
    '''
    directory='weibo'
    pics=os.listdir(directory)
    gray()
    for pic in pics[:10]:
        im=getBinary(os.path.join(directory,pic))
        imgs=getTrainPic(im)
        res=np.hstack([imgs[0],imgs[1],imgs[2],imgs[3]])
        figure()
        imshow(im)
        axis('off')
        figure()
        imshow(res)
        axis('off')

getTrainPicTest()

效果

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码
  • 效果
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档