Python图像处理【4】图像线性变换

0. 前言

图像线性变换是图像处理中的基本运算,通常用于调整图像的视觉效果,例如图像对比度、亮度、平移以及缩放等操作。我们可以用以下公式表示图像线性变换,对于输入图像 f ( x , y ) f(x,y) f(x,y),输出图像 g ( x , y ) g(x,y) g(x,y),线性变换表达式为:

g ( x , y ) = α f ( x , y ) + β g(x,y)=\alpha f(x,y)+\beta g(x,y)=αf(x,y)+β

在本节中,我们将学习 2D 线性变换的基本概念以及如何在图像中应用 2D 线性变换。我们首先从基础数学原理入手,以便在代码实现之前了解线性变化的核心思想,然后介绍如何在图像上应用欧几里得变换(例如,旋转、反射)和仿射转换。

1. 2D 线性几何变换数学原理

2D 线性几何变换是点转换,它们会被应用于图像中的每个像素。由于这些变换是线性的,因此可以使用矩阵乘法或加法等矩阵运算表示图像变换,有四类常见的线性变换:

  • 欧几里得变换 (euclidean transformation) 或等距映射 (isometry)
  • 相似性变换
  • 仿射变换
  • 单应性变换

最简单的变换称为欧几里得/等距转换,它保留了图像中像素间的欧几里得距离,且使用了相同的度量,属于正交变换,可以使用下式表示:

Ψ ( p ) = R p + t \Psi (p)= R p + t Ψ(p)=Rp+t
其中, R R R2×2 的正交矩阵, t t t2×1 的变换矢量, p = [ x , y ] p = [x,y] p=[x,y] 是要变换的点。欧几里得变换过程中距离、长度和面积是不变的,即等距变换后这些值保持不变。
等距映射具有三个自由度 (Degrees of Freedom, DoF),用于旋转矩阵 R R R 的角度 θ θ θ、用于沿 X X X 轴和 Y Y Y 轴方向的平移 t x t_x tx t y t_y ty,对应于变换矢量。欧几里得变换中的矩阵 R R R 是正交的,对于旋转变换而言矩阵行列式为 1,而对于反射变换而言行列式为 -1。在坐标系中,一个 2D 点表示为 [ x , y ] [x, y] [x,y],欧几里得变换的旋转和平移可以由坐标中的 3 × 3 3×3 3×3 矩阵 H H H 表示。
仅位置均匀缩放的等轴映射被称为相似性转换。它具有四个自由度,增加的自由度参数用于各向同性缩放。
仿射变换可以认为是非奇异线性变换,它具有六个自由度,增加的两个参数用于进行不均匀缩放。
投影变化是均匀坐标中的非奇异线性变换,它具有八个自由度。

四类线性变换

下图显示了 2D 线性几何变换的层次结构。如前所述,欧几里得是仿射的特殊情况,而后者又是投影变换的特殊情况:

线性几何变换的层次结构
最后,总结不同线性变换的属性如下:

线性变换的属性

2. 使用 scipy.ndimage 旋转图像

有时,我们可能需要几何变换作为图像处理任务中的预处理步骤。在本节中,我们将学习如何使用 scipy.ndimage 模块中的 rotate() 函数旋转图像。接下来,我们将逆时针旋转图像一定的角度,并使用样条插值。

(1) 从相应的 Python 库模块导入所需的函数,读取彩色输入图像:

from scipy.ndimage import rotate
from skimage.io import imread
from matplotlib import pyplot as plt
im = imread('1.png')

(2) 应用 scipy.ndimage 模块的 rotate() 函数,根据输入图像与旋转值执行旋转变换,逆时针旋转按照惯例应当以正角度表示,而顺时针则以负角度表示:

im = rotate(im, -45)
plt.figure(figsize=(5,5))
plt.imshow(im)
plt.axis('off') 
plt.show()

旋转图像

3. 使用 Numpy 翻转图像

在本节中,我们将学习如何使用 NumPy 的翻转操作来实现垂直 (flipped) 和水平 (flop) 翻转图像。

(1) 导入必需库,通过 Matplotlib.pyplot 模块读取输入图像,以获取图像 ndarray 作为输入:

import matplotlib.pyplot as plt
import numpy as np
im = plt.imread('1.png')

(2) 使用 Numpyflipud() 函数翻转图像 ndarray (垂直翻转)。

im_filpped = np.flipud(im)

(3) 绘制原始图像和翻转图像:

plt.figure(figsize=(10, 12))
plt.subplot(211), plt.imshow(im), plt.axis('off'), plt.title('original', size=10)
plt.subplot(212), plt.imshow(im_filpped), plt.axis('off'), plt.title('flipped', size=10) 
plt.show()

