用OpenCV构建文档扫描仪只需三个简单步骤:

  • 1.边缘检测
  • 2.使用图像中的边缘来找到代表被扫描纸张的轮廓。
  • 3.应用透视变换来获得文档的自顶向下视图。

只需三步,你就可以将自己的文档扫描应用程序提交到App Store。

1.如何实现4点透视变换

在你正式构建文档扫描仪之前,你需要创建一个transform.py模块,里面实现了four_point_transform功能。当您需要执行4点透视变换时,您应该使用这个模块。

所以我们别再浪费时间了。打开一个新文件,将其命名为transform.py,让我们开始吧。

# 导入必要的包
import numpy as np
import cv2
def order_points(pts):# 初始化一个坐标列表,该列表中的第一个元素是左上,第二个元素是右上,第三个元素是右下,第四个元素是左下rect = np.zeros((4, 2), dtype = "float32")# 左上角点的和最小,然而右下角的点的和最大s = pts.sum(axis = 1)rect[0] = pts[np.argmin(s)]rect[2] = pts[np.argmax(s)]# 现在,计算点之间的差值,右上角的差值最小,而左下角的差值最大diff = np.diff(pts, axis = 1)rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]# 返回有序坐标return rect

我们首先导入我们需要的包,接下来,让我们定义order_points函数。这个函数接受一个参数pts,它是一个由四个点组成的列表,指定矩形中每个点的(x, y)坐标。

矩形中各点的顺序必须一致,这一点非常重要。实际的排序本身可以是任意的,只要它在整个实现中是一致的。

就我个人而言,我喜欢按照左上、右上、右下和左下的顺序来指定我的点。
首先,我们将为四个有序点分配内存。
然后,我们会找到左上角的点,它的x + y和最小,以及右下角的点,它的x + y和最大。
当然,现在我们必须找到右上角和左下角的点。这里我们用np.diff来求点之间的差(即x - y)。
最后,我们将我们的有序坐标返回给调用函数。

我再一次强调,保持点的顺序是多么的重要。

你将在下一个函数中看到确切的原因:

def four_point_transform(image, pts):# 获得点的一致顺序,并将它们分别拆封rect = order_points(pts)(tl, tr, br, bl) = rect# 计算新图像的宽度,这将是右下角和左下角x坐标或右上角和左上角x坐标之间的最大距离widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA), int(widthB))# 计算新图像的高度,这将是右上角和右下角y坐标或左上角和左下角y坐标之间的最大距离heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))# 现在我们有了新图像的维数,构建目标点集以获得图像的“鸟瞰视图”(即自顶向下视图),再次指定左上、右上、右下和左下顺序中的点dst = np.array([[0, 0],[maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype = "float32")# 计算透视变换矩阵,然后应用它M = cv2.getPerspectiveTransform(rect, dst)warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))# 返回变换后的图像return warped

首先定义four_point_transform函数,它需要两个参数:image和pts
image变量是我们想要应用透视变换的图像。pts列表是包含我们要变换的图像的感兴趣区域的四个点的列表。
我们调用order_points函数,它以一致的顺序放置pts变量。然后为了方便,我们解包这些坐标。
现在我们需要确定新的扭曲图像的尺寸。
我们确定新图像的宽度,其中宽度是右下角和左下角x坐标或右上角和左上角x坐标之间的最大距离。
以类似的方式,我们确定新图像的高度,其中高度是右上角和右下角y坐标或左上角和左下角y坐标之间的最大距离。

还记得我是怎么说的吗?我们试图获得原始图像中ROI的自上而下的“鸟瞰图”?还记得我说过,代表ROI的四个点的一致顺序是至关重要的吗?
你可以看到原因。在这里,我们定义了4个点来代表我们“自上而下”的图像视图。列表中的第一个元素是(0,0),表示左上角。第二个元素是(maxWidth - 1,0),它对应于右上角。然后我们有(maxWidth - 1, maxHeight - 1)这是右下角。最后,我们有(0,maxHeight - 1),它是左下角。

这里的结论是,这些点定义是一致的顺序表示,这样我们就能得到图像的自上而下的视图。

