本文主要使用python + selenium来破解旋转突破的验证码;其中用到numpy和OpenCV(CV2)来进行图片拼接,转换,遮罩,识别等
getRotationMatrix2D比较简单,参数为:旋转圆心坐标(x,y), 旋转角度, 放缩比例
matchTemplate使用各种不同的方法来匹配图片,具体可以参考资料:
result = cv2.matchTemplate( img_all, img, cv2.TM_CCOEFF_NORMED)
min_max_loc = cv2.minMaxLoc(result)
其中matchTemplate的最后一个参数为匹配方法,具体说明见:https://docs.opencv.org/3.3.0/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d
min_max_loc返回(匹配度和匹配位置),但是匹配度与使用的最后一个参数相关,有的匹配方法,匹配度越大越好,有的相反。在此我们使用了TM_CCOEFF_NORMED,并取min_max_loc中的maxVal,越大(接近1)越相似。
核心代码如下:
def is_existed(img_all, img, semilar=0.9):
'''
在img_all中寻找img,当相似度到达semilar时停止
参数:
img_all:
img:
semilar:相似度阀值
返回: (maxValue, maxLoc, Angle)
maxValue: 相似度- 0:不相识,1:全相似
maxLoc:最相似的位置(x,y)
Angle:转换到最相似的旋转角度
'''
if semilar <0 or semilar >1:
semilar = 0.9
max_val = 0 # 相似度
# 取决于matchTemplate的最后一个参数,对于多数方法,越大越好;对于有2个方法,越小越好)
angle = 0 # 旋转角度
max_loc = (0,0)
h, w = img.shape[:2]
for i in range(0, 360, 1):
matRotate = cv2.getRotationMatrix2D((h*0.5, w*0.5), i, 1)
dst = cv2.warpAffine(img, matRotate, (h, w))
# if i%30==0:
# show(dst)
result = cv2.matchTemplate(
img_all, dst, cv2.TM_CCOEFF_NORMED)
min_max_loc = cv2.minMaxLoc(result) # 匹配程度最大的左上角位置 (x,y)
if min_max_loc[1] > max_val:
max_val = min_max_loc[1]
max_loc =min_max_loc[3]
angle = i
# print(f'匹配度 = {max_val}, 旋转角度 = {i}')
if max_val >= semilar: # 很高的匹配度,认为已经ok了
break
return (max_val, max_loc, angle)
上述代码比较粗糙,直接360度按步长为1度旋转,比较好的方法从粗到细,分段比较,速度快得多
比如先按步长10,分为0,10,20,…350度,比较最匹配的角度,然后以最匹配的角度的前后10度,以1度的步长搜索,这样两步共56次匹配就完成了(第一次匹配执行36次,第二次匹配执行20次),而原来需要执行360次匹配,新方法速度快了6倍。
如果需要更精细的匹配,比如精细到0.1度,那么原来需要执行3600次(360/0.1=3600),现在只要执行36+20+20=76次,新方法执行速度比原来快50倍
step = 10
start = 0
stop = 360
while True:
if step< 1:
break
max_val =0
while start < stop:
do_something() # 旋转start度, 并匹配,然后与max_val比较,如果更匹配,保存新的匹配度,匹配位置max_loc,旋转角度angle
start +=step
start = angle - step + step*0.1
stop = angle + step
step = step *0.1
上述代码没有判断图片大小,当图片很大的时候,每次的匹配速度很慢,此时应该金字塔下采样cv2.pyrDown对img和image_all进行同步缩小,然后再进行matchTemplate,这样速度会快的多。
对图像缩放有resize和金字塔下采样两种,通常使用金字塔下采样。每执行一次下采样时,宽,高各缩放为原图的一半(每执行一次上采样,宽高放大为原来2倍)
模拟代码如下:
# 希望将验证码缩小为150像素以下
h, w = img.shape[:2]
levels = 0
while h>150:
levels +=1
img = cv2.pyrDown(img)
h, w = img.shape[:2]
# 缩放img得到锁小次数,然后对img_all进行相同操作
for _ in range(levels):
img_all = cv2.pyrDown(img_all)