目標檢測初體驗(三)破解滑動驗證碼

  在我們日常登錄或註冊某個網站的時候,經常會出現滑動驗證碼,如下圖:
滑動驗證碼的例子
  本文將會講述如何利用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) + '\n')


#  轉換文件夾下的所有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("../", "")+"\n")


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項目。同時,筆者也深感國內在共享數據集這方面確實做得不夠好,很多文章會有這方面的內容,但卻不會公開數據,這無疑是令人失望。
  感謝大家的閱讀,歡迎持續關注~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章