【轉載】set the new width and height and then determine the ratio in change
> 本文由 [簡悅 SimpRead](http://ksria.com/simpread/) 轉碼, 原文地址 [zhuanlan.zhihu.com](https://zhuanlan.zhihu.com/p/64857243)
原文連結:
OpenCV OCR and text recognition with Tesseract - PyImageSearch
本教程將介紹如何使用 OpenCV OCR。我們將使用 OpenCV、Python 和 Tesseract 實現文字檢測和文字識別。
上一篇文章講了如何使用 OpenCV 的 EAST 深度學習模型執行文字檢測。使用該模型能夠檢測和定點陣圖像中文字的邊界框座標。
下一步就是使用 OpenCV 和 Tesseract 處理每一個包含文字的影象區域,識別這些文字並進行 OCR 處理。
使用 Tesseract 進行 OpenCV OCR 和文字識別
為了執行 OpenCV OCR 文字識別,首先需要安裝 Tesseract v4,它包括一個用於文字識別的高準確率的深度學習模型。
然後,我將展示如何寫一個 Python 程式,其中包括:
- 使用 OpenCV EAST 文字檢測器執行文字檢測,該模型是一個高準確率的深度學習文字檢測器,被用於檢測自然場景影象中的文字。
- 一旦我們使用 OpenCV 檢測出影象中的文字區域後,提取出每個文字 ROI 並將其傳入 Tesseract,從而構建完整的 OpenCV OCR 流程!
最後,我將展示一些使用 OpenCV 應用文字識別的示例結果,並討論該方法的侷限性和缺點。
How to install Tesseract 4
Tesseract 是一個很流行的開源 OCR 引擎,其在受限制的場景下能很好地執行,但是如果影象存在大量噪聲或者影象在應用 Tesseract 之前沒有經過合適的預處理,則效能較差。
深度學習影響了計算機視覺的幾乎所有方面,字元識別和手寫字型識別也不例外。基於深度學習的模型獲得了前所未有的文字識別精度,遠遠超出了傳統的特徵提取和機器學習方法。
Tesseract 引入了深度學習模型來進一步提升 OCR 準確率,Tesseract (v4) 最新版本支援基於深度學習的 OCR,準確率顯著提高。底層的 OCR 引擎使用的是一種
在這篇博文的後面,您將學習如何將 OpenCV 的 EAST 文字檢測演算法與 Tesseract v4 結合起來,以自動執行 OpenCV OCR。
Install OpenCV
要執行本教程的指令碼,您需要先安裝 3.4.2 或更高版本的 OpenCV。安裝教程可參考 這裡,該教程可確保您下載合適的 OpenCV 和 OpenCV-contrib 版本。
Install Tesseract 4 on Ubuntu
根據您使用的是 Ubuntu 18.04 還是 Ubuntu 17.04 或更早版本,用於在 Ubuntu 上安裝 Tesseract 4 的具體命令會有所不同。
使用 lsb_release
命令檢查 Ubuntu 版本:
$ lsb\_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.1 LTS
Release: 18.04
Codename: bionic
對於 Ubuntu 18.04 的使用者,Tesseract 4 是主 apt-get 庫的一部分,通過下列命令就可以安裝 Tesseract:
$ sudo apt install tesseract-ocr
如果您使用的是 Ubuntu 14、16 或 17 版本,那麼由於依賴需求,您需要一些額外的命令。
好訊息是 Alexander Pozdnyakov 建立了用於 Tesseract 的 Ubuntu PPA(Personal Package Archive),這使得在舊版本的 Ubuntu 上安裝 Tesseract 4 變得非常容易。
只需要向系統新增 alex-p/tesseract-ocr
PPA 庫,更新您的包定義,然後安裝 Tesseract:
$ sudo add-apt-repository ppa:alex-p/tesseract-ocr
$ sudo apt-get update
$ sudo apt install tesseract-ocr
如果沒有錯誤,那麼您應該已經成功安裝了 Tesseract 4。
Install Tesseract 4 on macOS
如果您的系統中安裝有 Homebrew(macOS「非官方」包管理器),那麼在 macOS 上安裝 Tesseract 4 只需要執行以下命令,確保指定 --HEAD
,即可在 Mac 電腦上安裝 Tesseract v4:
$ brew install tesseract --HEAD
如果您安裝的 Tesseract 版本不是 v4,那麼您需要先執行如下命令:
$ brew unlink tesseract
然後再執行帶 --HEAD
的安裝命令。
Verify your Tesseract version
安裝了 Tesseract 以後,您應該執行以下命令驗證 Tesseract 的版本:
$ tesseract -v
tesseract 4.0.0-beta.3
leptonica-1.76.0
libjpeg 9c : libpng 1.6.34 : libtiff 4.0.9 : zlib 1.2.11
Found AVX512BW
Found AVX512F
Found AVX2
Found AVX
Found SSE
只要在輸出中看到 tesseract 4
,那麼您就安裝了 Tesseract 的最新版本。
Install your Tesseract + Python bindings
安裝好 Tesseract 庫之後,我們需要安裝 Tesseract + Python 的捆綁,這樣我們的 Python 指令碼就可以通過 Tesseract,對 OpenCV 處理過的影象執行 OCR。
我們將使用 pip 來安裝 Pillow(PIL 的 Python 版本),然後安裝 pytesseract 和 imutils:
$ pip install pillow
$ pip install pytesseract
$ pip install imutils
現在開啟 Python shell,確認匯入 OpenCV 和 pytesseract 時沒有出錯: Now open up a Python shell and confirm that you can import both OpenCV and pytesseract :
$ python
Python 3.6.5 (default, Apr 1 2018, 05:46:30)
\[GCC 7.3.0\] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> import pytesseract
>>> import imutils
>>>
Understanding OpenCV OCR and Tesseract text recognition
現在我們已經在系統上成功安裝了 OpenCV 和 Tesseract ,我們需要簡要回顧一下我們的流程和相關命令。
首先,我們使用 OpenCV 的 EAST text detector 來檢測影象中的文字的存在。EAST 文字檢測器將提供文字 ROI 的邊界框座標_(x, y)_。
我們將提取每個文字 ROI,併入到 Tesseract v4 的 LSTM 深度學習文字識別演算法。LSTM 的輸出將給出實際的 OCR 結果。最後,在輸出影象上繪製 OpenCV OCR 結果。
其中使用到的 Tesseract
命令必須在 pytesseract 庫下呼叫。在呼叫 tessarct 庫時,我們需要提供幾個 flag。最重要的三個 flag 是 -l
、--oem
和 --ism
。這三個引數的詳細介紹可以檢視該文章的最後部分。
Project structure
使用 tree
命令在終端中檢視專案的目錄結構:
$ tree --dirsfirst
.
├── images
│ ├── example\_01.jpg
│ ├── example\_02.jpg
│ ├── example\_03.jpg
│ ├── example\_04.jpg
│ └── example\_05.jpg
├── frozen\_east\_text\_detection.pb
└── text\_recognition.py
1 directory, 7 files
我們的專案包含一個目錄和兩個重要的檔案:
images/
: 一個包含六個含有場景文字的測試影象的目錄。我們將使用這些影象進行 OpenCV OCR 操作。frozen_east_text_detection.pb
: EAST 文字檢測器。該 CNN 經過預訓練,可用於文字檢測。它是由 OpenCV 提供的。text_recognition.py
: 我們的 OCR 程式。我們將逐行講解該程式。它使用 EAST 文字檢測器找到影象中的文字區域,然後利用 Tesseract v4 進行文字識別。
Implementing our OpenCV OCR algorithm
開啟text_recognition.py
檔案並插入以下程式碼:
from imutils.object\_detection import non\_max\_suppression
import numpy as np
import pytesseract
import argparse
import cv2
最主要的是 pytesseract
和 OpenCV。imutils
包將用於非極大值抑制,因為 OpenCV 自帶的 NMSBoxes
函式無法相容 Python API。
接下來實現 decode_predictions
函式:
def decode\_predictions(scores, geometry):
# grab the number of rows and columns from the scores volume, then
# initialize our set of bounding box rectangles and corresponding
# confidence scores
(numRows, numCols) = scores.shape\[2:4\]
rects = \[\]
confidences = \[\]
# loop over the number of rows
for y in range(0, numRows):
# extract the scores (probabilities), followed by the
# geometrical data used to derive potential bounding box
# coordinates that surround text
scoresData = scores\[0, 0, y\]
xData0 = geometry\[0, 0, y\]
xData1 = geometry\[0, 1, y\]
xData2 = geometry\[0, 2, y\]
xData3 = geometry\[0, 3, y\]
anglesData = geometry\[0, 4, y\]
# loop over the number of columns
for x in range(0, numCols):
# if our score does not have sufficient probability,
# ignore it
if scoresData\[x\] < args\["min\_confidence"\]:
continue
# compute the offset factor as our resulting feature
# maps will be 4x smaller than the input image
(offsetX, offsetY) = (x \* 4.0, y \* 4.0)
# extract the rotation angle for the prediction and
# then compute the sin and cosine
angle = anglesData\[x\]
cos = np.cos(angle)
sin = np.sin(angle)
# use the geometry volume to derive the width and height
# of the bounding box
h = xData0\[x\] + xData2\[x\]
w = xData1\[x\] + xData3\[x\]
# compute both the starting and ending (x, y)-coordinates
# for the text prediction bounding box
endX = int(offsetX + (cos \* xData1\[x\]) + (sin \* xData2\[x\]))
endY = int(offsetY - (sin \* xData1\[x\]) + (cos \* xData2\[x\]))
startX = int(endX - w)
startY = int(endY - h)
# add the bounding box coordinates and probability score
# to our respective lists
rects.append((startX, startY, endX, endY))
confidences.append(scoresData\[x\])
# return a tuple of the bounding boxes and associated confidences
return (rects, confidences)
decode_predictions
在 這篇文章 中有詳細介紹。該函式:
- 使用基於深度學習的文字檢測器來檢測(不是識別)影象中的文字區域。
- 該文字檢測器生成兩個陣列,一個是給定區域包含文字的概率,另一個數組將這些概率對映到輸入影象中的邊界框座標位置。
EAST 文字檢測器生成兩個變數:
scores
:文字區域的概率。geometry
:文字區域的邊界框位置。
兩個變數都是 decode_predictions
函式的引數。該函式處理輸入資料,得出一個元組,其中包含文字邊界框位置和該邊界框包含文字的概率:
rects
:該值基於geometry
,其格式更加緊湊,方便我們稍後應用於 NMS。confidences
:該列表中的置信度值對應rects
中的每個矩形。
這兩個值都由 decode_predictions
函式得出。
注意:理想情況下,旋轉的邊界框也在 rects
內,但是提取旋轉邊界框不利於解釋本教程的一些概念。因此,我計算了水平的邊界框矩形(把 angle
考慮在內)。如果您想提取文字的旋轉邊界框並輸入到 Tesseract,您可以使用 angle = anglesData[x]
這裡獲取的角度。
下面我們來解析命令列引數:
\# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add\_argument("-i", "--image", type=str,
help="path to input image")
ap.add\_argument("-east", "--east", type=str,
help="path to input EAST text detector")
ap.add\_argument("-c", "--min-confidence", type=float, default=0.5,
help="minimum probability required to inspect a region")
ap.add\_argument("-w", "--width", type=int, default=320,
help="nearest multiple of 32 for resized width")
ap.add\_argument("-e", "--height", type=int, default=320,
help="nearest multiple of 32 for resized height")
ap.add\_argument("-p", "--padding", type=float, default=0.0,
help="amount of padding to add to each border of ROI")
args = vars(ap.parse\_args())
我們的 python 指令碼需要兩個命令列引數:
--image
:輸入影象的路徑。 --east
:預訓練 EAST 文字檢測器的路徑。
下列命令列引數是可選的:
--min-confidence
:檢測到的文字區域的最小概率。 --width
:影象輸入 EAST 文字檢測器之前需要重新調整的寬度,EAST 模型要求寬度是 32 的倍數。 --height
:與寬度類似。EAST 模型要求調整後的高度是 32 的倍數。 --padding
:填充到每個 ROI 邊框的(可選)畫素數量。如果您發現 OCR 結果不正確,那麼您可以嘗試 0.05、0.10 等值。
下面,我們將載入和預處理影象,並初始化關鍵變數:
\# load the input image and grab the image dimensions
image = cv2.imread(args\["image"\])
orig = image.copy()
(origH, origW) = image.shape\[:2\]
# set the new width and height and then determine the ratio in change
# for both the width and height
(newW, newH) = (args\["width"\], args\["height"\])
rW = origW / float(newW)
rH = origH / float(newH)
# resize the image and grab the new image dimensions
image = cv2.resize(image, (newW, newH))
(H, W) = image.shape\[:2\]
先將影象載入到記憶體中,並拷貝一份(稍後我們可以在上面繪製輸出結果)。
再獲取原始寬度和高度(第 84 行),然後從命令列引數中提取新的寬度和高度。使用原始寬高和新的寬高計算比率,用於稍後在指令碼中縮放邊界框座標。
然後調整影象大小,此處忽略長寬比。
接下來,我們將使用 EAST 文字檢測器:
\# define the two output layer names for the EAST detector model that
# we are interested in -- the first is the output probabilities and the
# second can be used to derive the bounding box coordinates of text
layerNames = \[
"feature\_fusion/Conv\_7/Sigmoid",
"feature\_fusion/concat\_3"\]
# load the pre-trained EAST text detector
print("\[INFO\] loading EAST text detector...")
net = cv2.dnn.readNet(args\["east"\])
先將兩個輸出層名稱轉換成列表格式。這兩個輸出層的介紹可以檢視 這裡。
然後將預訓練 EAST 神經網路載入到記憶體中。至少需要 OpenCV 3.4.2 版本,它由 cv2.dnn.readNet
實現。
\# construct a blob from the image and then perform a forward pass of
# the model to obtain the two output layer sets
blob = cv2.dnn.blobFromImage(image, 1.0, (W, H),
(123.68, 116.78, 103.94), swapRB=True, crop=False)
net.setInput(blob)
(scores, geometry) = net.forward(layerNames)
# decode the predictions, then apply non-maxima suppression to
# suppress weak, overlapping bounding boxes
(rects, confidences) = decode\_predictions(scores, geometry)
boxes = non\_max\_suppression(np.array(rects), probs=confidences)
為確定文字的位置,我們需要:
- 使用
cv2.dnn.blobFromImage
構建四維輸入資料blob
。詳情參見 這裡。 - 將
blob
傳入 EAST 神經網路中,獲取scores
和geometry
。 - 使用之前定義的
decode_predictions
函式對預測結果進行解碼。 - 通過 imutils 進行 非極大值抑制。NMS 實際上保留了最有可能的文字區域,剔除了其他重疊區域。
現在我們知道了文字區域的位置,接下來需要文字識別。我們開始在遍歷所有邊界框,並處理結果,為實際的文字識別做準備:
\# initialize the list of results
results = \[\]
# loop over the bounding boxes
for (startX, startY, endX, endY) in boxes:
# scale the bounding box coordinates based on the respective
# ratios
startX = int(startX \* rW)
startY = int(startY \* rH)
endX = int(endX \* rW)
endY = int(endY \* rH)
# in order to obtain a better OCR of the text we can potentially
# apply a bit of padding surrounding the bounding box -- here we
# are computing the deltas in both the x and y directions
dX = int((endX - startX) \* args\["padding"\])
dY = int((endY - startY) \* args\["padding"\])
# apply padding to each side of the bounding box, respectively
startX = max(0, startX - dX)
startY = max(0, startY - dY)
endX = min(origW, endX + (dX \* 2))
endY = min(origH, endY + (dY \* 2))
# extract the actual padded ROI
roi = orig\[startY:endY, startX:endX\]
我們定義一個 results
列表,用來包含我們的 OCR 邊界框和文字。然後在 boxes
上進行遍歷,我們:
- 基於之前計算的比率縮放邊界框。
- 填充邊界框。
- 最後,提取被填充的
ROI
。
本文的文字識別部分可以通過使用 Tesseract v4 來完成:
\# in order to apply Tesseract v4 to OCR text we must supply
# (1) a language, (2) an OEM flag of 4, indicating that the we
# wish to use the LSTM neural net model for OCR, and finally
# (3) an OEM value, in this case, 7 which implies that we are
# treating the ROI as a single line of text
config = ("-l eng --oem 1 --psm 7")
text = pytesseract.image\_to\_string(roi, config=config)
# add the bounding box coordinates and OCR'd text to the list
# of results
results.append(((startX, startY, endX, endY), text))
注意程式碼塊中的註釋,我們設定 Tesseract 的 config
引數(英語、LSTM 神經網路和單行文字)。
注:如果您獲取了錯誤的 OCR 結果,那麼您可能需要更改 --psm
的值。
pytesseract 庫進行剩下的操作,呼叫 pytesseract.image_to_string
,傳遞引數 roi
和 config
。
只用這兩行程式碼,您就使用 Tesseract v4 識別影象中的一個文字 ROI。記住,很多過程在底層進行。
我們的結果(邊界框值和實際的 text
字串)儲存在 results
列表中。
然後我們繼續該迴圈流程,對其他 ROI 進行處理。
接下來打印出最終結果,檢視它是否真的有用:
\# sort the results bounding box coordinates from top to bottom
results = sorted(results, key=lambda r:r\[0\]\[1\])
# loop over the results
for ((startX, startY, endX, endY), text) in results:
# display the text OCR'd by Tesseract
print("OCR TEXT")
print("========")
print("{}\\n".format(text))
# strip out non-ASCII text so we can draw the text on the image
# using OpenCV, then draw the text and a bounding box surrounding
# the text region of the input image
text = "".join(\[c if ord(c) < 128 else "" for c in text\]).strip()
output = orig.copy()
cv2.rectangle(output, (startX, startY), (endX, endY),
(0, 0, 255), 2)
cv2.putText(output, text, (startX, startY - 20),
cv2.FONT\_HERSHEY\_SIMPLEX, 1.2, (0, 0, 255), 3)
# show the output image
cv2.imshow("Text Detection", output)
cv2.waitKey(0)
先基於邊界框的 y 座標按自上而下的順序對結果進行排序。
對 results
進行迴圈,我們:
- 將 OCR 得到的文字列印到終端。
- 從文字中去掉非 ASCII 字元,因為 OpenCV 的 cv2.putText 函式不支援非 ASCII 字元。
- 繪製 ROI 的邊界框和識別的文字。
顯示輸出,並等待即將按下的任意鍵。
OpenCV text recognition results
現在我們已經實現了 OpenCV OCR 流程。執行以下命令:
$ python text\_recognition.py --east frozen\_east\_text\_detection.pb \\
--image images/example\_01.jpg
\[INFO\] loading EAST text detector...
OCR TEXT
========
OH OK
我們從一個簡單的示例圖片開始。
注意我們的 OpenCV OCR 系統正確地檢測出了影象中的文字,然後識別出文本。
下一個示例圖片更能代表我們在真實世界中看到的影象文字:
$ python text\_recognition.py --east frozen\_east\_text\_detection.pb \\
--image images/example\_02.jpg
\[INFO\] loading EAST text detector...
OCR TEXT
========
® MIDDLEBOROUGH
OpenCV OCR 系統正確地定位文字位置和識別文字。但是,在終端輸出中,有一個多餘的符號,這裡 Tesseract 可能被誤導,因為 OpenCV EAST 文字檢測器得到的邊界框與標誌牌後面的植物發生重疊。
下面我們來看另一個 OpenCV OCR 和文字識別的例子:
$ python text\_recognition.py --east frozen\_east\_text\_detection.pb \\
--image images/example\_03.jpg
\[INFO\] loading EAST text detector...
OCR TEXT
========
ESTATE
OCR TEXT
========
AGENTS
OCR TEXT
========
SAXONS
該示例中有三個單獨的文字區域。OpenCV 的文字檢測器能夠定位每一個文字區域,然後使用 OCR 準確識別出每個區域的文字內容。
下一個例子展示了在某些情況下新增 padding
的重要性:
$ python text\_recognition.py --east frozen\_east\_text\_detection.pb \\
--image images/example\_04.jpg
\[INFO\] loading EAST text detector...
OCR TEXT
========
CAPTITO
OCR TEXT
========
SHOP
OCR TEXT
========
|.
在對這個烘焙店店面進行 OCR 的第一次嘗試中,我們發現 “SHOP” 是被正確地識別,但是:
- “CAPUTO” 中的 “U” 被錯誤地識別為 “TI”。
- “CAPUTO’S” 中缺少 “'S”。
- “BAKE” 被錯誤識別為 “|.”。
現在通過新增一點填充,從而擴充套件 ROI 的邊界框座標,並準確識別文字:
$ python text\_recognition.py --east frozen\_east\_text\_detection.pb \\
--image images/example\_04.jpg --padding 0.05
\[INFO\] loading EAST text detector...
OCR TEXT
========
CAPUTO'S
OCR TEXT
========
SHOP
OCR TEXT
========
BAKE
只需在邊框的每個角落新增 5% 的填充,我們不僅能夠正確識別 “BAKE”,還能夠識別“CAPUTO” 中的 “U” 和“S”。
當然,OpenCV 也有的完全失敗的案例:
$ python text\_recognition.py --east frozen\_east\_text\_detection.pb \\
--image images/example\_05.jpg --padding 0.25
\[INFO\] loading EAST text detector...
OCR TEXT
========
Designer
OCR TEXT
========
a
我把填充增加到 25%,以適應這個標誌牌中單詞的角度 / 視角。這使得 “Designer” 可以正確地被 EAST 和 Tesseract v4 進行文字識別。但是較小的單詞失敗的原因,可能是字母的顏色與背景很相似。
在這種情況下,我們無能為力,但我建議參考下面的 Limitations and Drawbacks
部分,瞭解在遇到不正確的 OCR 結果時如何改進 OpenCV 文字識別流程。
Limitations and Drawbacks
記住,完美的 OCR 系統是不存在的,尤其是在現實世界條件下。期望 100% 的 OCR 準確率是不切實際的。
我們的 OpenCV OCR 系統可以很好地處理一些影象,但在處理其他影象時會失敗。該文字識別流程失敗的兩個主要原因:
- 文字傾斜 / 旋轉。
- The font of the text itself is not similar to what the Tesseract model was trained on. 文字字型與 Tesseract 模型訓練的字型相差太遠。
即使 Tesseract v4 與 v3 相比更加強大、準確率更高,但該深度學習模型仍然受限於它的訓練資料。如果文字字型與訓練資料字型相差太大,那麼 Tesseract 不可能對該文字進行很好地處理。
其次,Tesseract 假設輸入影象或 ROI 已經經過合適地預處理。但是當我們在自然場景影象上執行文字識別時,該假設不總是成立。
對於預處理乾淨過的影象,Tesseract 可以得到很好的影象。總的來說,我們的 OpenCV OCR 最適合於:(1)以影象的 90 度角 (即自上而下、鳥瞰) 捕獲的文字,(2)相對容易從背景中分割的文字。
如果實際情況並非如此,您可以應用透視變換來校正檢視,請記住,今天的 Python + EAST 文字檢測器不支援旋轉邊界框。
如果您需要更高的精確度,我建議您嘗試 “三大” 計算機視覺 API 服務之一:
每種方法都使用執行在雲伺服器上的更先進的 OCR 方法。
Summary
本教程介紹瞭如何使用 OpenCV OCR 系統執行文字檢測和文字識別。
為了實現該任務,我們:
- 利用 OpenCV 的 EAST 文字檢測器定點陣圖像中的文字區域。
- 提取每個文字 ROI,然後使用 OpenCV 和 Tesseract v4 進行文字識別。
該 OpenCV OCR 流程在一些情況下效果很好,在另一些情況下並不那麼準確。要想獲得最好的 OpenCV 文字識別結果,我建議您確保:
- 輸入的 ROI 應儘量經過清洗和預處理。在理想情況中,您的文字應該能夠與影象的其他部分完美分割,但是在現實情況下,這並不總是可能的。
- 文字是從相機 90 度角的情況下拍攝的,類似於自上而下、鳥瞰的角度。如果不是,可以使用透視變換來獲得更好的結果。
原始碼連結: