一般对图像的变化操作有放大、缩小、旋转等,统称为几何变换,对一个图像的图像变换主要有两大步骤,一是实现空间坐标的转换,就是使图像从初始位置到终止位置的移动。二是使用一个插值的算法完成输出图像的每个像素的灰度值。其中主要的图像变换有:仿射变换、投影变换、极坐标变换。

仿射变换##

二维空间坐标的仿射变换公式:

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y}
\end{matrix}
\right) =\left(
\begin{matrix}
a_{11} &&a_{12} \\
a_{21}&&a_{22}
\end{matrix}
\right) \left(
\begin{matrix}
x \\
y
\end{matrix}
\right) + \left(
\begin{matrix}
a_{13} \\
a_{23}
\end{matrix}
\right)
\]

在以下矩阵中:

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y} \\
1
\end{matrix}
\right) =A \left(
\begin{matrix}
x \\
y \\
1
\end{matrix}
\right)
\]

矩阵A就是仿射矩阵,因为它最后一行为(0,0,1)

\[A=
\left(
\begin{matrix}
a_{11}&a_{12}&a_{13} \\
a_{21}&a_{22}&a_{23} \\
0&0&1
1
\end{matrix}
\right)
\]

平移

平移是最简单的仿射变换如将空间坐标(x,y)沿着x轴移动100,沿着y轴移动200。平移后的坐标为(x+100,y+200)。将这个过程一般化后,假设任意的空间坐标(x,y)先沿着x轴平移Px再沿着y轴平移Py。得到的坐标为(x+Px,y+Py)。用矩阵表示这个平移过程为:

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y} \\
1
\end{matrix}
\right) =\left(
\begin{matrix}
1 &0&Px \\
0&1&Py \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
x \\
y \\
1
\end{matrix}
\right)
\]

对于Px和Py若大于0则表示沿着轴正向移动,若小于0则表示沿着轴负向移动。

放大缩小

在坐标轴中以原点为中心的放大与缩小S倍是指对其x轴方向的横坐标放缩成原坐标的横坐标距离中心点(0,0)的距离的S倍并对其y轴方向的横坐标放缩成原坐标的纵坐标距离原点的距离的S倍。其中若S大于1则表示增大,若小于1则表示缩小。放缩在矩阵中的表示为:

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y} \\
1
\end{matrix}
\right) =\left(
\begin{matrix}
s_x&0&0 \\
0&s_y&0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
x \\
y \\
1
\end{matrix}
\right)
\]

坐标(x,y)在坐标轴中以任意一点的坐标(x0,y0)为中心在水平和垂直方向上放缩S倍,放缩后的坐标为$$ ( (x_0+S_x(x-x_0),y_0+S_y(y-y_0) )$$用矩阵可以表示为:

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y} \\
1
\end{matrix}
\right) =\left(
\begin{matrix}
1 &0&X_0 \\
0&1&Y_0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
s_x&0&0 \\
0&s_y&0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
1 &0&-X_0 \\
0&1&-Y_0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
x \\
y \\
1
\end{matrix}
\right)
\]

坐标(x,y)绕原点顺时针旋转α(α>0),cosΘ=x/p sinΘ=y/p.其中p代表(x,y)到中心点(0,0)的距离。则

cos(Θ+α)=cosΘcosα-sinΘsinα=(x/p)cosα -(y/p)sinα=Ex/p

sin(Θ+α)=sinΘcosα+cosΘsinα=(y/p)cosα -(y/p)sinα=Ey/p

化解以上公式,使用矩阵表示为:

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y} \\
1
\end{matrix}
\right) =\left(
\begin{matrix}
cosα&-sinα&0 \\
sinα&cosα&0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
x \\
y \\
1
\end{matrix}
\right)
\]

放射矩阵的计算

如果已知坐标以及其放射变换后的矩阵,从而计算出变换后的坐标,就需要放射矩阵的计算,主要的实现方法有:方程法,矩阵法,插值算法。在OpenCV中有对应的实现函数,如使用方程法:cv2.getAffineTransform(src,dst) 该方法就是通过计算参数src到dst的对应仿射变换的矩阵,其中参数src和dst分别代表原坐标和变换后的坐标,并且均为3行2列的二维ndarray,数据必须为浮点型。实现代码:

import numpy as np

src=np.array([[0,0],[200,0],[0,200]],np.float32)
dst=np.array([[0,0],[100,0],[0,100]],np.float32)
A=cv2.getAffineTransform(src,dst) print(A)

运行结果:



在矩阵法中,需要预先知道具体的变化步骤,比如先放大再平移还是先移动再放大

