關註「WeiyiGeek」點我,點我 設為「特別關註」,每天帶你在B站玩轉網路安全運維、應用開發、物聯網IOT學習! 希望各位看友【關註、點贊、評論、收藏、投幣】,助力每一個夢想。 文章目錄 0x00 快速瞭解 EasyOCR 介紹 EasyOCR 參考來源 0x01 安裝部署 環境依賴 環境安裝 ...
關註「WeiyiGeek」點我,點我
設為「特別關註」,每天帶你在B站玩轉網路安全運維、應用開發、物聯網IOT學習!
希望各位看友【關註、點贊、評論、收藏、投幣】,助力每一個夢想。
文章目錄
0x00 快速瞭解
- EasyOCR 介紹
- EasyOCR 參考來源
0x01 安裝部署
- 環境依賴
- 環境安裝
- 方法參數
0x02 實踐案例
- 批量識別行程碼圖片
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, 從文本框中識別字元的方法,如果未給出 Horizontal_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.
- 解決辦法: 在你的電腦上下載安裝 https://aka.ms/vs/16/release/vc_redist.x64.exe 缺少的C++運行庫,重啟電腦。
問題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.5
和torchvision0.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】聯繫我喲!