【OpenCV】 ⚠️实战⚠️ 女子深夜久久不能入眠,300行写出全能扫描王! ☢️建议手收藏☢️

  • 概述
  • 图像透视
    • 获取透视矩阵
    • 透视变换
  • 预处理
  • 其他函数
  • 主函数
  • 输出结果
  • 最终转换结果

概述

今天带大家使用我们之前学会的知识来实现一个简易版的全能扫描王. 代码分为 3 个部分: 主函程序, 预处理, 其他程序.

图像透视

透视变换 (Perspective Transformation) 是将成像投影到一个新的视平面 (Viewing Plane). 通过改变图像或视屏的透视图, 我们可以更好的了解所需信息. 如图:

获取透视矩阵

格式:

M = cv2.getPerspectiveTransform(src, dst, solveMethod=None)

参数:
src: 源图像中四边形顶点的坐标
dst: 目标图像中相应四边形顶点的坐标

返回值:

  • M: 透视变换矩阵

矩阵例子:

[[ 8.75046884e+00 -9.23660638e+00  0.00000000e+00][ 4.43412734e-01 -6.70045886e+00  2.19700000e+03][ 2.51096074e-03 -3.35871928e-03  1.00000000e+00]]

透视变换

格式:

cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
  • 1

参数:

  • src: 输入图片
  • M: 透视变换矩阵
  • dsize: 输出图片的大小

返回值:

  • 透视变换后的图片

例子:

预处理

通过图片伸缩, 灰度转换, 高斯滤波, 边缘检测等方法, 实现图片预处理

代码:

import cv2
from matplotlib import pyplot as plt
from my_functions import resize
from my_functions import four_point_transformdef read_image(image_path, visualize=False):"""读取图片:param image_path: 图片路径:param visualize: 可视化, 默认为False:return: 返回原始图片, 裁剪后的图片, 边缘, 图片伸缩比例"""# 读取图片image = cv2.imread(image_path)# 计算伸缩比例ratio = image.shape[0] / 500.0# 深拷贝image_copy = image.copy()# 缩放大小image_resize = resize(image_copy, height=500)# 转换成灰度图image_gray = cv2.cvtColor(image_resize, cv2.COLOR_BGR2GRAY)# 高斯滤波image_gaussian = cv2.GaussianBlur(image_gray, (5, 5), 0)# Canny边缘检测edge = cv2.Canny(image_gaussian, 75, 200)if visualize:"""图片展示"""# 绘制子图f, ax = plt.subplots(2, 2, figsize=(8, 10))ax[0, 0].imshow(cv2.cvtColor(image_resize, cv2.COLOR_BGR2RGB))ax[0, 1].imshow(image_gray, "gray")ax[1, 0].imshow(image_gaussian, "gray")ax[1, 1].imshow(edge, "gray")# 去除x, y坐标ax[0, 0].set_xticks([])ax[0, 0].set_yticks([])ax[0, 1].set_xticks([])ax[0, 1].set_yticks([])ax[1, 0].set_xticks([])ax[1, 0].set_yticks([])ax[1, 1].set_xticks([])ax[1, 1].set_yticks([])# 标题ax[0, 0].set_title("original")ax[0, 1].set_title("image gray")ax[1, 0].set_title("image gaussian blur")ax[1, 1].set_title("image edge")plt.show()# 返回return image, image_resize, edge, ratiodef image_calculate_contours(image_resize, edge, visualize=False):"""计算轮廓:param image_resize: 裁剪后的图片:param edge: 边缘:param visualize: 可视化, 默认为False:return: 外接长方形数组"""# 轮廓检测contours, hierarchy = cv2.findContours(edge.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)# 轮廓排序contours = sorted(contours, key=cv2.contourArea, reverse=True)# 取最大的5个contours_5 = contours[:5]# 长方形数字approx_array = []# 遍历轮廓for c in contours_5:# 计算轮廓周长length = cv2.arcLength(c, True)# 计算大约轮廓approx = cv2.approxPolyDP(c, 0.02 * length, True)# 如果是四边形, 取出if len(approx) == 4:approx_array.append(approx)if visualize:"""图片展示"""# 绘制轮廓1draw_img_1 = cv2.drawContours(image_resize.copy(), contours, 0, (0, 255, 255), 2)# 绘制轮廓2draw_img_2 = cv2.drawContours(image_resize.copy(), contours_5, 0, (0, 255, 255), 2)# 绘制轮廓3draw_img_3 = cv2.drawContours(image_resize.copy(), approx_array, 0, (0, 255, 255), 2)# 绘制子图f, ax = plt.subplots(2, 2, figsize=(8, 10))ax[0, 0].imshow(cv2.cvtColor(image_resize, cv2.COLOR_BGR2RGB))ax[0, 1].imshow(cv2.cvtColor(draw_img_1, cv2.COLOR_BGR2RGB))ax[1, 0].imshow(cv2.cvtColor(draw_img_2, cv2.COLOR_BGR2RGB))ax[1, 1].imshow(cv2.cvtColor(draw_img_3, cv2.COLOR_BGR2RGB))# 去除x, y坐标ax[0, 0].set_xticks([])ax[0, 0].set_yticks([])ax[0, 1].set_xticks([])ax[0, 1].set_yticks([])ax[1, 0].set_xticks([])ax[1, 0].set_yticks([])ax[1, 1].set_xticks([])ax[1, 1].set_yticks([])# 标题ax[0, 0].set_title("original")ax[0, 1].set_title("contours")ax[1, 0].set_title("contours biggest 5")ax[1, 1].set_title("contours approx")plt.show()# 返回return approx_arraydef image_transform(image, approx_array, ratio, visualize=False):"""图片转换:param image: 原始图像:param approx_array: 外接长方形坐标数组:param ratio: 图片拉伸比例:param visualize: 可视化, 默认为False:return: 返回最终结果"""# 透视变换warped = four_point_transform(image, approx_array[0].reshape(4, 2) * ratio)# 转换为灰度图warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)# 二值化ret, thresh = cv2.threshold(warped_gray, 100, 255, cv2.THRESH_BINARY)if visualize:"""图片展示"""# 绘制子图f, ax = plt.subplots(2, 2, figsize=(8, 10))ax[0, 0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))ax[0, 1].imshow(cv2.cvtColor(warped, cv2.COLOR_BGR2RGB))ax[1, 0].imshow(warped_gray, "gray")ax[1, 1].imshow(thresh, "gray")# 去除x, y坐标ax[0, 0].set_xticks([])ax[0, 0].set_yticks([])ax[0, 1].set_xticks([])ax[0, 1].set_yticks([])ax[1, 0].set_xticks([])ax[1, 0].set_yticks([])ax[1, 1].set_xticks([])ax[1, 1].set_yticks([])# 标题ax[0, 0].set_title("original")ax[0, 1].set_title("warped")ax[1, 0].set_title("warped gray")ax[1, 1].set_title("thresh")plt.show()# 返回return warped_gray