\[\left(
\begin{matrix}
\overline{x} \\
\overline{y} \\
1
\end{matrix}
\right) =\left(
\begin{matrix}
1 &0&X_0 \\
0&1&Y_0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
s_x&0&0 \\
0&s_y&0 \\
0&0&1
\end{matrix}
\right) \left(
\begin{matrix}
x \\
y \\
1
\end{matrix}
\right)
\]

以上的矩阵变换就是平移仿射矩阵乘以缩放仿射矩阵得到的而不是缩放仿射矩阵乘以平移仿射矩阵得到的,由于等式是由右向左运行,所以必须要知道变化顺序。矩阵的乘法并不是矩阵的点乘,再Numpy中乘法是通过dot函数实现的,关于Numpy语法可以参考我之前写的博文。我们通过一个实例来了解矩阵的乘法计算。

假设先对一矩阵等比例放大二倍,然后水平与垂直方向上分别平移100,计算该矩阵的算法如下:

import numpy as np

#先对矩阵进行放大
s=np.array([[2,0,0],[0,2,0],[0,0,1]])
#再对矩阵进行平移
t=np.array([[1,0,100],[0,1,100],[0,0,1]])
#矩阵相乘
A=np.dot(t,s)
print(A)

运行结果:



一定要注意传入dot参数的顺序。

下面介绍插值算法,我们可以将图像理解为一个二维的函数,行数为H,列数为W的图像矩阵I:Z=F(x,y), 0<=x<W,0<=y<H,x⊆N,y⊆N

矩阵的列号对应x坐标,垂直方向为y轴,行号对应y坐标,称该函数为图像函数。

利用已知的整数坐标处的函数值估算非整数坐标处的函数值的方法主要有:最近邻插值(就是从四个相邻整数坐标中找到一个最近的)输出的图像经过放大后会有锯齿状的外观。双线性插值法有两个变量的插值函数的线性插值扩展,其核心是在两个方向分别进行一次线性插值。待插点像素值取原图像中与其相邻的4个点像素值的水平、垂直两个方向上的线性内插,即根据待采样点与周围4个邻点的距离确定相应的权重,从而计算出待采样点的像素值。有的时候需要更高阶的插值函数,如三次样条插值、Lengendre中心函数和sin(axs)函数,高阶插值常用二维离散卷积运算来实现。后续博客会对二维离散卷积运算做详细的描述。

对于双线性插值法在这表一次形象化的描述:



如图:先估计f1在(x,[y])处的函数值,再估计f1在(x,[y]+1)处的函数值,最后估计f1在(x,y)处的函数值

对于空间坐标变换和插值方法在已知的仿射变换矩阵上OpenV提供了warpAffine(src,M,dsize[,flags[,borderMode[,borderValue ]]])函数

参数 释义
src 图像矩阵
M 2行3列的仿射变换矩阵
dsize 一个二元元组,输出图像的大小
flags 插值法:INTE_NEAREST、INTE_LINEAR(默认)等
borderMode 填充模式,如:BORDER_CONSTANT等
borderValue 当borderMode=BORDER_CONSTANT时的填充值

下面使用python实现图像的几何变换:

import numpy as np
import cv2
import sys
import math img=cv2.imread('yun.jpg',cv2.IMREAD_GRAYSCALE)
cv2.imwrite('yun.jpg',img)
#原图的宽高
h,w=img.shape[:2]
#仿射变换矩阵 缩小2倍
A1=np.array([[0.5,0,0],[0,0.5,0]],np.float32)
A2=cv2.warpAffine(img,A1,(w,h),borderValue=126)
#缩小后平移
B1=np.array([[0.5,0,w/4],[0,0.5,h/4]],np.float32)
B2=cv2.warpAffine(img,B1,(w,h),borderValue=126)
#使图像旋转
C1=cv2.getRotationMatrix2D((w/2.0,h/2.0),30,1)
C2=cv2.warpAffine(img,C1,(w,h),borderValue=126) cv2.imshow('img',img)
cv2.imshow('A2',A2)
cv2.imshow('B2',B2)
cv2.imshow('C2',C2)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果

参考文献:

[1]ROBERT G.KEYS.Cubic Convolution Interpolation for Digital Image Processing.IEEE TRANSACTIONS ON ACOUSTICS.SPEECH,AND SIGNAL PROCESSING,1981

[1]R. Hartley and A. Zisserman, “Multiple View Geometry in Computer Vision,” 2-nd edition, Cambridge University Press, 2004.

今天就先写到这吧!投影变换和极坐标变换后续再写

