仿射变化的原理,使用及相关拓展的总结
前言
看了下原理计划上榜的文章,没错,我也会写标题了,不过本文内容无愧于题目。给大家详细讲一讲opencv里的仿射变换,也就是cv2.getAffineTransform和cv2.warpAffine这两个函数。
原本我通常会先写原理,然后再举个简单的例子,之后再举一个复杂点的拓展的例子。为了防止大家一看原理或者数学公式这类东西就跑,我先举个例子,大家理解了就能用,想提升的再往下看就好。
简单的例子
这个例子看懂了,遇到图像仿射变换的需求直接套就行。
我们来变下面这张图:
其中三个圆圈的中心点大概是,红[316,76],黄[215,369],蓝[413,371]。
那么我们把红点往左移,黄和蓝不变。移动后,红[215,76],黄[215,369],蓝[413,371]。
代码如下
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Mar 9 13:53:38 2020
@author: phoenix
"""
import cv2
import numpy as np
img=cv2.imread('/Users/phoenix/Documents/A.png')
rows,cols,ch=img.shape
pts1=np.float32([[316,76],[215,369],[413,371]])
pts2=np.float32([[215,76],[215,369],[413,371]])
M=cv2.getAffineTransform(pts1,pts2)
dst=cv2.warpAffine(img,M,(cols,rows))
cv2.imshow("dst",dst)
cv2.waitKey(0)
结果如下
再举个例子,如果保持红点不变,黄左移,蓝右移。
那么只要把代码中的pts2改一下,改成
pts2=np.float32([[316,76],[115,369],[513,371]])
结果如图
原理
简单理解就是:仿射变化的关键在于仿射变化矩阵M。
M可以通过cv2.getAffineTransform这个函数得到。
M是一个简单的2*3的矩阵。
假设一个点A坐标为(x1,y1),经过仿射变换矩阵M,在另一个图像上为A1点坐标为(x2,y2)。刚刚有说过M是一个2*3的矩阵,这里设
那么
也就是说
x2=a * x1 + b * y1 + c
y2=d * x1 + e * y1 + f
如果想做更深入的了解的话,可以看这个网址。
仿射变换
上面👆这个网址介绍的很详细
提升拓展
cv2.getAffineTransform这个函数没什么好说的,是单纯用来求仿射变换矩阵M
下面说说cv2.getAffineTransform这个函数
先看下它的help。
Help on built-in function warpAffine:
warpAffine(...)
warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
. @brief Applies an affine transformation to an image.
.
. The function warpAffine transforms the source image using the specified matrix:
.
. \f[\texttt{dst} (x,y) = \texttt{src} ( \texttt{M} _{11} x + \texttt{M} _{12} y + \texttt{M} _{13}, \texttt{M} _{21} x + \texttt{M} _{22} y + \texttt{M} _{23})\f]
.
. when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted
. with #invertAffineTransform and then put in the formula above instead of M. The function cannot
. operate in-place.
.
. @param src input image.
. @param dst output image that has the size dsize and the same type as src .
. @param M \f$2\times 3\f$ transformation matrix.
. @param dsize size of the output image.
. @param flags combination of interpolation methods (see #InterpolationFlags) and the optional
. flag #WARP_INVERSE_MAP that means that M is the inverse transformation (
. \f$\texttt{dst}\rightarrow\texttt{src}\f$ ).
. @param borderMode pixel extrapolation method (see #BorderTypes); when
. borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image corresponding to
. the "outliers" in the source image are not modified by the function.
. @param borderValue value used in case of a constant border; by default, it is 0.
.
. @sa warpPerspective, resize, remap, getRectSubPix, transform
总结下就是
1.这个方程是用来做图像仿射变化的,且是通过指定的矩阵来进行转换。
2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
其中
src:输入图像。
dst:输出图像,其大小为dsize,并且与src类型相同。
M: 转换矩阵。
dsize:输出图像的大小。
flags:插值方法(请参阅#InterpolationFlags)和可选方法的组合
borderMode:像素外推方法(不过看别的博客上写的边界像素模式?)(请参阅#BorderTypes);当borderMode =#BORDER_TRANSPARENT,表示目标图像中的像素对应于源图像中的“异常值”不会被该功能修改。(这里我觉得我的翻译没有问题,但我实在不知道他想表达什么)
borderValue:边界不变时使用的值;默认情况下为0。也就是黑色。
src,dst,M,dsize:输入图像,输出图像,转换矩阵,输出图像的大小。这四个比较基础,没什么好说的。
后面的**flags,borderMode,borderValue,这个三个变量很容易被大家忽略,其实还是非常非常有用**的。可以区别你对这个函数的理解深度。
flags:插值方法
flages表示插值方式,默认为flags=cv2.INTER_LINEAR(双线性插值)。
做个汇总给大家
INTER_NEAREST-最近邻插值
INTER_LINEAR-双线性插值(默认使用)
INTER_AREA-使用像素面积关系进行重采样。 这可能是首选的图像抽取方法,因为它可以提供无波纹的结果。 但是当图像放大时,它类似于INTER_NEAREST方法。
INTER_CUBIC-在4x4像素邻域上的双三次插值
INTER_LANCZOS4-在8x8像素邻域上的Lanczos插值
这么多插值方法怎么选择,我给大家推荐这篇博客OpenCV图像插值方法的比较
如果你看不太懂那篇博客。那么我给个不负责任的个人建议。
不在意图片质量:使用默认的就OK。
在意图片质量不在意代码的运行时间:使用最后一个。
既在意质量又在意时间:看到前面写的原理了吗,你可以利用矩阵反推回去,因为原图的质量是最高的
borderMode:像素外推方法(边界像素模式)
这个推荐大家看这个网站
BorderTypes
这是google翻译后的网页截图。
简单些解释呢,borderType是指要添加在图像周围的某种类型的边界,就像在图像周围创造一个边框(给你的图像加个相框的感觉)。
borderMode和borderValue加在一起,默认就是黑的。如果不理解往下看。
borderValue:边界不变时使用的值
这里的边界不变指的是borderMode=cv2.BORDER_CONSTANT。
举个例子这里有一张图片
当我用
dst=cv2.warpAffine(img,M,(cols,rows))
,borderMode和borderValue不选择,即为默认时。
仿射变化的结果是
记住这张图
接着我用
dst=cv2.warpAffine(img,M,(cols,rows),borderMode=cv2.BORDER_CONSTANT ,borderValue=(0,255,255))
或者
dst=cv2.warpAffine(img,M,(cols,rows) ,borderValue=(0,255,255))
结果是
没错原本黑色的地方被黄色(0,255,255)填满了。
而我如果更改borderMode,我使用
dst=cv2.warpAffine(img,M,(cols,rows),borderMode=cv2.BORDER_REFLECT,borderValue=(0,255,255))
结果是如图,右边填充的是h的镜像。
cv2.BORDER_REFLECT的效果就是fedcba|abcdefgh|hgfedcb
此时borderValue的值也就没什么用了。
结尾
这篇文章花了我好长时间啊,翻了很多资料和博客,力求做到详细正确。如果你看到了这里,麻烦给我点了赞且留言支持一下。如果你还有关于仿射变换的问题没有解决,麻烦发在评论里,我会回复帮你解决的。谢谢各位!
来源:CSDN
作者:西瓜6
链接:https://blog.csdn.net/qq_37924224/article/details/104751758