1.导言
今日咱们来看一个OCR相关的文档扫描项目。首要咱们先来介绍一些相关理论
1.1 什么是光学字符辨认 (OCR)
OCR(即光学字符辨认)是辨认图画中的文本并将其转化为电子形式的进程。这些图画可所以手写文本、打印文本(如文档、收据、名片等),甚至是自然场景相片。
简略来说,OCR 有两个部分。榜首部分是文本检测,确认图画内的文本部分。第二部分文本辨认,从图画中提取文本。 结合运用这些技术能够从任何图画中提取文本。详细的流程如下图所示
OCR 在各个职业都有广泛的运用(首要目的是削减人工操作)。它现已融入咱们的日常日子,并且有许多的运用。
1.2 运用范畴
OCR 越来越多地被各职业用于数字化,以削减人工作业量。这使得从商业文档、收据、发票、护照等中提取和存储信息变得十分简单和高效,几十年前,OCR 体系的构建十分昂贵且繁琐。但核算机视觉和深度学习范畴的前进使得咱们现在自己就能够构建一个OCR 体系。但构建 OCR 体系需求运用到咱们之前介绍的一系列办法。
2.项目背景介绍
背景:咱们有一张随手拍的发票相片如下,咱们期望辨认出文档信息并扫描
考虑:咱们怎么完成上述需求呢?
考虑:咱们怎么完成上述需求呢?
首要,咱们的算法应该能够正确的对齐文档,检测图画的边界,取得方针文本图画
其次,咱们能对方针文本图画的文档进行扫描
下面咱们来看一下详细怎么在Opencv中处理
这儿总共需求四大步
榜首步,边际检测,
第二步,提取概括。
第三步,透视改换,使得图画对齐,从上图能够看出,咱们的图片是一个倾斜的,咱们需求通过各种转化办法将其放平。
第四步,OCR辨认
3.边际检测
3.1 原始图画读取
首要,咱们读取要扫描的图画。
下述代码咱们核算了一个ratio
份额,这是由于咱们后续要对图画进行resize操作,里面每一个点的坐标也会有相同的一个改变,因而,咱们先算出来这样一个份额,能够推导出resize完之后图画的坐标改变,然后方便咱们后续在原图上进行修正。
# 读取输入
image = cv2.imread('images/receipt.jpg')
#坐标也会相同改变
ratio = image.shape[0] / 500.0 #这儿咱们首要得到一个份额,方便后续操作
orig = image.copy()
3.2 预处理
下面咱们对图形进行一些根本的预处理作业,包括resize、灰度处理、二值处理。
榜首,咱们界说了一个resize()函数,它的根本逻辑是依据输入的高度或宽度,主动的核算出宽度或高度。
第二,咱们将图形进行灰度处理
第三,咱们运用gaussian滤波器去除噪音点
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
image = resize(orig, height = 500) #
# 预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 灰度处理
gray = cv2.GaussianBlur(gray, (5, 5), 0) # 高斯滤波器
3.3 成果展现
通过上述得到预处理后的图片,通过canny边际检测。
# 展现预处理成果
edged = cv2.Canny(gray, 75, 200) # canny边际检测,得到边际
print("STEP 1: 边际检测")
cv2.imshow("Image", image) #原始图画
cv2.imshow("Edged", edged) #边际成果
cv2.waitKey(0)
cv2.destroyAllWindows()
现在咱们得到边际检测的成果,能够看到有许多个边际,咱们做文档扫描,需求的是最外面的成果,接下来咱们来详细怎么完成。
3.概括检测
咱们先来考虑一下最外面这个概括它有什么特色。
首要,它是最大的,因而,咱们能够依据它的面积或者周长进行排序,这儿咱们对面积进行排序。然后咱们要去找概括,这儿咱们遍历每一个概括,然后去核算概括的一个近似,由于直接算概括的时候不太好算,往往是一个不规则形状,咱们做一个矩形近似,然后此刻就只需求确认四个点就行。详细代码如下:
# 概括检测
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()
4.透视改换
透视改换(Perspective Transformation) ,也称为投影改换,它能够用于纠正图画畸变、完成视角改换和图画组成等运用。凭借透视改换,咱们能够从不同视角取得精确的图画数据,并进行更精确的剖析、处理和辨认。
它的根本原理是基于相机的投影模型,通过处理图画中的四个控制点,将原始图画上的恣意四边形区域映射到新的方位和形状上,咱们需求得到四个输入坐标和四个输出坐标
。通常情况下,透视改换会改变图画中的视角、缩放和旋转等特点。它有几个关键步骤如下:
-
控制点挑选:为了进行透视改换,咱们需求挑选原始图画中的
四个控制点
(例如四个角点),以界说方针区域的形状和方位。这些控制点应该在原始图画和方针图画之间有清晰的对应联系,然后通过高度和宽度信息,咱们核算出方针图画的四个控制点
-
透视改换矩阵:通过运用控制点的坐标,能够核算出透视改换矩阵,透视改换矩阵是一个3×3的矩阵。它包括了图画改换所需的所有信息。这儿需求输入坐标和输出坐标,然后运用
cv2.getPerspectiveTransform
函数获取改换矩阵。通过将透视改换矩阵运用于原始图画上的点,能够得到它们在方针图画中的对应方位。
接下来咱们来看一下详细是怎么完成的,首要咱们界说了一个ordr_points()
函数来获取坐标点,然后咱们界说four_point_transform
函数来完成透视改换。详细代码如下,
# 获取坐标点
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) #通过输入和输出坐标,能够核算出M矩阵
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
# 回来改换后成果
return warped
界说好上述函数之后,接下来看一下通过一起改换之后的成果,为了方便展现,咱们再进行二值化处理
# 透视改换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio) #这儿乘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("Scanned", resize(ref, height = 650))
cv2.waitKey(0)
cv2.destroyAllWindows()
能够看到咱们现在就得到了扫描之后得到的成果,并且咱们保存为scan.jpg
操作
5.OCR辨认
得到扫描后的文档之后,咱们需求对其间的字符进行辨认,这儿咱们要用到tesseract
工具包,咱们先来看一下怎么装置相关环境。
5.1 tesseract装置
装置地址:digi.bib.uni-mannheim.de/tesseract/
首要挑选一个适宜的版别进行装置就行,我这儿挑选最新的w64版别,怎么装置时一直点击下一步就行,但是咱们要记住装置的途径。
留意:咱们需求进行环境变量配置
把刚刚装置的途径添加到环境变量中即可
接下来咱们期望在python中运用它,因而要下载对应的python工具包。
装置命令如下:pip install pytesseract
5.2 字符辨认
咱们刚刚现已得到了扫描后的图画,并保存为scan.jpg
如下所示
接下来咱们期望把其间的文本字符悉数提取出来,咱们来看一下详细代码吧
from PIL import Image
import pytesseract
import cv2
import os
# 读取图片
image = cv2.imread('scan.jpg')
# 灰度处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值处理
gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
filename = "{}.png".format(os.getpid())
cv2.imwrite(filename, gray)
# OCR辨认,提取字符
text = pytesseract.image_to_string(Image.open(filename))
print(text)
x * KK KK K KR KH KR RK KK
WHOLE
FOODS
TM AR K CE T)
WHOLE FOODS MARKET ~ WESTPORT, CT 06880
399 POST RD WEST - (203) 227-6858
365 BACUN LS NP 499
$65 BACON LS NP 4.99
365 BACON LS NP 4.99
365 BACON LS NP 4.99
BROTH CHTC NP 2.19
FLOUR ALMUND NP 11.99
CHKN BRST BNLSS SK NP 18.80
HEAVY CREAM NP 3.39
BALSMC REDUCT NP 6.49
BEEF GRND 85/15 NP 5.04
JUICE COF CASHEW L NP = 8.99
DOCS PINT ORGANIC NF 14.49
HNY ALMOND BUTTER NP 9.99
xeee TAX = 00 9 BAL 101.33
TTA AATDA ABH HH oy
对比一下能够看到辨认的字符都比较精确。