为了获得图像自上而下的“鸟瞰图”,我们将使用cv2.getPerspectiveTransform函数。这个函数需要两个参数,rect,它是原始图像中4个感兴趣点的列表,dst,它是我们的变换点列表。cv2.getPerspectiveTransform函数返回M,这是实际的转换矩阵。

我们使用cv2.warpPerspective函数应用变换矩阵。我们传入图像、变换矩阵M,以及输出图像的宽度和高度。
cv2.warpPerspective的输出是我们变换后的图像,这是我们自上而下的视图。

2.构建文档扫描仪

打开您最喜欢的Python IDE,创建一个新文件,将其命名为scan.py,然后让我们开始。

# 导入必要的包
from transform import four_point_transform
from skimage.filters import threshold_local
import numpy as np
import argparse
import cv2
import imutils
# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True,help = "Path to the image to be scanned")
args = vars(ap.parse_args())

我们将从导入four_point_transform函数开始。我们还将使用imutils模块,它包含了调整大小、旋转和裁剪图像的函数。接下来,让我们从scikit-image中导入threshold_local函数。这个函数将帮助我们的扫描图像获得“黑白”的感觉。

最后,我们将使用NumPy进行数值处理,使用argparse解析命令行参数,使用cv2进行OpenCV绑定。
解析命令行参数。我们只需要一个参数——image,它是包含要扫描的文档的图像的路径。

现在我们有了图像的路径,我们可以继续。

2.1步骤1:边缘检测

使用OpenCV构建文档扫描应用程序的第一步是执行边缘检测。让我们来看看:

# 加载图像并计算旧高度与新高度的比率,克隆它,并调整它的大小
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = imutils.resize(image, height = 500)
# 将图像转换为灰度,模糊它,并在图像中找到边缘
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 显示原始图像和边缘检测图像
print("STEP 1: Edge Detection")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()

首先,我们从磁盘加载图像。为了加快图像处理速度,并使边缘检测更准确,我们调整了扫描图像的大小,使其高度为500像素。
我们还特别注意跟踪图像的原始高度与新高度的比率——这将允许我们对原始图像执行扫描,而不是调整大小的图像。
然后,我们将图像从RGB转换为灰度,执行高斯模糊以去除高频噪声,并执行Canny边缘检测。然后显示步骤1的输出。

看看下面的示例文档:

在左边你可以看到我在超市的收据。注意这张照片是如何以一个角度拍摄的。它绝对不是一个90度、自上而下的页面视图。此外,画面中还有我的书桌。当然,这不是对任何方法的“扫描”。我们有很多工作要做。
而在右侧,你可以看到进行边缘检测后的图像。我们可以清楚地看到收据的轮廓。
不错的开始。
让我们进入第二步。

2.2寻找轮廓

轮廓检测并不难。
事实上,在构建文档扫描仪时,你实际上有一个很大的优势……
花点时间考虑一下我们实际上在构建什么。
文档扫描仪只是扫描一张纸。
假设一张纸是长方形的。
矩形有四条边。
因此,我们可以创建一个简单的启发式算法来帮助我们构建文档扫描程序。
启发式是这样的:我们假设图像中有四个点的最大轮廓就是我们要扫描的那张纸。
这也是一个相当安全的假设——扫描程序简单地假设你要扫描的文档是我们图像的主要焦点。我们也可以假设(至少应该是)这张纸有四条边。

这正是下面的代码所做的:

# 找到边缘图像中的轮廓,只保留最大的轮廓,并初始化屏幕轮廓
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
# 循环迭代所有轮廓
for c in cnts:# 近似轮廓peri = cv2.arcLength(c, True)approx = cv2.approxPolyDP(c, 0.02 * peri, True)# 如果我们的近似轮廓有4个点,那么我们可以假设我们已经找到了我们的屏幕if len(approx) == 4:screenCnt = approxbreak
# 画出这张票的轮廓
print("STEP 2: Find contours of paper")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