其他函数

import numpy as np
import cv2def order_points(points):"""坐标点排序:param points: 轮廓坐标:return: 返回排序完的坐标"""# 一共4个坐标点, 左上, 右上, 右下, 左下rect = np.zeros((4, 2), dtype=np.float32)# 计算左上, 右下s = points.sum(axis=1)rect[0] = points[np.argmin(s)]  # 和最小的是左上rect[2] = points[np.argmax(s)]  # 和最大的是右下# 计算右上, 左下diff = np.diff(points, axis=1)rect[1] = points[np.argmin(diff)]  # 差最小的是右上rect[3] = points[np.argmax(diff)]  # 差最小的是左下# 返回return rectdef four_point_transform(image, pts):"""透视变换 (拉伸为长方形):param image: 原始图像:param pts: 坐标点:return: 透视变换后的图"""# 获取输入坐标点rect = order_points(pts)(top_left, top_right, bottom_left, bottom_right) = rect# 计算最大的w (勾股定理w = (Δx^2 + Δy^2)^1/2)widthA = np.sqrt(((bottom_right[0] - bottom_left[0]) ** 2) + ((bottom_right[1] - bottom_left[1]) ** 2))widthB = np.sqrt(((top_right[0] - top_left[0]) ** 2) + ((top_right[1] - top_left[1]) ** 2))maxWidth = max(int(widthA), int(widthB))# 计算最大的y (勾股定理y = (Δx^2 + Δy^2)^1/2)heightA = np.sqrt(((top_right[0] - bottom_right[0]) ** 2) + ((top_right[1] - bottom_right[1]) ** 2))heightB = np.sqrt(((top_left[0] - bottom_left[0]) ** 2) + ((top_right[1] - top_left[1]) ** 2))maxHeight = max(int(heightA), int(heightB))# 变换后对应坐标位置dst = np.array([[0, 0],[maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]],dtype=np.float32)# 计算变换矩阵M = cv2.getPerspectiveTransform(rect, dst)print("变换矩阵:\n", M)  # 调试输出# 透视变换wraped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))# 返回return wrapeddef resize(image, width=None, height=None, inter=cv2.INTER_AREA):"""修改图片大小:param image: 原图:param width: 宽:param height: 高:param inter: 模式:return: 修改好的图片"""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

主函数

import argparse
import cv2
from pre_process import read_image
from pre_process import image_calculate_contours
from pre_process import image_transformdef parse_opt():"""设置参数"""parser = argparse.ArgumentParser()parser.add_argument("--image_path", type=str, default="images/receipt2.jpg", help="图片路径")args = parser.parse_args()return argsdef main():"""主函数"""args = parse_opt()# 读取图片image, image_resize, edge, ratio = read_image(image_path=args.image_path, visualize=True)# 计算轮廓approx_array = image_calculate_contours(image_resize, edge, visualize=True)# 图片转换final_result = image_transform(image=image, approx_array=approx_array, ratio=ratio, visualize=True)# 保存最终结果cv2.imwrite("final_result.jpg", final_result)if __name__ == "__main__":main()

输出结果



最终转换结果

原图:


最终结果:

