在我們日常登錄或註冊某個網站的時候,經常會出現滑動驗證碼,如下圖:
本文將會講述如何利用darknet來破解滑動驗證碼,我們只要找到圖片中的缺口就可以了。
數據的採集和標註
筆者利用爬蟲在某網站爬取了約300張帶缺口的滑動驗證碼的圖片,並對這些驗證碼圖片進行標註,即標註缺口的位置。
我們使用的標註工具爲labelImg
,這是圖像標註方面一個非常好用的GUI工具。網上已經有很多關於安裝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
下面給出在幾張新的滑動驗證碼圖片上的識別結果:
總結
darknet在目標檢測方面的效果確實很不錯,如果我們肯用心去想,也許還會有很多有趣的應用,筆者後面將會講解點選驗證碼的識別
和車牌識別
等方面的內容。
CV在很多方面確實比NLP成熟很多,不管是模型,還是效果,還是應用。
本文中筆者用到的數據集稍後會公開至Github項目。同時,筆者也深感國內在共享數據集這方面確實做得不夠好,很多文章會有這方面的內容,但卻不會公開數據,這無疑是令人失望。
感謝大家的閱讀,歡迎持續關注~