Python识别验证码----数美图标点选

Python识别验证码----数美图标点选

  • 写在前面
  • 数据特点
  • 获取数据
  • 识别思路
    • 按顺序抠出F4
    • 定位f4
    • 计算相似度
      • 打标签(~~偷懒~~ )
      • 训练孪生网络
      • 使用模型
  • 识别结果
  • 改进点

写在前面

近日某众打码平台被跑路的消息一出,脚本圈中一片哗然(我并不是脚本圈的,只是喜欢看群里人吹逼而已 ),仿佛再也听不到那句熟悉的广告语了。这也预示着,第三方打码平台不靠谱了。但打码功能有时候又必不可少,这时候怎么办呢?当然是自己自己动手丰衣足食啦!最近工作不是很忙,准备撸一个用Python识别验证码的系列文章,该系列计划囊括各种时下比较流行的验证码形式,如滑块、四则运算、点选、手势、空间推理、谷歌等。已经跑通了的所有代码都放在了我的知识星球上,需要的话请自取。话不多说,开撸!

Python识别验证码----数美图标点选_第1张图片

数据特点

数美的图标点选和其他的图标点选差不多,要按顺序点击。
Python识别验证码----数美图标点选_第2张图片

获取数据

正常人都知道这些数据肯定是要写爬虫来抓的(如果你单身至今,当我没说 )。数美对于反爬这块还算良心,稍微分析下请求就会发现有些参数看似加密实则写死,所以构造下请求头和参数就能轻松获取到验证码图片的url。

识别思路

一个验证码是由两张图组成的,一个是后缀是_bg.jpg的背景图,一个是后缀是_fg.png的图标图。
Python识别验证码----数美图标点选_第3张图片

在这里插入图片描述

