文檔掃描OCR識別項目|含代碼【DataWhale項目|OpenCV】

參考課程:Opencv計算機視覺實戰(Python版).

基本材料準備

  1. 一張含有字體的文檔的圖片即可。
    圖片

思路介紹

1 從圖片中剪切並變換爲規整的矩形的文字圖片

  1. 經常要對一個分辨率大的圖像進行resize操作,理由是,爲了能夠在實驗過程中能夠在屏幕大小範圍內看到整個圖像的變化。比如實驗的圖像像素爲2448×3264。而電腦屏幕是1920×1080。除此之外,resize操作一旦啓用,且最後展示如果需要後期得到的某種用途的座標集合(比如輪廓Contours)與原圖的結合,就需要配合計算出ratio,並在結合的時候用到。
  2. image = resize(orig, height = 500)這裏將調用cv2.resize方法時可能出現的異常情況的處理代碼(if else)封裝起來。這樣當其他項目用到類似的操作時,只需要將函數copy過去,就可以。
  3. 對於圖片處理的經典操作:轉化爲灰度圖、高斯濾波去除輸入圖像的噪音(上一個項目信用卡數字識別,應該也是可以的)、Canny邊緣檢測、輪廓檢測、特定輪廓過濾(這裏爲了得到最外界的邊框通過對各邊框的周長大小排序得到)、輪廓近似處理(確保將傳入的不太規則的圖片的輪廓近似爲一個矩形,以進行周長、面積等計算操作)、輪廓內圖像規整變換(由傾斜狀態變換爲方正的狀態),其中規整變換涉及到繁瑣的數學計算。暫不講解。

2 安裝pytesseract

  1. 該網址 下載類似這樣格式名字的文件“tesseract-ocr-setup-4.00.00dev.exe”並安裝,其中安裝路徑“PATH1”要記下。
  2. 將安裝路徑添加到環境變量中。此時打開命令終端,輸入:tesseract -v 測試,顯示版本號則表示安裝成功。
  3. 在所用python版本下pip 安裝:pip install pytesseract
  4. 然後打開該python版本對應下的Lib\site-packages中 叫pytesseract的文件夾。編輯文件夾下的pytesseract.py文件。ctrl+F搜索“tesseract_cmd = '”這樣的一行代碼,將其值修改爲pytesseract安裝文件的絕對路徑。即在PATH1後再加上pytesseract.exe。即:tesseract_cmd = 'P:/pythonappsetup/Tesseract-OCR/tesseract.exe'。如此即可。

3 OCR過程

  1. 光學識別字符的過程就不贅述了,全在一句代碼pytesseract.image_to_string

代碼實現

  1. 規整變換圖片的代碼
# 導入工具包
import numpy as np
import argparse
import cv2

# 設置參數

# ap = argparse.ArgumentParser()
# ap.add_argument("-i", "--image", required = True,
# 	help = "Path to the image to be scanned")
# args = vars(ap.parse_args())

def order_points(pts):
	# 一共4個座標點
	rect = np.zeros((4, 2), dtype = "float32")

	# 按順序找到對應座標0123分別是 左上,右上,右下,左下
	# 計算左上,右下
	s = pts.sum(axis = 1)
	rect[0] = pts[np.argmin(s)]
	rect[2] = pts[np.argmax(s)]

	# 計算右上和左下
	diff = np.diff(pts, axis = 1)
	rect[1] = pts[np.argmin(diff)]
	rect[3] = pts[np.argmax(diff)]

	return rect

def four_point_transform(image, pts):
	# 獲取輸入座標點
	rect = order_points(pts)
	(tl, tr, br, bl) = rect

	# 計算輸入的w和h值
	widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
	widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
	maxWidth = max(int(widthA), int(widthB))

	heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
	heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
	maxHeight = max(int(heightA), int(heightB))

	# 變換後對應座標位置
	dst = np.array([
		[0, 0],
		[maxWidth - 1, 0],
		[maxWidth - 1, maxHeight - 1],
		[0, maxHeight - 1]], dtype = "float32")

	# 計算變換矩陣
	M = cv2.getPerspectiveTransform(rect, dst)
	warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

	# 返回變換後結果
	return warped

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
	dim = None
	(h, w) = image.shape[:2]
	if width is None and height is None:
		return image
	if width is None:
		r = height / float(h)
		dim = (int(w * r), height)
	else:
		r = width / float(w)
		dim = (width, int(h * r))
	resized = cv2.resize(image, dim, interpolation=inter)
	return resized

def cv_show(name,img):
	cv2.imshow(name,img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

# 讀取輸入
image = cv2.imread("./images/page.jpg")
# cv_show("image_original",image)
#座標也會相同變化
# 因爲接下來會做resize操作,而如果要像在原圖上得到反映,就需要知曉這個resize比例。
# 而之所以resize是因爲如果原圖展開,在電腦上展示會非常大,不能看到整體。但是我也在想是不是可以在實際生產中,可以去掉這一步!!!??
ratio = image.shape[0] / 500.0
orig = image.copy()


image = resize(orig, height = 500)

# 預處理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)

# 展示預處理結果
print("STEP 1: 邊緣檢測")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 輪廓檢測
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]

# 遍歷輪廓
for c in cnts:
	# 計算輪廓近似
	peri = cv2.arcLength(c, True)
	# C表示輸入的點集
	# epsilon表示從原始輪廓到近似輪廓的最大距離,它是一個準確度參數
	# True表示封閉的
	approx = cv2.approxPolyDP(c, 0.02 * peri, True)

	# 4個點的時候就拿出來
	if len(approx) == 4:
		screenCnt = approx
		break

# 展示結果
print("STEP 2: 獲取輪廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 透視變換
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)

# 二值處理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)
# 展示結果
print("STEP 3: 變換")
cv2.imshow("Original", resize(orig, height = 650))
cv2.imshow("Scanned", resize(ref, height = 650))
cv2.waitKey(0)
  1. 檢測圖片中的文字過程
from PIL import Image
import pytesseract
import cv2
import os

preprocess = 'blur' #thresh

image = cv2.imread('scan.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

if preprocess == "thresh":
    gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

if preprocess == "blur":
    gray = cv2.medianBlur(gray, 3)
    
filename = "{}.png".format(os.getpid())
cv2.imwrite(filename, gray)
    
text = pytesseract.image_to_string(Image.open(filename))
print(text)
os.remove(filename)

cv2.imshow("Image", image)
cv2.imshow("Output", gray)
cv2.waitKey(0)                                   

個人問題

  1. 對比信用卡數字識別項目中的處理,爲什麼信用卡的項目沒有用到Canny邊緣檢測?
  2. 整體來看,難點在於對於檢測出的紙張的輪廓區域如何規整成爲一個矩形的處理過程。具體來說就是定義的那兩個函數four_point_transformorder_points

=============================== T H E E N D ! =================================================

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章