Python使用EasyOCR庫對行程碼圖片進行OCR文字識別介紹與實踐

2022-06-05 21:01:18

關注「WeiyiGeek」點我,點我

設為「特別關注」,每天帶你在B站玩轉網路安全運維、應用開發、物聯網IOT學習!

希望各位看友【關注、點贊、評論、收藏、投幣】,助力每一個夢想。

文章目錄
0x00 快速瞭解

  • EasyOCR 介紹
  • EasyOCR 參考來源

0x01 安裝部署

  • 環境依賴
  • 環境安裝
  • 方法引數

0x02 實踐案例

  1. 批次識別行程碼圖片

0x03 入坑出坑


0x00 快速瞭解

EasyOCR 介紹

Q: 什麼是 EasyOCR ?

描述: EasyOCR 是一個用於從影象中提取文字的 python 模組, 它是一種通用的 OCR,既可以讀取自然場景文字,也可以讀取檔案中的密集文字。目前支援 80 多種語言和所有流行的書寫指令碼,包括:拉丁文、中文、阿拉伯文、梵文、西里爾文等。


Q: 使用 EasyOCR 可以幹什麼?

描述: EasyOCR 支援兩種方式執行一種是常用的CPU,而另外一種是需要GPU支援並且需安裝CUDA環境, 我們使用其可以進行圖片中語言文字識別, 例如小程式裡圖片識別、車輛車牌識別(即車債管理系統)。

Tips: 在其官網有demo演示,我們可以使用其進行簡單圖片ocr識別,地址為https://www.jaided.ai/easyocr/ 或者 https://huggingface.co/spaces/tomofi/EasyOCR

EasyOCR Framework

溫馨提示: 圖中 灰色插槽是可更換的淺藍色模組的預留位置,我們可以重構程式碼以支援可交換的檢測和識別演演算法 api


EasyOCR 參考來源

官網地址: https://www.jaided.ai/easyocr/

專案地址: https://github.com/JaidedAI/EasyOCR

實踐專案原始碼地址:https://github.com/WeiyiGeek/SecOpsDev/tree/master/Project/Python/EasyOCR/Travelcodeocr

檔案原文地址: https://www.bilibili.com/read/cv16911816

實踐視訊地址: https://www.bilibili.com/video/BV1nY4y1x7JG

溫馨提示: 該專案基於來自多篇論文和開源儲存庫的研究和程式碼,所有深度學習執行都基於 Pytorch ,識別模型是 CRNN 它由 3 個主要部分組成:特徵提取(我們目前使用 Resnet )和 VGG、序列標記( LSTM )和解碼​​( CTC )。 ❤️


0x01 安裝部署

環境依賴

環境依賴

  • Python 建議 3.8 x64 以上版本 (原本我的環境是 Python 3.7 安裝時各種稀奇古怪的錯誤都出來,不得已abandon放棄)
  • easyocr 包 -> 依賴 torch 、torchvision 第三方包

注意事項:

  • Note 1.本章是基於 cpu 與 GPU 下使用 EasyOCR, 如果你需要使用 GPU 跑, 那麼請你安裝相應的CUDA環境。
$ nvidia-smi -l
Fri May 27 14:57:57 2022
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 465.19.01    Driver Version: 465.19.01    CUDA Version: 11.3     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA Tesla V1...  Off  | 00000000:1B:00.0 Off |                    0 |
| N/A   41C    P0    36W / 250W |      0MiB / 32510MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
  • Note 2.最好在Python 3.8 x64 位系統上安裝使用 easyocr , 非常注意其不支援32位元的python。

  • Note 3.對於 Windows,請先按照 https://pytorch.org 的官方說明安裝 torch 和 torchvision。 在 pytorch 網站上,請務必選擇您擁有的正確 CUDA 版本。 如果您打算僅在 CPU 模式下執行,請選擇 CUDA = None。


環境安裝

描述: 此處我們使用 pip 安裝 easyocr 使用以及通過官方提供的Dockerfile。

pip 方式
對於最新的穩定版本:

pip install easyocr -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

對於最新的開發版本:

pip install git+git://github.com/jaidedai/easyocr.git

Dockerfile
描述: 由於國內網路環境因素, 此處我將官方提供的Dockerfile稍作更改。

$ cd /opt/images/easyocr && git clone https://github.com/JaidedAI/EasyOCR.git --depth=1
$ ls
Dockerfile  EasyOCR

$ cat Dockerfile
# pytorch OS is Ubuntu 18.04
FROM pytorch/pytorch
LABEL DESC="EasyOCR Enviroment Build with Containerd Images"
ARG service_home="/home/EasyOCR" 

# Enviroment && Software
RUN sed -i -e "s#archive.ubuntu.com#mirrors.aliyun.com#g" -e "s#security.ubuntu.com#mirrors.aliyun.com#g" /etc/apt/sources.list  && \
    apt-get update -y && \
    apt-get install -y \
    libglib2.0-0 \
    libsm6 \
    libxext6 \
    libxrender-dev \
    libgl1-mesa-dev \
    git \
    vim \
    # cleanup
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists

