当前位置:网站首页>使用OpenCV实现一个文档自动扫描仪
使用OpenCV实现一个文档自动扫描仪
2022-08-04 23:04:00 【Color Space】
导读
本文主要介绍如何使用 OpenCV + GrabCut实现一个文档自动扫描仪。(公众号:OpenCV与AI深度学习)
背景介绍
文档扫描是将物理文档转换为数字形式的过程。可以通过扫描仪或手机摄像头拍摄图像来完成。我们将在本文中讨论如何使用计算机视觉和图像处理技术有效地实现这一目标。
有许多软件解决方案和应用程序可以做到这一点。借助计算机视觉的力量,从物理文档到扫描文档的过程与将相机对准文档并单击图片没有太大区别。速度和易用性是此类解决方案的主要优势,它们可用于计算机和移动设备。
让我们看看如何使用经典的计算机视觉技术创建一个简单的 OpenCV 文档扫描仪,其中输入将是我们要扫描的文档的图像,而预期的输出将是正确对齐的文档扫描图像。
实现目标
如下图所示,给定一张包含文档的图片,通过代码自动将文档提取并矫正。

实现步骤
测试原图如下:

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

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

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

【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)
根据大小对检测到的轮廓进行排序
只保留检测到的最大轮廓
然后在空白画布上绘制这个检测到的最大轮廓

【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 pointsif len(corners) == 4:breakcv2.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)

角点排序:
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)

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

失败情况:

当文档的一部分在图像之外时,可能会丢失一个角落,GrabCut 无法扫描。这是使用 GrabCut 的唯一限制。在大多数其他情况下,我们的文档扫描仪运行良好。
这种方法的另一个限制是边缘和轮廓检测。如果背景中存在大量噪声,则会检测到许多不需要的边缘,并且在某些情况下,轮廓检测步骤可能会将这些边缘误认为是文档。此外,如果文档边缘与背景无法区分,则轮廓检测可能无法完全正常工作。
但 GrabCut 和轮廓检测并不是唯一经过验证的文档扫描方法。对于消费级文档扫描解决方案,首选角点检测和分割等深度学习技术,因为它们更强大。
参考链接:
https://learnopencv.com/automatic-document-scanner-using-opencv/
————————————————THE END——————————————
下载1:Pytorch常用函数手册
在「OpenCV与AI深度学习」公众号后台回复:Pytorch函数手册,即可下载学习全网第一份Pytorch函数常用手册,包括Tensors介绍、基础函数介绍、数据处理函数、优化函数、CUDA编程、多处理等十四章内容。
下载2:145个OpenCV实例应用代码
在「OpenCV与AI深度学习」公众号后台回复:OpenCV145,即可下载学习145个OpenCV实例应用代码(Python和C++双语言实现)。
边栏推荐
猜你喜欢
随机推荐
Redisson
一点点读懂regulator(四)
一招包治pycharm DEBUG报错 UnicodeDecodeError: ‘utf-8‘ codec can‘t decode
MySQL的JSON 数据类型1
JVM memory configuration parameter GC log
【3D建模制作技巧分享】ZBrush如何使用Z球
Leaflets of three bouquet of roses
good luck
kernel问题定位手段总结
当panic或者die被执行时,或者发生未定义指令时,如何被回调到
Linux系统重启和停止Mysql服务教程
Service Mesh落地路径
正则表达式绕过
[Paper Notes KDD2021] MixGCF: An Improved Training Method for Graph Neural Network-based Recommender Systems
【3D建模制作技巧分享】ZBrush模型制作流程:地精
panic: reflect: reflect.Value.SetString using value obtained using unexported field
测试技术:关于上下文驱动测试的总结
基于内容的图像检索系统设计与实现--颜色信息--纹理信息--形状信息--PHASH--SHFT特征点的综合检测项目,包含简易版与完整版的源码及数据!
The Record of Reminding myself
【游戏建模模型制作全流程】使用ZBrush制作骷髅王









