1. 程式人生 > 實用技巧 >【轉載】set the new width and height and then determine the ratio in change

【轉載】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 程式,其中包括:

  1. 使用 OpenCV EAST 文字檢測器執行文字檢測,該模型是一個高準確率的深度學習文字檢測器,被用於檢測自然場景影象中的文字。
  2. 一旦我們使用 OpenCV 檢測出影象中的文字區域後,提取出每個文字 ROI 並將其傳入 Tesseract,從而構建完整的 OpenCV OCR 流程!

最後,我將展示一些使用 OpenCV 應用文字識別的示例結果,並討論該方法的侷限性和缺點。

How to install Tesseract 4

Tesseract 是一個很流行的開源 OCR 引擎,其在受限制的場景下能很好地執行,但是如果影象存在大量噪聲或者影象在應用 Tesseract 之前沒有經過合適的預處理,則效能較差。

深度學習影響了計算機視覺的幾乎所有方面,字元識別和手寫字型識別也不例外。基於深度學習的模型獲得了前所未有的文字識別精度,遠遠超出了傳統的特徵提取和機器學習方法。

Tesseract 引入了深度學習模型來進一步提升 OCR 準確率,Tesseract (v4) 最新版本支援基於深度學習的 OCR,準確率顯著提高。底層的 OCR 引擎使用的是一種

迴圈神經網路(RNN)——LSTM 網路

在這篇博文的後面,您將學習如何將 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這篇文章 中有詳細介紹。該函式:

  1. 使用基於深度學習的文字檢測器來檢測(不是識別)影象中的文字區域。
  2. 該文字檢測器生成兩個陣列,一個是給定區域包含文字的概率,另一個數組將這些概率對映到輸入影象中的邊界框座標位置。

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 神經網路中,獲取 scoresgeometry
  • 使用之前定義的 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,傳遞引數 roiconfig

只用這兩行程式碼,您就使用 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 系統可以很好地處理一些影象,但在處理其他影象時會失敗。該文字識別流程失敗的兩個主要原因:

  1. 文字傾斜 / 旋轉。
  2. 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 文字識別結果,我建議您確保:

  1. 輸入的 ROI 應儘量經過清洗和預處理。在理想情況中,您的文字應該能夠與影象的其他部分完美分割,但是在現實情況下,這並不總是可能的。
  2. 文字是從相機 90 度角的情況下拍攝的,類似於自上而下、鳥瞰的角度。如果不是,可以使用透視變換來獲得更好的結果。

原始碼連結:

github