深度学习图像处理基础工具——opencv 实战2 文档扫描OCR

news2024/11/21 1:30:20

输入一个文档,怎么进行文档扫描,输出扫描后的图片呢?

今天学习了  opencv实战项目 文档扫描OCR

问题重构:输入图像 是一个含有文档的图像——> 目标是将其转化为 规则的扫描图片

 那么怎么实现呢?

问题分解:

1 边缘检测 

2 获取轮廓

3 对获取到的轮廓进行透视变换

4 OCR

如何边缘检测?

1 把图片读入——预处理(计算坐标缩放比例以便以后使用原图的时候使用,copy原图,

2.获取灰度图——高斯滤波去除噪声,边缘检测)

如何获取需要扫描的轮廓?

1.轮廓检测 检测出所有轮廓,对检测出的所有轮廓按照面积大小分类

2遍历所有轮廓(计算轮廓近似使用到了

cv2.approxPolyDP 是一个用于多边形逼近的函数。它使用Douglas-Peucker算法来减少多边形的点数。 返回的是多边形的顶点坐标数组 详细解释opencv python中的 cv.approxPolyDP_cv::approxpolydp-CSDN博客

 当坐标数组数是4的时候(是四边形的)  把返回结果拿出来

如何进行透视变换?

使用到了四点transformer函数(见后文) 变换  输入 image 原图 和上一步返回的坐标点 输出是变换后的结果

输入的坐标点可能是乱序的,怎么进行上下左右排序?

使用了 order_points 函数  思路:四个坐标 (a,b) a+b 最大的 是右下的点 最小的是左上 b-a  z最小的是右上 最大的是左下 (假设h >w)  返回的是 排序好的rect 

回到 四点transformer函数 

怎么计算透视变换的变换矩阵M  ?需要得到原坐标点 和变换后的坐标点

如何将四边形转化为 矩形?计算上w 和下w 的值 方法 两点间距离公式 取最大,同理 取左h 右h  取最大的高和宽 得到变换后坐标位置

计算变换矩阵  

用到了cv2.getPerspectiveTransform

opencv透视变换:GetPerspectiveTransform、warpPerspective函数的使用-CSDN博客

得到了变换矩阵,怎么进行透视变换?

使用到了cv2.warpPerspective函数,warpPerspective():对图像进行透视变换。简单来说,就是有这么一副图像,它的拍摄视角不是从正面拍摄的,而是带有一定的角度,我们希望能得到从正面观察的视角。 【Python+OpenCV 图像透视变换 warpPerspective函数】-CSDN博客

返回变换后结果warped

得到的warped 是这样的

还要进行二值处理、结果保存

warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)

最后得到的输出结果如下

代码:

# 导入工具包
import numpy as np
import argparse
import cv2

# 设置参数
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 rect

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

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

# 读取输入
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)[0]
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]

# 遍历轮廓
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 = approx
		break

