当前位置:网站首页>使用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++双语言实现)。
边栏推荐
- 为何越来越多人选择进入软件测试行业?深度剖析软件测试的优势...
- 文章占位 文章占位
- kernel问题定位手段总结
- 学生管理系统架构设计
- panic: reflect: reflect.Value.SetString using value obtained using unexported field
- Community Sharing|Tencent Overseas Games builds game security operation capabilities based on JumpServer
- 期货开户哪个平台好,要正规安全的
- Controller层代码这么写,简洁又优雅!
- 养殖虚拟仿真软件提供高沉浸式的虚拟场景互动操作体验学习
- 测试技术:关于上下文驱动测试的总结
猜你喜欢

【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)

Using ngrok to optimize web pages on raspberry pi (2)

Will we still need browsers in the future?(feat. Maple words Maple language)

3年,从3K涨薪到20k?真是麻雀啄了牛屁股 — 雀食牛逼呀

从“草原牛”到“数字牛”:蒙牛的数字化转型之道

Use ngrok to optimize web pages on raspberry pi (1)

BUG | The interface returns abnormal data

Pytest learning - fixtures

逆序对的数量

C5750X7R2E105K230KA(电容器)MSP430F5249IRGCR微控制器资料
随机推荐
【3D建模制作技巧分享】如何使用ZBrush导出效果图
视频gif如何制作?试试这个视频制作gif神器
VC bmp文件总结
得不到你的心,就用“分布式锁”锁住你的人
2022年全网最全接口自动化测试框架搭建,没有之一
社区分享|腾讯海外游戏基于JumpServer构建游戏安全运营能力
测试技术:关于上下文驱动测试的总结
如何根据地址获取函数名
Kernel函数解析之kernel_restart
typeScript-部分应用函数
2022/8/4 树上差分+线段树
质量管理大师爱德华·戴明博士经典的质量管理14条原则
特征工程资料汇总
【游戏建模模型制作全流程】ZBrush蜥蜴模型雕刻教程
【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)
一点点读懂regulator(二)
panic: reflect: reflect.Value.SetString using value obtained using unexported field
kernel问题定位手段总结
CS8416国产替代DP8416 数字音频接收器
【3D建模制作技巧分享】Maya模型如何导入zbrush