OCR识别图片和PDF上的文字

tesseract从来就不能“拿来就用”,识别效果受各方面的制约(文字的背景越单纯越好,像素越高越好),需要特征抽取技术、机器学习技术和深度学习技术配合。

识别前用OpenCV预处理图片以减少背景噪音对文字的干扰可以提高图片文字的识别率和正确率。OpenCV的话题自己可以成一本书,这里我们只是最浅层地使用它的功能函数。

第三方模块opencv-python帮助我们在python程序中应用openCV,老规矩:先安装(opencv-python)后引进(import cv2)。

常用的几个功能函数:

  • cv2.imread(图片文件,颜色模式参数)打开图片文件生成图片对象,颜色模式参数用1、0和-1。1是彩色模式(默认),忽略掉透明背景;0是灰度模式打开,一般说来图片以灰度模式打开文字识别效果更好,比如cv2.imread(‘example.png’, 0);-1保持图片原来模式打开。cv2.imread()返回读取的图片对象。
  • cv2.imshow(显示图片窗口的标题名字符串,图片对象)显示图片,图片对象是cv2.imread()返回值。

窗口的尺寸默认是自适应的WINDOW_AUTOSIZE,不能自己调节大小。若要改成可以自己调节大小的,需要用cv2.namedWindow(窗口名称, cv2.WINDOW_NORMAL),将WINDOW_AUTOSIZE改成WINDOW_NORMAL

  • cv2.destroyAllWindows()关闭所有窗口,关闭具体某个窗口用cv2.destroywindow(窗口名称)
  • cv2.waitKey(0)等待键盘敲击,返回值是敲击的键值。
  • cv2.imwrite(文件名字符串, 图片对象),将图片对象保存成图片文件

接下来用一段代码来看这几个函数的用法,以灰度模式打开示例图片文件,显示出来。然后根据敲入的键决定是关闭图片窗口还是保存后再关闭图片:

import cv2

img = cv2.imread('baby.jpeg', 0)		# 灰度模式打开图片
cv2.namedWindow('GreyModeOpen', cv2.WINDOW_NORMAL)
# 显示图片的窗口设置成为WINDOW_NORMAL,图片在窗口的显示的尺寸不合适时可以自行调整,第一个参数为窗口的标题
cv2.imshow('GreyModeOpen', img)		
# 显示图片,显示窗口标题:GreyModeOpen
cv2.waitKey(0)		# 等待敲击键盘,注意要在显示图片上敲击才有效

img = cv2.imread('baby.jpeg')		# 用默认的彩色模式打开图片
cv2.namedWindow('colorModeOpen', cv2.WINDOW_NORMAL)
# 窗口colorModeOpen可调节大小
cv2.imshow('colorModeOpen',img)
cv2.waitKey(0)

greyImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 将彩色模式转成(convert)灰色模式
cv2.namedWindow('convertGreyMode', cv2.WINDOW_NORMAL)
# 窗口colorModeOpen可调节大小
cv2.imshow('convertGreyMode', greyImg)		# 显示转换后的图片
k = cv2.waitKey(0)	# 等待敲击键盘,返回值放入k
if k == ord('s'): # 如果敲击的是’s’键,ord(‘s’)返回字符的统一码
    cv2.imwrite('babyGreyImg.png',greyImg)
    # 将转换的灰度模式图片保存成babyGreyImg

cv2.destroyAllWindows()	# 关闭所有打开的图片窗口 

相对于图片上的文字而言,图片其他部分都是背景,背景的杂音越少,识别效果越好。

接下来见样学样滴调用对图片上文字背景噪音的处理函数,一个是虚化处理(blur),另一个阙值处理(threshold):

(1)虚化处理对椒盐噪音很有效,椒盐噪音就是背景噪音是一个一个的小黑点,好像撒了椒盐似的。虚化是会虚化背景,但文字也会被虚化,对文字大的加重了的(扛得起)背景布满小黑点的预处理效果特别好。

(2)阙值处理用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断(阈值的选取依赖于具体的问题)。最简单粗暴的就是规定一个像素值,大于这个值是文字(黑色),小于它是背景(白色)。当然这个效果很一般,所以有很多复杂的变种,有兴趣的朋友可以搜索opencv的技术文档来研究,这里只是使用功能函数预处理图片,不去研究所以然。

(3)再有就是把虚化处理和阙值处理结合起来,先虚化再阙值。

反正我们就是看哪种功能函数或函数组合使图片文字更清晰、背景噪音少就用哪个方案预处理图片,提高文字识别的正确率。

下面这段代码用OpenCV的功能函数对图片进行预处理,显示一个处理结果后敲键盘上任意键切换到下一个处理结果,处理手法用作显示结果的窗口标题。哪种方法得到对背景噪音的清楚效果好,就在图像识别代码前加入那个预处理功能函数或函数组合:

import cv2

img = cv2.imread('example.png', 0)	# 以灰度模式打开图片生成图片对象

# 先列出用到的预处理手段:

# 虚化处理
blurMedian = cv2.medianBlur(img, 3)	# 中值虚化处理
blurGaussian = cv2.GaussianBlur(img,(5,5),0)	# 高斯虚化处理

# 阙值处理

# 最直接的阙值处理
simpleThreshold = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1]
# 规定一个阙值127,小于的是背景,大于的是文字(255)。cv2.THRESH_BINARY位置还有其他参数可控选择,参见opencv-python的技术文档。返回一个列表,列表有两个元素,第二个元素是处理的图片对象,所以索引用1

# 中值自适应阙值处理
adaptiveThreshold1 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
# cv2.ADAPTIVE_THRESH_MEAN_C,阙值取邻近区域的中值。返回处理后的图片对象,注意第一个参数图像对象img必须是灰度模式

# 高斯自适应阙值处理
adaptiveThreshold2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
# cv2.ADAPTIVE_THRESH_GAUSSIAN_C阙值是加了权重的邻近区域值的和,而这个权重的计算使用了高斯窗(Gaussian Window)

# OTSU二值化处理
otsuThreshold1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# 先高斯虚化过滤后,再做OTSU二值化处理
otsuThreshold2 = cv2.threshold(blurGaussian, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# 也可以先中值虚化后跟自适应阙值处理组合,先高斯虚化后过滤后再自适应阙值处理,看哪个预处理预处理效果好。接下来把处理手法名称字串加入列表,作为窗口显示的标题。

titles = ['Original Image', 'Gaussian filtered Image', 'Median blur', 'Global Thresholding(v=127)', 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding', "Otsu's Thresholding", "Otsu's Thresholding after Gaussian filter"]
# 预处理后显示窗口的窗口标题列表

images = [img, blurGaussian, blurMedian, simpleThreshold, adaptiveThreshold1, adaptiveThreshold2,otsuThreshold1, otsuThreshold2]
# 预处理后的图片对象

for i in range(len(images)):
    cv2.imshow(titles[i], images[i])
    # 显示预处理后的图片,窗口标题从标题列表中取,预处理后的图片对象从对象列表中取
    cv2.waitKey(0)
    # 等待敲击键盘结束本次循环,开始下一次循环。注意对敲键盘有反应必须在图片窗口是前端的情况下(前段窗口标题背景为蓝,后段窗口标题背景为灰色)

我用的样本有限,试的两张照片图片一张是高斯自适应处理的图片效果最好,另一张是OTSU二值化处理的效果好。