OpenCV实现图像变换(python)的更多相关文章

  1. python+opencv中最近出现的一些变化( OpenCV 官方的 Python tutorial目前好像还没有改过来?) 记一次全景图像的拼接

    最近在学习过程中发现opencv有了很多变动, OpenCV 官方的 Python tutorial目前好像还没有改过来,导致大家在学习上面都出现了一些问题,现在做一个小小的罗列,希望对大家有用 做的 ...

  2. opencv + numpy for python

    OpenCV的全称是:Open Source Computer Vision Library.OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows和Mac OS ...

  3. Ubuntu14.04(或Ubuntu16.04)安装openCV并测试python/C++

    网上关于opencv的安装已经有了不少资料,但是没有一篇资料能让我一次性安装成功,因此花费了大量时间去解决各种意外,希望这篇能给一些人带去便利,节省时间. 1.安装OpenCV所需的库 1 sudo ...

  4. 学习CV:《OpenCV 3计算机视觉Python语言实现第2版》中文PDF+英文PDF+代码

    理解与计算机视觉相关的算法.模型以及OpenCV 3 API背后的基本概念,有助于开发现实世界中的各种应用程序(比如:安全和监视领域的工具). OpenCV 3是一种先进的计算机视觉库,可以用于各种图 ...

  5. 使用OpenCV(C ++ / Python)进行人脸交换

    -- 图3.面部对齐.左:检测到面部标志和凸包.中:凸包上的点的Delaunay三角剖分.右:通过仿射扭曲三角形进行面部对齐. 1 人脸对齐 1.1 脸部地标检测 两个脸部的几何形状非常不同,因此我们 ...

  6. opencv读取图像python和c++版本的结果不同

    问题: 在读取同一张图像时,python读取的结果和c++读取的结果差异较大,测试图像中最大误差达到16. 原因: python的opencv采用的是4.1.1,c++采用的是3.1.0,在解析JPE ...

  7. 基于OpenCV全景拼接(Python)SIFT/SURF

    一.实验内容: 利用sift算法,实现全景拼接算法,将给定的两幅图片拼接为一幅. 二.实验环境: 主机配置: CPU :intel core i5-7300 2.50GHZ RAM :8.0GB 运行 ...

  8. opencv处理验证码python代码

    # -*- coding: utf-8 -*- # @Time : 2019-02-11 09:39 # @Author : cxa # @File : bgr2gry.py # @Software: ...

  9. OpenCV计算机视觉实战(Python版)资源

    疲劳检测 pan.baidu.com/s/1Ng_-utB8BSrXlgVelc8ovw #导入工具包 from scipy.spatial import distance as dist from ...

随机推荐

  1. 解决elment 动态多选框组(el-checkbox-group)无法设置默认值问题

    <el-checkbox-group v-model="form.showProperty"> <el-checkbox v-for="(item,id ...

  2. JSON小记

    json: { "1" : "2", "1" : "3" } 在json中如果有重复的key,会取最后一个key的值,如 ...

  3. C++生成dll以及调用(函数)和类

    C++新手,方法可能有很多,此方法仅仅是自己实验并可行,详细步骤如下: 生成dll文件和lib文件: (1) 新建项目-windows桌面向导,选择动态链接.dll以及空项目: (2)复制代码(头文件 ...

  4. CF825G Tree Queries

    [题意] 一棵树有 n个节点,初始均为白色,有两种操作: 1. 1 x 代表把结点 x 设置为黑色 2. 2 x 代表查询 x 到树上任意一个黑色结点的简单路径上的编号最小的结点的编号 输入 t 和 ...

  5. Java小白集合源码的学习系列:ArrayList

    ArrayList源码学习 本文基于JDK1.8版本,对集合中的巨头ArrayList做一定的源码学习,将会参考大量资料,在文章后面都将会给出参考文章链接,本文用以巩固学习知识. ArrayList的 ...

  6. HTTPS中的TLS

    1. SSL 与 TLS SSL:(Secure Socket Layer) 安全套接层,于 1994 年由网景公司设计,并于 1995 年发布了 3.0 版本TLS:(Transport Layer ...

  7. C++ 中的 unique 函数

    unique 函数是用来去除一个集合中重复元素的函数 若是在数组中,则调用此函数后,返回的除去重复元素的下一个指针的地方 若是在 vector中,则会返回重复元素下一个位置的迭代器,在调用erase函 ...

  8. 记录初试Netty(2)-服务端心跳检测

    今天在在搭建的netty框架中添加心跳机制,特此记录一下:      1.什么是心跳机制? 心跳是在TCP长连接中,客户端和服务端定时向对方发送数据包通知对方自己还在线,保证连接的有效性的一种机制 在 ...

  9. List去重问题与方法

    面试中经常被问到的list如何去重,用来考察你对list数据结构,以及相关方法的掌握,体现你的java基础学的是否牢固.我们大家都知道,set集合的特点就是没有重复的元素.如果集合中的数据类型是基本数 ...

  10. NanoProfiler-Step1翻译

    NanoProfiler NanoProfiler is a light weight profiling library written in C# which requires (NanoProf ...