網上關於驗證碼識別的開源專案眾多,但大多是學術型文章或者僅僅是一個測試 demo,那麼企業級的驗證碼識別究竟是怎樣的呢?
1. 前言
網上關於驗證麼識別的開源專案眾多,但大多是學術型文章或者僅僅是一個測試 demo,那麼企業級的驗證碼識別究竟是怎樣的呢?前方高能預警,這是一個生產水準的驗證碼識別專案,筆者可以向你們保證,它一定會是各位所見過的文章中最實用的,你甚至可以不需要懂程式碼寫程式碼就能輕鬆使用它訓練一個 99 識別率的模型。這才是企業級應該有的樣子:演算法開發負責框架,訓練只需要一個實習生。不僅操作上簡單,在可用性和穩定性上也是經得起考驗。效能上,筆者使用騰訊雲 1 核 1G 的機器測試:單次識別平均在 12ms 左右,再也不需要 GPU 部署了,CPU 一樣可以日調百萬。
不少初學者和筆者反應,安裝環境太難了,沒關係,都給你們安排好了,一行 pip 就能搞定環境的 MuggleOCR。
倉庫地址:https://pypi.org/project/muggle-ocr
MuggleOCR 的體積有 6MB,其中附帶了兩個通用模型:簡單通用驗證碼,普通 OCR。簡而言之就是,再也不用愁驗證碼的樣本不好標註了,它將是各位標註樣本的利器,簡單的驗證碼識別率能有 95% 以上,複雜的也有 50%-70% 左右,只需要結合官網校驗,輕鬆下載幾萬標註樣本。
除此之外,它可以支援呼叫使用本文框架(captcha_trainer)訓練的模型。呼叫只需要三行核心程式碼:
# 開啟一張驗證碼圖片
with open(r"1.png", "rb") as f:
img_bytes = f.read()
# 步驟 1
import muggle_ocr
# 步驟 2
sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.OCR)
# 步驟 3
text = sdk.predict(image_bytes=img_bytes)
print(text)
本專案旨在降低影像識別的門檻,讓深度學習技術能夠進入更多人的視線。任何人經過簡單的介紹,都可以輕易使用這項技術訓練一個商業化的成品。筆者選用的時下最為流行的 CNN Backbone+RNN+CTC(CRNN)進行端到端的不定長驗證碼識別,程式碼中預留了 CNNX/MobileNet/DenseNet121/ResNet50 等。其中可能你們搜不到 CNN5 和 CNNX,因為是小編自己拼湊的網路選項,專門為驗證碼最佳化定製的,在配置介面中可以隨意切換網路組合。前面介紹這麼多還沒進入正題,各位是不是好奇它到底是什麼模樣呢?2. 在專案中執行 app.py 來啟動 GUI 的介面 - 訓練專案原始碼:https://github.com/kerlomz/captcha_trainer
- 編譯版下載地址:https://github.com/kerlomz/captcha_trainer/releases
- 部署專案原始碼:https://github.com/kerlomz/captcha_platform
- 編譯版下載地址:https://github.com/kerlomz/captcha_platform/releases
注意:在 Windows 伺服器版中使用編譯版如果出現閃退,可以用 CMD 執行可執行檔案來檢視報錯,如果報錯為 cv2 ImportError: Dll load failed 請按照步驟:我的電腦——屬性——管理——新增角色和功能——勾選桌面體驗,點選安裝,安裝之後重啟即可。H16/H64 指的是隱藏神經元個數,根據上面的資料可知,訓練使用 GPU,部署預測使用 CPU 足矣。環境依賴花了超長篇幅,主要是寫給零開發基礎的使用者,有基礎的可以隨便跳過,也歡迎使用編譯版,可在上一章末尾找到下載地址。關於 CUDA 和 cuDNN 版本的問題,就讓不少人望而卻步,其實很簡單,如果使用 pypi 倉庫安裝的 TensorFlow,那麼 Linux 系統使用 CUDA 9.0,Windows 使用 CUDA 10.0,因為倉庫中的 whl 安裝檔案都是根據對應的 CUDA 版本編譯的。也就是版本繫結死了,如果有需要可以去搜尋 TensorFlow Wheel 找第三方編譯的版本,如果妄圖自行編譯我這裡勸退一下,坑很多。在專案中的 requirements.txt 已經整理好所有依賴模組。一鍵pip install -r requirements.txt
1)安裝相關依賴 不用理會上面的清單,在專案中的 requirements.txt 已經整理好所有依賴模組。可以直接在專案路徑下執行pip3 install -r requirements.txt
注意預設情況會安裝到全域性的 Python 環境下,筆者強烈建議在虛擬環境進行,做好專案間的環境隔離,可以藉助 Virtualenv 或 Anaconda 等等實現。筆者個人使用的是 Virtualenv,如果有修改程式碼需求的,可直接在 PyCharm 上操作。virtualenv -p /usr/bin/python3 venv # venv 是虛擬環境的名稱,也是路徑名.
cd venv/ # 進入環境.
source bin/activate # 啟用當前環境.
cd captcha_trainer # captcha_trainer 是專案名.
pip3 install -r requirements.txt # 在剛剛建立的環境下安裝當前專案的依賴
2.1.2 Ubuntu 16.04 下的 CUDA/cuDNN網上很多教程,但是靠譜的不多,自己在不同的機器上部署過幾次,以身說法,14.04 桌面版支援不好,需要主機板支援關閉 SecureBoot,Ubuntu 16.04 的坑少一點,大多的坑都發生在安裝好之後,在登陸介面無限迴圈無法進入桌面。網上很多教程提示要加驅動黑名單什麼的,筆者親測沒那個必要。就簡單的幾步:1. 下載好安裝包 必須下載 runfile 型別的安裝包,即字尾名為. run 的安裝包,因為 deb 安裝包預設安裝自帶驅動,這是導致登陸迴圈的罪魁禍首。- NVIDIA 驅動下載:https://www.geforce.cn/drivers
- CUDA 下載地址:https://developer.nvidia.com/cuda-10.0-download-archive
- cuDNN 下載地址:https://developer.nvidia.com/cudnn (需要註冊 NVIDIA 賬號且登陸,下載 deb 安裝包)
2. 關閉圖形介面 進入字元介面,快捷鍵 Ctrl+alt+F1,將 GUI 服務關閉sudo service lightdm stop
命令中的版本自己對應下載的版本改,在上面的下載地址根據自己的顯示卡型號下載最新版,切記是 runfile 格式的安裝包。以下 3xx.xx 為版本號,請下載最新驅動。sudo chmod a+x NVIDIA-Linux-x86_64-3xx.xx.run //獲取執行許可權
sudo ./NVIDIA-Linux-x86_64-3xx.xx.run –no-x-check –no-nouveau-check –no-opengl-files //安裝驅動
安裝後使用 nvidia-smi 命令驗證,若出現顯示卡資訊,則表示安裝成功sudo apt-get install build-essential libx11-dev libxmu-dev libxi-dev libglu1-mesa-dev libgl1-mesa-glx libglu1-mesa freeglut3-dev
2) 執行安裝程式,按提示繼續就好了,直到出現是否安裝驅動選項,選擇不安裝即可。sudo sh cuda_9.0.176_384.81_linux.run
安裝完成還需要配置環境變數,將以下內容就追加到 ~/.bashrc 檔案的尾部export PATH=/usr/local/cuda-9.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
最後在終端執行 sudo ldconfig 命令更新環境變數,重啟機器,重新啟用 GUI 即可。sudo service lightdm start
一直有人說 Windows 不適合做深度學習,其實筆者覺得還是蠻友好的。巨硬的系統安裝環境簡單一百倍,只要到官網下載對應的安裝包,本專案建議 CUDA 10.0,Windows 2019 的話可以使用 Win10 版替代,CUDA 安裝的時候同樣不安裝驅動,包括一個 VS 的選項也去掉(不取消安裝會很慢並可能安裝失敗),然後下載對應的 cuDNN 替換到 CUDA 安裝路徑即可,一般為:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0。開始之前,先解決一個世紀疑惑,有不少朋友常常私信我 “訓練一個 x 位數英文數字驗證碼需要多少樣本?” 諸如此類的問題,筆者在此統一回復,樣本需要多少數量需要根據樣本的特徵複雜程度來決定。變形
旋轉
模糊
背景干擾
前景干擾
字型種類
標籤數目 / 驗證碼位數
分類數目 / 字符集大小
一般只包含以上 1-2 種的為簡單,2-3 種為複雜,3 種以上屬於特別複雜。樣本量依次遞增,從幾百,幾千,幾萬,到幾十萬不等,其中,分類數目(字符集帶)多寡對數量級影響較大,例如中文幾千字符集的驗證碼一般 10w 起步,筆者文中末尾的驗證碼用了 100w 樣本。PS:親們不要再考驗框架的健壯性了,樣本量連一個 Batch Size 都達不到的,千萬不要嘗試,根本跑不起來。目前為止,入坑準備工作還差一步,巧婦難為無米之炊,首先,既然是訓練,得要先有資料,筆者這裡提供一份路人皆知的 mnist 手寫識別的資料集。可以在騰訊雲下載:https://share.weiyun.com/5pzGF4V本專案所有配置都是引數化的,不需要改動任何程式碼,可以直接透過視覺化介面操作,訓練幾乎圖片驗證碼。訓練框架介面可以大致劃分為幾個部分:2.Project Configuration - 專案配置區 4.Training Configuration - 訓練配置區1. 神經網路區 的配置項看起來很多,對於新手來說,只需先選擇好使用的網路,在樣本配置區選擇樣本路徑之後,會自動配置圖片有關的引數,保持預設推薦引數即可。筆者一般使用 CNNX+GRU+CTC 網路進行不定長驗證碼的訓練。2. 專案配置區 的配置項在網路選好之後配置專案名,按回車或者點選空白處確認。3. 樣本源配置區 的配置項用來配置樣本源的路徑,訓練樣本是根據此路徑進行打包成 TFRecords 格式,驗證樣本可以不指定,使用 [Validation Set Num] 引數隨機從訓練集總抽樣成驗證集,這裡預設隨機抽取數目為 300 個,可以在介面上自行修改。4. 訓練配置區 的配置項負責定義訓練完成的條件如:結束準確率,結束 COST,結束 Epochs,批次大小。如果最後無法滿足可以手動停止,然後點選 [Compile] 編譯匯出最新的訓練模型。5. 功能控制區 的配置項,設定完上面步驟,先點選 [Make Dataset] 打包樣本,再點選[Start Training] 開始訓練。如若使用 CrossEntropy 作為解碼器需要注意標籤數 LabelNum 和圖片尺寸需要滿足的關係,因為網路為多標籤而設計(一般的多標籤採用直接連線多個分類器,這也是有一部分網上的開原始碼你們修改了圖片就無法執行的原因之一),卷積層的輸出 outputs 經過了以下變換:Reshape([label_num, int(outputs_shape[1] / label_num)])
為了保證 int(outputsshape[1] / labelnum) 運算能夠得到正整數維度,這意味著他們之間存在某種數學關係,對 CNN5+Cross Entropy 網路結構而言,Conv2D 層的步長皆為 1,那麼需要保證以下等式成立:所以有時候需要對輸入的圖片 Resize,一般 4 位驗證碼不容易出現這種問題,位數為 3,5,6,7 容易出現不滿足等式的問題,這個等價關係如果不好計算的話,建議使用 CTC Loss。例如使用 CNN5+CrossEntropy 組合,則輸入寬度與輸入高度需要滿足:同理如果 CNN5+RNN+CTC,卷積層之後的輸出經過以下變換:Reshape([-1, outputs_shape[2] * outputs_shape[3]])
原輸出(batchsize, outputsshape[1], outputsshape[2], outputsshape[3]),RNN 層的輸入輸出要求為(batch, timesteps, num_classes),為了接入 RNN 層,經過以上的操作,又引出一個 Time Step(時間步長)的概念。可以把 timesteps 可以理解為圖片切片,每個切片需要和標籤對應。進入 RNN 層之後 timesteps 的值也是經過卷積池化變換之後 outputsshape[1],而 CTC Loss 的輸入要求為 [batchsize, frames, num_labels],若 timesteps 小於標籤數目,可以理解為圖片切片數小於標籤數,一個切片對應了多個標籤,那麼肯定是無法計算損失的,也就是無法從損失函式中找到極小值,梯度無法下降。timesteps 最合理的值一般是標籤數的 2 倍,為了達到目的,也可以透過對輸入 Resize 來間接調整卷積池化之後的 outputs_shape[1],一般情況下 timesteps 直接關聯於圖片寬度,大多情況只需按比例 Resize 寬度即可。注意:如果訓練集的命名方式和我提供的新手訓練集不一樣,可以根據實際情況修改 ExtractRegex 的正規表示式。強烈建議不知道如何寫正規表示式的朋友按照筆者的定義規範命名。目前這個功能只支援在 yaml 配置檔案中修改,GUI 介面尚不支援修改該引數。DatasetPath 和 SourcePath 引數允許配置多個路徑,如果需要把多種樣式的圖片混合一起訓練,或者打算訓練一套通用識別模型的使用者,這非常方便。分類數目 / 字符集(Category)已經包括了大多數驗證碼和 OCR 的情況,大多數情況下不需要自定義,一般的圖形驗證碼是大小寫不敏感的,一般不要輕易選擇區分大小寫的分類,推薦預設的 ALPHANUMERIC_LOWER ,會自動將大寫的轉為小寫,字符集定義很靈活,除了配置備註上提供的幾種正規化,還支援訓練中文,自定義字符集用 list 表示,參考如下:Category: ['你', '好', '世', '界', '北', '京', '大', '學']如果是單標籤分類,可以配合 LabelNum=1,例如:Category: ["飛機", "鞋子", "水杯", "麵包", "橫幅", "訂書機", "壁畫", "貓砂", ......]其檔名示例:飛機_0123456789012.png如果是多標籤分類,可以配合 LabelSplit=&,例如:Category: ["飛機", "鞋子", "水杯", "麵包", "橫幅", "訂書機", "壁畫", "貓砂", ......]其檔名示例:飛機 & 鞋子 & 水杯_1231290424123.png注意:中文字符集一般比數字英文大很多,收斂時間較長,同樣也需要更多的樣本量,千萬不要想著幾千張圖片訓練幾千字符集的驗證碼,畢竟機器也不是神 形如上圖的圖片能輕鬆訓練到 98% 以上的識別率。ImageWidth、ImageHeight 引數只要和當前圖片尺寸匹配即可,其實這裡的配置主要是為了方便後面的部署智慧策略。這個 Pretreatment 引數主要是圖片預處理用的,例如下面這個有趣的 GIF 動圖, 透過觀察,滾動勻速,位數固定,那麼一定存在某兩個固定的幀,完全包含前三和後三位的內容。這種就可以採用拼接的形式,將包含完整 6 位的內容的圖片拼接為一張,使用 Pretreatment/ConcatFrames 引數,選取前後兩個幀進行水平拼接,適用於處理滾動型 GIF,而閃爍型 GIF 可以使用 BlendFrames 引數進行圖層融合。1. 經過 採集標註樣本形如 xxx_隨機數. png 2. 樣本打包 可以透過 GUI 介面的 [Make Dataset],或者使用 make_dataset.py 手動配置打包樣本,打包的目的主要是為了減少硬碟的 IO 讀寫。有時候準備的樣本比較少,訓練結果不滿意,重新採集了一部分樣本怎麼加入訓練呢?對於增量的樣本打包可以使用[Attach Dataset],無需重新打包。PS:使用原始碼的同學需要具備一定的程式設計基礎,儘量不去修改核心函式和靜態定義以免出現錯誤,修改程式碼的時候請確保配套的部署專案對應的地方也一併修改了。按照上面的介紹,講解雖多,但實際上只需要配置極少數的引數,就可以開始訓練了,高階玩家一般配置不超過 10 秒。1. 建立好專案後,在 PyCharm 中執行 trains.py,也可以在啟用 Virtualenv 下使用終端亦或在安裝依賴的全域性環境下執行2. 本文建議全程使用 GUI 介面進行操作,原始碼使用 GUI 僅需啟動 app.py 即可。python3 trains.py
下圖為訓練通用模型的過程截圖,耐心等待訓練結束即可。
訓練結束會在專案路徑的 out 下看到以下結構的檔案,pb 為模型,yaml 為模型配置檔案,下面該到部署環節了。一般驗證碼識別在企業中很少以 SDK 的形式被使用,大多是以微服務出現的,獨立於其他的業務,獨立運營和維護,那麼企業級的部署服務又是怎樣的呢?專案地址:https://github.com/kerlomz/captcha_platform可以為各位提供一個參考,Tornado 服務僅作為一個例子,企業一般採用 gRPC 叢集遠端呼叫。如需要整合到專案裡透過 sdk 呼叫的,可以參考 MuggleOCR 的做法,它的核心繼承了 captcha_platform/sdk/pb/sdk.py:https://pypi.org/project/muggle-ocr/支援多模型部署
支援模型熱拔插
版本控制靈活
支援批次識別
智慧模型分發
筆者封裝了 Graph 會話管理,設計會話池,允許同時管理多模型,實現多模型動態部署方案。1) 訓練好的 pb 模型只要放在 graph 路徑下,yaml 檔案放在 model 路徑下(操作順序很重要,yaml 主要用於服務發現,透過 ModelName 引數定位對應的 pb 模型,如果順序顛倒,服務是無法載入尚未放置進來的模型的)。使用 SDK 呼叫時,yaml 和 pb 模型必須在同一路徑下。2) 解除安裝一個正在服務的模型,只需要刪除 yaml 和對應的 pb 模型即可。(模型已載入於記憶體所以無所謂順序)3) 更新一個已經部署載入的模型,只需按先後順序放置 pb 模型和高版本的 yaml 檔案,服務會自動發現並載入,舊模型優先順序被取代,不會再被呼叫,便可按上述方法解除安裝棄用的模型釋放記憶體。一切管理操作均無需重啟服務,可以無感知切換,方便維護提高了可用性。其次,如果讀者有很多驗證碼需求需要逐個定製,訓練時將所有尺寸一樣的圖片訓練成一個模型,服務根據圖片尺寸會自動定位對應的模型。當然也可以透過傳遞 model_name 引數精確控制多模型呼叫,這樣的設計允許定製化和通用性共存,當讀者們積累到一定量的樣本集時可以像 MuggleOCR 一樣訓練一套通用識別模型作為備用模型。模型之間亦彼此獨立,每增加部署一個模型,僅僅增加了少量的記憶體或視訊記憶體佔用,不少小企業也吃過定製模型的虧,找個人定製模型,每個模型都要獨立啟用一個服務,無形增加了成本,每個程式若重複載入一遍整個框架無疑是極大的資源浪費。前面有提到批次識別,有這種需求的使用者相對較少,這裡只做簡單介紹,給一個 12306 的例子,如圖所示:一張圖中包含了多個需要識別的部分,而框架中的 CorpParams 支援將大圖切割為小圖一併傳入,原本一個請求對於服務只能傳一張圖,現在可以透過裁剪功能一次傳入 9 張圖。程式碼如下:FieldParam:
CorpParams: [
{
"start_pos": [118, 0],
"interval_size": [0, 0],
"corp_num": [1, 1],
"corp_size": [60, 30]
},
{
"start_pos": [5, 40],
"interval_size": [5, 5],
"corp_num": [4, 2],
"corp_size": [66, 66]
}
]
OutputCoord: True
FieldParam/CorpParams 引數可以裁剪合併批次,該用法可避免多次呼叫。但是識別專案提供多種後端實現版本:Tornado/Flask/gRPC/Sanic,其中 Flask 和 Tornado 搭載了加密介面 / captcha/auth/v2,類似於微信公眾號開發介面的 SecretKey 和 AccessKey 介面,有興趣的可以在 demo.py 中閱讀呼叫原始碼瞭解。部署服務可以使用 package.py 編譯為可執行檔案,本文中提供的編譯版也是基於 Pyinstaller 打包編譯的,編譯版不需要考慮更換機器需要重新安裝環境,若使用原始碼部署的話,環境配置同訓練專案一樣,使用專案中提供的 requirements.txt 一鍵安裝全部依賴,部署服務預設推薦的是 CPU 版的 TensorFlow。部署服務推薦 Tornado 後端,目前最穩定的版本。# 埠 19952
python3 tornado_server.py
# 方案1,裸啟動, 埠 19951
python flask_server.py
# 方案2,使用gunicorn,埠 5000
pip install gunicorn
gunicorn -c deploy.conf.py flask_server:app
# 埠 19953
python3 sanic_server.py
# 埠 50054
python3 grpc_server.py
# 前臺執行
./captcha_platform_tornado
#後臺執行
nohup ./captcha_platform_tornado &
Windows:Windows 平臺下都是透過 python3 xxx_server.py 啟動對應的服務,注意,Tornado、Flask、Sanic 的效能在 Windows 平臺都大打折扣,gRPC 是 Google 開源的 RPC 服務,有較為優越的效能。編譯版直接執行編譯後的 exe 可執行檔案即可。請求為 JSON 格式,形如:{"image": "iVBORw0KGgoAAAANSUhEUgAAAFoAAAAjCAIAAA...base64 編碼後的影像二進位制流"}該返回為 JSON 格式,形如:{'uid': "9b5a6a34-9693-11ea-b6f9-525400a21e62", "message": "xxxx", "code": 0, "success": true}4. gRPC 服務:需要安裝依賴,grpcio、grpcio_tools 和對應的 grpc.proto 檔案,可以直接從專案中的示例程式碼 demo.py 中提取。python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./grpc.proto
grpcio、grpcio_tools 是根據 grpc.proto 使用上述命令生成的。class GoogleRPC(object):
def __init__(self, host: str):
self._url = '{}:50054'.format(host)
self.true_count = 0
self.total_count = 0
def request(self, image, model_type=None, model_site=None):
import grpc
import grpc_pb2
import grpc_pb2_grpc
channel = grpc.insecure_channel(self._url)
stub = grpc_pb2_grpc.PredictStub(channel)
response = stub.predict(grpc_pb2.PredictRequest(
image=image, split_char=',', model_type=model_type, model_site=model_site
))
return {"message": response.result, "code": response.code, "success": response.success}
if __name__ == '__main__':
result = GoogleRPC().request("base64編碼後的圖片二進位制流")
print(result)
1. 顏色提取的思路,可以採用 HSV/K-means 聚類進行顏色的分離提取:效果如下: 弊端顯而易見,會有較大的特徵丟失,識別率有較大的提升瓶頸,經過測試,中英文 + 漢字的識別率在 90% 左右。(1)同時預測顏色和字元內容,這種方法看起來比較正統,但是成本較高,需要標註每張圖的顏色和字元內容,這個要求有多高呢,一般的打碼平臺是無法提供這樣的結果的,打碼平臺只返回對應顏色的內容,只能人工標註,那麼需要多少樣本呢?按照筆者訓練的識別率 98 的模型用了 100w 左右的樣本。一張這樣的樣本標註假設需要 0.1 元,那麼 100w 樣本需要 10w 標註費用,假設 0.01 元,也要 1w 的標註費用。但是驗證碼高質量的人工標註幾乎是不存在的,因為很多樣本,人眼的識別率是不如機器的,總體標註的準確率大概也只能在 85 左右。看起來並不可取,有一種節約成本的辦法,可以透過演算法生成樣本,但是呢,生成的識別率英文數字還可以,中文的識別率就低的可憐了。(2)每個顏色分別訓練一個模型, 這種方法看起來有點蠢,但是確實比較合適有效的辦法了,可以輕鬆藉助打碼平臺的返回結果標註樣本。需要的顏色可以透過官網提供的欄位取到,返回結果透過打碼平臺識別得到,這樣一組合,樣本就有了。這種方法的成本相對較低,樣本數不變的前提下,打碼價格低於人工標註的成本。但是筆者訓練的是一種顏色的樣本用了 100w。每個顏色分別訓練這樣成本還是下不來。四種顏色就是 500w 樣本。官網的每次獲取圖片的時候顏色隨機出現的機率也不一定是 1/4。 (3)把所有顏色都透過顏色變換為一種顏色,整體思路同(2)。如下圖,筆者將黑色轉換為紅色,但是樣本成本只有採集一種顏色的成本。看起來是目前位置最佳的方案了,事實也是如此的。但是呢,100w 的樣本對於普通人來說也是一筆不小的花銷,即便有了樣本能做出來也需要花費不少的時間和精力。 不過採集樣本不是單純的接打碼平臺就完事了,需要經過官網判斷,只有透過驗證,正確的樣本才儲存下來。這樣有效的樣本對提高識別率才有幫助。 筆者實時對接官網對實驗模型進行檢驗,結果如上圖,測試了 200 + 次,識別率達到 98% 以上,識別速度的話,相較於 1.1 的方法省去了顏色提取,大大縮短了時間,CPU 大概 5-8 毫秒左右,模型大概 3mb。所以選擇合適的方案解決問題才是最終的目的,希望這個專案和這篇介紹能帶大家入門企業級的驗證碼識別。