【计算机视觉】OpenCV篇(3) - 图像几何变换(仿射变换/透视变换)

图像的几何变换从原理上看主要包括两种:基于2×3矩阵的仿射变换(平移、缩放、旋转和翻转等)、基于3×3矩阵的透视变换。

  •  仿射变换

基本的图像变换就是二维坐标的变换:从一种二维坐标(x,y)到另一种二维坐标(u,v)的线性变换:

如果写成矩阵的形式,那就是:

作如下定义:

矩阵T(2×3)就称为仿射变换的变换矩阵,R为线性变换矩阵,t为平移矩阵,简单来说,仿射变换就是线性变换+平移。变换后直线依然是直线,平行线依然是平行线,直线间的相对位置关系不变,因此非共线的三个对应点便可确定唯一的一个仿射变换,线性变换4个自由度+平移2个自由度→仿射变换自由度为6。

仿射变换在OpenCV中的实现如下:

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('drawing.jpg')
rows, cols = img.shape[:2]# 变换前的三个点
pts1 = np.float32([[50, 65], [150, 65], [210, 210]])
# 变换后的三个点
pts2 = np.float32([[50, 100], [150, 65], [100, 250]])# 生成变换矩阵,维数:2*3
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (cols, rows))plt.subplot(121), plt.imshow(img), plt.title('input')
plt.subplot(122), plt.imshow(dst), plt.title('output')
plt.show()

 运行结果:

其实平移、旋转、缩放和翻转等变换就是对应了不同的仿射变换矩阵,下面分别来看:

 

(1)平移

平移就是x和y方向上的直接移动,可以上下/左右移动,自由度为2,变换矩阵可以表示为:

# 平移图片
import numpy as nprows, cols = img.shape[:2]# 定义平移矩阵,需要是numpy的float32类型
# x轴平移100,y轴平移50
M = np.float32([[1, 0, 100], [0, 1, 50]])
dst = cv2.warpAffine(img, M, (cols, rows))cv2.imshow('shift', dst)
cv2.waitKey(0)

 

  

 

 

 

(1)通过比例进行缩放

import cv2 as cv
import numpy as np# 图片缩放
img = cv.imread('images/animal.jpg', flags=1)  # flags=1读取为彩色,flags=0读取为灰度
cv.imshow('i', img)
h, w, channel = img.shape  # 以行列形式存储, 第几行到第几行为图像高度
dst_h = int(h*0.5)
dst_w = int(w*0.5)
# 最近邻域差值 双线性插值 像素关系重采样 立方差值
dst = cv.resize(img, (dst_w, dst_h))  # 默认双线性差值
cv.imshow('img', dst)
cv.waitKey(0)

OpenCV提供了resize函数来改变图像的大小,C++中的函数原型如下:

void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR );

函数参数说明:

src:输入,原图像,即待改变大小的图像;

dst:输出,改变大小之后的图像,这个图像和原图像具有相同的内容,只是大小和原图像不一样而已;

dsize:输出图像的大小。如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:

dsize = Size(round(fx*src.cols), round(fy*src.rows))

其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。

fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width/src.cols来计算;

fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height/src.rows来计算;

interpolation:这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:

  • INTER_NEAREST - 最邻近插值
  • INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
  • INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
  • INTER_CUBIC - 4x4像素邻域内的双立方插值
  • INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值

函数使用说明:

  1. dsize和fx/fy不能同时为0,要么你就指定好dsize的值,让fx和fy空置直接使用默认值,就像resize(img, imgDst, Size(30,30)); 要么你就让dsize为0,指定好fx和fy的值,比如fx=fy=0.5,那么就相当于把原图两个方向缩小一倍!
  2. 至于最后的插值方法,正常情况下使用默认的双线性插值就够用了。几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>Lanczos插值;但是效率和效果成反比,所以根据自己的情况酌情使用。
  3. 正常情况下,在使用之前dst图像的大小和类型都是不知道的,类型从src图像继承而来,大小也是从原图像根据参数计算出来。但是如果你事先已经指定好dst图像的大小,那么你可以通过下面这种方式来调用函数:
resize(src, dst, dst.size(), 0, 0, interpolation);

 

(2)通过矩阵变换进行缩放  

import cv2 as cv
import numpy as np# 图片缩放
img = cv.imread('../images/moon.jpg', flags=1)  # flags=1读取为彩色,flags=0读取为灰度
h, w = img.shape[:2]
mat_shift = np.float32([[0.5, 0, 0], [0, 0.5, 0]])  # 缩放矩阵
dst = cv.warpAffine(img, mat_shift, (int(w/2), int(h/2)))
cv.imshow('img1', img)
cv.imshow('img2', dst)
cv.waitKey(0)

 

 

转载于:https://www.cnblogs.com/carsonzhu/p/10763438.html

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

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

相关文章

Linux学习第五篇之文件处理命令touch、cat、tac、more、less、head、tail

一、touch命令: 命令名称:touch 命令所在路径:/bin/touch 执行权限:所有用户 语法:touch [文件名] 功能描述:创建空文件 例子: touch leanring.file 说明:在当前目录下创建空文件l…

OpenCL 与 CUDA

根据网站资料,简单地汇编一下CUDA与OpenCL的区别。如有错误请指出。 题外话: 美国Sandia国家实验室一项模拟测试证明:由于存储机制和内存带宽的限制,16核、32核甚至64核处理器对于超级计算机来说,不仅不能带来性能提升…

DBMS (数据库管理系统) 是什么

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 数据库管理系统(英语:database management system,缩写:DBMS) 是一种针对对…

