当前位置: 首页 > news >正文

计算机视觉案例分享之实时文档扫描

目录

一、核心技术原理

1. 图像预处理

2. 轮廓检测

3. 透视矫正

4. 内容增强

二、代码实现

1. 导入库

2. 辅助函数定义

2.1 图像显示函数

2.2 图像缩放函数

2.3 点排序函数

2.4 透视变换函数

3. 主程序

3.1 初始化摄像头

3.2 主循环 - 实时处理

3.3 资源释放

总结


一、核心技术原理

为什么需要实时文档扫描?

在数字化办公趋势下,纸质文档的电子化需求日益增长。传统方案存在明显痛点:

  • 扫描仪:便携性差,无法满足户外、即时扫描需求;
  • 普通拍照:文档易因拍摄角度产生透视变形(如 “梯形” 效果),文字模糊、边缘倾斜;
  • 第三方 APP:部分工具功能冗余,隐私数据存在泄露风险。

1. 图像预处理

原始摄像头图像包含大量噪声(如光线不均、背景杂物),直接检测轮廓会产生干扰。预处理的目标是 “突出文档边缘,抑制无用信息”,主要包含 3 个操作:

  • 灰度化(BGR→GRAY):将彩色图像转为单通道灰度图,减少计算量(彩色图 3 个通道,灰度图 1 个通道)。
    关键代码:gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  • 高斯模糊(Gaussian Blur):用 5×5 的高斯核平滑图像,消除高频噪声(如纸张褶皱、背景纹理),避免边缘检测时误判。
    关键代码:gray = cv2.GaussianBlur(gray, (5, 5), 0)
  • 边缘检测(Canny):通过 “双阈值” 算法提取图像中明暗变化剧烈的区域(即文档边缘),输出黑白二值图(白色为边缘,黑色为背景)。
    关键代码:edged = cv2.Canny(gray, 75, 200)(75 为低阈值,200 为高阈值,可根据光线调整)

2. 轮廓检测

预处理后,文档边缘已清晰可见,下一步是通过 “轮廓检测” 找到文档的外形轮廓。轮廓本质是图像中连续的像素点集合,对应文档的边界。

  • 提取轮廓:使用cv2.findContours函数,仅保留最外层轮廓(cv2.RETR_EXTERNAL),并简化轮廓存储(cv2.CHAIN_APPROX_SIMPLE,只保留端点,减少数据量)。
    关键代码:cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
  • 筛选有效轮廓:现实场景中,背景可能存在多个轮廓(如桌面物品),需按 “面积” 排序(保留前 3 个最大轮廓),再通过 “形状” 筛选 —— 文档通常是四边形,因此保留 “轮廓近似后顶点数为 4” 且 “面积足够大”(避免误判小杂物)的轮廓。
    关键代码:approx = cv2.approxPolyDP(c, 0.05 * peri, True)0.05*peri为近似精度,peri是轮廓周长)

3. 透视矫正

由于拍摄角度问题,文档在图像中常呈现 “梯形” 或 “平行四边形”(透视变形),需通过 “透视变换” 将其转为标准矩形(正面视图),这是文档扫描的核心步骤。

  • 原理:透视变换通过一个 3×3 的变换矩阵(M),将原始图像中任意四边形的 4 个顶点,映射到目标图像的 4 个顶点(标准矩形的四个角),从而消除透视变形。
  • 步骤
    1. 排序顶点:将检测到的四边形顶点按 “左上→右上→右下→左下” 排序(通过 “顶点坐标和 / 差” 判断:和最小为左上,和最大为右下;差最小为右上,差最大为左下);
      关键函数:order_points(pts)
    2. 计算目标尺寸:根据原始四边形的边长,确定矫正后矩形的宽高(取两组对边长度的最大值,避免内容裁剪);
    3. 生成变换矩阵:用cv2.getPerspectiveTransform(rect, dst)计算矩阵Mrect为原始顶点,dst为目标顶点);
    4. 应用透视变换:用cv2.warpPerspective将原始图像按矩阵M变换,得到矫正后的文档图像。
      关键代码:warped = cv2.warpPerspective(orig, M, (maxWidth, maxHeight))

4. 内容增强

