前言
通过上一篇的文章大家已经对图片滑块验证码已经有了初步的了解,图片滑块验证码的核心关键在于图片识别接下来接入讲解。因为初版滑块图片识别虽然能识别验证码,通过一些策略调整也相对提高了一些图片识别率,总的来说26%的识别率还是低。本文主要讲解通用滑块图片解决方案,通过Python OpenCV 图片滑块验证码 滑块验证码 自动识别方案 模板比对识别 识别成功率达85%+。
一、图片滑块验证功能分析
1、图片滑块验证功能概述
创建验证码接口请求后,返回主图和滑块图片地址
2、Web前端 显示 滑块和验证码背景图
3、滑块验证码 验证接口 传参
滑动滑块停止后获得x坐标,并以x坐标向服务端发起校验,下图为校验通过截图。
PS:其他内容不在累述,详细内容请看第一篇文章,接下来上上关键核心点。
二、图片滑块验证码 自动化识别方案 模板比对识别 分析与实现
2.1、OpenCV 模板匹配
2.1.1 OpenCV 模板匹配 简述
模板匹配就是在给定的图片中,查找和模板最相似的区域,算法的输入包括模板和图片,通过不断移动模板图片,计算其与图片对应区域匹配度,将匹配度最高区域选择为最终结果,详细内容请阅读下一篇博客。
2.1.2 OpenCV 模板匹配 实现
result=cv.matchTemplate( img,template,method)
参数:
1 > template :模板
2 > method: 实现模板匹配的算法,主要有:
1.平方差匹配(CV_TM_SQDIFF):利用模板与图像之间的平方差进行匹配,最好的匹配是0,匹配越差,匹配的值越大。
2.相关匹配(CV_TM_CCORR):利用模板与图像间的乘法进行匹配,数值越大表示匹配程度较高,越小表示匹配效果差。
3.利用相关系数匹配(CV_TM_CCOEFF):利用模板与图像间的相关系数匹配,1表示完美的匹配,-1表示最差的匹配。
注意:完成匹配后使用cv.minMaxLoc()方法查找最大值所在的位置即可,如果使用平方差作为比较方法,则最小值位置是最佳匹配位置。
2.2、OpenCV 模板匹配 示例代码
2.2.1 OpenCV 模板匹配 示例代码
import cv2
def getImageXindex(getBigImg,getSmallImg):
# 获取验证码背景图
bj_rgb = cv2.imread("./data/getBigImg.png")
# 背景图片灰度处理
bj_gray = cv2.cvtColor(bj_rgb,cv2.COLOR_BGR2GRAY)
# 滑块验证码图片
hk_rgb = cv2.imread("./data/getSmallImg.png",0) # 以灰度模式加载图片
# 模板匹配 TM_CCOEFF_NORMED 归一化相关匹配法
res = cv2.matchTemplate(bj_gray,hk_rgb,cv2.TM_CCOEFF_NORMED)
# 获取模板匹配值
lo = cv2.minMaxLoc(res)
print(lo)
print(lo[3][0])
if __name__ == "__main__":
# 滑块验证码 背景图
getBigImg = "./data/getBigImg1.png"
# 滑块验证码 滑块图
getSmallImg = "./data/getSmallImg1.png"
getImageXindex()
2.2.2 OpenCV 模板匹配 示例结果解析
> ython test.py
(-0.5933699011802673, 0.9847355484962463, (238, 143), (168, 110))
168
三、图片滑块验证码 自动化识别方案 模板比对识别 测试
3.1、滑块图片验证 自动识别率 部分测试代码示例
#!/usr/bin/env python
# -*- coding=utf-8 -*-
__author__ = 'Benjamin'
__CreateAt__ = '2022/8/2-14:06'
import threading
import requests
import random
import time
import test
import datetime
# 格式化Header头
def CheckStr(Header):
Headers = {}
for i in Header.split("\n"):
if i != None or i != "" or i != '':
data = i.split("\n")
if data[0] != None or data[0] != '' or data[0] != '\n':
twodata = data[0].split(": ")
try:
Headers[twodata[0]] = twodata[1]
except:
pass
return Headers
GetHeaderP = CheckStr(open("headersMain", "r+").read())
# 多线程启动 下载 验证码背景图 和 滑块验证图片
def downloadImgThread():
thread_list = []
imgUrl = ["xxxx/imageCode/getBigImg","xxxxx/imageCode/getSmallImg"]
imgName = ['getBigImg.png','getSmallImg.png']
for iter in range(len(imgUrl)):
thread_list.append(threading.Thread(target=downloadImg, args=(imgUrl[iter],imgName[iter])))
for task in thread_list:
task.start()
task.join()
def downloadImg(imgUrl,imgName):
url = "{}?0.{}".format(imgUrl,random.randint(1040385533093276,99210935326926565))
getBigImg = requests.get(url=url, headers=GetHeaderP).content
with open(imgName, 'wb') as f:
f.write(getBigImg)
def RefreshImage():
url = "xxx/imageCode/createImage"
requests.post(url=url, headers=GetHeaderP).text
downloadImgThread()
def setCode(getBigImg,getSmallImg):
start = time.time()
RefreshImage()
x = test.getImageXindex(getBigImg,getSmallImg)
rsp = checkCode(x)
if "success" in rsp:
end = time.time() - start
print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),"第{}次校验成功,x坐标为:{},校验结果为:{},校验耗时:{}秒".format(str(i+1),str(x),str(rsp),str(round(end,2))))
return True
elif "fail" in rsp:
end = time.time() - start
print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),"第{}次校验失败,x坐标为:{},校验结果为:{},校验耗时:{}秒".format(str(i + 1), str(x), str(rsp), str(round(end, 2))))
return False
return False
def checkCode(xIndex):
url = "/imageCode/checkCode"
data = {"xIndex": xIndex}
rsp = requests.post(url=url, headers=GetHeaderP, data=data).text
return rsp
if __name__ == "__main__":
# 滑块验证码 背景图
getBigImg = "getBigImg.png"
# 滑块验证码 滑块图
getSmallImg = "getSmallImg.png"
sucessList = []
errorList = []
forLen = 10
for i in range(forLen):
result = setCode(getBigImg,getSmallImg)
if result:
sucessList.append(result)
else:
errorList.append(result)
print("len(sucessList):{},len(errorList):{},result:{}%".format(len(sucessList), len(errorList), round(len(sucessList)/forLen,2)*100))
3.2、OpenCV 图片模板匹配 测试结果展示
由于测试样本和次数单次采样结果不作为最终的评判标准,可能存在不匹配的情况。