# COPY EasyOCR is Github(https://github.com/JaidedAI/EasyOCR.git)
COPY ./EasyOCR "$service_home"

# Build
RUN cd "$service_home" \
  && pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ \
  && python setup.py build_ext --inplace -j 4 \
  && python -m pip install -e . 

環境驗證

# Windows 環境
pip freeze | findstr "easyocr"
easyocr @ file:///E:/%E8%BF%85%E9%9B%B7%E4%B8%8B%E8%BD%BD/easyocr-1.4.2-py3-none-any.whl
# Linux & 容器環境
$ pip freeze | grep "EasyOCR"
-e git+https://github.com/JaidedAI/EasyOCR.git@7a685cb8c4ba14f2bc246f89c213f1a56bbc2107#egg=easyocr

# python 命令列中使用
>>> from pprint import pprint  # 方便格式化輸出
>>> import easyocr
>>> reader = easyocr.Reader(['ch_sim','en'])
CUDA not available - defaulting to CPU. Note: This module is much faster with a GPU.
>>> result = reader.readtext('00e336dbde464c809ef1f6ea568d4621.png')
>>> pprint(result)
[([[354, 46], [444, 46], [444, 76], [354, 76]], '中國移動', 0.981803297996521),
 ([[477, 55], [499, 55], [499, 75], [477, 75]], '46', 0.3972922105840435),
 ([[533, 55], [555, 55], [555, 75], [533, 75]], '5G', 0.5360637875500641),
 ([[354, 76], [474, 76], [474, 104], [354, 104]],
  '中國移動四 ',
  0.25950584649873865),
 ([[489, 57], [625, 57], [625, 95], [489, 95]],
  'GMl s @',
  0.011500043801327683),
 ([[693, 55], [801, 55], [801, 95], [693, 95]], 'Q92%', 0.022083675488829613),
 ([[864, 60], [950, 60], [950, 92], [864, 92]], '09:03', 0.9793587315696877),
 ([[884, 158], [938, 158], [938, 214], [884, 214]], '@', 0.29484160211053734),
 ([[123, 298], [592, 298], [592, 361], [123, 361]],
  '通訊行程卡提供服務>',
  0.6739866899213806),
 ([[115, 429], [384, 429], [384, 497], [115, 497]],
  '通訊行程卡',
  0.9159307714297187),
 ([[153, 596], [848, 596], [848, 704], [153, 704]],
  '通訊巨量資料行程卡',
  0.2522292283860262),
 ([[303, 723], [699, 723], [699, 785], [303, 785]],
  '疫情防控;人人有責',
  0.7030201163942564),
 ([[347, 844], [653, 844], [653, 892], [347, 892]],
  '請收下綠色行程卡',
  0.9120484515458063),
 ([[248, 950], [754, 950], [754, 1004], [248, 1004]],
  '157****2966的動態行程卡',
  0.9868984946820241),
 ([[173, 1045], [345, 1045], [345, 1105], [173, 1105]],
  '更新於:',
  0.972654586401667),
 ([[360, 1049], [829, 1049], [829, 1100], [360, 1100]],
  '2022.05.2509:03:56',
  0.9411191664033213),
 ([[110, 1670], [633, 1670], [633, 1732], [110, 1732]],
  '您於前14夭內到達或途經:',
  0.8531442220608394),
 ([[648, 1674], [788, 1674], [788, 1730], [648, 1730]],
  '重慶市',
  0.9605511910615995),
 ([[104, 1778], [898, 1778], [898, 1810], [104, 1810]],
  '結果包含您在前14天內到訪的國家(地區) 與停留4小時以上的國內城市',
  0.6574011574316847),
 ([[272, 1825], [729, 1825], [729, 1863], [272, 1863]],
  '色卡僅對到訪地作提醒。不關聯健康狀況',
  0.8806245499955613),
 ([[383, 1891], [607, 1891], [607, 1933], [383, 1933]],
  '本服務聯合提供',
  0.9781898210349773),
 ([[119, 1966], [337, 1966], [337, 2006], [119, 2006]],
  'CAICT 中國信通院',
  0.3636917908522541),
 ([[435, 1963], [533, 1963], [533, 1999], [435, 1999]],
  '中國電信',
  0.08182162046432495),
 ([[624, 1966], [702, 1966], [702, 1990], [624, 1990]],
  '中國移動',
  0.9323447942733765),
 ([[812, 1966], [892, 1966], [892, 1990], [812, 1990]],
  '中國聯通',
  0.9082608819007874),
 ([[441, 1993], [531, 1993], [531, 2005], [441, 2005]],
  'CINA TUUUC0',
  0.028013896371299665),
 ([[629, 1987], [701, 1987], [701, 2003], [629, 2003]],
  'ChnaMobile',
  0.7021787396208221),
 ([[815, 1989], [893, 1989], [893, 2003], [815, 2003]],
  'Chnoumco',
  0.19655737186726854),
 ([[107, 2077], [281, 2077], [281, 2119], [107, 2119]],
  '證通查來了!',
  0.9745880948510078),
 ([[467, 2075], [825, 2075], [825, 2117], [467, 2117]],
  '全國行動電話卡"一證通查',
  0.9208412317655043),
 ([[79, 2131], [269, 2131], [269, 2173], [79, 2173]],
  '立即點選進入',
  0.6082888941606105),
 ([[510, 2128], [644, 2128], [644, 2172], [510, 2172]],
  '防範詐騙',
  0.952128529548645),
 ([[663, 2129], [793, 2129], [793, 2173], [663, 2173]],
  '保護你我',
  0.9819014668464661)]
