1. 程式人生 > >機器視覺 OpenCV—python 基於LSTM網路的OCR文字檢測與識別

機器視覺 OpenCV—python 基於LSTM網路的OCR文字檢測與識別

一、背景與環境搭建

OpenCV的文字識別流程:

  1. OpenCV EAST 文字檢測器執行文字檢測,
  2. 我們提取出每個文字 ROI 並將其輸入 Tesseract,從而構建完整的 OpenCV OCR 流程!
  • 環境搭建 Tesseract (v4) 最新版本支援基於深度學習的 OCR,準確率顯著提高。底層的 OCR 引擎使用的是一種迴圈神經網路(RNN)——LSTM 網路。
pip install tesseract
pip install opencv-python
pip install pillow
pip install pytesseract
pip install imutils
  • OpenCV OCR 文字識別流程圖 在這裡插入圖片描述 我們使用 OpenCV 的 EAST 文字檢測器來檢測影象中的文字。
    • EAST 文字檢測器將提供文字 ROI 的邊界框座標。
    • 我們將提取每個文字 ROI,將其輸入到 Tesseract v4 的 LSTM 深度學習文字識別演算法。LSTM 的輸出將提供實際 OCR 結果。
    • 我們將在輸出影象上繪製 OpenCV OCR 結果。

整個過程中使用到的 Tesseract 命令必須在 pytesseract 庫下呼叫。在呼叫 tessarct 庫時,我們需要提供大量 flag。(tessarct --help) 最重要的三個 flag 是 -l、–oem 和 --ism。

$ tesseract --help-oem
OCR Engine modes:
  0    Legacy engine only.
  1    Neural nets LSTM engine only.
  2    Legacy + LSTM engines.
  3    Default, based on what is available.

-l flag 控制輸入文字的語言,本教程示例中使用的是 eng(英語),在這裡你可以看到 Tesseract 支援的所有語言:https://github.com/tesseract-ocr/tesseract/wiki/Data-Files。 –oem(OCR 引擎模式)控制 Tesseract 使用的演算法型別。執行以下命令即可看到可用的 OCR 引擎模式: –psm 控制 Tesseract 使用的自動頁面分割模式:

$ tesseract --help-psm
Page segmentation modes:
 0    Orientation and script detection (OSD) only.
 1    Automatic page segmentation with OSD.
 2    Automatic page segmentation, but no OSD, or OCR.
 3    Fully automatic page segmentation, but no OSD. (Default)
 4    Assume a single column of text of variable sizes.
 5    Assume a single uniform block of vertically aligned text.
 6    Assume a single uniform block of text.
 7    Treat the image as a single text line.
 8    Treat the image as a single word.
 9    Treat the image as a single word in a circle.
10    Treat the image as a single character.
11    Sparse text. Find as much text as possible in no particular order.
12    Sparse text with OSD.
13    Raw line. Treat the image as a single text line,
      bypassing hacks that are Tesseract-specific.

對文字 ROI 執行 OCR,我發現模式 6 和 7 效能較好,但是如果你對大量文字執行 OCR,那麼你可以試試 3(預設模式)。 如果你得到的 OCR 結果不正確,那麼我強烈推薦調整 --psm,它可以對你的輸出 OCR 結果產生極大的影響。

$ 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 提供的,你也可以在「Downloads」部分下載它。
  • text_recognition.py:我們的 OCR 指令碼。我們將逐行 review 該指令碼。它使用 EAST 文字檢測器找到影象中的文字區域,然後利用 Tesseract v4 執行文字識別。
  • EAST 文字檢測器生成兩個變數: scores:文字區域的概率。 geometry:文字區域的邊界框位置。

關於指令碼引數

  • 我們的指令碼需要兩個命令列引數: –image:輸入影象的路徑。 –east:預訓練 EAST 文字檢測器的路徑。
  • 下列命令列引數是可選的: –min-confidence:檢測到的文字區域的最小概率。 –width:影象輸入 EAST 文字檢測器之前需要重新調整的寬度,我們的檢測器要求寬度是 32 的倍數。 –height:與寬度類似。檢測器要求調整後的高度是 32 的倍數。 –padding:新增到每個 ROI 邊框的(可選)填充數量。如果你發現 OCR 結果不正確,那麼你可以嘗試 0.05、0.10 等值。

二、文字檢測與識別

from imutils.object_detection import non_max_suppression
import numpy as np
import pytesseract
import argparse
import cv2

