<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于深度學(xué)習(xí)的圖標(biāo)型驗(yàn)證碼識(shí)別系統(tǒng)

          共 16917字,需瀏覽 34分鐘

           ·

          2021-03-04 00:06

          深度學(xué)習(xí)應(yīng)用于圖像處理領(lǐng)域應(yīng)該說有很長(zhǎng)一段時(shí)間了,相關(guān)的研究成果也有很多的積累了,從項(xiàng)目和實(shí)踐入手是我覺得的最好最快速有效的學(xué)習(xí)手段,當(dāng)下很多主流的驗(yàn)證碼識(shí)別系統(tǒng)大都是基于神經(jīng)網(wǎng)絡(luò)設(shè)計(jì)開發(fā)而來的,在處理圖像數(shù)據(jù)方面,神經(jīng)網(wǎng)絡(luò)有著無與倫比的優(yōu)勢(shì),本人最開始接觸到卷積神經(jīng)網(wǎng)絡(luò)也是從驗(yàn)證碼是被項(xiàng)目開始入手的。本項(xiàng)目從零開始介紹整體的實(shí)踐思路,以我們熟知的12306網(wǎng)站驗(yàn)證碼為例進(jìn)行分析實(shí)戰(zhàn),從數(shù)據(jù)采集、圖像處理、模型搭建、預(yù)測(cè)識(shí)別、界面開發(fā)幾個(gè)節(jié)點(diǎn)進(jìn)行針對(duì)性的設(shè)計(jì)開發(fā),實(shí)現(xiàn)了驗(yàn)證碼識(shí)別的完整流程,達(dá)到了很高的精度要求和速度要求,能夠滿足基本的調(diào)用需求。在模型方面,對(duì)比使用了多種經(jīng)典的神經(jīng)網(wǎng)絡(luò)模型來進(jìn)行實(shí)驗(yàn),選取最優(yōu)的實(shí)驗(yàn)結(jié)果來用于界面開發(fā)和調(diào)用識(shí)別。
          整個(gè)項(xiàng)目基于python3.6開發(fā)實(shí)現(xiàn),項(xiàng)目中包含整體項(xiàng)目所使用到的數(shù)據(jù)、代碼腳本、模型文件和界面文件的所有數(shù)據(jù)文件。項(xiàng)目文件截圖如下所示:

          上述文件解釋說明如下表所示:
          文件名稱文件說明
          screenshot/軟件截圖目錄
          getData.py圖像驗(yàn)證碼數(shù)據(jù)采集模塊
          imageCut.py圖像驗(yàn)證碼數(shù)據(jù)切分處理模塊
          dataHelper.py模型數(shù)據(jù)加載預(yù)處理模塊
          myModel.py模型訓(xùn)練模塊
          resnetModel.pyResnet模塊
          predict.py離線模塊預(yù)測(cè)識(shí)別模塊
          guiDemo.py界面可視化模塊
          texts.txt文本標(biāo)簽集合
          valid_ip_all.jsonIP代理池?cái)?shù)據(jù),避免爬蟲被封禁
          imageModel.h5訓(xùn)練好的圖像識(shí)別模型文件
          textModel.h5訓(xùn)練好的文本識(shí)別模塊文件
          接下來針對(duì)整個(gè)建模流程的各個(gè)節(jié)點(diǎn)進(jìn)行詳細(xì)說明。
          一、數(shù)據(jù)采集
          做深度學(xué)習(xí)的,基礎(chǔ)條件就是:數(shù)據(jù)+算力,想要建模實(shí)踐,首先就需要把所需的數(shù)據(jù)給準(zhǔn)備好了,下面我們開始本文的第一步工作:源站驗(yàn)證碼數(shù)據(jù)采集,這就是一個(gè)單純地?cái)?shù)據(jù)爬蟲工作,詳細(xì)的實(shí)現(xiàn)原理本來也就沒什么,我也就不多講解了,核心代碼部分如下:
          #!usr/bin/env python
          #encoding:utf-8
          from __future__ import division
           
           
          '''
          __Author__:沂水寒城
          功能: 網(wǎng)絡(luò)驗(yàn)證碼數(shù)據(jù)采集模塊
          '''

           
           
           
          def buildProxy():
              '''
              構(gòu)建代理信息
              '''

              header_list=generateRandomUA(num=500)
              header={'User-Agent':random.choice(header_list)}
              ip_proxy=random.choice(ip_list)
              one_type,one_ip,one_port=ip_proxy[0],ip_proxy[1],ip_proxy[2]
              proxy={one_type:one_type+'://'+one_ip+':'+one_port}
              return header,proxy
           
           
          def getPageHtml(url,header,proxy,num_retries=3):
              '''
              多代理形式、超時(shí)重試機(jī)制,獲取數(shù)據(jù)
              '''

              try:
                  response=requests.get(url,headers=header,proxies=proxy,timeout=5)
                  return response
              except Exception as e:
                   time.sleep(random.randint(3,8))
                   while num_retries:
                      num_retries-=1
                      print('Left tring number is:  ', num_retries)
                      return getPageHtml(url,header,proxy,num_retries)
           
           
          def getVCPics(img_url,start,end,saveDir):
              '''
              下載驗(yàn)證碼數(shù)據(jù)
              '''

              if not os.path.exists(saveDir):
                  os.makedirs(saveDir)
              for i in range(start,end):
                  print("Downloading",i+1,"......")
                  header,proxy=buildProxy()
                  try:
                      img=getPageHtml(img_url,header,proxy,num_retries=3)
                      pic_name=saveDir+str(i+1)+'.jpg'
                      file_pic=open(pic_name,'ab')
                      file_pic.write(img.content)
                      file_pic.close()
                      time.sleep(random.randint(0.1,1))
                  except:
                      pass
           
           
          if __name__ == '__main__':
              print('captchaDataCollection!!!')
              url="要爬取的驗(yàn)證碼鏈接"
              #驗(yàn)證碼數(shù)據(jù)采集
              getVCPics(url,0,500,'originalData/play/')
          為了避免被源站檢測(cè)到我們固定的IP地址在頻繁地發(fā)送數(shù)據(jù)請(qǐng)求導(dǎo)致的被拉黑的情況出現(xiàn),這里我采用了隨機(jī)UA偽裝+動(dòng)態(tài)IP代理的方式來進(jìn)行了規(guī)避處理,buildProxy函數(shù)是整個(gè)具體的實(shí)現(xiàn),任何場(chǎng)合里面,這個(gè)方法都可以直接拿去使用的,盡可能地提升爬蟲的工作效率。
          二、圖像切分處理
          原始采集到的驗(yàn)證碼數(shù)據(jù)是沒有辦法去直接使用的,需要進(jìn)行切分處理,這樣就轉(zhuǎn)化為了單個(gè)子圖識(shí)別的問題了,相對(duì)于原始大圖來說識(shí)別的難度就小了很多了,當(dāng)然了這里也不是說所有的圖像驗(yàn)證碼數(shù)據(jù)都是可以這樣去操作的,12306的驗(yàn)證碼數(shù)據(jù)是很規(guī)整的類型,可以基于一定的閾值對(duì)其進(jìn)行劃分切割處理,很方便就可以得到單個(gè)的子圖數(shù)據(jù)了,下面是一些原始驗(yàn)證碼數(shù)據(jù)樣例:

          可以看到:每幅圖片里面均包含了8張子圖,在第一行上面是文本標(biāo)簽數(shù)據(jù),也就是說12306一張驗(yàn)證碼數(shù)據(jù)我們要完成兩種類型圖像數(shù)據(jù)的識(shí)別工作。分析到這一步,接下來的工作就比較清晰了,我們首先要編寫圖像切割方法,完成對(duì)原始圖像數(shù)據(jù)的切割處理,下面是切割部分的核心代碼:
          def singleCut(img_path,row,col,cutDir):
              '''
              單張驗(yàn)證碼圖像數(shù)據(jù)切割處理
              '''

              img=Image.open(img_path)
              print('image_shape: ', img.size)
              name=img_path.split('/')[-1].strip().split('.')[0].strip()
              if not os.path.exists(cutDir):
                  os.makedirs(cutDir)
              w,h=img.size
              if row<=h and col<=w:
                  print('Original image info: %sx%s, %s, %s' % (w,h,img.format,img.mode))
                  rowheight=h//row
                  colwidth=w//col
                  for r in range(row):
                      for c in range(col):
                          box=(c*colwidth,r*rowheight,(c+1)*colwidth,(r+1)*rowheight)
                          if not os.path.exists(cutDir+name+'/'):
                              os.makedirs(cutDir+name+'/')
                          num=len(os.listdir(cutDir+name+'/'))
                          img.crop(box).save(cutDir+name+'/'+str(num)+'.png')
                  print('Total: ',num)
              else:
                  print('Wrong Parameters!!!')
          下面是實(shí)際切割的樣例數(shù)據(jù),原圖如下:

          上面的代碼能做的是對(duì)輸入的下半部分【也就是主體圖像數(shù)據(jù)】部分進(jìn)行切割,在調(diào)用上面的代碼之前,需要的操作如下:
          #截取文字
          box=(120,3,177,22)  #(左上角坐標(biāo),右下角坐標(biāo))
          res=img.crop(box)
          res.save('text.jpg')
          res.show()
          #截取主體圖像數(shù)據(jù)
          box=(0,33,293,190)  #(左上角坐標(biāo),右下角坐標(biāo))
          res=img.crop(box)
          res.save('image.jpg')
          res.show()
          其中里面的切割參數(shù)是經(jīng)過我的嘗試之后得出來的,不是絕對(duì)固定的,坐標(biāo)偏差1、2都是可以的,可以根據(jù)自己的喜好進(jìn)行實(shí)際的調(diào)整,經(jīng)過上面的切割結(jié)果如下。
          文本數(shù)據(jù)部分:

          圖像數(shù)據(jù)部分:

          之后,我們就可以針對(duì)image.jpg也就是主體的圖像數(shù)據(jù)內(nèi)容進(jìn)行切割處理,結(jié)果如下所示:

          之后就可以對(duì)自己爬取得到的所有數(shù)據(jù)按照上面的切割處理方式進(jìn)行同樣的處理操作了。完成上述工作之后,我們就可以著手創(chuàng)建自己的數(shù)據(jù)集了,驗(yàn)證碼的識(shí)別其實(shí)本質(zhì)上只是一個(gè)圖像的分類任務(wù),既然是分類,我們就需要知道一開始一共有多少個(gè)類別,這里我們搜集出來了一共有 80 個(gè)類別,具體類別名稱如下所示:
          打字機(jī)
          調(diào)色板
          跑步機(jī)
          毛線
          老虎
          安全帽
          沙包
          盤子
          本子
          藥片
          雙面膠
          龍舟
          紅酒
          拖把
          卷尺
          海苔
          紅豆
          黑板
          熱水袋
          燭臺(tái)
          鐘表
          路燈
          沙拉
          海報(bào)
          公交卡
          櫻桃
          創(chuàng)可貼
          牌坊
          蒼蠅拍
          高壓鍋
          電線
          網(wǎng)球拍
          海鷗
          風(fēng)鈴
          訂書機(jī)
          冰箱
          話梅
          排風(fēng)機(jī)
          鍋鏟
          綠豆
          航母
          電子秤
          紅棗
          金字塔
          鞭炮
          菠蘿
          開瓶器
          電飯煲
          儀表盤
          棉棒
          籃球
          獅子
          螞蟻
          蠟燭
          茶盅
          印章
          茶幾
          啤酒
          檔案袋
          掛鐘
          刺繡
          鈴鐺
          護(hù)腕
          手掌印
          錦旗
          文具盒
          辣椒醬
          耳塞
          中國(guó)結(jié)
          蜥蜴
          剪紙
          漏斗

          蒸籠
          珊瑚
          雨靴
          薯?xiàng)l
          蜜蜂
          日歷
          口哨
          整理創(chuàng)建好的數(shù)據(jù)集如下所示:

          一共有80個(gè)子文件夾,每個(gè)子文件夾里面存放的是一個(gè)類別的數(shù)據(jù),為了方便模型的計(jì)算,這里使用數(shù)字【文本類別對(duì)應(yīng)的索引值】來標(biāo)識(shí)不同的類別,下面簡(jiǎn)單看幾個(gè)類別的數(shù)據(jù)。
          0類:

          5類:

          52類:

          等。。。。。。
          三、模型搭建
          完成了基礎(chǔ)數(shù)據(jù)集的準(zhǔn)備和處理工作后就可以進(jìn)行神經(jīng)網(wǎng)絡(luò)模型的搭建工作了,這里我們主要使用了lenet、vgg、resnet幾種模型,其他的模型也可以按照同樣的形式進(jìn)行組織,代碼設(shè)計(jì)的很靈活,可以自主地進(jìn)行替換處理。下面是建模部分核心代碼:
          def resnetModel(num_classes,deep=18,h=16,w=10,way=1):
              '''
              resnet 模型
              '''

              if deep==18:
                  model=ResnetBuilder.build_resnet_18((way, h, w), num_classes)
              elif deep==34:
                  model=ResnetBuilder.build_resnet_34((way, h, w), num_classes)
              elif deep==50:
                  model=ResnetBuilder.build_resnet_50((way, h, w), num_classes)
              model.compile(optimizer='sgd',loss='categorical_crossentropy',metrics=['accuracy'])
              print(model.summary()) 
              return model
           
           
          def vggModel(num_classes,epochs,x_train,h=16,w=10,way=1):
              '''
              VGG-16模型
              '''

              print('x_train.shape: ',x_train.shape)
              input_shape=(h,w,way) 
              model=Sequential()  
              model.add(Conv2D(64,(3,3),strides=(1,1),input_shape=input_shape,padding='same',activation='relu',kernel_initializer='uniform'))  
              model.add(Conv2D(64,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))  
              model.add(MaxPooling2D(pool_size=(2,2)))  
              model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))  
              model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))  
              model.add(MaxPooling2D(pool_size=(2,2)))  
              model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))  #512
              model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))  
              model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))  
              model.add(MaxPooling2D(pool_size=(2,2))) 
              model.add(Flatten())  
              model.add(Dense(1024,activation='relu'))  #4096
              model.add(Dropout(0.5))  
              model.add(Dense(1024,activation='relu'))  
              model.add(Dropout(0.5))  
              model.add(Dense(num_classes,activation='softmax'))  
              model.compile(loss='categorical_crossentropy',optimizer='sgd',metrics=['accuracy'])  
              print(model.summary()) 
              return model
           
           
          def selfModel(num_classes,epochs,x_train,h=16,w=10,way=1):
              '''
              自定義基礎(chǔ)模型 sparse_categorical_crossentropy
              '''

              print('x_train.shape: ',x_train.shape)
              input_shape=(h,w,way) 
              model = models.Sequential([
                                      layers.Conv2D(64, (33), padding='same', activation='relu', input_shape=input_shape),
                                      layers.MaxPooling2D(),  # 19 -> 9
                                      layers.Conv2D(64, (33), padding='same', activation='relu'),
                                      layers.MaxPooling2D(),  # 9 -> 4
                                      layers.Conv2D(64, (33), padding='same', activation='relu'),
                                      layers.MaxPooling2D(),  # 4 -> 2
                                      layers.GlobalAveragePooling2D(),
                                      layers.Dropout(0.25),
                                      layers.Dense(256, activation='relu'),
                                      layers.Dense(num_classes, activation='softmax'),
                                  ])
              model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])
              print(model.summary())
              return model
           
           
          def main(dataDir='dataset/',nepochs=100,h=66,w=66,way=3,deep=18,flag='self',resDir='result/self/'):
              '''
              主函數(shù)
              '''

              if not os.path.exists(resDir):
                  os.makedirs(resDir)
              X,y=dataHelper.loadDataset(dataDir=dataDir,h=h,w=w)
              X=preProcess(X)
              y,num_classes=oneHotEncode(y)
              #數(shù)據(jù)集分割
              X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.30,random_state=7)
              #模型定義初始化
              if flag=='self':
                  model=selfModel(num_classes,nepochs,X_train,h=h,w=w,way=way)
              elif flag=='vgg':
                  model=vggModel(num_classes,nepochs,X_train,h=h,w=w,way=way)
              else:
                  model=resnetModel(num_classes,deep=deep,h=h,w=w,way=way)
              # 當(dāng)標(biāo)準(zhǔn)評(píng)估停止提升時(shí),降低學(xué)習(xí)速率
              reduce_lr = ReduceLROnPlateau(verbose=1)
              history = model.fit(X_train,y_train,epochs=nepochs,validation_data=(X_test, y_test),callbacks=[reduce_lr])
              resHandle(history,resDir,model,X_test,y_test)
              print('Finished.....................................................')
          我們的模型訓(xùn)練工作在GPU上進(jìn)行,下面是部分訓(xùn)練輸出截圖:

          基于GPU的訓(xùn)練速度還是非常快的,我嘗試在CPU環(huán)境里面跑一下,發(fā)現(xiàn)真的是很慢,而且電腦卡到了爆炸,我們接下來看一下對(duì)應(yīng)模型的實(shí)驗(yàn)結(jié)果。每個(gè)模型對(duì)應(yīng)的結(jié)果文件示例圖如下所示:

          以resnet-34為例,展示其訓(xùn)練過程可視化曲線:


          我們?cè)疾杉臄?shù)據(jù)量并不大,對(duì)于模型的訓(xùn)練有一定的影響,這個(gè)主要是考慮到不想對(duì)12306網(wǎng)站造成過大的壓力,這里可以根據(jù)自己的需要盡心調(diào)整。
          四、模型識(shí)別預(yù)測(cè)與可視化調(diào)用
          完成了模型的訓(xùn)練工作后,我們就得到了可以使用的離線模型文件,之后想要對(duì)圖像進(jìn)行識(shí)別就可以直接加載離線模型文件進(jìn)行識(shí)別預(yù)測(cè)了,為了使得我們的使用更加直觀方便,這里編寫了簡(jiǎn)單的界面工具來輔助進(jìn)行預(yù)測(cè)分析,核心代碼如下:
          def upload_image():
              '''
              上傳圖像
              '''

              try:
                  file_path=filedialog.askopenfilename()
                  uploaded=Image.open(file_path)
                  uploaded.thumbnail(((top.winfo_width()/2.25),(top.winfo_height()/2.25)))
                  im=ImageTk.PhotoImage(uploaded)
                  sign_image.configure(image=im)
                  sign_image.image=im
                  label.configure(text='')
                  executeButton(file_path)
              except:
                  pass
           
           
          upload=Button(top,text=u"點(diǎn)擊上傳圖像",command=upload_image,padx=10,pady=5)
          upload.configure(background='#63B8FF', foreground='#364156',font=('arial',20,'bold'))
          upload.pack(side=BOTTOM,pady=50)
          sign_image.pack(side=BOTTOM,expand=True)
          label.pack(side=BOTTOM,expand=True)
          heading = Label(top, text=u"12306驗(yàn)證碼識(shí)別機(jī)器人",pady=20, font=('arial',30,'bold'))
          heading.configure(background='#40E0D0',foreground='#364156')
          heading.pack()
          top.mainloop()

          使用樣例如下:

          后臺(tái)輸出如下:

          自己本地測(cè)試了幾百?gòu)垐D片,識(shí)別的精度還是很高的,能夠滿足自動(dòng)識(shí)別的需求。


          作者:沂水寒城,CSDN博客專家,個(gè)人研究方向:機(jī)器學(xué)習(xí)、深度學(xué)習(xí)、NLP、CV

          Blog: http://yishuihancheng.blog.csdn.net


          贊 賞 作 者



          更多閱讀



          2020 年最佳流行 Python 庫(kù) Top 10


          2020 Python中文社區(qū)熱門文章 Top 10


          5分鐘快速掌握 Python 定時(shí)任務(wù)框架

          特別推薦




          點(diǎn)擊下方閱讀原文加入社區(qū)會(huì)員

          瀏覽 68
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产乱╳╳╳╳AⅤ视频 | 国产一区二区三区 | 国产精品秘 久久久久久奇米影视 | 中文字幕无码人妻在线二区 | 麻豆AV片 |