# 設定 --detail=0 輸出更簡單
>>> result = reader.readtext('00e336dbde464c809ef1f6ea568d4621.png', detail = 0) 

使用說明

  • Note 1.在使easyocr.Reader(['ch_sim','en'])於將模型載入到記憶體中(可能會耗費一些時間), 並且我們需要設定預設閱讀的語言列表, 可以同時使用多種語言,但並非所有語言都可以一起使用, 而通常會採用英語與其他語言聯合。

下面列舉出可用語言及其語言對應列表 (https://www.jaided.ai/easyocr/) :

# 對於我們來說常用語言如下:
# Language	Code Name
Simplified Chinese	ch_sim
Traditional Chinese	ch_tra
English	en

溫馨提示: 所選語言的模型權重將自動下載,或者您可以從模型中心 並將它們放在~/.EasyOCR/model資料夾中

  • Note 2.如果--gpu=True設定為True, 而機器又沒有GPU支援的化將預設採用 CPU ,所以通常你會看到如下提示:
# 如果您沒有 GPU,或者您的 GPU 記憶體不足,您可以通過新增 gpu=False. 
CUDA not available - defaulting to CPU. Note: This module is much faster with a GPU.
  • Note 3.在reader.readtext('引數值')函數中的引數值,可以是圖片路徑、也可是影象檔案位元組或者 OpenCV 影象物件(numpy 陣列)以及網際網路上影象的URL 等幾種方式.
# 影象路徑
reader.readtext('chinese.jpg')

# 影象URL
reader.readtext('https://www.weiyigeek.top/wechat.jpg')

# 圖形位元組
with open("chinese_tra.jpg", "rb") as f:
  img = f.read()
result = reader.readtext(img)

# 影象作為 numpy 陣列(來自 opencv)傳遞
img = cv2.imread('chinese_tra.jpg')
result = reader.readtext(img)
  • Note 3.從上面結果可以看出輸出結果將採用列表格式,每個專案分別代表一個邊界框(四個點)、檢測到的文字和可信度
 ([[347, 844], [653, 844], [653, 892], [347, 892]],  # 邊界 1 --> 2 -> 3 -> 4
  '請收下綠色行程卡',       # 文字
  0.9120484515458063),     # 可信度
  • Note 4.我們也可以在命令列中直接呼叫easyocr。
# 語法範例:
usage: easyocr [-h] -l LANG [LANG ...] [--gpu {True,False}] [--model_storage_directory MODEL_STORAGE_DIRECTORY]
  [--user_network_directory USER_NETWORK_DIRECTORY] [--recog_network RECOG_NETWORK]
  [--download_enabled {True,False}] [--detector {True,False}] [--recognizer {True,False}]
  [--verbose {True,False}] [--quantize {True,False}] -f FILE
  [--decoder {greedy,beamsearch,wordbeamsearch}] [--beamWidth BEAMWIDTH] [--batch_size BATCH_SIZE]
  [--workers WORKERS] [--allowlist ALLOWLIST] [--blocklist BLOCKLIST] [--detail {0,1}]
  [--rotation_info ROTATION_INFO] [--paragraph {True,False}] [--min_size MIN_SIZE]
  [--contrast_ths CONTRAST_THS] [--adjust_contrast ADJUST_CONTRAST] [--text_threshold TEXT_THRESHOLD]
  [--low_text LOW_TEXT] [--link_threshold LINK_THRESHOLD] [--canvas_size CANVAS_SIZE]
  [--mag_ratio MAG_RATIO] [--slope_ths SLOPE_THS] [--ycenter_ths YCENTER_THS] [--height_ths HEIGHT_THS]
  [--width_ths WIDTH_THS] [--y_ths Y_THS] [--x_ths X_THS] [--add_margin ADD_MARGIN]

# 案例:
$ easyocr -l ch_sim en -f chinese.jpg --detail=1 --gpu=False
$ easyocr -l ch_sim en -f .\0a1e948e90964d42b435d63c9f0aa268.png --detail=0 --gpu=True
  # CUDA not available - defaulting to CPU. Note: This module is much faster with a GPU.
....
請收下綠色行程卡
191****8499的動態行程卡
更新於:2022.05.2510:49:21
您於前14夭內到達或途經:  重慶市
結果包含您在前14天內到訪的國家(地區)與停留4小時以上的國內城市
.....

方法引數

描述: 官方提供的包的模組方法以及引數說明, 參考地址 ( https://www.jaided.ai/easyocr/documentation/ )

  • 1.EasyOCR 的基礎類別
easyocr.Reader(['ch_sim','en'], gpu=False, model_storage_directory="~/.EasyOCR/.",download_enabled=True, user_network_directory="~/.EasyOCR/user_network",recog_network="recog_network",detector=True,recognizer=True)
# download_enabled :如果 EasyOCR 無法找到模型檔案,則啟用下載
# model_storage_directory: 模型資料目錄的路徑
# user_network_directory: 使用者定義識別網路的路徑
# detector : 載入檢測模型到記憶體中
# recognizer : 載入識別模型到記憶體中
  • 2.Reader 物件的主要方法, 有 4 組引數:General、Contrast、Text Detection 和 Bounding Box Merging, 其返回值為列表形式。
reader.readtext(
  'chinese.jpg',image,decoder='greedy',beamWidth=5,batch_size=1,workers=0,allowlist="ch_sim",blocklist="ch_tra",detail=1,paragraph=False,min_size=10,rotation_info=[90, 180 ,270],
  contrast_ths = 0.1, adjust_contrast = 0.5,
  text_threshold = 0.7, low_text = 0.4,link_threshold = 0.4, canvas_size = 2560, mag_ratio = 1,
  slope_ths = 0.1, ycenter_ths = 0.5, height_ths = 0.5, width_ths = 0.5, add_margin = 0.1, x_ths = 1.0, y_ths = 0.5

)

# Parameters 1: General
--batch_size : 當其值大於 1 時將使 EasyOCR 更快,但使用更多記憶體。
--allowlist : 強制 EasyOCR 僅識別字元子集。  對特定問題有用(例如車牌等)
--detail : 將此設定為 0 以進行簡單輸出.
--paragraph :將結果合併到段落中
--min_size: 過濾小於畫素最小值的文字方塊
--rotation_info:允許 EasyOCR 旋轉每個文字方塊並返回具有最高置信度分數的文字方塊。例如,對所有可能的文字方向嘗試 [90, 180 ,270]。

# Parameters 2: Contrast
--contrast_ths : 對比度低於此值的文字方塊將被傳入模型 2 次,首先是原始影象,其次是對比度調整為「adjust_contrast」值,結果將返回具有更高置信度的那個。
--adjust_contrast : 低對比度文字方塊的目標對比度級別


# Parameters 3: Text Detection (from CRAFT)
--text_threshold: 文字置信度閾值
--link_threshold: 連結置信度閾值
--canvas_size: 最大影象尺寸,大於此值的影象將被縮小。
--mag_ratio: 影象放大率

# Parameters 4: Bounding Box Merging
height_ths (float, default = 0.5) - 盒子高度的最大差異,不應合併文字大小差異很大的框。
width_ths (float, default = 0.5) - 合併框的最大水平距離。
x_ths (float, default = 1.0) - 當段落 = True 時合併文字方塊的最大水平距離。
y_ths (float, default = 0.5) - 當段落 = True 時合併文字方塊的最大垂直距離。
  • 3.detect method, 檢測文字方塊的方法。
Parameters
  image (string, numpy array, byte) - Input image
  min_size (int, default = 10) - Filter text box smaller than minimum value in pixel
  text_threshold (float, default = 0.7) - Text confidence threshold
  low_text (float, default = 0.4) - Text low-bound score
  link_threshold (float, default = 0.4) - Link confidence threshold
  canvas_size (int, default = 2560) - Maximum image size. Image bigger than this value will be resized down.
  mag_ratio (float, default = 1) - Image magnification ratio
  slope_ths (float, default = 0.1) - Maximum slope (delta y/delta x) to considered merging. Low value means tiled boxes will not be merged.
  ycenter_ths (float, default = 0.5) - Maximum shift in y direction. Boxes with different level should not be merged.
  height_ths (float, default = 0.5) - Maximum different in box height. Boxes with very different text size should not be merged.
  width_ths (float, default = 0.5) - Maximum horizontal distance to merge boxes.
  add_margin (float, default = 0.1) - Extend bounding boxes in all direction by certain value. This is important for language with complex script (E.g. Thai).
  optimal_num_chars (int, default = None) - If specified, bounding boxes with estimated number of characters near this value are returned first.

Return horizontal_list, free_list - horizontal_list is a list of regtangular text boxes. The format is [x_min, x_max, y_min, y_max]. free_list is a list of free-form text boxes. The format is [[x1,y1],[x2,y2],[x3,y3],[x4,y4]]. 
  • 4.recognize method, 從文字方塊中識別字元的方法,如果未給出 Horizo​​ntal_list 和 free_list,它將整個影象視為一個文字方塊。
Parameters
  image (string, numpy array, byte) - Input image
  horizontal_list (list, default=None) - see format from output of detect method
  free_list (list, default=None) - see format from output of detect method
  decoder (string, default = 'greedy') - options are 'greedy', 'beamsearch' and 'wordbeamsearch'.
  beamWidth (int, default = 5) - How many beam to keep when decoder = 'beamsearch' or 'wordbeamsearch'
  batch_size (int, default = 1) - batch_size>1 will make EasyOCR faster but use more memory
  workers (int, default = 0) - Number thread used in of dataloader
  allowlist (string) - Force EasyOCR to recognize only subset of characters. Useful for specific problem (E.g. license plate, etc.)
  blocklist (string) - Block subset of character. This argument will be ignored if allowlist is given.
  detail (int, default = 1) - Set this to 0 for simple output
  paragraph (bool, default = False) - Combine result into paragraph
  contrast_ths (float, default = 0.1) - Text box with contrast lower than this value will be passed into model 2 times. First is with original image and second with contrast adjusted to 'adjust_contrast' value. The one with more confident level will be returned as a result.
  adjust_contrast (float, default = 0.5) - target contrast level for low contrast text box

Return list of results 

0x02 實踐案例

1.批次識別行程碼圖片

描述: 公司有業務需求做一個行程碼識別, 當前是呼叫某雲的文字識別介面來識別行程碼, 而其按照呼叫次數進行計費, 所以為了節約成本就要Python參考了Github上大佬的們專案, 擷取部分函數,並使用Flask Web 框架進行封裝,從而實現通過網頁進行請求呼叫,並返回JSON字串。

專案原始碼Github地址:https://github.com/WeiyiGeek/SecOpsDev/tree/master/Project/Python/EasyOCR/Travelcodeocr

專案實踐
步驟 01.安裝flask及其依賴模組的。

pip install flask -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

步驟 02.專案路徑以及圖片路徑 D:\Study\Project

PS D:\Study\Project> ls
    目錄: D:\Study\Project
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2022/5/25     15:59                img
-a----         2022/5/25     19:34           3966 setup.py

步驟 03.基於Flask web框架下進行呼叫EasyOCR執行圖片文字識別的python程式碼.

# -*- coding: utf-8 -*-
# ####################################################################
# Author: WeiyiGeek
# Description: 基於easyocr實現巨量資料通訊行程卡圖片識別資訊獲取-Flask專案。
# Time: 2022年5月25日 17點31分
# Blog: https://www.weiyigeek.top
# Email: [email protected]
# ====================================================================
# 環境依賴與模組安裝, 建議 Python 3.8.x 的環境下進行
# pip install flask
# pip install easyocr
# ====================================================================
# 行程碼有綠色、黃色、橙色、紅色四種顏色。
# 1、紅卡:行程中的中高風險地市將標記為紅色字型作提示。
# 2、橙卡:新冠肺炎確診或疑似患者的密切接觸者。
# 3、黃卡:海外國家和地區。
# 4、綠卡:其他地區。行程卡結果包含在前14天內到訪的國家(地區)與停留4小時以上的國內城市。色卡僅對到訪地作提醒,不關聯健康狀況。
# #####################################################################
import os,sys
import cv2
import re
import glob
import json
import easyocr
from flask import Flask, jsonify, request,render_template
from datetime import datetime
from werkzeug.utils import secure_filename
import numpy as np
import collections

app = Flask(__name__)

# 專案執行路徑與行程碼圖片路徑定義
RUNDIR = None
IMGDIR = None
colorDict= {"red": "紅色", "red1": "紅色", "orange": "橙色", "yellow": "黃色", "green": "綠色"}

def getColorList():
  """
  函數說明: 定義字典存放 HSV 顏色分量上下限 (HSV-RGB)
  例如:{顏色: [min分量, max分量]}
      {'red': [array([160, 43, 46]), array([179, 255, 255])]}
  返回值: 專門的容器資料型別,提供Python通用內建容器、dict、list、set和tuple的替代品。
  """
  dict = collections.defaultdict(list)

  # 紅色
  lower_red = np.array([156, 43, 46])
  upper_red = np.array([180, 255, 255])
  color_list = []
  color_list.append(lower_red)
  color_list.append(upper_red)
  dict['red']=color_list
 
  # 紅色2
  lower_red = np.array([0, 43, 46])
  upper_red = np.array([10, 255, 255])
  color_list = []
  color_list.append(lower_red)
  color_list.append(upper_red)
  dict['red2'] = color_list

  # 橙色
  lower_orange = np.array([11, 43, 46])
  upper_orange = np.array([25, 255, 255])
  color_list = []
  color_list.append(lower_orange)
  color_list.append(upper_orange)
  dict['orange'] = color_list
 
  # 黃色
  lower_yellow = np.array([26, 43, 46])
  upper_yellow = np.array([34, 255, 255])
  color_list = []
  color_list.append(lower_yellow)
  color_list.append(upper_yellow)
  dict['yellow'] = color_list

  # 綠色
  lower_green = np.array([35, 43, 46])
  upper_green = np.array([77, 255, 255])
  color_list = []
  color_list.append(lower_green)
  color_list.append(upper_green)
  dict['green'] = color_list

  return dict

def getTravelcodeColor(img_np):
  """
  函數說明: 利用閾值返回行程碼主頁顏色
  引數值: cv2.imread() 讀取的影象物件(np陣列)
  返回值: 行程卡顏色{紅、橙、綠}
  """
  hsv = cv2.cvtColor(img_np, cv2.COLOR_BGR2HSV)
  maxsum = -100
  color = None
  color_dict = getColorList()
  for d in color_dict:
    mask = cv2.inRange(hsv,color_dict[d][0],color_dict[d][1])
    # cv2.imwrite(os.path.join(os.path.abspath(os.curdir),"img",d+'.jpg')  ,mask)
    binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)[1]
    binary = cv2.dilate(binary,None,iterations=2)
    cnts, hiera = cv2.findContours(binary.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    sum = 0
    for c in cnts:
      sum+=cv2.contourArea(c)
    if sum > maxsum :
      maxsum = sum
      color = d

  return colorDict[color]


def information_filter(file_path,img_np,text_str):
  """
  函數說明: 提出ocr識別的行程碼
  引數值:字串,檔名稱
  返回值:有效資訊組成的字典
  """
  # 健康碼欄位
  try:
    re_healthcode = re.compile('請收下(.{,2})行程卡')
    healthcode = re_healthcode.findall(text_str)[0]
  except Exception as _:
    healthcode = getTravelcodeColor(img_np)  # 文字無法識別時採用圖片顏色識別
    print("[*] Get Photo Color = ",healthcode)

  # 電話欄位
  re_phone = re.compile('[0-9]{3}\*{4}[0-9]{4}')
  phone_str = re_phone.findall(text_str)[0]

  # 日期欄位
  re_data = re.compile('2022\.[0-1][0-9]\.[0-3][0-9]')
  data_str = re_data.findall(text_str)[0]

  # 時間欄位
  re_time = re.compile('[0-9][0-9]:[0-9][0-9]:[0-9][0-9]')
  time_str = re_time.findall(text_str)[0]

  # 地區城市欄位
  citys_re = re.compile('到達或途經:(.+)結果包含')
  citys_str = citys_re.findall(text_str)[0].strip().split('(')[0]

  result_dic = {"status": "succ", "file": file_path ,"型別": healthcode, "電話": phone_str, "日期": data_str, "時間": time_str, "行程": citys_str}
  print("\033[032m",result_dic,"\033[0m")
  return result_dic


def getTravelcodeInfo(filename, img_np):
  """
  函數說明: 返回以JSON字串格式過濾後結果
  引數值:檔名稱,影象作為 numpy 陣列(來 opencv傳遞
  返回值:JSON字串格式
  """
  # 灰度處理
  img_gray = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
  # 閾值二進位制 - > 127 設定為255(白),否則0(黑) -> 淡白得更白,淡黑更黑
  _,img_thresh = cv2.threshold(img_gray,180,255,cv2.THRESH_BINARY)
  # 影象 OCR 識別
  text = reader.readtext(img_thresh, detail=0, batch_size=10) 
  result_dic = information_filter(filename, img_np, "".join(text))
  return result_dic

# Flask 路由 - 首頁
@app.route('/')
@app.route('/index')
def Index():
  return "<h4 style='text-algin:center'>https://www.weiyigeek.top</h4><script>window.location.href='https://www.weiyigeek.top'</script>"

# Flask 路由 - /tools/ocr
@app.route('/tools/ocr',methods=["GET"])
def Travelcodeocr():
  """
  請求路徑: /tools/ocr
  請求引數: (/tools/ocr?file=20220520/test.png, /tools/ocr?dir=20220520)
  """
  filename = request.args.get("file")
  dirname = request.args.get("dir")
  if (filename):
    img_path = os.path.join(IMGDIR, filename)
    if (os.path.exists(img_path)):
      print(img_path)  # 列印路徑
      img_np = cv2.imread(img_path)

      try:
        result_dic_succ = getTravelcodeInfo(filename,img_np)
      except Exception as err:
        print("\033[31m"+ img_path + " -->> " + str(err) + "\033[0m")
        return json.dumps({"status":"err", "img": filename}).encode('utf-8'), 200, {"Content-Type":"application/json"} 
      
      return json.dumps(result_dic_succ, ensure_ascii=False).encode('utf-8'), 200, {"Content-Type":"application/json"}
    else:
      return jsonify({"status": "err","msg": "檔案"+img_path+"路徑不存在."})

  elif (dirname and os.path.join(IMGDIR, dirname)):
    result_dic_all = []
    result_dic_err = []
    img_path_all =  glob.iglob(os.path.join(os.path.join(IMGDIR,dirname)+"/*.[p|j]*g"))   # 正則匹配 png|jpg|jpeg 字尾的字尾,返回的是迭代器。
    for img_path in img_path_all:
      print(img_path) # 列印路徑
      img_np = cv2.imread(img_path)

      try:
        result_dic_succ = getTravelcodeInfo(os.path.join(dirname,os.path.basename(img_path)),img_np)
      except Exception as err:
        print("\033[31m"+ img_path + " -->> " + str(err) + "\033[0m") # 輸出識別錯誤的影象
        result_dic_err.append(img_path)
        continue

      # 成功則加入到List列表中
      result_dic_all.append(result_dic_succ)

    res_succ_json=json.dumps(result_dic_all, ensure_ascii=False)
    res_err_json=json.dumps(result_dic_err, ensure_ascii=False)

    with open(os.path.join(IMGDIR, dirname, dirname + "-succ.json"),'w') as succ:
      succ.write(res_succ_json)
    with open(os.path.join(IMGDIR, dirname,  dirname + "-err.json"),'w') as error:
      error.write(res_err_json)

    return res_succ_json.encode('utf-8'), 200, {"Content-Type":"application/json"}
  else:
    return jsonify({"status": "err","msg": "請求引數有誤!"})


# Flask 路由 - /tools/upload/ocr
@app.route('/tools/upload/ocr',methods=["GET","POST"])
def TravelcodeUploadocr():
  if request.method == 'POST':
    unix = datetime.now().strftime('%Y%m%d-%H%M%S%f')
    f = request.files['file']
    if (f.mimetype == 'image/jpeg' or f.mimetype == 'image/png'):
      filedate = unix.split("-")[0]
      filesuffix = f.mimetype.split("/")[-1]
      uploadDir = os.path.join('img',filedate)

      # 判斷上傳檔案目錄是否存在
      if (not os.path.exists(uploadDir)):
        os.makedirs(uploadDir)

      img_path = os.path.join(uploadDir,secure_filename(unix+"."+filesuffix))  # 圖片路徑拼接
      print(img_path)     # 列印路徑
      f.save(img_path)    # 寫入圖片

      # 判斷上傳檔案是否存在
      if (os.path.exists(img_path)):
        img_np = cv2.imread(img_path)
        try:
          result_dic_succ = getTravelcodeInfo(os.path.join(filedate,os.path.basename(img_path)),img_np)
        except Exception as err:
          print("\033[31m"+ err + "\033[0m")
          return json.dumps({"status":"err", "img": img_path}).encode('utf-8'), 200, {"Content-Type":"application/json"}
        return json.dumps(result_dic_succ, ensure_ascii=False).encode('utf-8'), 200, {"Content-Type":"application/json"}
      else:
        return jsonify({"status": "err","msg": "檔案"+img_path+"路徑不存在!"})
    else:
      return jsonify({"status": "err","msg": "不能上傳除 jpg 與 png 格式以外的圖片"})
  else:
    return render_template('index.html')

# 程式入口
if __name__ == '__main__':
  try:
    RUNDIR = sys.argv[1]
    IMGDIR = sys.argv[2]
  except Exception as e:
    print("[*] Uage:"+ sys.argv[0] + " RUNDIR IMGDIR")
    print("[*] Default:"+ sys.argv[0] + " ./ ./img" + "\n" )
    RUNDIR = os.path.abspath(os.curdir)
    IMGDIR = os.path.join(RUNDIR,"img")
  # finally:
  #   if os.path.exists(RUNDIR):
  #     RUNDIR = os.path.abspath(os.curdir)
  #   if os.path.exists(IMGDIR):
  #     IMGDIR = os.path.join(RUNDIR,"img")

  # 使用easyocr模組中的Reader方法, 設定識別中英文兩種語言
  reader = easyocr.Reader(['ch_sim', 'en'], gpu=False) 
  # 使用Flask模組執行web
  app.run(host='0.0.0.0', port=8000, debug=True)

步驟 03.執行該指令碼並使用瀏覽進行指定行程碼圖片路徑以及識別提取。

python .\setup.py
  # Using CPU. Note: This module is much faster with a GPU.
  # * Serving Flask app 'index' (lazy loading)
  # * Environment: production
  #   WARNING: This is a development server. Do not use it in a production deployment.
  #   Use a production WSGI server instead.
  # * Debug mode: on
  # * Running on all addresses (0.0.0.0)
  #   WARNING: This is a development server. Do not use it in a production deployment.
  # * Running on http://127.0.0.1:8000
  # * Running on http://10.20.172.106:8000 (Press CTRL+C to quit)
  # * Restarting with stat
  # Using CPU. Note: This module is much faster with a GPU.
  # * Debugger is active!
  # * Debugger PIN: 115-313-307

溫馨提示: 從上面的Python指令碼中可以看出我們可使用file引數指定圖片路徑或者使用dir引數指定行程碼圖片存放目錄(預設在img目錄下的子目錄)。

例如,獲取單個行程碼圖片資訊,我本地瀏覽器存取http://127.0.0.1:8000/tools/ocr?file=20220530/00e336dbde464c809ef1f6ea568d4621.png地址,將會返回如下JSON字串。

D:\Study\Project\img\20220530\00e336dbde464c809ef1f6ea568d4621.png
127.0.0.1 - - [01/Jun/2022 16:58:58] "GET /tools/upload/ocr HTTP/1.1" 200 -
{'status': 'succ', 'file': '20220530\\00e336dbde464c809ef1f6ea568d4621.png', '型別': '綠色', '電話': '157****2966', '日期': '2022.05.25', '時間': '09:03:56', '行程': '重慶市'} 

例如,獲取多個行程碼圖片識別資訊,我本地瀏覽器存取http://127.0.0.1:8000/tools/ocr?dir=20220530地址,將會返回如下圖所示結果。

例如, 我們可以上傳並識別行程碼圖片資訊,本地瀏覽器存取http://127.0.0.1:8000/tools/upload/ocr地址,將會返回如下圖所示結果。


0x03 入坑出坑

問題1.通過pip install 安裝easyocr離線的whl包是報ERROR: No matching distribution found for torch

  • 錯誤資訊:
pip install ./easyocr-1.4.2-py3-none-any.whl -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
ERROR: Could not find a version that satisfies the requirement torch (from easyocr) (from versions: none)
ERROR: No matching distribution found for torch
  • 解決辦法: python.exe -m pip install --upgrade pip

問題2.在Python3.7的環境中安裝easyocr依賴的torch模組的whl安裝包報not a supported wheel on this platform.錯誤

  • 錯誤資訊:
$ pip install torch-1.8.0+cpu-cp37-cp37m-win_amd64.whl -i https://pypi.tuna.tsinghua.edu.cn/simple/
WARNING: Requirement 'torch-1.8.0+cpu-cp37-cp37m-win_amd64.whl' looks like a filename, but the file does not exist
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/
ERROR: torch-1.8.0+cpu-cp37-cp37m-win_amd64.whl is 
  • 錯誤原因: 平臺與下載的whl不符合, 此處我遇到的問題明顯不是這個導致的,百度後我想是由於pip版本與python版本、以及系統平臺聯合導致。
  • 解決辦法:
# 解決1.假如,你是linux你可以通過 https://download.pytorch.org/whl/torch_stable.html 找到所需版本。
檔名解釋:cpu或顯示卡/檔名-版本號-python版本-應該是編譯格式-平臺-cpu型別(intel也選amd64)
# torch-1.8.0+cpu-cp37-cp37m-win_amd64.whl

# 解決2.將 torch-1.8.0+cpu-cp37-cp37m-win_amd64.whl 更名為 torch-1.8.0+cpu-cp37-cp37m-win32.whl

問題3.在執行呼叫torch模組的py指令碼時報Error loading "D:\****\lib\site-packages\torch\lib\asmjit.dll" or one of its dependencies.錯誤

  • 錯誤資訊:
Microsoft Visual C++ Redistributable is not installed, this may lead to the DLL load failure.
It can be downloaded at https://aka.ms/vs/16/release/vc_redist.x64.exe
Traceback (most recent call last):
.....
OSError: [WinError 193] <no description> Error loading "D:\Program Files (x86)\Python37-32\lib\site-packages\torch\lib\asmjit.dll" or one of its dependencies.

問題4.在安裝opencv_python_headless進行依賴模組安裝時報ERROR: No matching distribution found for torchvision>=0.5錯誤

  • 錯誤資訊:
Using cached https://mirrors.aliyun.com/pypi/packages/a4/0a/39b102047bcf3b1a58ee1cc83a9269b2a2c4c1ab3062a65f5292d8df6594/opencv_python_headless-4.5.4.60-cp37-cp37m-win32.whl (25.8 MB)
ERROR: Could not find a version that satisfies the requirement torchvision>=0.5 (from easyocr) (from versions: 0.1.6, 0.1.7, 0.1.8, 0.1.9, 0.2.0, 0.2.1, 0.2.2, 0.2.2.post2, 0.2.2.post3)
ERROR: No matching distribution found for torchvision>=0.5
  • 解決辦法: 如果你的 python 版本為3.7.x,那麼你只能安裝 torch 1.5torchvision0.6

問題5.在執行easyocr文字識別時出現Downloading detection model, please wait. This may take several minutes depending upon your network connection.提示

  • 問題描述: 在首次使用時會自動下載EasyOCR模組所需的模型, 而由於國內網路環境,通常會報出超時錯誤,此時我們提前從官網下載其所需的資料模型,並安裝在指定目錄中。
  • 模型下載: https://www.jaided.ai/easyocr/modelhub/
# 主要下載以下模型(如有其它需要請自行選擇下載)
english_g2 : https://github.com/JaidedAI/EasyOCR/releases/download/v1.3/english_g2.zip
zh_sim_g2 : https://github.com/JaidedAI/EasyOCR/releases/download/v1.3/zh_sim_g2.zip
CRAFT : https://github.com/JaidedAI/EasyOCR/releases/download/pre-v1.1.6/craft_mlt_25k.zip

# 模型安裝位置
# windows
C:\Users\WeiyiGeek\.EasyOCR\model

# Linux
/home/weiyigeek/.EasyOCR\model

作者:WeiyiGeek
原文連線: https://blog.weiyigeek.top/2022/5-8-658.html

文章書寫不易,如果您覺得這篇文章還不錯的,請給這篇專欄 【點個贊、投個幣、收個藏、關個注,轉個發】(人間五大情),這將對我的肯定,謝謝!。

本文章來源 我的Blog站點WeiyiGeek 公眾賬號 以及 我的BiliBili專欄 (技術交流、友鏈交換請郵我喲),謝謝支援!(๑′ᴗ‵๑) ❤
歡迎各位志同道合的朋友一起學習交流,如文章有誤請留下您寶貴的知識建議,通過郵箱【master#weiyigeek.top】聯絡我喲!