飞道的博客

文档智能(一):基于OpenCV的文档图像校正

372人阅读  评论(0)

文档智能(一):基于OpenCV的文档图像校正
发表时间2023年1月7日 创作地点湖北省武汉市 作者:ixy_com&[Aneerban Chakraborty]

封面图片来源:DocTr

本文关键词:文档智能、文档图像校正、OpenCV、形态学变换、边缘检测、轮廓检测、透视变换

1、研究背景

基本概念:文档智能是指通过计算机进行自动阅读、理解以及分析商业文档的过程,是自然语言处理和计算机视觉交叉领域的一个重要研究方向。近年来,数字图像处理技术以及深度学习的飞速发展,极大地推动了文档智能领域的发展,以文档版面分析、文档信息抽取、文档视觉问答、文档图像分类以及文档图像校正等任务为代表的的文档智能任务得到了性能上的显著提升。

任务来源:为积极响应企业数字化转型发展的现实需求,我部承担了基于知识图谱的**研究任务。其中,收集得到的大规模文档数据中有极大比例的PDF扫描件。与可编辑文档PDF格式不同,该类型文档不可编辑,实为图像数据;同时,由于数据收集方等各种不可控因素,导致大量文档图像数据出现扭曲、倾斜、颠倒等情况;因此,为了便于数据处理,提升PDF扫描件的字符识别精度,首先对其进行文档图像校正处理。

---- 数字智能研发


内容说明:在本文中,我将简要介绍基于OpenCV的文档图像校正基本流程,并代码实践的形式加以展示。如下图所示,目前相关研究材料非常充足,本文仅做简单记录,用以交流。基本目标为:介绍OpenCV的基本使用,包括但不限于形态学处理、边缘检测、角点检测、透视变换等章节内容,核心内容为数字图像处理相关技术。全文内容包含以下部分:

图 1-1 文档图像校正相关研究(万方数据)

内容目录:

  • OpenCV基本使用(opencv-contrib-python)
  • 形态学处理(Morphological operation)
  • GrabCut图像分割(GrabCut)
  • 边缘检测(Edge Detection)
  • 角点检测(Corner Points)
  • 透视变换(Perspective Transform)
  • 局限性(Limitations)

2、研究内容

2.1 OpenCV基本使用

Opencv是一个开源的计算机视觉库,内部包含丰富的数字图像处理工具。其支持多平台安装使用,安装教程与相关简介本文不做赘述。利用Opencv可以进行图像处理、特征检测与描述、视频分析、相机校准与3D重建、机器学习、计算摄影以及目标检测等任务。相关工具箱的集成化程度非常高,可参见Opencv 4.0文档

这里用图像滤波举例,将使用各种低通滤波器(LPF)进行图像模糊,并对图像应用自定义滤波器(二维卷积)。LPF有助于降低噪音,高通滤波器HPF有助于在图像中找到边缘。OpenCV中的 c v . f i l t e r 2 D ( ) cv.filter2D() cv.filter2D()函数即可实现上述功能。首先读入原始图像,如下图:

图 2.1-1 原始图像示例
# 使用自定义的卷积核(滤波器)对输入图像进行二维卷积操作
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt


"""
print(np.__version__)
print(cv.__version__)
1.21.2
4.3.0
"""

img = cv.imread('./OpenCV.jpg')
kernel = np.ones((5, 5), np.float32)/25
dst = cv.filter2D(img, -1, kernel)

plt.subplot(121)
plt.title('Original image')
plt.imshow(img)
# 第二张子图
plt.subplot(122)
plt.title('Filtered image') 
plt.imshow(dst)

plt.show()

 

图 2.1-2 图像滤波示例
# 对输入图像进行均值模糊
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt


"""
print(np.__version__)
print(cv.__version__)
1.21.2
4.3.0
"""

img = cv.imread('./OpenCV.jpg')

dst = cv.blur(img, (5, 5))

plt.subplot(121)
plt.title('Original image')
plt.imshow(img)

plt.subplot(122)
plt.title('Blurred image')
plt.imshow(dst)

plt.show()

 

2.2 形态学处理