"""
	EAST 文字檢測器生成兩個變數:
	    scores:文字區域的概率。
	    geometry:文字區域的邊界框位置。
"""

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 = []

	for y in range(0, numRows):
        #提取分數(概率),然後是幾何資料,用於匯出圍繞文字的潛在邊界框座標
		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]

		for x in range(0, numCols):                       # 迴圈列數
			if scoresData[x] < args["min_confidence"]:    # 忽略低置信度
				continue

			# 計算偏移因子作為我們的結果特徵 
			# maps will be 4x smaller than the input image
			(offsetX, offsetY) = (x * 4.0, y * 4.0)

			# 提取預測的旋轉角並計算
			angle = anglesData[x]
			cos = np.cos(angle)
			sin = np.sin(angle)

			# 使用幾何體來推導bounding box的寬度和高度
			h = xData0[x] + xData2[x]
			w = xData1[x] + xData3[x]

			# 計算文字預測bounding box的起始和結束(x,y)座標
			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)
			
			# 將 bounding box座標和概率得分新增到各自的列表中
			rects.append((startX, startY, endX, endY))
			confidences.append(scoresData[x])

	return (rects, confidences)  #返回bounding boxes的元組及其置信度
	
"""
構造引數分析器並解析引數
"""
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())

"""
	載入+預處理我們的影象並初始化關鍵變數
"""
image = cv2.imread(args["image"])
orig = image.copy()
(origH, origW) = image.shape[:2]

# 設定新的寬度和高度,然後確定寬度和高度的變化比。
(newW, newH) = (args["width"], args["height"])
rW = origW / float(newW)
rH = origH / float(newH)

# 調整影象大小並獲取新的影象尺寸
image = cv2.resize(image, (newW, newH))
(H, W) = image.shape[:2]


"""
	EAST文字檢測器:
	EAST檢測器模型定義兩個輸出層名稱:
		第一個是輸出概率,
		第二個可以用來匯出文字的邊框座標
"""
layerNames = [
	"feature_fusion/Conv_7/Sigmoid",
	"feature_fusion/concat_3"]

# 載入預訓練的EAST文字檢測器
print("[INFO] loading EAST text detector...")
net = cv2.dnn.readNet(args["east"])


"""
	至少 OpenCV 3.4.2 才能呼叫  cv2.dnn.readNet
	見證【奇蹟】的第一步:確定文字位置
	從影象中構建一個BLOB(團),然後執行模型的前向傳遞以獲得兩個輸出層集合。
"""

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)

# 解碼預測,然後應用非極大值抑制來抑制弱重疊的 bounding boxes(包圍盒)。  
(rects, confidences) = decode_predictions(scores, geometry)
boxes = non_max_suppression(np.array(rects), probs=confidences)

'''
	識別文字
	遍歷邊界框,並處理結果
'''

results = []

for (startX, startY, endX, endY) in boxes:   # 遍歷 bounding box
	# 根據相應比例縮放 bounding box 座標
	startX = int(startX * rW)
	startY = int(startY * rH)
	endX = int(endX * rW)
	endY = int(endY * rH)

	# 填充邊界框:同時計算x和y方向的增量
	dX = int((endX - startX) * args["padding"])
	dY = int((endY - startY) * args["padding"])

	# 將邊界分別填充到邊界框的每一側。
	startX = max(0, startX - dX)
	startY = max(0, startY - dY)
	endX = min(origW, endX + (dX * 2))
	endY = min(origH, endY + (dY * 2))

	# 提取被填充的 ROI
	roi = orig[startY:endY, startX:endX]

	# 設定 Tesseract config 引數(英語、LSTM神經網路和單行文字)。
	config = ("-l eng --oem 1 --psm 7")
	text = pytesseract.image_to_string(roi, config=config)

	# 將邊界框座標和OCR文字新增到結果列表中
	results.append(((startX, startY, endX, endY), text))


"""
	輸出結果:
"""
# 將結果框的座標從上到下排序
results = sorted(results, key=lambda r:r[0][1])

for ((startX, startY, endX, endY), text) in results:
	print("OCR TEXT")
	print("========")
	print("{}\n".format(text))

	# 剝離非ASCII文字,利用OpenCV的影象繪製文字,然後繪製文字和bounding box邊框
	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)

	cv2.imshow("Text Detection", output)
	cv2.waitKey(0)
	cv2.destroyAllWindows()
 python text_recognition.py --east frozen_east_text_detection.pb \
	--image images/example_01.jpg
[INFO] loading EAST text detector...
OCR TEXT
========
OH OK

關於文字位置確定:請檢視https://www.pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/