美文网首页
为了破解XX验证码,我所做的尝试

为了破解XX验证码,我所做的尝试

作者: 叮宕 | 来源:发表于2018-08-11 00:03 被阅读63次

首先呢?问题是很简单的,如图,用过的人都知道,可是要让计算机识别出具体的位置就不好整了。


yan1.jpg

这是一部血泪史

自己先硬搞

一,我把图片分成了两部分。

一部分是右上角的字,一部分是底下的图。


第一部分 第二部分

这个实现很简单,所幸每个验证码大小是确定的,直接用python的PIL库直接裁就好了,就算不是确定的,因为色彩很分明,用色彩的分布也好分开。

def firstcuts(img): 
    gray=img.convert('L')
    boxtar=(第一部分的做坐标)
    boxsou=(第二部分的做坐标)
    sou=img.crop(boxsou)
    tar=img.crop(boxtar)
    return tar,sou

二,从简单的开始对第一部分的这几个字进行切分

这个是典型的类似普通文字验证码的切割,我们不用什么水来灌,因为每个字之间没有粘连在一起,所以我用了最暴力的方法:投影

具体说来就是
直接二位的图形投影到一位的X轴上,因为边界分明,选好阈值就能很好的切割

def cuttar(img,threshold=0):   #二值化垂直投影
    tar=img.convert('1')
    data=np.array(tar)
    cond=data==255
    data[cond]=1
    h,w = data.shape
        ver_list = []
    for x in range(w):
            black=0
            for y in range(h):
                    if data[y][x]==0:
                            black=black+1
            ver_list.append(black)

    
    l,r=threshold,threshold
    flag=False
    cuts=[]
    for i ,count in enumerate(ver_list):
            if flag is False and count > threshold:
                    l=i
                    flag=True
            if flag and count==threshold:
                    r=i-1
                    flag=False
            if l!=r and r-l>10:
                        cuts.append((l,r))
    return cuts

这个代码一看就会懂,这是效果:


第一部分文字切分

三,然后开始搞下面这个难搞的图片,我们想让计算机识别出字的位置,就要去掉一些干扰。

因为设计验证码的人很聪明,一般区分图片的方法都是通过图片的色彩空间,具体点说类似与色彩的直方图之类的,我试着画了几个不同的验证码的,发现色彩很平均。

当然聪明反被聪明误,因为色彩分配的很平均,但是要显示字啊,字很靠近黑或者白,这样,我们先将图片转成灰度的,然后通过选取合适的阈值来去掉中间的部分,只保留靠近黑白两边的颜色

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

def fliter(img,fliterhow=3):
    #去除中间大多数的无用值
    img=img.convert('L')
    (w,h)=img.size
    x=w/4
    y=h/4
    img=img.resize((x,y))
    data=np.array(img)

    #计算均值及要过滤掉的范围
    datamean=data.mean()
    datamin=data.min()
    datamax=data.max()
    fliternum=(datamax-datamin)/fliterhow
    cond=(data>datamean-fliternum)&(data<datamean+fliternum)
    data[cond]=-1
    return data

def pltfliter(img,flited):
    #画出过滤效果
        plt.figure("fliter")
        plt.subplot(1,2,1),plt.title('sou')
        plt.imshow(img,cmap='gray')
        plt.subplot(1,2,2),plt.title('flited')
        plt.imshow(data,cmap='gray')
        plt.show()

在上面的函数中通过fliterhow参数来决定我们去掉中间的范围是多少。
效果如下:


过滤掉中间值的第二部分

然后我能想的招是先试试聚类

先试验了kmeans算法
import numpy as np
def loadDataSet(imgarray):
    imgdata=[]
    threshold=5
    [rows, cols] = imgarray.shape
    for i in range(rows - 1):
            for j in range(cols-1):
                if (imgarray[i][j]>(0+threshold))&(imgarray[i][j]<(255-threshold)): 
                imgdata.append([i,j])
                '''
                piexnum=imgarray[i][j]
                if(255-piexnum)>piexnum:
                    imgdata.append([i,j,2])
                else:
                    imgdata.append([i,j,1])         
                '''
    return np.array(imgdata)

def distEclud(vecA,vecB):
    return np.sqrt(sum(np.power(vecA-vecB,2))).all()

def randCent(data,k):
    n=np.shape(data)[1]
    centroids=np.mat(np.zeros((k,n)))
    #构建簇质点
    for j in range(n):
        minJ=np.min(data[:,j])
        rangeJ=np.max(data[:,j]-minJ)
        centroids[:,j]=minJ+rangeJ*np.random.rand(k,1)

    return centroids.astype(int)

def KMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
    datanum=np.shape(dataSet)[0]
    print("一共有%s 行数据"%datanum)
    if datanum==0:
        return None,None
    clusterAssment=np.mat(np.zeros((datanum,2)))
    centroids=createCent(dataSet,k)
    print("init centroid")
    print centroids
    clusterChanged=True
    times=0
    while clusterChanged:
        times=times+1
        print("第%s次迭代"%times)
        if times>1:
            break
        clusterChanged=False
        for itemindex in range(datanum):
            minDist=float("inf")
            minIndex=-1
            for centroidindex in range(k):
                distJI=distMeas(centroids[centroidindex,:],dataSet[itemindex,:])
                
                if distJI<minDist:
                    minDist=distJI
                    minIndex=centroidindex
            if clusterAssment[itemindex,0] !=minIndex:
                clusterChanged=True
                clusterAssment[itemindex,:]=minIndex,minDist**2
        print("once assment is")
        print(clusterAssment)
            
        for cent in range(k):
            thiscentcond=clusterAssment[:,0].A==cent
            thiscentindex=np.nonzero(thiscentcond)[0]
            if len(thiscentindex)==0:
                continue
            else:   
                ptsInClust=dataSet[thiscentindex]
                newcentitem=np.mean(ptsInClust,axis=0)
                centroids[cent,:]=newcentitem
                print("the %s new cent item is"%cent)
                print(newcentitem)
            
    return centroids,clusterAssment