首先想想看,要解决哪些问题,才能实现按顺序点击:

  • 从图标图中按顺序(按顺序点击的依据)抠出4个图标,我愿称之为F4
  • 在背景图中定位已经被旋转缩放后的4个图标,并抠出来,我愿称之为f4
  • 计算出F4们与f4们之间的相似度(搞基配对

按顺序抠出F4

稍微懂点CV的老铁应该知道,这种图好扣的很。转成灰度图,OTSU阈值分割,膨胀一下就能得到比较好的F4们的连通区域了。

# image是图标图
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
   _, threshold_img = cv2.threshold(gray_img, 100, 255, cv2.THRESH_OTSU)
kernel = np.ones([3, 3], np.uint8)
dialte_img = cv2.dilate(threshold_img, kernel, 2)

结果就是酱紫。
在这里插入图片描述
有了上面的结果后,按顺序抠图就简单了。从左到右遍历下康康纵向的像素和是不是0就OJBK了。

# F4
roi_image = []

i = 0
while i < image.shape[1]:
     if(np.sum(dialte_img[:,i]) > 0):
          start_col = i
          while np.sum(dialte_img[:,i]) > 0:
              i += 1
          end_col = i
          # 抠图
          roi_image.append(image[:,start_col:end_col])
     else:
          i += 1

抠图效果
在这里插入图片描述

定位f4

如果有老铁看过我之前写那篇识别数美拼图滑块的流水账,估计会想着继续用模板匹配去背景图上找图标的位置。但如果你头铁试了一下的话,会发现,找的位置一点都!不!准!因为模板匹配没有旋转不变性和缩放不变性。所谓不变性就是变了等于没变。也就是说,模板匹配不适于去匹配已经被旋转和缩放后的目标,即使它们从人眼看来是一个东西。

本来想用SIFT来做定位的,但SIFT申请了专利,我opencv降版本都白嫖不了…

后来想想,算了,上YoloV5吧,反正YoloV5n对显卡要求不高,我这4G小霸王训练个定位图标的模型还是可以的。说干就干。

经过半个小时的标注,标注了90多张图。然后用YoloV5n pytorch版训练了一个13.9M的模型,mAP高达98!!!不得不说,Yolo牛逼!(如果各位看官对yolov5不熟,可以参考官方github)

有了模型之后,就需要改写一下官方提供的predict.py,因为predict.py太臃肿,而且所有的预测结果是画在图上的,就像酱紫。
Python识别验证码----数美图标点选_第4张图片
但我们想要的结果是f4们的位置。所以像可视化啊、dump日志啊什么杂七杂八的全可以删掉。需要注意的是:predict.py里面有两种坐标表示方式,一种是xywh,还有一种是xyxyxywh是目标矩形框的左上角的归一化后的坐标和矩形的归一化后的宽高。xyxy是目标矩形框的左上角坐标和右下角坐标,并且坐标没有被归一化。

至于用哪种方式表示位置的话,见仁见智了。反正都能互相转换。

我这边为了opencv好抠图,就用的xyxy,然后稍微封装了一下。

# pos是个列表,是用来抠f4们直接能用的坐标
pos = yolo_detector.detect(bg_img)
# 存放f4的列表
bg_roi_imgs = []
for p in pos:
	# 抠图
    bg_roi_imgs.append(bg_img[p[0]:p[1], p[2]:p[3]])

计算相似度

F4,f4都有了,那就差他们搞基配对了。搞基的逻辑其实也挺简单,大概酱紫。

# 按顺序存放好基友的矩形框
rects = []

for i in range(len(puzzle_images)):
    best_score = 0
    best_rect = None
    for j in range(len(bg_roi_imgs)):
        score = 计算相似度(puzzle_images[i], bg_roi_imgs[j])
        if dis > best_score:
            best_score = dis
            best_rect = [pos[j][2], pos[j][0], pos[j][3], pos[j][1]]
    rects.append(best_rect)

那么相似度怎么算?一开始我想这要不算个感知哈希?结果发现不太行。要不算个HOG特征然后算余弦距离?结果他喵的比感知哈希还拉跨…

算了,用孪生网络一把梭,不就是打标签嘛,我打还不行吗…

打标签(偷懒 )

至于打标签嘛,学过ML或DL的都知道,数据用业务场景的真实数据肯定是最好的,因为数据分布最为相似。但我很懒…我就投机取巧的做了图像增强。思路就是随便找了几张网图,然后把种子随机旋转,缩放得到贴图。再把贴图随机找个网图贴上去然后抠出来。

import cv2
import numpy as np
import os
import time

def random_rotate(img):
    rows, cols, channels = img.shape
    angle = [0, 20, 45, 60, -20, -45]
    aa = np.random.randint(len(angle))
    rotate = cv2.getRotationMatrix2D((rows * 0.5, cols * 0.5), angle[aa], 1)
    res = cv2.warpAffine(img, rotate, (cols, rows))
    return res

def random_resize(img):
    scale = [1.0, 1.2, 1.3,1.5, 1.7 ,2, 2.5]
    x = np.random.randint(len(scale))
    img = cv2.resize(img, (0, 0), fx=scale[x], fy=scale[x])
    return img

def gen_random_img(bg, fg):
    fg_ = fg.copy()
    fg_ = random_rotate(fg_)
    fg_ = random_resize(fg_)
    fg_r, fg_c = fg_.shape[0], fg_.shape[1]
    x = np.random.randint(bg.shape[1]-fg_c)
    y = np.random.randint(bg.shape[0]-fg_r)
    roi = bg[y:y+fg_r, x:x+fg_c].copy()
    for i in range(roi.shape[0]):
        for j in range(roi.shape[1]):
            if np.sum(fg_[i,j,:]) > 30:
                roi[i, j, 0] = fg_[i, j, 0]
                roi[i, j, 1] = fg_[i, j, 1]
                roi[i, j, 2] = fg_[i, j, 2]
    return roi

bgs = os.listdir('random_bg')

for fg_path in os.listdir('./images_background/'):
    filename = os.listdir(os.path.join('./images_background/', fg_path))[0]
    for i in range(100):
        bg_i = np.random.randint(len(bgs))
        bg = cv2.imread('random_bg/'+bgs[bg_i])
        fg = cv2.imread(os.path.join(os.path.join('./images_background/',fg_path),filename))
        roi = gen_random_img(bg, fg)
        cv2.imwrite(os.path.join(os.path.join('./images_background/',fg_path),str(round(time.time()*1000)))+'.jpg', roi)

然后就有了大概酱紫的数据集

Python识别验证码----数美图标点选_第5张图片

训练孪生网络

pytorch版本的孪生网络github上有很多,选一个看得最顺眼的就行。我选的是backbone是VGG16,损失函数是三元组损失的。大概训练了7个epoch后精度就还行了(后来实验证明有点过拟合了 )。

使用模型

有了模型之后,直接调用模型预测就好,反正给的结果是个概率值。概率值越高,说明越像。

# 按顺序存放好基友的矩形框
rects = []

for i in range(len(puzzle_images)):
    best_score = 0
    best_rect = None
    for j in range(len(bg_roi_imgs)):
        score = siamese_model.detect_image(puzzle_images[i], bg_roi_imgs[j])
        if dis > best_score:
            best_score = dis
            best_rect = [pos[j][2], pos[j][0], pos[j][3], pos[j][1]]
    rects.append(best_rect)

这个时候rects里面就会有F4们在背景图中的位置了。

识别结果

为了方便查看识别结果,我把F4和背景图都贴到了一张图上,然后框框上的数字就是依次点击的顺序。

# 背景图
bg_img = cv2.imread(bg_path)
# F4们
fg_img = cv2.imread(fg_path)
# 新图
back = np.zeros([340, 600, 3], dtype=np.uint8)
# 获得依次点击的矩形框信息
rects = get_result(bg_img, fg_img, model, detector)

for i, rect in enumerate(rects):
    # 可视化
    bg_img = cv2.rectangle(bg_img, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 255), 3)
    bg_img = cv2.putText(bg_img, str(i + 1), (rect[0], rect[1]), cv2.FONT_HERSHEY_SIMPLEX, 1.1, (0, 255, 255), 2)

back[:bg_img.shape[0], :bg_img.shape[1], :] = bg_img

# 把F4们贴到图的最下方
for i in range(fg_img.shape[0]):
    for j in range(fg_img.shape[1]):
        if fg_img[i, j, 0] != 0 and fg_img[i, j, 1] != 0 and fg_img[i, j, 2] != 0:
            back[i+bg_img.shape[0], j, 0] = fg_img[i, j, 0]
            back[i+bg_img.shape[0], j, 1] = fg_img[i, j, 1]
            back[i+bg_img.shape[0], j, 2] = fg_img[i, j, 2]

Python识别验证码----数美图标点选_第6张图片
Python识别验证码----数美图标点选_第7张图片
测试了下,依次点击的正确率大概65%的样子。

改进点

1.抠F4的时候我没考虑那种隔得很开的图标,比如下图中的AI会抠成AI。可以考虑直接用定位f4的yolo模型来抠图,效果肯定比这个好。
在这里插入图片描述
2.因为懒,标注的数据太少,孪生网络的数据我只标注了50多种图标,实际上测试时图标种类远不止50多种,导致模型容易过拟合。比如下图中12的图标中都有类似s形的缝隙。模型就算错了。如果不懒,效果不会差。

Python识别验证码----数美图标点选_第8张图片
3.构造一个网络结构做到端到端识别。

你可能感兴趣的:(天下无码,人工智能,深度学习,yolo,pytorch,验证码识别)