Eclipse4JavaEE安装SpringBoot

第一步:下载SpringBoot SpringBoot官网下载链接 第二步:在Eclipse里进行安装 打开Eclipse,菜单栏Help -》Install New Software,进入下图界面,点击Add 设置Name和Location,Name看自己喜好,Locat…

django中使用原生sql

django中使用原生sqlfrom django.db import connection cursor connection.cursor() cursor.execute("select * from xx where id1") row cursor.fetchone() users User.objects.raw("select * from user where namexx") for user in users: print(use…

从零开始玩转 logback、完整配置详解

官网地址:https://logback.qos.ch/manual/index.html 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 概述 LogBack是一个日志框架,它与Log4j可以说是同出一源&a…

Rust 编程 前言

虽然不是那么明显,但 Rust 程序设计语言的本质在于 赋能(empowerment):无论你现在编写的是何种代码,Rust 能让你在更为广泛的编程领域走得更远,写出自信。 比如,“系统层面”(“syst…

ffmpeg解码器优化

在以前的视频项目中,用到了几种商业版的H.264解码器。虽然性能稳定,支持DXVA或CUDA,在高清视频播放效果上不错,但是存在一个共同的缺陷-存在帧间延迟。经过我的测算,大概有3帧的缓冲延迟。当帧率在20fps以上时&#xf…

PHP 结合 Boostrap 结合 js 实现学生列表删除编辑以及搜索功能(完结)

这个自己的小项目要先告一段落了。可能还有许多bug。请见谅 删除学生功能 PHP: // 这里是通过前端代码HTML中的 url 传过来的&#xff0c;用 $_GET 来获取(相关HTML代码可以看一下到主页看一下前几条博客)if (empty($_GET[num])) exit(<h1>找不到您要删除的学生的学号<…

ActiveMQ_Windows版本的安装部署

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1, 保证电脑上安装了jdk6以上版本的java&#xff0c;并配置了好环境变量 &#xff1b; 2, 官方下载地址&#xff1a;http://activemq.a…

Java 自定义异常(转载)

1.异常的分类 1. 非运行时异常(Checked Exception) Java中凡是继承自Exception但不是继承自RuntimeException的类都是非运行时异常。 2. 运行时异常&#xff08;Runtime Exception/Unchecked Exception&#xff09; RuntimeException类直接继承自Exception类&#xff0c;称为运…

如何将markdown转换为wxml

话说我要为技术博客写一个小程序版&#xff0c;我的博客解决方案是 hexo github-page&#xff0c;格式当然是技术控们喜欢的 markdown 了 。但小程序使用的却是独有的模版语言 WXML。我总不能把之前的文章手动转换成小程序的 wxml 格式吧&#xff0c;而网上也没完善的转换库&a…

巧妙喝水打败多种疾病

喝水&#xff0c;我们每天都会做的一件事&#xff0c;殊不知&#xff0c;喝水得当能打败多种疾病问题! 方法/步骤 一、很多人都听说过早晨喝一杯水对身体有好处&#xff0c;有人喝盐水?有人喝蜂蜜水?还有人为了美白喝柠檬水?到底喝什么水最好呢?人体经过了一宿的代谢&…

git 几个commit点合并成一个commit点

在用git做版本控制器的时候&#xff0c;经常会遇到以下情况&#xff1a; 1、在做1个功能的时候&#xff0c;你自己觉得代码没问题了&#xff0c;就本地commit&#xff0c;然后提交代码&#xff0c;在gitlab上发起和并请求&#xff0c;老大看完之后&#xff0c;觉得你还有修改的…

JNDI 是什么

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 JNDI : 简单理解&#xff0c;就是把固定的连接方式剥离出来&#xff0c;单独写在一个配置文件里。(下载.properties里面通过InputStream…

并发编程模型

并发编程模型 一.分类 按照线程通信机制可以分为共享内存模型和消息传递模型&#xff1a; 1.共享内存模型&#xff1a;线程之间共享程序的公共状态&#xff0c;编程之间通过读写内存中的公共状态来隐式进行通信。相互通信的进程共享某些数据结构或共享存储区&#xff0c;进程通…

换工作,让我里外不是人,到底错在哪儿

看看时间&#xff0c;现在是凌晨4点多&#xff0c;窗外时不时一道闪电&#xff0c;雨也一阵一阵的&#xff0c;雷声像逐渐远离的喧嚣的火车一样。我不是睡眠困难者&#xff0c;但是&#xff0c;昨晚吃完晚饭之后&#xff0c;在衣服都未脱的情况下&#xff0c;晕晕乎乎的睡到现在…

Flink Kafka consumer的消费策略配置

val helloStream: FlinkKafkaConsumer011[String] new FlinkKafkaConsumer011[String]("hello", valueDeserializer, kafkaProps) // 指定消费策略 helloStream.setStartFromEarliest() // - 从最早的记录开始&#xff1b; helloStream.setStartFromLatest() //…

spdk/dpdk 编译相关问题汇总

下载 到官网上下载最新的spdk 代码。 解决编译依赖 yum install libaio.x86_64 libaio-devel.x86_64 编译dpdk 特别注意的是dpdk:依赖 /lib/modules/uname -a/build 下执行的内核已经存在&#xff0c;并且编译过&#xff0c;为此需要&#xff1a; 下载内核&#xff1b;安装依赖…

深入浅出 消息队列 ActiveMQ

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 一、 概述与介绍 ActiveMQ 是Apache出品&#xff0c;最流行的、功能强大的即时通讯和集成模式的开源服务器。ActiveMQ 是一个完全支持JM…