2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 深度学习数据增强方法 利用仿射变换实现图像进行各种操作如平移 缩放 旋转 翻转

深度学习数据增强方法 利用仿射变换实现图像进行各种操作如平移 缩放 旋转 翻转

时间:2023-06-30 22:39:59

相关推荐

深度学习数据增强方法 利用仿射变换实现图像进行各种操作如平移 缩放 旋转 翻转

导读

在深度学习的数据增强中,我们经常需要对图像进行各种增强操作如平移、缩放、旋转、翻转等,这些其实都是图像的仿射变换。通过本篇文章,你能够知道它们的实现原理以及如何应用它们。本文讲述如何通过仿射变换来实现数据增强。

仿射变换

简单来说,“仿射变换”就是:“线性变换”+“平移”。

先看什么是线性变换?

变换前是直线的,变换后依然是直线直线比例保持不变变换前是原点的,变换后依然是原点

仿射变换从几何直观只有两个要点:

变换前是直线的,变换后依然是直线直线比例保持不变

少了原点保持不变这一条,比如平移

线性变换和仿射变换都是是通过矩阵乘法来实现的。

假设有一个向量空间k:

k = ( x , y ) k=(x,y) k=(x,y)

还有一个向量空间j:

j = ( x ′ , y ′ ) j=(x',y' ) j=(x′,y′)

如果我们想要将向量空间由k变为j,可以通过下面的公式进行变换

j = k ∗ w + b j=k∗w+b j=k∗w+b

将上式进行拆分可得

x ′ = w 00 ∗ x + w 01 ∗ y + b 0 y ′ = w 10 ∗ x + w 11 ∗ y + b 1 x'=w_{00}*x+w_{01}*y+b_0\\ y'=w_{10}*x+w_{11}*y+b_1 x′=w00​∗x+w01​∗y+b0​y′=w10​∗x+w11​∗y+b1​

我们再将上式转换为矩阵的乘法

