zoukankan      html  css  js  c++  java
  • 目标检测初体验(三)破解滑动验证码

      在我们日常登录或注册某个网站的时候,经常会出现滑动验证码,如下图:
    滑动验证码的例子
      本文将会讲述如何利用darknet来破解滑动验证码,我们只要找到图片中的缺口就可以了。

    数据的采集和标注

      笔者利用爬虫在某网站爬取了约300张带缺口的滑动验证码的图片,并对这些验证码图片进行标注,即标注缺口的位置。
      我们使用的标注工具为labelImg,这是图像标注方面一个非常好用的GUI工具。网上已经有很多关于安装labelImg的教程,本文不再具体介绍。我们打开labelImg,如下图:
    labelImg
      在labelImg中我们选择打开目录,选择标注图片所在的目录,并进行标注。标注的时候,先选择创建区块,标注滑动验证码中缺口所在的矩形框,并保存其类别(这里我们将类别设置为box),标注的例子如下:
    标注的例子
    标注完当前图片后,点击保存,默认保存路径为标注图片所在文件夹,文件格式为xml,名字与图片名字一致,比如上图标注后生成的xml文件内容如下:

    <annotation>
    	<folder>images</folder>
    	<filename>1.jpg</filename>
    	<path>/Users/jclian/PycharmProjects/SlideCaptcha/images/1.jpg</path>
    	<source>
    		<database>Unknown</database>
    	</source>
    	<size>
    		<width>480</width>
    		<height>240</height>
    		<depth>3</depth>
    	</size>
    	<segmented>0</segmented>
    	<object>
    		<name>box</name>
    		<pose>Unspecified</pose>
    		<truncated>0</truncated>
    		<difficult>0</difficult>
    		<bndbox>
    			<xmin>319</xmin>
    			<ymin>102</ymin>
    			<xmax>387</xmax>
    			<ymax>170</ymax>
    		</bndbox>
    	</object>
    </annotation>
    

    上述的xml文件告诉我们很多信息,在size中我们可以知道标注图片的大小,在object中我们可以知道标注的矩形框的位置信息和类别名称。
      在labelImg中点击下一张即可进行下一章图片的标注。我们需要花费不少时间来完成爬取的约300张图片的标注,不要怕麻烦。

    数据处理

      在这一步中,我们将把标注的图片进行数据处理,加工成darknet所支持的数据格式。
      整个项目的结构如下:
    项目结构
    我们的标注图片位于slide_train_images目录下,利用read_xml_2_label.py脚本,将标注的xml文档转化为darknet支持的txt形式,并记录训练图片的路径。脚本代码如下:

    # -*- coding: utf-8 -*-
    # author: Jclian91
    # place: Pudong Shanghai
    # time: 2020/5/16 1:06 下午
    import os
    import xml.etree.ElementTree as ET
    
    
    #  change a single xml to txt
    def single_xml_to_txt(xml_file):
        tree = ET.parse(xml_file)
        root = tree.getroot()
        #  txt saved file path
        if not os.path.exists("../slide_captcha_train_labels"):
            os.system("mkdir ../slide_captcha_train_labels")
    
        txt_file = "../slide_captcha_train_labels/%s" % xml_file.split("/")[-1].replace(".xml", ".txt")
        with open(txt_file, 'w') as txt_file:
            for member in root.findall('object'):
    
                picture_width = int(root.find('size')[0].text)
                picture_height = int(root.find('size')[1].text)
                class_name = member[0].text
    
                #  类名对应的index
                class_num = class_names.index(class_name)
                box_x_min = int(member[4][0].text)  # 左上角横坐标
                print(xml_file, "box_x_min", box_x_min)
                box_y_min = int(member[4][1].text)  # 左上角纵坐标
                box_x_max = int(member[4][2].text)  # 右下角横坐标
                box_y_max = int(member[4][3].text)  # 右下角纵坐标
                # 转成相对位置和宽高
                x_center = (box_x_min + box_x_max) / (2 * picture_width)
                y_center = (box_y_min + box_y_max) / (2 * picture_height)
                width = (box_x_max - box_x_min) / picture_width
                height = (box_y_max - box_y_min) / picture_height
                print(class_num, x_center, y_center, width, height)
                txt_file.write(str(class_num) + ' ' + str(x_center) + ' ' + str(y_center) + ' ' + str(width) + ' ' + str(height) + '
    ')
    
    
    #  转换文件夹下的所有xml文件为txt
    def dir_xml_to_txt(dir_path):
        for file in os.listdir(dir_path):
            if file.endswith(".xml"):
                xml_file = os.path.join(dir_path, file)
                single_xml_to_txt(xml_file)
            with open("../slide_captcha_train.txt", "a", encoding="utf-8") as h:
                if file.endswith(".jpg"):
                    h.write(os.path.join(dir_path, file).replace("../", "")+"
    ")
    
    
    if __name__ == '__main__':
        #  class name
        class_names = ['box']
        #  path of XML files
        dir_path = '../slide_captcha_train_images'
        dir_xml_to_txt(dir_path)
    

    生成的slide_captcha_train_labels下的0.txt文件的内容如下:

    0 0.7604166666666666 0.44583333333333336 0.14166666666666666 0.2833333333333333
    

    其中0为类别编号,这里只有一类,因此所有的类别编号均为0。后面的数字为标注的矩形框的x中心点,y中心点,宽度,高度与图片的宽度、高度的比值。同时,生成的slide_captcha_train.txt(记录训练图片的路径)的前几行如下:

    slide_captcha_train_images/63.jpg
    slide_captcha_train_images/189.jpg
    slide_captcha_train_images/77.jpg
    slide_captcha_train_images/162.jpg
    slide_captcha_train_images/176.jpg
    slide_captcha_train_images/88.jpg
    

      slide_captcha.names存储本次识别的类别名称,即box;slide_captcha.data储存训练和验证数据信息,如下:

    classes= 1
    train = train.txt
    valid = val.txt
    names = slide_captcha.names
    backup = backup
    

    其中val.txt为空,模型训练过程中生成的模型文件保存在backup文件夹中。

    模型训练

      关于模型训练方面的具体步骤,可以参考文章 目标检测初体验(二)自制人脸检测功能
      笔者在GPU上利用以下脚本进行模型训练:

    ./darknet detector train slide_captcha.data cfg/yolov3-tiny.cfg ./weights/darknet53.conv.74 --gpus 1,2,8,9
    

    训练结果如下:
    模型训练结果

    模型预测

      我们利用已经训练好的模型对新的滑动验证码图片进行验证,使用以下命令:

    ./darknet detector test slide_captcha.data cfg/yolov3-tiny.cfg slide_captcha_model/yolov3-tiny.backup test_images/1.jpg -thresh 0.5 --gpus 1,2,8,9
    

      下面给出在几张新的滑动验证码图片上的识别结果:

    识别结果1
    识别结果2
    识别结果3

    总结

      darknet在目标检测方面的效果确实很不错,如果我们肯用心去想,也许还会有很多有趣的应用,笔者后面将会讲解点选验证码的识别车牌识别等方面的内容。
      CV在很多方面确实比NLP成熟很多,不管是模型,还是效果,还是应用。
      本文中笔者用到的数据集稍后会公开至Github项目。同时,笔者也深感国内在共享数据集这方面确实做得不够好,很多文章会有这方面的内容,但却不会公开数据,这无疑是令人失望。
      感谢大家的阅读,欢迎持续关注~

  • 相关阅读:
    【Docker】(6)---Dockerfile文件
    【Docker】(5)---springCloud注册中心打包Docker镜像
    【Docker】(4)搭建私有镜像仓库
    【Docker】(3)---linux部署Docker及Docker常用命令
    【Docker】(2)---仓库、镜像、容器
    【Docker】(1)---Docker入门篇
    【Maven】---Nexus私服配置Setting和Pom
    【Maven】---Linux搭建Nexus3.X私服
    【Maven】---坐标与依赖
    go语言path包和filepath包的学习与使用
  • 原文地址:https://www.cnblogs.com/jclian91/p/12952442.html
Copyright © 2011-2022 走看看