第一个loadDataSet函是将刚刚过滤来的第二部分的图像数组当参数传入,得到kmeans需要的数据集。
第二个distEclud用来测量两个点之间的距离
第三个randCent用来生成k个随机 质心
第四个 KMeans

试了下效果不好,又在kmeans基础上试了二分的kmeans

def biKmeans(dataSet,k,distMeas=distEclud):
    datanum=np.shape(dataSet)[0]
    clusterAssment=np.mat(np.zeros((datanum,2)))
    centroid0=np.mean(dataSet,axis=0).tolist()[0]
    centList=[centroid0]
    for j in range(datanum):
        clusterAssment[j,1]=distMeas(np.mat(centroid0),dataSet[j,:])**2

    while(len(centList)<k):
        lowestSSE=np.inf
        for centindex in range(len(centList)):
            ptsInCurrCluster=dataSet[np.nonzero(clusterAssment[:,0].A==centindex)[0],:]
            centroidMat,splitClustAss=KMeans(ptsInCurrCluster,2,distMeas)
            sseSplit=sum(splitClustAss[:,1])
            sseNotSplit=sum(clusterAssment[np.nonzero(clusterAssment[:,0].A!=centindex)[0],1])
            print("ssesplit and not:")
            print(sseSplit,sseNotSplit)
            if(sseSplit+sseNotSplit)<lowestSSE:
                bestCentToSplit=centindex
                bestNewCents=centroidMat
                bestClustAss=splitClustAss.copy()
                lowestSSE=sseSplit+sseNotSplit

        bestClustAss[np.nonzero(bestClustAss[:,0].A==1)[0],0]=len(centList)
        bestClustAss[np.nonzero(bestClustAss[:,0].A==0)[0],0]=bestCentToSplit
        print 'the bestcenttosplit is ',bestCentToSplit
        print 'the len o fbestClustass is ',len(bestClustAss)

        centList[bestCentToSplit]=bestNewCents[0,:]
        centList.append(bestNewCents[1,:])
        clusterAssment[np.nonzero(clusterAssment[:,0].A==bestCentToSplit)[0],:]=bestClustAss
    return  centList,clusterAssment     

然后就出现了一个棘手的问题,等的时间过长
原因是640x480的图片一共点数有个30多万个点,虽然通过上一部过滤了大部分,可是还有个4万多个点,这迭代几次就要命了,没办法,只能先借助PIL库压缩下图片。

用代码画出来,测试下聚类的效果:

from PIL import Image ,ImageOps
import matplotlib.pyplot as plt
import numpy as np
import cut
import filtration
from cluster import *
def get_cutedcaptcha_sou(img):
    captchalist,sou=cut.get_cuted_code(img)
    return captchalist,sou
img=Image.open('测试图片')
captchalist,sou=get_cutedcaptcha_sou(img)
imgarray=filtration.fliter(sou,3.1)
dataset=loadDataSet(imgarray)
print("shape")
print(np.shape(dataset))
cen,ass=KMeans(dataset,30)
print("cen is")
print(cen)
plt.figure("fliter")
plt.subplot(1,2,1),plt.title('sou')
plt.imshow(sou,cmap='gray')
plt.subplot(1,2,2),plt.title('flited')
plt.imshow(imgarray,cmap='gray')
for item in cen:
    x=item[0,0]
    y=item[0,1]
    plt.scatter(x,y, color='r')

plt.show()

聚类测试

四,我想的当然不是一步到位,想法是先用聚类搞出个十几个可能的位置,然后给个字的宽带,拿这个区域和第一部分被切出来的那几个字做比较,做比较的方法使用k邻近就好

但是我突然想到了,百度有OCR的接口

试试百度的

首先生成access_token

import ssl
import json

def getToken():
    ak="XXX"
    sk="XXX"
# client_id 为官网获取的AK, client_secret 为官网获取的SK
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id='+ak+'&client_secret='+sk
    request = urllib2.Request(host)
    request.add_header('Content-Type', 'application/json; charset=UTF-8')
    response = urllib2.urlopen(request)
    content = response.read()

    if (content):
         token=json.loads(content)
         return token['access_token']

将图片变成base64,然后url编码,然后请求百度的ocr带返回位置的高精度api

import requests
import sys, urllib, urllib2, json
import base64
import StringIO
import baidutoken
from cStringIO import StringIO
def getPost(img):
    url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate'
    access_token={'access_token':baidutoken.getToken()}
    headers = {'Content-Type':'application/x-www-form-urlencoded'}
    r=requests.post(url, headers=headers,params=access_token,data=getImageData(img))
        print(r.text)

def getImageData(img):
    data = {}
    data['language_type'] = "CHN_ENG"
    data['probability']="true"
    data['image'] = image_to_base64(img)
    decoded_data = urllib.urlencode(data)
    return decoded_data

def image_to_base64(img):
    output_buffer = StringIO()
    img.save(output_buffer, format='JPEG')
    binary_data = output_buffer.getvalue()
    base64_data = base64.b64encode(binary_data)
    return base64_data

结果是屁都没识别出来,识别的文字数量是0

吐血,我不想搞了,什么时候搞成再更新这篇文章

相关文章

网友评论

      本文标题:为了破解XX验证码,我所做的尝试

      本文链接:https://www.haomeiwen.com/subject/iehrbftx.html