《OpenCV》——图像透视转换

图像透视转换简介

  • 在 OpenCV 里,图像透视转换属于重要的几何变换,也被叫做投影变换。下面从原理、实现步骤、相关函数和应用场景几个方面为你详细介绍。

原理

在这里插入图片描述

实现步骤

  • 选取对应点:要在源图像和目标图像上分别找出至少四个对应的点。这些对应点不能共线,因为它们是计算透视变换矩阵的关键依据。
  • 计算透视变换矩阵:利用 OpenCV 的 cv2.getPerspectiveTransform 函数,依据前面选取的对应点来计算透视变换矩阵。
  • 应用透视变换:使用 cv2.warpPerspective 函数,将计算得到的透视变换矩阵应用到源图像上,从而得到透视变换后的图像。

相关函数

  • cv2.getPerspectiveTransform
    • 功能:计算透视变换矩阵。
    • 语法:cv2.getPerspectiveTransform(src, dst)
    • 参数:
      • src:源图像中四个点的坐标,数据类型为 np.float32。
      • dst:目标图像中对应的四个点的坐标,数据类型为 np.float32。
  • 返回值:返回一个 3×3 的透视变换矩阵。
  • cv2.warpPerspective
    • 功能:对图像应用透视变换。
    • 语法:cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])
    • 参数:
      • src:源图像。
      • M:透视变换矩阵。
      • dsize:输出图像的大小,格式为 (width, height)。
      • dst(可选):输出图像。
      • flags(可选):插值方法,如 cv2.INTER_LINEAR 等。
      • borderMode(可选):边界填充模式。
      • borderValue(可选):边界填充值。
  • 返回值:返回透视变换后的图像。

应用场景

  • 图像校正:校正因拍摄角度倾斜而产生畸变的图像,例如校正拍摄的文档图像,使其呈现为标准的矩形。
  • 虚拟现实:在虚拟现实场景中,将二维图像转换为具有透视效果的三维场景,增强沉浸感。
  • 自动驾驶:对车载摄像头拍摄的图像进行透视变换,以获取道路的鸟瞰图,辅助车辆进行路径规划和障碍物检测。

图像透视转换实例

对以下图片进行图像透视转换:
在这里插入图片描述

实例步骤

导入所需库

import numpy as np
import cv2

写入所需函数

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    # 初始化 dim 为 None,用于存储调整后的图像尺寸
    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))
    # 使用 cv2.resize 函数根据 dim 和指定的插值方法对图像进行缩放
    resized = cv2.resize(image, dim, interpolation=inter)
    # 返回缩放后的图像
    return resized

# 定义一个函数用于显示图像
# name: 显示窗口的名称
# img: 要显示的图像
def cv_show(name,img):
    # 使用cv2.imshow函数显示图像,第一个参数是窗口名称,第二个参数是要显示的图像
    cv2.imshow(name,img)
    # 使用cv2.waitKey(0)等待用户按键,参数为0表示无限等待
    cv2.waitKey(0)

# 定义一个函数用于对输入的四个点进行排序
# pts: 输入的四个点的坐标,是一个形状为(4, 2)的numpy数组
def order_points(pts):
    # 创建一个形状为(4, 2)的全零数组,数据类型为float32,用于存储排序后的点
    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

# 定义一个函数用于进行四点透视变换
# image: 输入的原始图像
# pts: 输入的四个点的坐标,是一个形状为(4, 2)的numpy数组
def four_point_transform(image,pts):
    # 调用order_points函数对输入的四个点进行排序
    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))
    # 创建一个形状为(4, 2)的numpy数组,用于存储变换后的四个点的坐标
    dst = np.array([[0,0],[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtype="float32")

    # 使用cv2.getPerspectiveTransform函数计算透视变换矩阵
    M = cv2.getPerspectiveTransform(rect,dst)
    # 使用cv2.warpPerspective函数进行透视变换,得到变换后的图像
    warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))
    # 返回变换后的图像
    return warped

获取图片信息并处理图片

import cv2

# 读取指定路径的图片,返回一个表示图像的多维数组
image = cv2.imread('dan_zi.jpg')
# 调用自定义的cv_show函数展示原始图像,窗口名为'image'
cv_show('image', image)

# 计算原始图像高度与500像素的比例,后续用于恢复尺寸
ration = image.shape[0] / 500.0
# 复制原始图像,避免后续操作修改原始数据
orig = image.copy()
# 调用resize函数将图像高度调整为500像素,保持宽高比
image = resize(orig, height=500)
# 调用cv_show函数展示调整大小后的图像,窗口名为'1'
cv_show('1', image)

# 打印提示信息,表明进入轮廓检测步骤
print("STEP 1: 轮廓检测")
# 将调整大小后的图像从BGR颜色空间转换为灰度颜色空间
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 运用Otsu's算法进行二值化处理,得到二值化后的图像
edged = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 在二值化图像的副本上查找轮廓,使用RETR_LIST检索模式和CHAIN_APPROX_SIMPLE近似方法
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]
# 在图像副本上绘制所有检测到的轮廓,颜色为红色,线条宽度为1像素
image_contours = cv2.drawContours(image.copy(), cnts, -1, (0, 0, 255), 1)
# 调用cv_show函数展示绘制了所有轮廓的图像,窗口名为'image_contours'
cv_show("image_contours", image_contours)

