目录
前言
需求背景是某网站进行每次进行下单操作时都会进行图片滑块验证码校验,关于图片滑块验证码一般常规的操作是人工手动校验识别,那么有没有办法通过程序去实现自动滑块呢?
自动化即通过机器或程序实现代替人工去重复执行一个工作或动作以完成预期工作,实现自动化需先详细分析可行最短路径和实现自动化过程中可能遇见的异常场景给予补偿方案,本文主要讲解自动识别方案的可行性和详细分析过程。
实战图片相似度对比与图片中如何找到正方形,最终结果图片滑块验证识别率仅为20%左右,
一、图片滑块验证功能分析
1、图片滑块验证功能概述
功能概述为实际业务分析过程,具体指对图片滑块验证码功能使用流程和原理进行分析和推测,发现其中规律后再思考如何自动化的过程。
1、生成验证码,用户请求creatImage接口创建验证码图片和滑块图片,并生成随机x、y坐标
2、获取图片资源,根据用户会话session获取最新的验证图片和滑块,以y坐标显示滑块位置
3、校验滑块图片位置,滑动滑块由前端JavaScript获取最终滑动的x坐标
4、校验验证码,根据步骤3获得x坐标发起校验,若通过继续执行;不通过则从新获取验证码
5、验证码校验通过后,使用校验成功的x坐标进行传参进行下一个接口的使用
2、创建验证码与获取验证码图片接口分析
创建验证码接口请求后,返回主图和滑块图片地址,同时返回y坐标;
3、滑动滑块校验验证码成功接口分析
滑动滑块停止后获得x坐标,并以x坐标向服务端发起校验,下图为校验通过截图。
4、图片滑块验证码x和y坐标的含义
根据上述信息获得yIndex=21(服务端返回),xIndex=74(手动滑动得出),xIndex的数字是滑块刚好到指定灰色区域距离。在图片识别领域xy坐标图如下图所示,74x21为QQ截图显示图片分辨率大小,获取正确的坐标距离应以原图大小显示的数据才是正确数据。
5、验证码校验通过接口传参分析
这里未做具体案例展示,因为不同的业务可能传参不同需要以实际的接口传参为准。
可能存在主要2中方案
1、xIndex参数校验通过后为参数传递凭证
2、xIndex参数校验通过后,返回token密钥为凭证
二、图片滑块自动化识别方案分析与实现
上述一章节已对图片滑块验证码功能实现进行详细分析剖析了其原理,服务端会返回y坐标高度,前端加载验证码图片和滑块图片,手动滑动图片后获得x坐标去请求校验是否滑动正确。接下来思考下图1和2的左侧边距的距离,如何通过程序获得x坐标?
2.1 图片相似度对比方案 图片截取或切图 实现
为了实现图片相似度对比,需要提前将图片切割为对比图片分辨率大小一致更准确。
2.1.1 原图展示
2.1.2 Python代码和功能原理
import cv2
# 存储切图
def saveImg(x,img2):
# 放大图片12倍数进行存储
scaleX = 0.6
scaleY = 0.6
im2 = cv2.resize(img2, None, fx=scaleX * 20, fy=scaleY * 20, interpolation=cv2.INTER_LINEAR)
cv2.imwrite("re/%s.jpg" % str(x), im2)
# 读取图片,并根据xy坐标进行切图
def getHengList(getBigImg, y):
getBigImg = cv2.imread(getBigImg)
for x in range(54):
img2 = getBigImg[y:y+38, x*5:x*5+38] # 切图对比
saveImg(x, img2)
if __name__=="__main__":
y = 100
getBigImg, y = "getBigImg.png", y
getHengList(getBigImg, y)
y坐标,y:y+38代表获取像素高度为38的图片
x坐标,x*5:x*5+38代表获取像素宽度为38的图片,循环向右依次截取图片,其中循环54次为最大获取宽度54*5=270像素
2.1.3 根据y坐标切图结果
切图结果仅用于展示实际应用过程中可不存储切图结果,这里只是展示使用分析原理。
2.1.4 思考根据根据切图结果中找到带有灰色蒙层的图?
查询过现有的图片相似度解决方案,滑块图和切割验证图功能已实现,获得相同大小的2张图再对比图片相似度,需要提前了解其基本实现原理。
2.2 图片相似度对比方案
2.2.1 图片相似度对比 列举方案
图片相似度返回值范围[0,1],返回值越大越相似,这部分代码通过搜索获得
1、像素对比,通过图片像素来比较,比较简单对使用场景有要求
2、余弦方法,把图片表示一个向量,通过计算向量之间的余弦值来表征图片的相似度
3、SSIM(结构相似性指数),基于sklearn中的scikit-image中的ssim来计算的一种全参考性的图像质量评价指标,分别从图像的亮度,对比度,结构三个方面度量图像的相似性
4、直方图,以三通道直方图为例,捕捉颜色信息的相似性,只要颜色分布相似就判断两者相似度较高
2.2.2 图片相似度对比 图片处理方案
原图、灰度化、二值化或其他图片颜色处理,比如尝试原图方案进行图片相似度对比方案未获得预期结果,再尝试灰度话图片相似度对比。对比单张图片与多张图片后收集识别成功率数据,判断获得x坐标的准确率是是否?能找到带完整灰色蒙层的图吗?
2.2.3 图片相似度对比 失败结果展示
Python OpenCV程序运行结果
切图对比结果预览,此处调整了获取图片x间距以获得更大的精度
尝试使用笛卡尔集以下方案
图片处理: 【原图】、【灰度化】、【二值化】
相似度对比:【像素对比】、【余弦方法】、【SSIM】、【直方图】、【或其他方案】
样本数据过多图片过多,一部分图片背景太过相似,尝试过几种方案后发现无法准确找到带有灰色蒙层的图片,因为验证码显示灰色区域图片为随机,还有部分图片背景想过相似,即使是人工也无法快速准确找到目标图,已放弃该图片自动识别方案。
不过这个也可作为替代之一的方案,图片滑块验证人工筛选图片校验通过后,已校验的验证码数据存在时间差,采用提前1分钟或几分钟先校验验证码,带需要使用时使用已校验的验证码卡验证时间差。
2.3 图像识别 找到图片中的正方形
经历过上面的操作失败后,继续思考图片识别的新方案,是否能通过找到图片中的正方形从而识别滑块的位置?
2.3.1 找到图片中的正方形 标记
Python代码
import cv2 as cv
def getImageBox(img):
image = cv.imread(img)
copy = image.copy()
grey = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
decrease_noise = cv.fastNlMeansDenoising(grey, 10, 15, 7, 21)
blurred = cv.GaussianBlur(decrease_noise, (3, 3), 0)
canny = cv.Canny(blurred, 20, 40)
thresh = cv.threshold(canny, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)[1]
contours = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for c in contours:
# obtain the bounding rectangle coordinates for each square
x, y, w, h = cv.boundingRect(c)
# With the bounding rectangle coordinates we draw the green bounding boxes
cv.rectangle(copy, (x, y), (x + w, y + h), (36, 255, 12), 2)
cv.imshow('copy', copy)
cv.waitKey(0)
cv.destroyAllWindows()
if __name__=="__main__":
getImageBox("getBigImg.png")
图片显示结果
2.3.2 找到图片中的正方形 过滤
通过对现有样本数据采样分析的得知能够标记处灰色蒙层区域滑块并能获取到绘制方框的x坐标。下述代码已简化处理流程不显示图片,并设置灰色蒙层方框大小的范围值进行过滤。
Opencv色彩空间:GRAY、XYZ、HSV、YCrCb、HLS
Python代码
import cv2 as cv
import random
def getImageBox(img):
image = cv.imread(img)
copy = image.copy()
# 尝试不同图片处理方案,获取图片中正方形识别效率
# grey = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
grey = cv.cvtColor(image,cv.COLOR_BGR2HSV)
decrease_noise = cv.fastNlMeansDenoising(grey, 10, 15, 7, 21)
blurred = cv.GaussianBlur(decrease_noise, (3, 3), 0)
canny = cv.Canny(blurred, 20, 40)
thresh = cv.threshold(canny, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)[1]
contours = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for c in contours:
# obtain the bounding rectangle coordinates for each square
x, y, w, h = cv.boundingRect(c)
# 预期目标正方形图片大小,实际颜色标记并非边和边完全相等
if 40 < w < 43 and 40 < h < 43:
print(w, h, x,y)
# With the bounding rectangle coordinates we draw the green bounding boxes
cv.rectangle(copy, (x, y), (x + w, y + h), (255, 48, 48), 2)
return x
return random.randint(0,270)
if __name__=="__main__":
getImageBox("getBigImg.png")
GRAY方案 图片识别结果1
GRAY方案 图片识别结果2
HSV方案 图片识别结果3
XYZ方案 图片识别结果4
2.3.3 找到图片中的正方形 总结
通过上述2个章节对图片处理得出方框标记,2.3.1中能够图片中的正方形进行标记但是也有许多其他长方形存在,图片显示结果1和2位GRAY方案识别,图片显示结果3为HSV色彩模型方案识别,阅读代码后2.3.2更改图片处理方案GRAY灰色更改为HSV色彩模型与XYZ色彩模型后,通过降噪和高斯模糊等图片处理,对图片轮廓识别成功准确率能达到100%的标记正确效果。
采样图片100张,识别率最高为XYZ色彩模型,图片识别结果4预览图26张,得出结论单次识别准确率26%。如果图片未被识别则给出随机数x取值返回作为补偿试错提高识别率。
图片处理方案显示对比展示
2.4 图片滑块识别 总结
2.4.1 实现方案总结
1、图片相似度对比,先对图片进行截取再对比,图片相似度对比常用方法验证
2、图片查找正方形,图片处理后进行轮廓查找,尝试不同的处理方式查找轮廓的结果不同
3、识别失败给随机数补偿,这个根据具体业务而定,仅为增加校验成功率
2.4.2 项目实战总结
总体识别率偏低仅为26%,投入到自动化图片滑块验证实际验证平均没1-15秒内尝试1-20次能识别成功,从某种程序上可以替代人工达到早期效率不太高的自动化生成上,能达到替代人工的目的,但是有待继续改善提升识别准确率和识别效率。
2.4.3 实战数据演示
每间隔30秒执行一次图片滑块验证校验,下图是每次识别成功需要的次数和单次消耗时间。