垂直翻转

(4) 使用函数 numpy.filphr() 水平翻转输入图像:

im = plt.imread('2.png')
im_filpped = np.fliplr(im)
plt.figure(figsize=(15, 12))
plt.subplot(121), plt.imshow(im), plt.axis('off'), plt.title('original', size=10)
plt.subplot(122), plt.imshow(im_filpped), plt.axis('off'), plt.title('flopped', size=10) 
# np.fliplr(im)
plt.show()

水平翻转

4. 使用 scipy.ndimage 实现仿射变换

仿射变换将每个像素 f ( x , y ) f(x,y) f(x,y) 从输入图像变换到输出图像中的位置 ( x ′ , y ′ ) = T ( x , y ) (x',y')= T(x,y) (x,y)=T(x,y)。如果变换的像素坐标位于输出图像中的两个像素之间,通常通过反射(扭曲,warping)解决。在本节中,我们将使用 Scipy 库的 ndimage 模块函数实现图像上的仿射变换。

4.1 仿射变换原理

对于输出图像中位置 ( x ′ , y ′ ) (x',y') (x,y) 处的每个像素,利用以下公式从输入图像中相应位置处获取像素值:

( x , y ) = T − 1 ( x ′ , y ′ ) (x,y)=T^{-1}(x',y') (x,y)=T1(x,y)

如果输入图像中的像素位于两像素之间,则使用来自相邻像素的插值(例如,使用双线性插值)像素值进行计算。下图显示了向扭曲和反扭曲的概念:

扭曲和反扭曲的概念
反向映射过程如下:

  • 创建输出图像
  • 对于输出图像中的每个像素,找到输入图像中的对应像素
  • 赋予输出像素对应的像素值

下图给出了每个仿射变换操作的矩阵 (M):

仿射变换矩阵

我们将通过将仿射转换矩阵传递给 affine_transfom() 函数函数来实现图像转换,affine_transfom() 函数的输入参数如下:

scipy.ndimage.affine_transform(input, matrix, offset=0.0, output_shape= None, output=None, order=3, mode='constant', cval=0.0, prefilter=True)

应用仿射变换。输出图像中的位置 o 处的像素值由输入图像中的位置为 np.dot(matrix, o) + offset 处的像素值确定,其中矩阵和偏移量分别是传递给 affine_transform() 函数的 matriixoffset 参数。

4.2 实现仿射变换

接下来,我们使用 Scipy 库的 ndimage 模块实现仿射变换。

(1) 首先,导入所需的所有 Python 库:

from skimage.io import imread
from scipy.ndimage import affine_transform
import numpy as np
import matplotlib.pylab as plt

(2) 读取图像并通过传递 3×3 变换矩阵和偏移量使用函数 affine_transform() 来执行变换。 在这里,我们沿 x 轴和 y 轴(使用 @ 运算符乘上相应的变换矩阵)将图像沿正(逆时针)方向旋转 45 度:

im = imread("1.png")
rot_mat = np.array([[np.cos(np.pi/4),np.sin(np.pi/4), 0],[-np.sin(np.pi/4),np.cos(np.pi/4), 0], [0,0,1]])
shr_mat = np.array([[1, 0.45, 0], [0, 0.75, 0], [0, 0, 1]])
transformed = affine_transform(im, rot_mat@shr_mat, offset=[-im.shape[0]/4+25, im.shape[1]/2-50, 0], output_shape=im.shape)

(3) 绘制输入和输出图像,运行代码生成的输出图像:

plt.figure(figsize=(20,10))
plt.subplot(121), plt.imshow(im), plt.axis('off'), plt.title('Input image', size=10)
plt.subplot(122), plt.imshow(transformed), plt.axis('off'), plt.title('Output image', size=10)
plt.show()

仿射变换结果

小结

图像线性变换是图像处理中的基本运算,在图像处理应用程序中用途广泛,例如增强图像对比度、调节图像亮度、平移图像以及缩放图像等操作,是常见的图像预处理技术。在本节中,我们学习了 2D 线性变换的基本概念以及如何在图像中应用 2D 线性变换,首先从基础数学原理入手,然后使用不同 Python 图像处理库实现了在图像上应用欧几里得变换(例如,旋转、反射)和仿射转换等线性变换。

系列链接

Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【5】图像扭曲与逆扭曲详解
Python图像处理【6】通过哈希查找重复和类似的图像

猜你喜欢

转载自blog.csdn.net/qq_30167691/article/details/128357890