# 打印提示信息,表明进入获取最大轮廓步骤
print("STEP 2:获取最大轮廓")
# 按轮廓面积从大到小对检测到的轮廓进行排序,选取面积最大的轮廓
screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]

# 计算最大轮廓的周长,参数True表示轮廓是封闭的
peri = cv2.arcLength(screenCnt, True)
# 对最大轮廓进行多边形逼近,以减少轮廓上的点数,第二个参数为逼近精度
screenCnt = cv2.approxPolyDP(screenCnt, 0.02 * peri, True)
# 打印逼近后轮廓的形状信息
print(screenCnt.shape)

# 在图像副本上绘制逼近后的最大轮廓,颜色为绿色,线条宽度为2像素
image_contour = cv2.drawContours(image.copy(), [screenCnt], -1, (0, 255, 0), 2)

# 展示绘制了最大逼近轮廓的图像,窗口名为'image_contour'
cv2.imshow("image_contour", image_contour)
# 等待用户按键,防止窗口立即关闭
cv2.waitKey(0)

进行透视转换

# 调用之前定义的 four_point_transform 函数对原始图像进行四点透视变换
# screenCnt.reshape(4, 2) * ration 是将之前获取的轮廓点恢复到原始图像的尺寸
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ration)
# 将透视变换后的图像保存为 invoice_new.jpg
cv2.imwrite("invoice_new.jpg", warped)
# 创建一个名为 "xxxxx" 的窗口,并且该窗口大小可以调整
cv2.namedWindow("xxxxx", cv2.WINDOW_NORMAL)
# 在 "xxxxx" 窗口中显示透视变换后的图像
cv2.imshow("xxxxx", warped)
# 等待用户按键,防止窗口立即关闭
cv2.waitKey(0)

# 将透视变换后的图像从 BGR 颜色空间转换为灰度颜色空间
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
# 调用 resize 函数将灰度图像的宽度调整为 400 像素
warped = resize(warped, 400)
# 对调整大小后的灰度图像使用 Otsu's 算法进行二值化处理
warped = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 调用自定义的 cv_show 函数显示二值化后的图像,窗口名为 "1111"
cv_show("1111", warped)

# 创建一个 1x1 的矩形结构元素,用于形态学操作
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1))
# 对二值化后的图像进行闭运算,填充小孔和连接相邻物体
closeX = cv2.morphologyEx(warped, cv2.MORPH_CLOSE, rectKernel)
# 调用自定义的 cv_show 函数显示闭运算后的图像,窗口名为 'gradX'
cv_show('gradX', closeX)

结果显示

在这里插入图片描述

invoice_new.jpg
在这里插入图片描述

在这里插入图片描述

如果不想使用这张照片,换其他图片也是可以的,处理步骤都是相同的

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

条件变量 实现2生产者2消费者模型

1个生产者在生产的时候,另个生产者不能生产(生产者之间互斥) 条件变量用于线程同步,线程挂起/被唤醒。 条件变量和互斥锁共同保证生产者之间互斥生产者和消费者的同步。 思路: 1 定义、初始化共享资源 a 缓冲区:存储物品…

一个开源 GenBI AI 本地代理(确保本地数据安全),使数据驱动型团队能够与其数据进行互动,生成文本到 SQL、图表、电子表格、报告和 BI

一、GenBI AI 代理介绍(文末提供下载) github地址:https://github.com/Canner/WrenAI 本文信息图片均来源于github作者主页 在 Wren AI,我们的使命是通过生成式商业智能 (GenBI) 使组织能够无缝访问数据&…

使用C#开发一款通用数据库管理工具

由于经常使用各种数据库,笔者自己动手丰衣足食,使用C#开发了一款通用数据库管理工具,支持Mysql、Oracle、Sqlite、SQL Server等数据库的表、视图、存储过程、函数管理功能,并支持导入导出、数据字典生成、拖拽式跨机器跨库数据一键…

sqli-labs靶场通关

sqli-las通关 mysql数据库5.0以上版本有一个自带的数据库叫做information_schema,该数据库下面有两个表一个是tables和columns。tables这个表的table_name字段下面是所有数据库存在的表名。table_schema字段下是所有表名对应的数据库名。columns这个表的colum_name字段下是所有…

DNS缓存详解(DNS Cache Detailed Explanation)

DNS缓存详解 清空DNS缓存可以让网页访问更快捷。本文将从什么是DNS缓存、为什么清空DNS缓存、如何清空DNS缓存、清空DNS缓存存在的问题四个方面详细阐述DNS缓存清空的相关知识。 一、什么是DNS缓存 1、DNS缓存的定义: DNS缓存是域名系统服务在遇到DNS查询时自动…

【VM】VirtualBox安装CentOS8虚拟机

阅读本文前,请先根据 VirtualBox软件安装教程 安装VirtualBox虚拟机软件。 1. 下载centos8系统iso镜像 可以去两个地方下载,推荐跟随本文的操作用阿里云的镜像 centos官网:https://www.centos.org/download/阿里云镜像:http://…