# 展示结果
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.imshow("warped", resize(warped, height = 650))
cv2.waitKey(0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1593686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode.45题:跳跃游戏II

Leetcode.45题:跳跃游戏II /* 题意的理解: nums[0] 只能跳 1 ~ nums[0]步 依次类推:从nums[0] - nums[n - 1] 最少需要多少步数 nums 2 3 1 1 4 nums[0] 2,初始只能跳 1/2步,如跳1步,达到nums[1] 而nums[1] 3,顾第二…

Python学习笔记22 - 文件操作

文件读写的原理 文件读写的操作 常用的文件打开模式 文件对象的常用方法 with语句(上下文管理器)

kubekey 离线安装harbor、k8s、kubesphere

目录 参考文献 了解kubekey 英文和中文 前提条件 部署准备 下载kubukey 离线包配置和制作 配置离线包 制作离线包 离线安装集群 复制KubeKey 和制品 artifact到离线机器 创建初始换、安装配置文件 安装镜像仓库harbor 初始化harbor 项目 修改配置文件 安装k8s集…

一.表单校验

为什么要表单验证? 是为了减轻服务器的压力,让用户体验更好,保证输入的数据符合要求 常用的表单验证 日期格式 表单元素是否为空 用户名和密码 E-mail 地址 身份证号码 实现验证的思路 问题:当输入的表单数据不符合要求时&…

内网渗透-windows权限维持的方法

windows权限维持的方法 文章目录 windows权限维持的方法一、影子账户二、粘滞键后门三、logon scripts后门五、注册表自启动后门六、屏幕保护程序后门七、计划任务后门八、服务自启动后门九、黄金票据十、白银票据十二、bitsadmin十五、CLR劫持 一、影子账户 1.使用如下命令创…

你真的会处理python代码异常吗?

Python 使用称为异常(exception)的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停…

CodeGemma: Open Code Models Based on Gemma

最近几天,对大模型生成代码的能力比较感兴趣,特此学习了一下大模型怎么在IDE上进行,自动补全你想要的代码。 大模型源码理解-以Gemma为例子 最近也在使用阿里的通义灵码,感觉还挺好用的。通义灵码 摘要: CodeGemma基…

Linux ARM平台开发系列讲解(QEMU篇) 1.2 新添加一个Linux kernel设备树

1. 概述 上一章节我们利用QEMU成功启动了Linux kernel,但是细心的小伙伴就会发现,我们用默认的defconfig是没有找到设备树源文件的,但是又发现kernel启动时候它使用了设备树riscv-virtio,qemu,这是因为qemu用了一个默认的设备树文件,该章节呢我们就把这个默认的设备树文件…

【设计模式学习】单例模式和工厂模式

꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转…

明月三千里许红:25年磨一剑 拒绝网红式短期主义

这是一个关于明月三千里烤肉品牌如何在25年企业经营风口浪尖中,寻求长久生存和发展的故事;也是一个关于明月三千里烤肉品牌如何在25年中追逐流量的同时,不失去初心和品质的深度思考! 在当今快速变化的餐饮市场中,淄博烤…

【CSS】利用Vue实现数字翻滚动画效果

利用Vue实现数字翻滚动画效果 在很多数据可视化的需求中,动态呈现数据变化是一个常见且具有较强视觉冲击力的手段,尤其是数字的实时变化。今天我们将探讨如何使用 Vue.js 和 CSS3 来实现数字的翻滚动画效果,即模拟真实物体在Z轴上翻动的效果…

使用cmake进行打包,包含可执行程序和动态依赖库

平常代码开发中,有时候需要将写的程序打包成压缩包放到目标设备上进行运行测试。用CMake管理工程,实现使用make -jnproc package指令可以将工程进行打包,可执行文件存储在bin文件夹中,依赖库存储在lib文件夹中。 示例 1.工程目录结…

选择自动化工具是一个关键的决策过程

好的自动化软件测试工具,不仅可以有效的缩短全生命周期的交付周期,还可以提高测试的有效性,还可以保证更好的高质量的交付。工具的选型是一项重要的决策过程,工具的采用涉及到企业的效率、成本和长期发展。 1、需求分析 确组织希…

云服务器web环境之mariadb

1.安装mariadb服务 yum install mariadb-server 启动mariadb服务 systemctl start mariadb.service 输入mysql就能使用数据库了。 2.服务相关操作 systemctl stop mariadb.service systemctl restart mariadb.service 2.配置开机自启动 systemctl enable mariadb.serv…

python--5--len()

学习目标: len()#range,list,列表、元组,字符串,包括空格,返回字典的“键值对”个数 学习内容: len()#range,list,列表、元组,字符串,包括空格,返回字典的“键值对”个数 len()--返回对象长度 len()#range,list,列…

matlab学习(三)(4.9-4.15)

一、空域里LSB算法的原理 1.原理: LSB算法通过替换图像像素的最低位来嵌入信息。这些被替换的LSB序列可以是需要加入的水印信息、水印的数字摘要或者由水印生成的伪随机序列。 2.实现步骤: (1)将图像文件中的所有像素点以RGB形…

华为海思数字芯片设计笔试第四套

声明 下面的题目作答都是自己认为正确的答案,并非官方答案,如果有不同的意见,可以评论区交流。 这些题目也是笔者从各个地方收集的,感觉有些题目答案并不正确,所以在个别题目会给出自己的见解,欢迎大家讨论…

1688采集API 接口:轻松实现电商数据采集与官方订单API接口对接

各大平台电商软件大全,优质服务 1688采集API接口,一款助力电商从业者轻松实现阿里巴巴与1688平台商品数据采集接口,在数据为王的电商时代,掌握高效的采集与管理技能,无疑是成功的一把钥匙。 功能概述: 1.…

神经射频脉冲术,破解疼痛之锁

一位十余年糖尿病病史的患者,右足开始出现疼痛和麻木的症状三个多月,给他的生活带来了极大的困扰。他曾在多家医院就诊,但治疗效果并不理想。直到他走进了北京精诚博爱医院,这里为他带来了希望和转机。 经过详细的检查&#xff0c…

分享一款免费阅读的软件:Legado,已免费开源(文末有福利)

对于喜欢阅读的人来说,一定经历过从一本厚厚的修仙书籍到MP3、MP4的阅读时代,再到现今的手机软件。 但是现在的阅读软件经常会遇见以下问题:比如广告弹窗太多,排版乱,甚至很多的APP都进入会员时代,再加上一…