【OpenCV】 300行写出全能扫描王相关推荐

  1. 【OpenCV】 ⚠️实战⚠️ 女子深夜久久不能入眠,300行写出全能扫描王! ☢️建议手收藏☢️

    [OpenCV] ⚠️实战⚠️ 女子深夜久久不能入眠,300行写出全能扫描王! ☢️建议手收藏☢️ 概述 图像透视 获取透视矩阵 透视变换 预处理 其他函数 主函数 输出结果 最终转换结果 概述 今天 ...

  2. 制作一个类“全能扫描王”的简易扫描软件(opencv)

    相信很多人手机里都装了个"扫描全能王"APP,平时可以用它来可以扫描一些证件.文本,确实很好用,第一次用的时候确实感觉功能很强大啊算法很牛逼啊.但是仔细一想,其实这些实现起来也是很 ...

  3. OpenCV制作一个类“全能扫描王”的简易扫描软件

    转自:https://www.cnblogs.com/skyfsm/p/7324346.html 相信很多人手机里都装了个"扫描全能王"APP,平时可以用它来可以扫描一些证件.文本 ...

  4. OpenCV实现“全能扫描王”的图像矫正功能

    前言: 相信很多人手机里都装了个"扫描全能王"APP,平时可以用它来可以扫描一些证件.文本,确实很好用,第一次用的时候确实感觉功能很强大啊算法很牛逼啊.但是仔细一想,其实这些实现起 ...

  5. 增强 扫描王 源码_OpenCV探索之路(二十二):制作一个类“全能扫描王”的简易扫描软件...

    相信很多人手机里都装了个"扫描全能王"APP,平时可以用它来可以扫描一些证件.文本,确实很好用,第一次用的时候确实感觉功能很强大啊算法很牛逼啊.但是仔细一想,其实这些实现起来也是很 ...

  6. 基于 requests 的全能扫描王爬虫实践

    投稿人:Alan 摘要:全能扫描王是文件扫描留档的重要工具,本文利用requests爬虫将手机客户端的扫描文件,同步至电脑端. 一.背景 在审计工作中,需要大批量扫描文件留档,全能扫描王成为主流的手机 ...

  7. Python 五行代码实现类似全能扫描王和office Lens的扫描彩色增强滤镜效果

    安装第三方cv工具包:zisan pip install zisan zisan 开源的Github源码库:https://github.com/JintuZheng/zisan 项目代码持续更新维护 ...

  8. 不到100行写出一个完整的web框架

    本篇文章是为了后面的Django做准备的,这是学习web框架的底层知识,没有直接用到socket,而是用Python自带的库wsgiref. 1.wsgiref模块 它就相当于是一个服务器,会帮我们做 ...

  9. 智能扫描王 v1.0.0

    类型:系统工具 版本:v1.0.0 大小:8.1M 更新:2019/3/4 语言:简体 等级: 平台:安卓, 4.0以上 下载地址: 智能扫描王 v1.0.0(1) 智能扫描王 v1.0.0(2) 智 ...

  10. python写出XLSX

    python与xlsxwriter python与xlsxwriter 1.准备阶段: 2.细嚼慢咽 创建Excel文件 创建工作表sheet 设置工作表 1.设置列宽 2.定义格式 3.按行写出代码 ...

最新文章

  1. 实战ELK(9) Elasticsearch地理位置
  2. 计算机系统的工作方式,计算机的操作系统与工作方式
  3. 小白记事本--学不明白还怕忘记指针--loading未完待续
  4. 在css中使用hover来控制其他元素的样式,该两个元素必须是父子元素
  5. 虚拟机上网设置教程之如何用虚拟机上网
  6. Android Theme.AppCompat 中,你应该熟悉的颜色属性
  7. MVP简单使用+RecyclerView
  8. 14周:设计会员登录页面
  9. PHP中 sprintf()格式化输出详解
  10. CSDN的个人主页如何添加微信二维码
  11. 计算机导论课后总结三
  12. C++保留两位小数的四种方法
  13. 10分钟教你用Python实现微信翻译机器人
  14. 引流复盘:从知乎引流20万粉,我只用了1个月
  15. KCL:蚂蚁自研的配置策略语言
  16. php蛇蛇大作战,贪吃蛇大作战
  17. 2019汇智动力学院课程、服务体系震撼升级
  18. gprMax中任意不规则形状建模与模拟
  19. 一些获取当前登录Windows系统用户信息的命令
  20. 物联网竞赛-ZigBee控制灯(IAR独立新建工程)

热门文章

  1. spss常态检验_利用SPSS检验数据是否符合正态分布
  2. win10系统计算机如何分盘,win10怎么进行分盘_win10电脑如何合理分盘
  3. 传图识别字体--求字体网
  4. 电信网关改造无线打印服务器,电信天翼网关路由改桥接流程
  5. 单独使用mybatis整合mysql案例
  6. 在屏幕上绘制圆形函数
  7. 圆的面积函数定义及调用
  8. usaco - Raucous Rockers
  9. 常用三种正态检验方法
  10. camel研究_【卡瑞利珠单抗·CameL研究者说】任秀宝教授:卡瑞利珠单抗治疗NSCLC疗效与安全性俱佳,受指南重磅推荐后再获批肺癌适应症...