使用OpenCV提供的 c v 2. m o r p h o l o g y E x ( ) cv2.morphologyEx() cv2.morphologyEx()函数对输入图像做形态学的开与闭操作,其过程依赖于腐蚀(Erode)与膨胀(Dilate)。在本案例中,我们对输入图像进行闭操作,即先执行膨胀、再执行腐蚀操作。反复执行图像的闭操作,将得到输入图像的空白页面。

说明:因为后文需要对数据图像进行边缘检测,为了避免文档图像中的条纹或字符影响边缘检测的结果,这里将其处理为空白页面,实现过程如下代码block所示,其中示例数据来自于github。

# 首先读入待处理的原始图像
img = cv2.imread('./inputs/img1.jpg', cv2.IMREAD_COLOR)
plt.imshow(img)

图 2.2-1 待处理的文本图像数据
# 图像预处理:输入图像的尺寸缩放以便于处理
# 首先获取输入文本图像的shape以及对应的最大值
max_dim = max(img.shape)
# 定义size的阈值
dim_limit = 1080
# 进行size判断
if max_dim > dim_limit:
    resize_tor = dim_limit/max_dim
    img = cv2.resize(img, None, fx=resize_tor, fy=resize_tor)
    
# 将原始图像拷贝一份
origin_img = copy()
# 重复执行闭操作,从而移除文档图像中的文本
kernel = np.ones((5,5),np.uint8)
# 这里以执行次数为4举例,通过与原图对比可以发现,文本图像中的文字已多半被移除。但仍视具体情况需调整迭代次数!
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations= 4)
plt.imshow(img)

图 2.2-2 迭代执行图像闭操作后的文本图像

2.3 GrabCut图像分割

经过上述多个步骤的文本图像预处理之后,得到了一张版面为空白的文本图像数据。则接下来的工作为:去除文本图像版面区域以外的背景。这里使用GrabCut方式实现图像数据分割。

GrabCut方法说明:

  • 在本例中,文档图像区域被认为是前景对象(foreground),其边界框以外的所有区域都被认为是背景(background);
  • 使用GrabCut方法可以自动实现背景区域的消除,则剩下的区域就是前景对象了;

如果手动地绘制前景对象的边界框,则人为主观性太强。因此,这里使用GrabCut方法自动地确定前景和背景,实现如下:

# 通过GrabCut算法实现前景对象的分割
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (20,20,img.shape[1]-20,img.shape[0]-20)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]

处理效果如下图所示,可见前景对象以外的区域均被分割出来。

图 2.3-1 图像分割后的前景对象

2.4 边缘检测

经过上述小节的处理后,现在的文本图像数据以没有干扰背景的空白页面呈现,于是就可以有效地执行边缘检测操作。这里通过使用Canany算法来实现文档区域边缘的检测。其调用形式如下:
e d g e s = c v 2. C a n n y ( i m a g e , t h r e s h o l d 1 , t h r e s h o l d 2 [ , e d g e s [ , a p e r t u r e S i z e [ , L 2 g r a d i e n t ] ] ] ) edges = cv2.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] ) edges=cv2.Canny(image,threshold1,threshold2[,edges[,apertureSize[,L2gradient]]])
参数说明:

  • image:为输入图像;
  • threshold1:第一个阈值,first threshold for the hysteresis procedure.
  • threshold2:第二个阈值, second threshold for the hysteresis procedure.
  • apertureSize:是Sobel operator的孔径大小(aperture size).
  • L2gradient:作为一个标记,表明是否使用给为准确的L2范数来计算图像梯度。

处理步骤:

(1)首先对输入图像进行灰度化处理,使其满足canny算法的处理需求;

(2)然后对灰度图像进行高斯模糊处理,去除图像中的噪声;

(3)对处理后的图像做边缘检测;

(4)对处理后的图像做膨胀操作(dilate),获取文档的轮廓线。

# 基于canny-edge的边缘检测,处理后的效果如下图所示:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (11, 11), 0)
# Edge Detection.
canny = cv2.Canny(gray, 100, 200)
canny = cv2.dilate(canny, cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)))
plt.imshow(canny)

图 2.4-1 边缘检测后的图像数据

2.5 轮廓检测

说明1:边缘检测与轮廓检测的区别?

轮廓检测的目标是确定闭合物体的形状,特别是因为对于具有相同颜色强度的连续点,寻找轮廓的方法是确定的,而边缘检测是通过检测颜色强度内的变化来进行的。参考相关issue

