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

OpenCV篇——项目(二)OCR文档扫描

目录

文档扫描项目说明

前言

文档扫描代码总体演示

OCR文档识别代码总体演示:

​编辑

代码功能详解

1. 预处理阶段

2. 边缘检测

3. 轮廓处理

4. 透视变换

5. 后处理

主要改进说明:

使用建议:


文档扫描项目说明

前言

本项目实现了一个自动化文档扫描系统,能够将倾斜拍摄的文档图像校正为正面视角的矩形图像。系统通过边缘检测、轮廓识别和透视变换技术,模拟真实文档扫描仪的功能。该解决方案适用于纸质文档数字化、表单识别等场景,可有效处理拍摄角度倾斜、透视变形等问题。

文档扫描代码总体演示
# 导入工具包
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())def order_points(pts):# 一共4个坐标点rect = np.zeros((4, 2), dtype = "float32")# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下# 计算左上,右下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 rectdef four_point_transform(image, pts):# 获取输入坐标点rect = order_points(pts)(tl, tr, br, bl) = rect# 计算输入的w和h值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 warpeddef 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 imageif 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# 读取输入
image = cv2.imread(args["image"])
#坐标也会相同变化
ratio = image.shape[0] / 500.0
orig = image.copy()image = resize(orig, 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: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# imutils的作用是兼容OpenCV 3.X和4.X
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
screenCnt= None
# 遍历轮廓
for c in cnts:# 计算轮廓近似peri = cv2.arcLength(c, True)# C表示输入的点集# epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数# True表示封闭的approx = cv2.approxPolyDP(c, 0.02 * peri, True)# 4个点的时候就拿出来if len(approx) == 4:screenCnt = approxbreak# 展示结果
print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()# 透视变换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)# 二值处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)
# 展示结果
print("STEP 3: 变换")
cv2.imshow("Original", resize(orig, height = 650))
cv2.imshow("Scanned", resize(ref, height = 650))
cv2.waitKey(0)

边缘检测:

获取轮廓:

透视变换:

OCR文档识别代码总体演示:
from PIL import Image
import pytesseract
import cv2
import ospreprocess = "blur" # thresh
image = cv2.imread('scan.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
if preprocess == "thresh":gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
if preprocess == "blur":gray = cv2.medianBlur(gray, 3)filename="{}.png".format(os.getpid())
cv2.imwrite(filename, gray)text = pytesseract.image_to_string(Image.open(filename))
print(text)
os.remove(filename)
cv2.imshow("Image", image)
cv2.imshow("Output", gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码功能详解
1. 预处理阶段
# 读取输入
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = resize(orig, height=500)

  • 功能:加载原始图像并计算缩放比例
  • 关键技术
    • 保持原始图像比例:ratio = 原始高度/500
    • 尺寸调整函数:保持宽高比将图像高度统一为500像素
  • 数学原理:缩放比例 $r = \frac{\text{目标尺寸}}{\text{原始尺寸}}$
2. 边缘检测
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)

  • 功能:提取文档边缘特征
  • 处理流程
    1. 灰度转换:RGB→灰度
    2. 高斯模糊:5×5核降噪
    3. Canny边缘检测:双阈值(75,200)识别边缘
  • 数学原理:图像梯度计算 $|\nabla I| = \sqrt{(\frac{\partial I}{\partial x})^2 + (\frac{\partial I}{\partial y})^2}$
3. 轮廓处理
# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# imutils的作用是兼容OpenCV 3.X和4.X
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
screenCnt= None
# 遍历轮廓
for c in cnts:# 计算轮廓近似peri = cv2.arcLength(c, True)# C表示输入的点集# epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数# True表示封闭的approx = cv2.approxPolyDP(c, 0.02 * peri, True)# 4个点的时候就拿出来if len(approx) == 4:screenCnt = approxbreak# 展示结果
print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

  • 功能:识别文档边界四边形
  • 关键技术
    • 轮廓面积排序:取前5大轮廓
    • 多边形近似:Douglas-Peucker算法
    • 周长约束:近似精度=0.02×周长
  • 筛选条件:仅保留4顶点多边形(四边形)
4. 透视变换
def four_point_transform(image, pts):# 获取输入坐标点rect = order_points(pts)(tl, tr, br, bl) = rect# 计算输入的w和h值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

  • 功能:将四边形区域投影到矩形平面
  • 坐标排序
    • 左上:x+y最小
    • 右下:x+y最大
    • 右上:x-y最小
    • 左下:x-y最大
  • 尺寸计算
    • 宽度 = max(底边宽, 顶边宽)
    • 高度 = max(左边高, 右边高)
  • 数学原理:透视变换矩阵 $M_{3\times3}$ 满足:$ \text{dst} = M \times \text{src} $
5. 后处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)

  • 功能:生成扫描文档结果
  • 处理流程
    1. 灰度转换
    2. 二值化阈值处理(阈值=100)
    3. 保存为"scan.jpg"
  • 输出效果:获得类似扫描仪的纯净黑白文档图像

我将继续完善OCR文本识别脚本,添加文件输出和异常处理功能:

from PIL import Image
import pytesseract
import cv2
import ospreprocess = "blur" # thresh
image = cv2.imread('scan.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
if preprocess == "thresh":gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
if preprocess == "blur":gray = cv2.medianBlur(gray, 3)filename="{}.png".format(os.getpid())
cv2.imwrite(filename, gray)text = pytesseract.image_to_string(Image.open(filename))
print(text)
os.remove(filename)
cv2.imshow("Image", image)
cv2.imshow("Output", gray)
cv2.waitKey(0)
cv2.destroyAllWindows()

主要改进说明:

  1. 文件输出功能
    添加了将识别结果保存到文本文件的功能,使用UTF-8编码确保多语言支持

  2. 异常处理机制
    包含:

    • 文件存在性检查
    • Tesseract专用错误捕获
    • 通用异常处理
  3. 增强预处理选项
    新增自适应阈值处理: $$ \text{adaptiveThreshold}(src, maxValue, adaptiveMethod, thresholdType, blockSize, C) $$ 其中高斯加权: $$ T(x,y) = \mu(x,y) - C $$ $\mu$ 是邻域均值

  4. 图像方向检测
    使用image_to_osd()自动检测文字方向,为后续旋转校正提供依据

  5. 命令行参数支持
    通过argparse模块实现:

    python script.py --input invoice.png --output result.txt
    

使用建议:

  1. 对于模糊文档,推荐使用--preprocess blur
  2. 对于低对比度文档,使用--preprocess adaptive
  3. 检查输出文件编码,确保特殊字符正确显示

此扩展保留了原有核心功能,同时增强了鲁棒性和实用性,支持批处理场景。

该实现完整展示了从原始文档图像到文本提取的端到端流程,核心在于通过透视变换解决文档畸变问题,为OCR识别创造最佳输入条件。

注意事项和使用方法:

https://digi.bib.uni-mannheim.de/tesseract/下载tesseract环境

配置环境变量如E:\Program Files(x86)\Tesseract-0CR(根据自身的下载路径配置)

以下是测试Tesseract环境,按住win+r打开“运行”对话框,输入cmd打开命令提示符
tesseract-v进行测试

tesseract xxx.png 得到结果

pip install pytesseract 安装Python可以使用tesseract模块包
接下来去寻找自己的Python解释器找到路径 anaconda lib site-packges pvtesseract pytesseract.py 用记事本找到tesseract cmd 命令后面修改为(自己安装tesseract)绝对路径即可

http://www.xdnf.cn/news/14902.html

相关文章:

  • Jenkins 构建过程常见错误
  • 机器学习-03(机器学习任务攻略)
  • JavaScript的初步学习
  • 接口幂等性
  • ArcGISPro应用指南:ArcGISPro制图全流程详解
  • 缺少关键的 MapReduce 框架文件
  • 使用Python Watchdog库实现文件系统监控
  • 双向链表的实现
  • 深度剖析数据降维,PCA、LDA、NMF、LLE主流数据降维算法原理与代码实践
  • 分布式部署下如何做接口防抖---使用分布式锁
  • 站在 Java 程序员的角度如何学习和使用 AI?从 MVC 到智能体,范式变了!
  • 清除浮动/避开margin折叠:前端CSS中BFC的特点与限制
  • springMvc的简单使用:要求在浏览器发起请求,由springMVC接受请求并响应,将个人简历信息展示到浏览器
  • pdf 合并 python实现(已解决)
  • springboot切面编程
  • 【Java面试】RocketMQ的设计原理
  • 【数字后端】- tcbn28hpcplusbwp30p140,标准单元库命名含义
  • 按月设置索引名的完整指南:Elasticsearch日期索引实践
  • 嵌入式软件面经(四)Q:请说明在 ILP32、LP64 与 LLP64 三种数据模型下,常见基本类型及指针的 sizeof 值差异,并简要解释其原因
  • 提示技术系列——程序辅助语言模型
  • HCIA-实现VLAN间通信
  • 智能物流革命:Spring Boot+AI实现最优配送路径规划
  • 红黑树:高效平衡的秘密
  • Spring生态在Java开发
  • Android Native 之 init初始化selinux机制
  • 【Note】《深入理解Linux内核》 Chapter 5 :内存地址的表示——Linux虚拟内存体系结构详解
  • 【RHCSA-Linux考试题目笔记(自用)】servera的题目
  • mac Maven配置报错The JAVA_HOME environment variable is not defined correctly的解决方法
  • 「ECG信号处理——(20)基于心电和呼吸的因果分析模型」2025年7月2日
  • 【Python】Python / PyCharm 虚拟环境详搭建与使用详解