OpenCV实现图像变换(python)-仿射变换原理

摘要:
OpenCV中有相应的实现函数。例如,方程方法:cv2.getAffineTransform用于计算从参数src到dst的对应仿射变换的矩阵,其中参数src和dst分别表示原始坐标和变换后的坐标,并且都是三行两列的二维数组。数据必须是浮点类型。

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

仿射变换##

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

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

在以下矩阵中:

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

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

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

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

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

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

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

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

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

坐标(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( egin{matrix} overline{x} \ overline{y} \ 1 end{matrix} ight) =left( egin{matrix} cosα&-sinα&0 \ sinα&cosα&0 \ 0&0&1 end{matrix} ight) left( egin{matrix} x \ y \ 1 end{matrix} ight) ]

放射矩阵的计算
如果已知坐标以及其放射变换后的矩阵,从而计算出变换后的坐标,就需要放射矩阵的计算,主要的实现方法有:方程法,矩阵法,插值算法。在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)

运行结果:
OpenCV实现图像变换(python)-仿射变换原理第1张
在矩阵法中,需要预先知道具体的变化步骤,比如先放大再平移还是先移动再放大

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

以上的矩阵变换就是平移仿射矩阵乘以缩放仿射矩阵得到的而不是缩放仿射矩阵乘以平移仿射矩阵得到的,由于等式是由右向左运行,所以必须要知道变化顺序。矩阵的乘法并不是矩阵的点乘,再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)

运行结果:
OpenCV实现图像变换(python)-仿射变换原理第2张
一定要注意传入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)函数,高阶插值常用二维离散卷积运算来实现。后续博客会对二维离散卷积运算做详细的描述。
对于双线性插值法在这表一次形象化的描述:

OpenCV实现图像变换(python)-仿射变换原理第3张
如图:先估计f1在(x,[y])处的函数值,再估计f1在(x,[y]+1)处的函数值,最后估计f1在(x,y)处的函数值

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

参数释义
src图像矩阵
M2行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()

运行效果

OpenCV实现图像变换(python)-仿射变换原理第4张

参考文献:
[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)-仿射变换原理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Keil MDK使用J-LINK分别在Sram,Nor Flash以及Sdram中调试代码的原理和方法Linux内核定时器struct timer_list下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

图像切割之(五)活动轮廓模型之Snake模型简单介绍

图像切割之(五)活动轮廓模型之Snake模型简单介绍 zouxy09@qq.com http://blog.csdn.net/zouxy09 在“图像切割之(一)概述”中咱们简单了解了眼下主流的图像切割方法。以下咱们主要学习下基于能量泛函的切割方法。这里学习下Snake模型简单的知识,Level Set(水平集)模型会在后面的博文中说到。 基于能量泛...

二维码(QR code)基本知识

1.二维码定义:   二维码(2-Dimensional Bar Code),是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的。它是指在一维条码的基础上扩展出另一维具有可读性的条码,使用黑白矩形图案表示二进制数据,被设备扫描后可获取其中所包含的信息。一维条码的宽度记载着数据,而其长度没有记载数据。二维条码的长度、...

Qt绘图之QGraphicsScene QGraphicsView QGraphicsItem详解

Graphics View提供了一个界面,它既可以管理大数量的定制2D graphical items,又可与它们交互,有一个view widget可以把这些项绘制出来,并支持旋转与缩放。这个柜架也包含一个事件传播结构,对于在scene中的这些items,它具有双精度的交互能力。Items能处理键盘事件,鼠标的按,移动、释放、双击事件,也可以跟踪鼠标移动。...

如何通俗的解释仿射变换?

原文地址:https://www.matongxue.com/madocs/244.html 简单来说,“仿射变换”就是:“线性变换”+“平移”。 先看什么是线性变换? 1 线性变换 线性变换从几何直观有三个要点: 变换前是直线的,变换后依然是直线 直线比例保持不变 变换前是原点的,变换后依然是原点 比如说旋转: 比如说推移: 这两个叠加也是线...

glViewport()函数和glOrtho()函数的理解

在OpenGL中有两个比较重要的投影变换函数,glViewport和glOrtho.glOrtho是创建一个正交平行的视景体。 一般用于物体不会因为离屏幕的远近而产生大小的变换的情况。比如,常用的工程中的制图等。需要比较精确的显示。 而作为它的对立情况, glFrustum则产生一个透视投影。这是一种模拟真是生活中,人们视野观测物体的真实情况。例如:观察两...

1050. 螺旋矩阵(25)

1050. 螺旋矩阵(25) 本题要求将给定的N个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第1个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为m行n列,满足条件:m*n等于N;m>=n;且m-n取所有可能值中的最小值。 输入格式: 输入在第1行中给出一个正整数N,第2行给出N个待填充的正整数。所有数字不超过104...