通过上述小节的边缘检测,得到文档图像数据的边缘,这里进一步通过轮廓检测得到这些边缘所对应的闭合轮廓。调用过程如下:
c o n t o u r s , h i e r a r c h y = c v 2. f i n d C o n t o u r s ( i m a g e , m o d e , m e t h o d [ , c o n t o u r s [ , h i e r a r c h y [ , o f f s e t ] ] ] ) contours, hierarchy = cv2.findContours( image, mode, method[, contours[, hierarchy[, offset]]] ) contours,hierarchy=cv2.findContours(image,mode,method[,contours[,hierarchy[,offset]]])

# 对边缘检测后的文档图像数据进行轮廓检测
con = np.zeros_like(img)
# 对检测得到的边缘获取其轮廓
contours, hierarchy = cv2.findContours(canny, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# 保留最大的轮廓,将其绘制在画布上,效果如下图:
page = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
con = cv2.drawContours(con, page, -1, (0, 255, 255), 3)

图 2.5-1 轮廓检测结果

2.6 角点检测

文档图像校正的最终目标是要进行文档区域的对齐(align),为了实现此目标,需要获取文档其余的四个角点坐标。通过轮廓检测后,已经获取到了文档图像的轮廓信息,进一步通过使用 c v 2. a p p r o x P o l y D P cv2.approxPolyDP cv2.approxPolyDP获取角点数据。实现过程如下:

# 对轮廓检测后的数据进行角点检测
con = np.zeros_like(img)
for c in page:
    # arcLength计算轮廓周长或曲线长度
  epsilon = 0.02 * cv2.arcLength(c, True)
  corners = cv2.approxPolyDP(c, epsilon, True)
  if len(corners) == 4:
      break
cv2.drawContours(con, c, -1, (0, 255, 255), 3)
cv2.drawContours(con, corners, -1, (0, 255, 0), 10)
corners = sorted(np.concatenate(corners).tolist())
 
for index, c in enumerate(corners):
  character = chr(65 + index)
  cv2.putText(con, character, tuple(c), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 1, cv2.LINE_AA)

图 2.6-1 角点检测结果

2.7 透视变换

至此,获取到了文档图像数据的四个角点坐标,利用 c v 2. g e t P e r s p e c t i v e T r a n s f o r m cv2.getPerspectiveTransform cv2.getPerspectiveTransform来对其进行透视变换,从而实现图像的对齐,得到校正之后的文档图像数据。

# 透视变换
M = cv2.getPerspectiveTransform(np.float32(corners), np.float32(destination_corners))
# Perspective transform using homography.
final = cv2.warpPerspective(orig_img, M, (destination_corners[2][0], destination_corners[2][1]), flags=cv2.INTER_LINEAR)

最终实现效果如下:

图 2.7-1 图像对齐效果

3、内容总结

至此,文档图像校正过程基本结束。基于此方法基本可以批量实现扭曲文档图像的校正处理,然后进行后去的文档智能任务将得到极大便捷,比如光学字符识别等。回归本文,相关内容总结如下:

  • 对OpenCV的基本使用"浅尝辄止",该工具库足够强大到难以想象,实践中积累;
  • 介绍了基本的形态学处理,包括腐蚀与膨胀操作,以及其组合操作,开闭操作;
  • 对边缘检测、轮廓检测进行了实践;
  • 基于GrabCut对文档图像做了分割处理,去掉了图像背景信息;
  • 局限:当文档前景对象与图像背景区域高度相似或融合、当图像区域未完全在整张图像区域内,则此种方法失效!
  • 疑问:此种方法符合进行效果的评估? 漏报率和错报率?
  • 后续:将结合相关文献,实践基于深度学习的文档图像校正方法,相关内容将发布在神经网络漫谈系列栏目。

4、参考文献

[1] 参考文献: 原文同步转载链接

[2] 周丽,冯百明,关煜,等. 面向智能手机拍摄的变形文档图像校正[J]. 计算机工程与科学,2022,44(1):102-109. DOI:10.3969/j.issn.1007-130X.2022.01.012.

[3] GrabCut算法:参考开源DOC链接.

5、版权声明

[1] 版权声明:本文为博主原创文章,转载或者引用本文内容请注明来源及原作者


转载:https://blog.csdn.net/AIHUBEI/article/details/128596301
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场