矫正后的文档可能存在光线不均(如阴影),需通过 “二值化” 将灰度图转为黑白图,突出文字内容,模拟扫描仪的 “黑白模式”。

  • 阈值二值化:设定一个阈值(如 220),将灰度值高于阈值的像素设为 255(白色,背景),低于阈值的设为 0(黑色,文字),消除灰度渐变干扰。
    关键代码:ref = cv2.threshold(warped, 220, 255, cv2.THRESH_BINARY)[1]

二、代码实现

1. 导入库

import numpy as np
import cv2
  • numpy:用于数值计算和数组操作
  • cv2:OpenCV 库,用于图像处理和计算机视觉任务

2. 辅助函数定义

2.1 图像显示函数
def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(6)  # 显示6毫秒后继续执行
  • 封装了 OpenCV 的图像显示功能,方便在处理过程中查看中间结果
2.2 图像缩放函数
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None  # 存储目标尺寸(h, w) = image.shape[:2]  # 获取图像原始尺寸# 如果未指定宽度和高度,返回原图if width is None and height is None:return image# 如果只指定高度,按比例计算宽度if width is None:r = height / float(h)dim = (int(w * r), height)# 如果只指定宽度,按比例计算高度else:r = width / float(w)dim = (width, int(h * r))# 执行缩放resized = cv2.resize(image, dim, interpolation=inter)return resized
  • 保持图像宽高比不变的情况下调整图像大小
  • 使用面积插值法 (INTER_AREA),适用于缩小图像
2.3 点排序函数
def order_points(pts):rect = np.zeros((4, 2), dtype='float32')  # 存储排序后的点# 计算每个点的x+y之和s = pts.sum(axis=1)rect[0] = pts[np.argmin(s)]  # 和最小的是左上角点rect[2] = pts[np.argmax(s)]  # 和最大的是右下角点# 计算每个点的x-y差值diff = np.diff(pts, axis=1)rect[1] = pts[np.argmin(diff)]  # 差值最小的是右上角点rect[3] = pts[np.argmax(diff)]  # 差值最大的是左下角点return rect
  • 将四边形的四个点按 "左上、右上、右下、左下" 的顺序排列
  • 为后续透视变换做准备
2.4 透视变换函数
def four_point_transform(image, pts):rect = order_points(pts)  # 获取排序后的点(tl, tr, br, bl) = rect  # 解包四个点# 计算目标图像的宽度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))# 计算目标图像的高度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
  • 将透视变形的文档转换为正面视图
  • 通过计算原始四边形的尺寸确定目标图像的大小
  • 使用透视变换矩阵实现视角矫正

3. 主程序

3.1 初始化摄像头
cap = cv2.VideoCapture(0)  # 打开默认摄像头
if not cap.isOpened():print("Cannot open camera")exit()
3.2 主循环 - 实时处理
while True:flag = 0  # 文档检测标志ret, image = cap.read()  # 读取一帧图像orig = image.copy()  # 保存原始图像副本if not ret:  # 读取失败则退出print("不能读取摄像头")breakcv_show("image", image)  # 显示原始图像# 预处理:转为灰度图 -> 高斯模糊 -> 边缘检测gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)gray = cv2.GaussianBlur(gray, (5, 5), 0)  # 减少噪声edged = cv2.Canny(gray, 75, 200)  # 边缘检测cv_show('1', edged)# 轮廓检测cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]# 按面积排序,取最大的3个轮廓cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:3]# 绘制轮廓image_contours = cv2.drawContours(image, cnts, -1, (0, 255, 0), 2)cv_show("image_contours", image_contours)# 遍历轮廓,寻找四边形(文档)for c in cnts:peri = cv2.arcLength(c, True)  # 计算轮廓周长# 多边形逼近approx = cv2.approxPolyDP(c, 0.05 * peri, True)area = cv2.contourArea(approx)  # 计算轮廓面积# 筛选条件:面积足够大且是四边形if area > 400 and len(approx) == 4:screenCnt = approxflag = 1  # 标记检测到文档print(peri, area)print('检测到文档')break# 如果检测到文档,进行处理if flag == 1:# 绘制文档轮廓image_contours = cv2.drawContours(image, [screenCnt], 0, (0, 255, 0), 2)cv_show("image", image_contours)# 透视变换矫正文档warped = four_point_transform(orig, screenCnt.reshape(4, 2))cv_show("warped", warped)# 文档内容提取:转为灰度图 -> 二值化warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)ref = cv2.threshold(warped, 220, 255, cv2.THRESH_BINARY)[1]cv_show("ref", ref)
3.3 资源释放
cap.release()  # 释放摄像头
cv2.destroyAllWindows()  # 关闭所有窗口

