一文解决!opencv中的仿射变换(仿射变化的原理,使用,提升拓展的总结)

谁都会走 提交于 2020-03-10 00:32:38

仿射变化的原理,使用及相关拓展的总结

前言

看了下原理计划上榜的文章,没错,我也会写标题了,不过本文内容无愧于题目。给大家详细讲一讲opencv里的仿射变换,也就是cv2.getAffineTransformcv2.warpAffine这两个函数。
原本我通常会先写原理,然后再举个简单的例子,之后再举一个复杂点的拓展的例子。为了防止大家一看原理或者数学公式这类东西就跑,我先举个例子,大家理解了就能用,想提升的再往下看就好。

简单的例子

这个例子看懂了,遇到图像仿射变换的需求直接套就行。
我们来变下面这张图:
一张图片A
其中三个圆圈的中心点大概是,红[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的矩阵,这里设
M=[abcdef] M= \begin{gathered} \begin{bmatrix} a & b &c \\ d & e & f \end{bmatrix} \end{gathered}
那么
[x2y2]=[abde][x1y1]+[cf] \begin{gathered} \begin{bmatrix} x2 \\ y2 \end{bmatrix}= \begin{bmatrix} a & b\\ d & e\end{bmatrix} \begin{bmatrix} x1 \\ y1 \end{bmatrix}+ \begin{bmatrix} c \\ f \end{bmatrix} \end{gathered}
也就是说
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翻译后的网页截图。
bordertypes
简单些解释呢,borderType是指要添加在图像周围的某种类型的边界,就像在图像周围创造一个边框(给你的图像加个相框的感觉)。
borderMode和borderValue加在一起,默认就是黑的。如果不理解往下看。

borderValue:边界不变时使用的值

这里的边界不变指的是borderMode=cv2.BORDER_CONSTANT。
举个例子这里有一张图片
abcdefgh
当我用

dst=cv2.warpAffine(img,M,(cols,rows))

,borderMode和borderValue不选择,即为默认时。
仿射变化的结果是
abcdefgh仿射变化结果
记住这张图
接着我用

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的镜像。
BORDER_REFLECT
cv2.BORDER_REFLECT的效果就是fedcba|abcdefgh|hgfedcb
此时borderValue的值也就没什么用了。

结尾

这篇文章花了我好长时间啊,翻了很多资料和博客,力求做到详细正确。如果你看到了这里,麻烦给我点了赞且留言支持一下。如果你还有关于仿射变换的问题没有解决,麻烦发在评论里,我会回复帮你解决的。谢谢各位!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!