[ x ′ y ′ ] = [ w 00 w 01 b 0 w 10 w 11 b 1 ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} w_{00}&w_{01}&b_0\\w_{10}&w_{11}&b_1 \end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[w00​w10​​w01​w11​​b0​b1​​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​

通过参数矩阵M 就可以实现两个向量空间之间的转换,在进行仿射变换的时候我们也只需要一个矩阵M就可以实现平移、缩放、旋转和翻转变换。

接下来,会先介绍原理然后利用OpenCV来实现相应的例子,这里主要利用OpenCV的warpAffine函数来实现仿射变换

warpAffine函数参数:

src:输入的图像数组

M:仿射变换矩阵

dsize:变换后图像的大小

flags:使用的插值算法

borderValue:边界的填充值

图像平移

在平面坐标系有点P ( x , y )和点P ′ ( x ′ , y ′ )

如果我们想要将P点移动到P ′

通过下面的变换就可以实现

x ′ = x + Δ x y ′ = y + Δ y x'=x+\Delta x \\ y' = y + \Delta y x′=x+Δxy′=y+Δy

其中 Δ x \Delta x Δx 和 Δ y \Delta y Δy就是x方向上和y方向上的偏移量,我们将其转换为矩阵的形式

[ x ′ y ′ ] = [ 1 0 Δ x 0 1 Δ y ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} 1&0&\Delta x\\0&1&\Delta y\end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[10​01​ΔxΔy​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​

上面的矩阵M就是仿射变换的平移参数,接下来我们利用OpenCV中的warpAffine函数来实现

# --*--coding: utf-8 --*...import cv2import numpy as npimport matplotlib.pyplot as pltdef show_compare_img(original_img,transform_img):_,axes = plt.subplots(1,2)#显示图像axes[0].imshow(original_img)axes[1].imshow(transform_img)#设置子标题axes[0].set_title("original image")axes[1].set_title("warpAffine transform image")plt.show()def translation_img():# 定义一个图像平移矩阵# x向左平移(负数向左,正数向右)200个像素# y向下平移(负数向上,正数向下)500个像素M = np.array([[1, 0, -200], [0, 1, 500]], dtype=np.float)# 读取需要平移的图像img = cv2.imread("test.jpg")# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 定义平移后图像的大小,保持和原图大小一致dsize = img.shape[:2][::-1]# 便于大家观察这里采用绿色来填充边界translation_img = cv2.warpAffine(img, M, dsize, borderValue=(0, 255, 0))# 显示图像show_compare_img(img, translation_img)translation_img()

图像翻转

有时候我们我们需要对图像进行水平翻转、垂直翻转、镜像翻转(同时进行水平和垂直翻转),想要实现这个功能并不难,我们可以通过opencv内置的flip方法很容易实现,还可以通过numpy的索引来实现,这里我们主要介绍通过仿射变换矩阵来实现这个功能

上图中的A 、 B 、 C 、 D 表示图像的四个顶点,如果我们需要对图像进行水平翻转,那么我们就需要将A点和B 点进行交换,C 点和D点进行交换,沿着x轴的中线进行对称交换位置,通过下面的式子可以实现水平翻转

x ′ = − x + w x′=−x+w x′=−x+w

上式中的w ww表示图像的宽,同理可得垂直翻转的实现公式

y ′ = − y + h y ′=−y+h y′=−y+h

上式中的h表示的是图像的高

变换矩阵翻转图像

图像翻转的变换矩阵

水 平 翻 转 的 变 换 矩 阵 : M = [ − 1 0 w 0 1 0 ] 水平翻转的变换矩阵: M=\left[ \begin{matrix} -1&0&w\\0&1&0 \end{matrix} \right] 水平翻转的变换矩阵:M=[−10​01​w0​]

垂 直 翻 转 的 变 换 矩 阵 : M = [ 1 0 0 0 − 1 h ] 垂直翻转的变换矩阵: M=\left[ \begin{matrix} 1&0&0\\0&-1&h \end{matrix} \right] 垂直翻转的变换矩阵:M=[10​0−1​0h​]

镜 像 的 变 换 矩 阵 : M = [ − 1 0 w 0 − 1 h ] 镜像的变换矩阵: M=\left[ \begin{matrix} -1&0&w\\0&-1&h \end{matrix} \right] 镜像的变换矩阵:M=[−10​0−1​wh​]

def flip_img(horizontal_flip,vertical_flip,img):#获取输入图片的宽和高height,width = img.shape[:2]#初始化变换矩阵M = np.array([[0, 0, 0], [0, 0, 0]], dtype=np.float)#水平翻转if horizontal_flip:M[0] = [-1,0,width]#垂直翻转if vertical_flip:M[1] = [0,-1,height]# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 定义缩放后图片的大小img_flip = cv2.warpAffine(img, M, (width,height))# 显示图像show_compare_img(img, img_flip)img = cv2.imread("test.jpg")flip_img(True,True,img)

OpenCV的flip函数翻转图像

flip函数参数:

src:输入的图像数组flipCode:图像翻转参数,1表示水平翻转,0表示垂直翻转,-1表示镜像翻转

img = cv2.imread("test.jpg")#水平翻转horizontal_flip_img = cv2.flip(img,1)#垂直翻转vertical_flip_img = cv2.flip(img,0)#镜像翻转mirror_flip_img = cv2.flip(img,-1)

numpy的索引翻转图像

img = cv2.imread("test.jpg")#水平翻转horizontal_flip_img = img[:,::-1]#垂直翻转vertical_flip_img = img[::-1]#镜像翻转mirror_flip_img = img[::-1,::-1]

图像缩放

如果我们想要对坐标系的P PP点进行缩放操作,通过下面的公式就可以实现

x ′ = f x ∗ x y ′ = f y ∗ y x′=f_x*x\\ y'=f_y*y x′=fx​∗xy′=fy​∗y

通过,在x xx和y yy前面添加一个缩放系数即可,同样我们将其转换为矩阵形式

[ x ′ y ′ ] = [ f x 0 0 0 f y 0 ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} f_x&0&0\\0&f_y&0\end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[fx​0​0fy​​00​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​

通过上面的矩阵M 我们就可以实现对图片的缩放

def scale_img():#定义宽缩放的倍数fx = 0.5#定义高缩放的倍数fy = 2#定义一个图像缩放矩阵M = np.array([[fx,0,0],[0,fy,0]],dtype=np.float)#读取图像img = cv2.imread("test.jpg")#获取图片的宽和高height,width = img.shape[:2]#将图片由BGR转为RGBimg = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#定义缩放后图片的大小scale_img = cv2.warpAffine(img,M,(int(width*fx),int(height*fy)))#显示图像show_compare_img(img,scale_img)scale_img()

这里使用仿射变换实现的图像缩放其实和resize函数的效果是一样的

图像旋转

围绕原点旋转

我们先来看看一个二维平面上的点在围绕原点是如何旋转的

在这里插入图片描述 上图中点v在围绕原点旋转 θ \theta θ度之后得到了点v ′ ,我们将坐标点用极坐标的形式来表示可以得到 v ( r c o s ϕ , r s i n ϕ ) v(rcos\phi,rsin\phi) v(rcosϕ,rsinϕ),所以 v ′ ( r c o s ( θ + ϕ ) , r s i n ( θ + ϕ ) ) v'(rcos(\theta+\phi),rsin(\theta+\phi)) v′(rcos(θ+ϕ),rsin(θ+ϕ))利用正弦和余弦将其展开可得

对 于 v 点 来 说 : x = r c o s ϕ y = r s i n ϕ 对 于 v ′ 来 说 : x ′ = r c o s ( θ + ϕ ) = r c o s θ ∗ c o s ϕ − r s i n θ ∗ s i n ϕ y ′ = r s i n ( θ + ϕ ) = r s i n θ ∗ c o s ϕ + r c o s θ ∗ s i n ϕ 然 后 将 x 和 用 代 入 上 式 得 : x ′ = x ∗ c o s ϕ − y ∗ s i n ϕ y ′ = y ∗ c o s ϕ + x ∗ s i n ϕ \begin{aligned} 对于v点来说&:\\ &x = rcos\phi \\ &y=rsin\phi \\ 对于v′ 来说&:\\ &x'=rcos(\theta+\phi)=rcos\theta*cos\phi-rsin\theta*sin\phi\\ &y'=rsin(\theta+\phi)=rsin\theta*cos\phi+rcos\theta*sin\phi\\ 然后将x和用代入上式得&:\\ &x'=x*cos\phi-y*sin\phi\\ &y'=y*cos\phi+x*sin\phi\\ \end{aligned} 对于v点来说对于v′来说然后将x和用代入上式得​:x=rcosϕy=rsinϕ:x′=rcos(θ+ϕ)=rcosθ∗cosϕ−rsinθ∗sinϕy′=rsin(θ+ϕ)=rsinθ∗cosϕ+rcosθ∗sinϕ:x′=x∗cosϕ−y∗sinϕy′=y∗cosϕ+x∗sinϕ​

然后再将上式用矩阵M表示,可得

[ x ′ y ′ ] = [ c o s ϕ − s i n ϕ 0 s i n ϕ c o s ϕ 0 ] [ x y 1 ] = M [ x y 1 ] \left[ \begin{matrix} x′\\y′ \end{matrix} \right] = \left[ \begin{matrix} cos\phi&-sin\phi&0\\sin\phi&cos\phi&0\end{matrix} \right] \left[ \begin{matrix} x\\y\\1 \end{matrix} \right] = M\left[ \begin{matrix} x\\y\\1 \end{matrix} \right] [x′y′​]=[cosϕsinϕ​−sinϕcosϕ​00​]⎣⎡​xy1​⎦⎤​=M⎣⎡​xy1​⎦⎤​

特别注意:我们在建立直角坐标系的时候是以左下角为原点建立的,然而对于图像而言是以左上角为原点建立的,所以我们需要对角度 θ \theta θ进行取反,结合三角函数的特性,M 矩阵的表达式如下

M = [ c o s ϕ − s i n ϕ 0 s i n ϕ c o s ϕ 0 ] M= \left[ \begin{matrix} cos\phi&-sin\phi&0\\sin\phi&cos\phi&0\end{matrix} \right] M=[cosϕsinϕ​−sinϕcosϕ​00​]

还需要注意的是这里的角度都是弧度制,所以我们还需要对其进行转换,转换代码如下

#将角度转换为弧度制radian_theta = theta/180 * np.pi

将图片围绕原点进行逆时针旋转θ \thetaθ度的代码如下

def rotate_img_original(theta):#将角度转换为弧度制radian_theta = theta/180 * np.pi#定义围绕原点旋转的变换矩阵M = np.array([[np.cos(radian_theta),np.sin(radian_theta),0],[-np.sin(radian_theta),np.cos(radian_theta),0]])# 读取图像img = cv2.imread("test.jpg")#定义旋转后图片的宽和高height,width = img.shape[:2]# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#围绕原点逆时针旋转\theta度rotate_img = cv2.warpAffine(img,M,(width,height))#显示图像show_compare_img(img,rotate_img)rotate_img_original(45)

细心的同学也许已经发现了,上图中围绕图像中心旋转后的图片部分被裁剪掉了,如果我们想让旋转之后的图片仍然是完整,应该如何修改呢?

def rotate_img_point(point_x,point_y,theta,img,is_completed=False):#将角度转换为弧度制radian_theta = theta / 180 * np.pi#定义围绕任意点旋转的变换矩阵M = np.array([[np.cos(radian_theta), np.sin(radian_theta),(1-np.cos(radian_theta))*point_x-point_y*np.sin(radian_theta)],[-np.sin(radian_theta), np.cos(radian_theta),(1-np.cos(radian_theta))*point_y+point_x*np.sin(radian_theta)]])# 将图片由BGR转为RGBimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 定义旋转后图片的宽和高height, width = img.shape[:2]#判断旋转之后的图片是否需要保持完整if is_completed:#增大旋转之后图片的宽和高,防止被裁剪掉new_height = height * np.cos(radian_theta) + width * np.sin(radian_theta)new_width = height * np.sin(radian_theta) + width * np.cos(radian_theta)#增大变换矩阵的平移参数M[0, 2] += (new_width - width) * 0.5M[1, 2] += (new_height - height) * 0.5height = int(np.round(new_height))width = int(np.round(new_width))# 围绕原点逆时针旋转\theta度rotate_img = cv2.warpAffine(img, M, (width, height))# 显示图像show_compare_img(img, rotate_img)img = cv2.imread("test.jpg")height,width = img.shape[:2]#定义围绕图片的中心旋转point_x,point_y = int(width/2),int(height/2)rotate_img_point(point_x,point_y,45,img,True)

参考资料

/question/20666664

https://xiulian./article/details/103845581

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。