使用OpenCV实现一个文档自动扫描仪

 
 

点击上方“小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达

导读

本文主要介绍如何使用 OpenCV + GrabCut实现一个文档自动扫描仪。(公众号:OpenCV与AI深度学习)

背景介绍

    文档扫描是将物理文档转换为数字形式的过程。可以通过扫描仪或手机摄像头拍摄图像来完成。我们将在本文中讨论如何使用计算机视觉和图像处理技术有效地实现这一目标。

    有许多软件解决方案和应用程序可以做到这一点。借助计算机视觉的力量,从物理文档到扫描文档的过程与将相机对准文档并单击图片没有太大区别。速度和易用性是此类解决方案的主要优势,它们可用于计算机和移动设备。

    让我们看看如何使用经典的计算机视觉技术创建一个简单的 OpenCV 文档扫描仪,其中输入将是我们要扫描的文档的图像,而预期的输出将是正确对齐的文档扫描图像。

实现目标

    如下图所示,给定一张包含文档的图片,通过代码自动将文档提取并矫正。

74e98324b8b8a6456a29f83daba5cf49.png

实现步骤

    测试原图如下:

850abb2a4d9980d914e2be870da07a3a.jpeg

实现步骤:

【1】通过形态学处理,得到一个空白页。这里直接用闭运算即可,闭运算是膨胀,然后是腐蚀。不断重复这些关闭操作,直到你得到一个空白页。 

179f7e920f33f54cdde7640c6ab777d7.png

kernel = np.ones((5,5),np.uint8)
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations= 3)

72822e07080f9811b07d0b3c666220f7.png

为什么我们想要一个空白文档呢?因为后面会进行边缘检测,并且我们不希望被页面的文字内容干扰该。

【2】用GrabCut去掉背景。

  • 它只需要在前景中的对象周围设置一个边界框,边界框之外的所有内容都被视为背景。  

  • GrabCut 会自动消除所有背景,即使在边界框内也是如此。现在剩下的就是前景对象。 

我们将角落 20 像素作为背景,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]

56707dc41e1701c7179cd58da5f93f8b.png

【3】Canny边缘检测 + 轮廓提取。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (11, 11), 0)
# Edge Detection.
canny = cv2.Canny(gray, 0, 200)
canny = cv2.dilate(canny, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
  • 首先将空白页的图像转换为灰度,因为canny只对灰度图像起作用。

  • 然后执行高斯模糊以去除图像中的噪声。 

  • 最后,对图像进行精确边缘检测。 

  • 此外,放大图像以获得文档的细轮廓。

# Blank canvas.
con = np.zeros_like(img)
# Finding contours for the detected edges.
contours, hierarchy = cv2.findContours(canny, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Keeping only the largest detected contour.
page = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
con = cv2.drawContours(con, page, -1, (0, 255, 255), 3)
  • 根据大小对检测到的轮廓进行排序

  • 只保留检测到的最大轮廓 

  • 然后在空白画布上绘制这个检测到的最大轮廓

24ef2c928a5f886ecd088d3609925c55.png

【4】角点检测 + 排序。

# Blank canvas.
con = np.zeros_like(img)
# Loop over the contours.
for c in page:
  # Approximate the contour.
  epsilon = 0.02 * cv2.arcLength(c, True)
  corners = cv2.approxPolyDP(c, epsilon, True)
  # If our approximated contour has four points
  if len(corners) == 4:
      break
cv2.drawContours(con, c, -1, (0, 255, 255), 3)
cv2.drawContours(con, corners, -1, (0, 255, 0), 10)
# Sorting the corners and converting them to desired shape.
corners = sorted(np.concatenate(corners).tolist())


# Displaying the corners.
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)

2c2a5924c09f018ac5ddc839f30ee734.png

角点排序:

def order_points(pts):
    '''Rearrange coordinates to order:
      top-left, top-right, bottom-right, bottom-left'''
    rect = np.zeros((4, 2), dtype='float32')
    pts = np.array(pts)
    s = pts.sum(axis=1)
    # Top-left point will have the smallest sum.
    rect[0] = pts[np.argmin(s)]
    # Bottom-right point will have the largest sum.
    rect[2] = pts[np.argmax(s)]


    diff = np.diff(pts, axis=1)
    # Top-right point will have the smallest difference.
    rect[1] = pts[np.argmin(diff)]
    # Bottom-left will have the largest difference.
    rect[3] = pts[np.argmax(diff)]
    # Return the ordered coordinates.
    return rect.astype('int').tolist()

确定目标坐标:一旦获得文档的角点,接下来只需要目标坐标来执行透视变换和对齐文档。

【5】透视变换对齐文档。

# Getting the homography.
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)

d3bbf53d6511d8a65aa7b58391abfeab.jpeg

【6】扩展测试。

    我们在 23 种不同的背景和不同的方向上进行了测试,自动文档扫描仪几乎在所有情况下都运行良好。

999265f885ce1963a71682532eccf51f.jpeg

    失败情况:

e6c73b50c521f14db09dd5aa64fa46d4.jpeg

    当文档的一部分在图像之外时,可能会丢失一个角落,GrabCut 无法扫描。这是使用 GrabCut 的唯一限制。在大多数其他情况下,我们的文档扫描仪运行良好。

    这种方法的另一个限制是边缘和轮廓检测。如果背景中存在大量噪声,则会检测到许多不需要的边缘,并且在某些情况下,轮廓检测步骤可能会将这些边缘误认为是文档。此外,如果文档边缘与背景无法区分,则轮廓检测可能无法完全正常工作。

    但 GrabCut 和轮廓检测并不是唯一经过验证的文档扫描方法。对于消费级文档扫描解决方案,首选角点检测和分割等深度学习技术,因为它们更强大。

参考链接:

https://learnopencv.com/automatic-document-scanner-using-opencv/

 
 

好消息!

小白学视觉知识星球

开始面向外开放啦👇👇👇

 
 

e45e5570ac1950b7196f6411d6ecfc01.jpeg

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。


下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。


下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。


交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值