我们首先在边缘图像中找到轮廓。我们还处理了OpenCV 2.4、OpenCV 3或OpenCV 4返回的轮廓。
我喜欢做的一个性能改进是按面积对轮廓进行排序,只保留最大的轮廓。这允许我们只检查最大的轮廓,而忽略其余的。
然后我们开始循环,并近似点数。
如果近似的轮廓线有四个点,我们假设我们已经在图像中找到了文档。
同样,这是一个相当安全的假设。扫描程序将假定(1)要扫描的文档是图像的主要焦点,(2)文档是矩形的,因此将有四条不同的边。

现在让我们看看我们的示例图像:

如你所见,我们已经成功地利用边缘检测图像来找到文件的轮廓,用环绕收据的绿色矩形来说明。
最后,让我们进入第3步。

2.3步骤3:应用透视变换和阈值

构建移动文档扫描仪的最后一步是取代表文档轮廓的四个点,并应用透视变换获得图像自上而下的“鸟瞰图”。
让我们来看看:

# 应用四点变换获得原始图像自上而下的视图
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 将变换后的图像转换为灰度,然后使用阈值给它“黑白”纸的效果
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
T = threshold_local(warped, 11, offset = 10, method = "gaussian")
warped = (warped > T).astype("uint8") * 255
# 显示原始和扫描图像
print("STEP 3: Apply perspective transform")
cv2.imshow("Original", imutils.resize(orig, height = 650))
cv2.imshow("Scanned", imutils.resize(warped, height = 650))
cv2.waitKey(0)

执行透视变换。事实上,所有繁重的工作都是由four_point_transform函数处理的。我们将向four_point_transform传递两个参数:第一个是我们从磁盘加载的原始图像(不是调整大小的图像),第二个参数是表示文档的轮廓,乘以调整大小的比例。
你可能会想,为什么要乘以调整后的比例呢?
我们乘以调整后的比例,因为我们执行了边缘检测,并在高度为500像素的调整后的图像上发现了轮廓。
然而,我们希望对原始图像进行扫描,而不是调整大小的图像,因此我们将轮廓点乘以调整大小的比例。

为了获得黑白感觉的图像,我们然后将变换后的图像,转换为灰度图像,并应用自适应阈值。最后,显示输出。

3.Python + OpenCV文档扫描结果


左边是我们从磁盘上下载的原始图像。在右边,我们有扫描图像!
注意扫描图像的视角是如何变化的-我们有一个自上而下的,90度的图像视图。
多亏了我们的自适应阈值,我们的文档也有了漂亮、干净的黑白感觉。
我们已经成功地建立了我们的文档扫描仪!

总结

在这篇博文中,我向您展示了如何使用OpenCV用不到75行Python代码构建一个文档扫描仪。
文件扫描可以分为三个不同而简单的步骤。
第一步是应用边缘检测。
第二步是在图像中找到我们想要扫描的文档的轮廓。
最后一步是应用透视变换来获得一个自上而下的,90度的图像视图,就像我们扫描文档一样。
您还可以选择应用阈值来获得漂亮、干净的黑白感觉。

参考目录

https://www.pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/