总结

这个程序实现了一个简单但功能完整的实时文档扫描系统,主要流程包括:

  1. 从摄像头获取实时图像
  2. 对图像进行预处理(灰度化、模糊、边缘检测)
  3. 检测图像中的轮廓,筛选出可能是文档的四边形轮廓
  4. 对检测到的文档进行透视变换,矫正为正面视图
  5. 对矫正后的文档进行二值化处理,突出显示文档内容

通过这个程序,可以将倾斜或变形的文档拍摄成类似扫描件的效果,提高文档的可读性。


文章转载自:

http://DhbpUKAv.gkjyg.cn
http://aSxiQjWi.gkjyg.cn
http://Tyo9zkX7.gkjyg.cn
http://j4jJWHhd.gkjyg.cn
http://t0ZEjDkz.gkjyg.cn
http://LDM4IiWM.gkjyg.cn
http://eXeOld7f.gkjyg.cn
http://aeZfBBSK.gkjyg.cn
http://bkWXoOcs.gkjyg.cn
http://cwfhV3Vc.gkjyg.cn
http://6EFiVWXM.gkjyg.cn
http://KN6cJQxh.gkjyg.cn
http://Z4bPueOF.gkjyg.cn
http://dGObnxgp.gkjyg.cn
http://o24fdk3q.gkjyg.cn
http://1I1Xzl2m.gkjyg.cn
http://ki9lGgq3.gkjyg.cn
http://dEk4LVID.gkjyg.cn
http://xrPNJcHV.gkjyg.cn
http://10tGMHLO.gkjyg.cn
http://CQ9Car7B.gkjyg.cn
http://aazv6Z9q.gkjyg.cn
http://plO2FzQK.gkjyg.cn
http://BrX8Jgln.gkjyg.cn
http://SBhjmCyk.gkjyg.cn
http://7OMJ58SJ.gkjyg.cn
http://ifpnZd78.gkjyg.cn
http://R8cphArT.gkjyg.cn
http://ZuouWYIe.gkjyg.cn
http://OVfUXkkh.gkjyg.cn
http://www.dtcms.com/a/384939.html

相关文章:

  • 提升PDF处理效率,Stirling-PDF带你探索全新体验!
  • 【React】闭包陷阱
  • 4.RocketMQ集群高级特性
  • 周选择日历组件
  • Golang引用类型
  • Go的Gob编码介绍与使用指南
  • Golang语言入门篇001_Golang简介
  • Kafka消息队列进阶:发送策略与分区算法优化指南
  • 台积电生态工程深度解析:从晶圆厂到蜂巢的系统架构迁移
  • 机器学习-网络架构搜索
  • 邪修实战系列(5)
  • 突破限制:Melody远程音频管理新体验
  • 深入解析Seata:一站式分布式事务解决方案
  • static_cast:C++类型系统的“正经翻译官”
  • Python面试题及详细答案150道(126-135) -- 数据库交互篇
  • 【新书预告】《大模型应用开发》
  • MySQL 视图的创建与查看:从基础操作到核心技巧
  • 企业内容管理(ECM)软件推荐与应用解析
  • 利用postgres_proto和pgproto配合验证测试postgres协议
  • 联咏nt98568点亮sensor步骤
  • 大模型操作SQL查询Text2SQL
  • 风力发电乙级资质需要哪些人员配备
  • 【JavaScript】实现一个高精度的定时器
  • 无偏估计-
  • SQL-流程控制函数
  • TNNLS-2015《Linear-Time Subspace Clustering via Bipartite Graph Modeling》
  • 线性代数 · 行列式 | 子式 / 主子式 / 顺序主子式 / 余子式 / 代数余子式
  • LLM的MTP论文阅读
  • 软考-系统架构设计师 软件工程详细讲解
  • MATLAB 实现基于 GMM-HMM的语音识别系统