2024第十五届蓝桥杯网安赛道省赛题目--rc4

rc4 一、查壳 无壳,32位 二、IDA分析 1.main 2.sub_401005 根据题目以及该函数的内容都可以让我们确定这是个rc4加密题。 所以

区块链项目孵化与包装设计:从概念到市场的全流程指南

区块链技术的快速发展催生了大量创新项目,但如何将一个区块链项目从概念孵化成市场认可的产品,是许多团队面临的挑战。本文将从孵化策略、包装设计和市场落地三个维度,为你解析区块链项目成功的关键步骤。 一、区块链项目孵化的核心要素 明确…

【机器学习】自定义数据集 使用scikit-learn中svm的包实现svm分类

一、支持向量机(support vector machines. ,SVM)概念 1. SVM 绪论 支持向量机(SVM)的核心思想是找到一个最优的超平面,将不同类别的数据点分开。SVM 的关键特点包括: ① 分类与回归: SVM 可以用于分类&a…

电信传输基本理论/5G网络层次架构——超三万字详解:适用期末考试/考研/工作

电信传输的基本概念 信息、通信、电信、电信传输的定义 信息 信息指的是消息中的有效信息量 通信 通信指的是利用传输媒质将信息从一段传输到另一端 电信 电信的意思是利用电子技术来将信息从一段传输到另一端 电信传输 电信传输的概念就是将含有信息的电信号进行传输…

代码练习3

1 #include <stdio.h>void draw(int n) {for (int i n; i > 1; i--) {// 打印空格for (int j 0; j < n - i; j) {printf(" ");}// 打印星号for (int j 0; j < 2 * i - 1; j) {printf("*");}// 换行printf("\n");} }int main()…

好用的翻译工具

最近看到个好用的翻译工具&#xff0c;叫沉浸式翻译 沉浸式翻译 - 双语对照网页翻译插件 | PDF翻译 | 视频字幕翻译 我下载的是谷歌插件 点击下载插件会跳转到使用文档&#xff0c;跟着一步步操作即可 翻译的效果&#xff0c;我这里用的是免费版的&#xff0c;如果需要加强&…

Linux-CentOS的yum源

1、什么是yum yum是CentOS的软件仓库管理工具。 2、yum的仓库 2.1、yum的远程仓库源 2.1.1、国内仓库 国内较知名的网络源(aliyun源&#xff0c;163源&#xff0c;sohu源&#xff0c;知名大学开源镜像等) 阿里源:https://opsx.alibaba.com/mirror 网易源:http://mirrors.1…

el-table组件样式如何二次修改?

文章目录 前言一、去除全选框按钮样式二、表头颜色的修改 前言 ElementUI中的组件el-table表格组件提供了丰富的样式&#xff0c;有一个全选框的el-table组件&#xff0c;提供了全选框和多选。 一、去除全选框按钮样式 原本默认是有全选框的。假如有一些开发者&#xff0c;因…

一起学SysML v2规范(01)

1 00:00:01,560 --> 00:00:05,840 今天我们开始一个新的系列 2 00:00:06,690 --> 00:00:08,190 一起学SysML v2 3 00:00:08,200 --> 00:00:09,570 规范 4 00:00:15,770 --> 00:00:17,040 这里说一起学 5 00:00:17,050 --> 00:00:18,920 就是说我和大家一起学…

(9)下:学习与验证 linux 里的 epoll 对象里的 EPOLLIN、 EPOLLHUP 与 EPOLLRDHUP 的不同。小例子的实验

&#xff08;4&#xff09;本实验代码的蓝本&#xff0c;是伊圣雨老师里的课本里的代码&#xff0c;略加改动而来的。 以下是 服务器端的代码&#xff1a; 每当收到客户端的报文时&#xff0c;就测试一下对应的 epoll 事件里的事件标志&#xff0c;不读取报文内容&#xff0c;…

【Python】第七弹---Python基础进阶:深入字典操作与文件处理技巧

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】【Python】 目录 1、字典 1.1、字典是什么 1.2、创建字典 1.3、查找 key 1.4、新增/修改元素 1.5、删除元素 1.6、遍历…

[Linux]从零开始的STM32MP157 U-Boot移植

一、前言 在上一次教程中&#xff0c;我们了解了STM32MP157的启动流程与安全启动机制。我们还将FSBL的相关代码移植成功了。大家还记得FSBL的下一个步骤是什么吗&#xff1f;没错&#xff0c;就是SSBL&#xff0c;而且常见的我们将SSBL作为存放U-Boot的地方。所以本次教程&…

消息队列篇--原理篇--常见消息队列总结(RabbitMQ,Kafka,ActiveMQ,RocketMQ,Pulsar)

1、RabbitMQ 特点&#xff1a; AMQP协议&#xff1a;RabbitMQ是基于AMQP&#xff08;高级消息队列协议&#xff09;构建的&#xff0c;支持多种消息传递模式&#xff0c;如发布/订阅、路由、RPC等。多语言支持&#xff1a;支持多种编程语言的客户端库&#xff0c;包括Java、P…