imutils基础(4)构建一个文档扫描仪相关推荐

  1. 使用Python,OpenCV构建移动文档扫描仪

    使用Python,OpenCV构建移动文档扫描仪 1. 效果图 2. 步骤 3. 源码 参考 1. 效果图 图1,鸟瞰图 图2,角度不太一样,鸟瞰图的效果也不一致: 2. 步骤 使用OpenCV构建文 ...

  2. VuePress构建一个文档管理网站

    序言 目前无论笔记还是项目文档,大部分我都会通过 Markdown来记录,并且大部分文档写完都只存在自己电脑上,每次查找起来都需要耗费一些时间 自己的写的一部分技术教程由于初次记录时了解知识不多,内容 ...

  3. 文档扫描仪的构建——使用Python,OpenCV应用透视变换来获得图像的自顶向下的“鸟瞰图”

    使用Python,OpenCV应用透视变换来获得图像的自顶向下的"鸟瞰图" 1. 效果图 2. 应用透视变换的步骤 3. 优化:矩形角点的获取 4. 源码 参考 这篇博客演示了如何 ...

  4. 富士通ScanSnap的S1500文档扫描仪 - 最佳的方式去无纸化

    富士通ScanSnap的S1500文档扫描仪 - 最佳的方式去无纸化 富士通ScanSnap的S1500文档扫描仪是清理所有这些文件是很重要的一个好办法,但都塞满了你的家或办公室.这种扫描仪可以让你保 ...

  5. SwiftUI 使用Apple Visionkit构建文档扫描仪

    实战需求 SwiftUI 使用Apple Visionkit构建文档扫描仪 本文价值与收获 看完本文后,您将能够作出下面的界面 核心功能 扫描一个或多个文件 共享扫描的文档 如果不需要文件,则删除 基 ...

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

    导读 本文主要介绍如何使用 OpenCV + GrabCut实现一个文档自动扫描仪.(公众号:OpenCV与AI深度学习) 背景介绍 文档扫描是将物理文档转换为数字形式的过程.可以通过扫描仪或手机摄像 ...

  7. SwiftUI OCR功能大全之 基于 SwiftUI 构建文档扫描仪

    在这篇文章中,让我们快速了解如何使用文档扫描仪扩展 SwiftUI,该扫描仪使用设备相机扫描 iOS 中的文档. 为了实现这一点,我们将使用 Apple 的Vision框架创建一个VNDocument ...

  8. 使用 OpenCV 构建文档扫描仪

    介绍 在本文中,我们将使用 OpenCV 库来开发 Python 文档扫描器. OpenCV 的简要概述: OpenCV 是一个开源库,用于各种计算机语言的图像处理,包括 Python.C++ 等.它 ...

  9. 使用语义分割架构的文档扫描仪 DeepLabV3

    0 介绍 地址:https://learnopencv.com/deep-learning-based-document-segmentation-using-semantic-segmentatio ...

最新文章

  1. Ext fucionchart插件
  2. Nginx Keepalived安装部署完整步骤
  3. 解决win7“该文件没有与之关联的程序来执行该操作”
  4. CentOS/Linux 解决 SSH 连接慢
  5. MySQL函数笔记_MySQL笔记之数学函数详解
  6. mysql循环建表_MySQL 开发准则(总结自阿里巴巴开发手册)
  7. ARM指令集的最新版本包括针对JavaScript的优化
  8. 【软考】软考简易版知识点复习指南汇总
  9. 释放被束缚的页面 – V1.1.0
  10. cs61a 2018spr hw05 的一个题目
  11. html 页面只能打印一半,打印机只能打印一部分-打印机只能打印一半是什么问?打印机只能打印 – 手机爱问...
  12. 出席华盛顿大学以人为本用户体验设计领导力活动 探讨区块链的用户体验 | ArcBlock 活动...
  13. 区块链技术发展现状与展望 论文阅读摘要(袁勇、王飞跃)
  14. 职场四种人:打工者、职业人、企业人和社会人,你是那种人?
  15. linux 编辑模式使用sed,sed命令的用法和vim编辑器的使用
  16. 混合精度训练、分布式训练等训练加速方法
  17. 数商云医药医疗行业B2B平台:如何赋能企业数字化转型,破局传统医药通路难题
  18. (转)从17家顶级基金退出情况中反思投资机构退出之道
  19. 打包项目的时候报错:Failed to execute goal on project ...: Could not resolve dependencies for project ...
  20. OA系统:易用性与灵活性缺一不可

热门文章

  1. windows 查看文件的md5值
  2. Caffe 代码解读之全连接层 inner product layer
  3. 视网膜血管分割方法整理
  4. Windows python3安装word2vec模块常见错误处理
  5. 3A游戏的未来:实景三维技术解放人工建模
  6. jquery禁用右键、文本选择功能、复制的代码
  7. 老笔记整理二:网页小问题汇总
  8. 洛谷 P3518 [POI2011] SEJ-Strongbox 题解
  9. 如何选择电脑--送给我的大一学弟学妹
  10. cnpm报错:Error: Cannot find module ‘diagnostics_channel‘