主頁 > 後端開發 > Python使用EasyOCR庫對行程碼圖片進行OCR文字識別介紹與實踐

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

2022-06-11 19:50:08 後端開發

關注「WeiyiGeek」點我,點我

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

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

image.png

文章目錄
0x00 快速了解

  • EasyOCR 介紹
  • EasyOCR 參考來源

0x01 安裝部署

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

0x02 實踐案例

  1. 批量識別行程碼圖片

0x03 入坑出坑


0x00 快速了解

EasyOCR 介紹

Q: 什么是 EasyOCR ?

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


Q: 使用 EasyOCR 可以干什么?

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

image.png

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

image.png

EasyOCR Framework

image.png

溫馨提示: 圖中 灰色插槽是可更換的淺藍色模塊的占位符,我們可以重構代碼以支持可交換的檢測和識別演算法 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 = https://www.cnblogs.com/WeiyiGeek/archive/2022/06/05/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地址,將會回傳如下圖所示結果,

image.png

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

image.png


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.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】聯系我喲!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/488691.html

標籤:其他

上一篇:剛剛發現的可視化動態圖庫ipyvizzu,太好看了

下一篇:配置中心的設計-nacos vs apollo

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more