导读
在图像处理中,我们经常需要对图像进行各种操作如平移、缩放、旋转、翻转等,这些操作都属于图像的仿射变换,我们通过一个变换矩阵就能很容易的实现。本篇文章详细的介绍了各种操作的实现原理,以及代码的实现和使用
仿射变换
仿射变换也称仿射投影,是指几何中,对一个向量空间进行线性变换并接上一个平移,变换为另一个向量空间。所以,仿射变换其实也就是在讲如何来进行两个向量空间的变换
假设有一个向量空间k:
![9f555a0227775b44326bd7f75adde823.png](https://i-blog.csdnimg.cn/blog_migrate/c2c534376b03727e708da2afc09579d7.jpeg)
向量空间k
和另一个向量空间j:
![43f2c7c909f17662af8191a615b71c42.png](https://i-blog.csdnimg.cn/blog_migrate/630bbdf09ca16cf2e5d56efd5ace7f1b.jpeg)
向量空间j
如果我们想要将向量空间由k变为j,可以通过下面的公式来实现:
![628b7a61d2066a4d258e59b65951d69c.png](https://i-blog.csdnimg.cn/blog_migrate/847e6c24ad5ccb1f849f99ddca6d2c6c.jpeg)
向量空间变换
将上式进行拆分可得
![e84e33f4bfa9ca6d972c7d74965d0288.png](https://i-blog.csdnimg.cn/blog_migrate/5242ed40b1d20a6f8f9e578f679c0720.jpeg)
向量空间坐标变换
我们再将上式转换为矩阵的乘法
![1d6ef379b8c3cbc5272eb4c231fd5c92.png](https://i-blog.csdnimg.cn/blog_migrate/9e6aad5cb27c465aa573c6751cb0f162.jpeg)
向量空间变换的矩阵表示
上式表明通过一个两行三列的矩阵M就可以实现两个向量空间之间的转换,在进行仿射变换的时候我们也只需要一个矩阵M就可以实现图像的平移、缩放、旋转和翻转变换。
接下来,会先介绍原理然后利用OpenCV来实现相应的例子,这里主要利用OpenCV的warpAffine函数来实现仿射变换
warpAffine函数参数:
- src:输入的图像数组
- M:仿射变换矩阵
- dsize:变换后图像的大小
- flags:使用的插值算法
- borderValue:边界的填充值
图像平移
在平面坐标系有点P(x,y)和点P′(x′,y′),如果我们想要将P点移动到P′通过下面的变换就可以实现
![80a039a631d909cbe58282acf145e3c1.png](https://i-blog.csdnimg.cn/blog_migrate/4a624a27cdeb373408aeeb41ecde2a6e.jpeg)
坐标平移
其中Δx和Δy就是x方向上和y方向上的偏移量,我们将其转换为矩阵的形式上面的矩阵MM就是仿射变换的平移参数,接下来我们利用OpenCV中的warpAffine函数来实现
![6bf3ed94e6ee626c6bb2555ea657e5ee.png](https://i-blog.csdnimg.cn/blog_migrate/1b887e18cca70d9efc95bc7b0a7b3df9.jpeg)
坐标平移的矩阵形式
上面的矩阵M就是仿射变换的平移参数,接下来我们利用OpenCV中的warpAffine函数来实现图像的平移
![f496932abc233f2cd8c9045a8ff4995e.png](https://i-blog.csdnimg.cn/blog_migrate/e15c268266133df62111452b43766d4a.jpeg)
仿射变换实现图像平移
![ee9d4ba3c7fa04c3b9fb3f2040cc1d48.png](https://i-blog.csdnimg.cn/blog_migrate/d142c3e3b5acb2eb2e25d0ff59757746.jpeg)
图像平移
图像翻转
有时候我们我们需要对图像进行水平翻转、垂直翻转、镜像翻转(同时进行水平和垂直翻转),想要实现这个功能并不难,我们可以通过opencv内置的flip方法很容易实现,还可以通过numpy的索引来实现,当然也可以通过仿射变换矩阵来实现这个功能
![fdb1238d80daad02c1588b82b4a5d547.png](https://i-blog.csdnimg.cn/blog_migrate/260be7ff837b5e9da1fb9b5cf2f8dfeb.jpeg)
上图中的A、B、C、D表示图像的四个顶点,如果我们需要对图像进行水平翻转,那么我们就需要将A点和B点进行交换,C点和D点进行交换,沿着x轴的中线进行对称交换位置,通过下面的式子可以实现水平翻转
![4b21c816c252d6c9f5407491a6db8390.png](https://i-blog.csdnimg.cn/blog_migrate/247cfd40bb53a438fe6384bb038e9c57.jpeg)
水平翻转
上式中的w表示图像的宽,同理可得垂直翻转的实现公式
![29ffb6165270e02901c77be87680084a.png](https://i-blog.csdnimg.cn/blog_migrate/cb23e3d147aa85153d3b1324d5840b42.jpeg)
垂直翻转
上式中的h表示的是图像的高
- 利用变换矩阵翻转图像
水平翻转的变换矩阵M:
![095fa5c29435b4e8a0659cd846228361.png](https://i-blog.csdnimg.cn/blog_migrate/3bffa80362d38a2af4d4c73b173e4c6c.jpeg)
仿射变换水平翻转矩阵
垂直翻转的变换矩阵M:
![0704d9c103181a0ec708caeea3ba2ec8.png](https://i-blog.csdnimg.cn/blog_migrate/7233533dfaebba3d62e6ef4d9f9472bb.jpeg)
仿射变换垂直翻转矩阵
镜像翻转的变换矩阵M:
![a2add0167fdb7a22e04cf3389f5e761a.png](https://i-blog.csdnimg.cn/blog_migrate/8ccbeb65c7f056f092bbcacb026135ec.jpeg)
仿射变换镜像翻转矩阵
![241d2bcc28c0b57190e9ee69d00017f6.png](https://i-blog.csdnimg.cn/blog_migrate/a6f883ccbc9f60642b29693148489bd1.jpeg)
仿射变换实现图像的镜像翻转
![724868e046d4019640be290d5f25159f.png](https://i-blog.csdnimg.cn/blog_migrate/a12da256d694f070d6f4108f8b7c33c6.jpeg)
镜像翻转
- OpenCV的flip函数翻转图像
flip函数参数:
- src:输入的图像数组
- flipCode:图像翻转参数,1表示水平翻转,0表示垂直翻转,-1表示镜像翻转
![ad74ec17d72785bc73d6a139660039be.png](https://i-blog.csdnimg.cn/blog_migrate/8b056d173aa974b460938acd6622e6f4.jpeg)
flip函数实现图像翻转
- numpy的索引翻转图像
![a48a16a67e42546201deb3ca76c5734f.png](https://i-blog.csdnimg.cn/blog_migrate/86622f2717e99c82902f3e1979a60c79.jpeg)
numpy通过索引数组实现图像翻转
图像缩放
如果我们想要对坐标系的P点进行缩放操作,通过下面的公式就可以实现
![25cf6e0b96ef6704b30350ed9326a7e4.png](https://i-blog.csdnimg.cn/blog_migrate/884afbbc91b123cb2075a8ae602e3fd8.jpeg)
图像缩放
通过在x和y前面添加一个缩放系数即可,同样我们将其转换为矩阵形式
![840510f4636cdaf4d19436a7feb1f0f7.png](https://i-blog.csdnimg.cn/blog_migrate/7922c91b14a669dc52458a88f6f6d093.jpeg)
图像缩放的矩阵形式
通过上面的矩阵M我们就可以实现对图片的缩放
![d4b1afb1b183b7a3827ca57a56c119d2.png](https://i-blog.csdnimg.cn/blog_migrate/cd013aef99054ba5699a20b3c6f00157.jpeg)
仿射变换实现图像缩放
![ec68047b346305ec96eeca845cb27e13.png](https://i-blog.csdnimg.cn/blog_migrate/c90d33b5e32b9fae55a1f05843564646.jpeg)
仿射变换实现图像缩放
这里使用仿射变换实现的图片缩放其实和resize函数的效果是一样的,缩放时选择的插值函数不同最终效果会有所偏差
图像旋转
- 围绕原点旋转
我们先来看看一个二维平面上的点在围绕原点是如何旋转的
![b0477fc964426394afb952b648711426.png](https://i-blog.csdnimg.cn/blog_migrate/65d615d6a7df1ff1991327b34a8b829e.jpeg)
任意一点围绕原点旋转
上图中点v在围绕原点旋转θ度之后得到了点v′,我们将坐标点用极坐标的形式来表示可以得到v(rcosϕ,rsinϕ),所以v′(rcos(θ+ϕ),rsin(θ+ϕ))利用正弦和余弦展开式将其展开可得,对于v点来说:
![1255df84ba2fcb2515ce29f992903c95.png](https://i-blog.csdnimg.cn/blog_migrate/f6206e774e7f038cc0e69184dd5c8651.jpeg)
v点坐标展开式
对于v′来说:
![15dbce48a5400088bc6ab282a283a6e5.png](https://i-blog.csdnimg.cn/blog_migrate/f74225dffc68073aa73e8460d0507b2b.jpeg)
v'点坐标展开式
然后我们再将x和y代入上式可得
![a2e286f4543b0fcff738972a559dc367.png](https://i-blog.csdnimg.cn/blog_migrate/b1866df2cc2b59a06c0a57869f93bb35.jpeg)
变换后的x和y的坐标
然后再将上式用矩阵M表示,可得
![8fe93fcab086195a6bda943dd99bbd97.png](https://i-blog.csdnimg.cn/blog_migrate/9a2931c2cdfbe6c06952fc84e90f8f20.jpeg)
旋转后坐标的矩阵表示形式
特别注意:我们在建立直角坐标系的时候是以左下角为原点建立的,然而对于图像而言是以左上角为原点建立的,所以我们需要对角度θ进行取反,结合三角函数的特性,M矩阵的表达式如下
![0aee78fdc6e54eb83d46c58d8fd670c0.png](https://i-blog.csdnimg.cn/blog_migrate/5b137147e8271d2869f936ef9ab55a57.jpeg)
围绕原点旋转的仿射变换矩阵
还需要注意的是这里的角度都是弧度制,所以我们在使用的时候还需要对其进行转换,转换代码如下
#将角度转换为弧度制radian_theta = theta/180 * np.pi#np.pi指的是π
将图片围绕原点进行逆时针旋转θ度的代码如下
![6710102fef02b8aad9fa4f90981f32ed.png](https://i-blog.csdnimg.cn/blog_migrate/d4615a1b2e60fb55222e85ddaa11bbf8.jpeg)
将图像围绕原点旋转
- 围绕任意点旋转
如果我们想围绕任意坐标点旋转呢?其实也并不难,下图的v点在围绕c点(a,b)旋转90度得到v′。其实我们可以将其等价于,先将V点平移到V1点,然后再将V1点围绕原点旋转90度得到V2点,最后再将V2点沿着V点平移的反方向平移相同长度,最终得到V'。这样我们就将围绕任意坐标点旋转的问题转换成了围绕原点旋转的问题
![13aeb06767415921b839b2f52372afd5.png](https://i-blog.csdnimg.cn/blog_migrate/fa56727eb1384138d16c4dbd72b37139.jpeg)
我们来回顾一下,围绕原点旋转坐标的变换公式
![df0cc251b9b7684f4567b46ccf972a15.png](https://i-blog.csdnimg.cn/blog_migrate/06430722b0bb6e4e317e8a42fe0c5ff8.jpeg)
围绕原点旋转
在围绕原点旋转变换公式的基础上,我们将其改进为围绕任意点c(a,b)旋转,我们现在原来的坐标进行平移,得到变换后的坐标,最后再沿着之前平移的反方向进行平移,就得到围绕任意点旋转的变换公式
![d500e5542c010db5a275bafa0b2168e9.png](https://i-blog.csdnimg.cn/blog_migrate/7fdfeb05a32a86f2bdd7994994a90eb5.jpeg)
围绕任意点旋转
将其展开可得
![36ca0444483c216f0eb797eae496608f.png](https://i-blog.csdnimg.cn/blog_migrate/c1ca36ad539d5694d415d2982ac87cde.jpeg)
围绕任意点旋转的展开式
将上式用矩阵M表示:
![40f23002c1ed98cd65a7f15c4ba30335.png](https://i-blog.csdnimg.cn/blog_migrate/7b9959a4ddf34009f679ff54be264330.jpeg)
矩阵形式表示围绕点c进行旋转
上式中的c(a,b)表示旋转中心,因为坐标系问题需要对θ进行取反,最终M矩阵的表达式如下
![4186ede23b1b9bb4295930b81d2fade6.png](https://i-blog.csdnimg.cn/blog_migrate/6c9c301bc45d039f8fa87f141261842b.jpeg)
旋转矩阵
![f86a32a8653c01963830cda81e547e75.png](https://i-blog.csdnimg.cn/blog_migrate/7fb922ded423c795ea50fbd02b53cbaa.jpeg)
围绕图像中心旋转图像
![cd996d2430f302481ed26c158c6d62b4.png](https://i-blog.csdnimg.cn/blog_migrate/efa26676e0993d095f3df3dc9392f0b8.jpeg)
围绕图像中心旋转图像
细心的同学也许已经发现了,上图中围绕图像中心旋转后的图片部分被裁剪掉了,如果我们想让旋转之后的图片仍然是完整,应该如何修改呢?
![9f4c62e3fa7f443c41f452881190a7c0.png](https://i-blog.csdnimg.cn/blog_migrate/e29723f20fe7a6c03f353074ea181b04.jpeg)
围绕图像中心旋转后仍保持完整
![10552013b6113ca52bfb6aafdcc712df.png](https://i-blog.csdnimg.cn/blog_migrate/9635a9b4bf0641b9f7583b841518cac2.jpeg)