除了改变图像大小(缩放)之外,我们也常需要在图像帧内调整其方向或位置。实现此目的的两种基本几何变换是旋转和平移。这些操作改变像素的空间关系,而不一定改变它们的强度值(尽管插值可能会略微改变)。它们对于图像对齐、校正相机倾斜或为非旋转不变的识别算法准备数据等任务非常重要。和缩放一样,旋转和平移都属于仿射变换。这意味着它们保持点、直线和平面不变。经过仿射变换后,平行线依然平行。我们通常在OpenCV等库中使用单个函数来执行这些操作,通常是通过定义一个变换矩阵。旋转图像旋转是指图像绕着一个称为旋转中心的固定点,按一定角度进行转动。旋转中心:此点定义了图像旋转的枢轴。通常是图像的中心,但您可以选择任意点 (x, y)。角度:指定图像旋转的量,通常以度为单位。正角度通常对应逆时针旋转,而负角度表示顺时针旋转(尽管库约定可能有所不同,因此请务必查阅文档)。缩放(可选):有时旋转函数也允许同时进行均匀缩放(放大或缩小)。缩放因子为 1.0 意味着不缩放。当您旋转图像时,特别是旋转角度不是90度倍数时,原始的矩形像素网格会被映射到新的位置。这会引出两个需要考虑的问题:新像素位置:原始像素 $(x, y)$ 的强度值在旋转后会到哪里?这可以通过三角学计算,通常封装在旋转矩阵中。对于绕原点 (0,0) 逆时针旋转角度 $\theta$,新坐标 $(x', y')$ 与旧坐标 $(x, y)$ 的关系如下: $$ \begin{pmatrix} x' \ y' \end{pmatrix} = \begin{pmatrix} \cos \theta & -\sin \theta \ \sin \theta & \cos \theta \end{pmatrix} \begin{pmatrix} x \ y \end{pmatrix} $$ 当绕着不同的中心旋转时,平移操作会与此旋转结合。空白区域与插值:在图像帧内旋转矩形图像,通常会留下一些空白区域,这些区域没有原始像素直接映射。此外,计算出的新坐标 $(x', y')$ 可能会落在输出网格的像素位置之间。我们需要一种方法来确定输出图像中整数网格位置的像素值。这个过程称为插值。常见的方法包括:最近邻插值:分配最接近的原始像素的值。速度快,但可能产生块状结果。双线性插值:根据四个最近的原始像素的加权平均值计算值。结果更平滑,通常更受欢迎。OpenCV等库会处理这些计算。您通常提供图像、旋转中心、角度和可选的缩放因子。库会计算所需的变换矩阵并应用它,使用您通常可以指定的插值方法。# 使用OpenCV的例子(细节取决于库版本) import cv2 import numpy as np # 假设图像已加载(例如,使用cv2.imread) rows, cols = image.shape[:2] # 旋转中心:图像中心 center = (cols / 2, rows / 2) # 角度:逆时针45度 angle = 45 # 缩放:1.0(不缩放) scale = 1.0 # 计算旋转矩阵 M_rotate = cv2.getRotationMatrix2D(center, angle, scale) # 使用warpAffine应用旋转 # 第三个参数是输出图像大小(宽度,高度) rotated_image = cv2.warpAffine(image, M_rotate, (cols, rows)) # 'rotated_image' 现在是旋转后的版本平移图像平移更简单:它涉及图像在水平、垂直或两个方向上的移动。想象一下在屏幕上滑动图像,而不旋转或调整大小。位移参数:您需要指定x方向 ($t_x$) 和y方向 ($t_y$) 的位移量。正的 $t_x$ 将图像向右移动。正的 $t_y$ 将图像向下移动。负值则分别向左和向上移动。变换是直观的:原始位置在 $(x, y)$ 的每个像素都会移动到新位置 $(x', y')$,其中: $$ x' = x + t_x \ y' = y + t_y $$这可以用平移矩阵表示。为了配合OpenCV的 warpAffine 等函数(它需要一个用于仿射变换的2x3矩阵),平移矩阵如下所示:$$ M_{translate} = \begin{pmatrix} 1 & 0 & t_x \ 0 & 1 & t_y \end{pmatrix} $$在这里,$t_x$ 和 $t_y$ 直接表示水平和垂直位移。像旋转一样,平移也可能使图像部分移出可见帧,并产生需要填充的空白区域(通常用黑色或某种其他指定的背景颜色)。# 使用OpenCV的例子 import cv2 import numpy as np # 假设图像已加载 rows, cols = image.shape[:2] # 位移量:向右50像素,向下20像素 tx = 50 ty = 20 # 定义平移矩阵 M_translate = np.float32([[1, 0, tx], [0, 1, ty]]) # 使用warpAffine应用平移 # 第三个参数是输出图像大小(宽度,高度) translated_image = cv2.warpAffine(image, M_translate, (cols, rows)) # 'translated_image' 现在是平移后的版本结合变换与 warpAffine正如示例所示,OpenCV的 cv2.warpAffine 等函数功能多样。它们接受一个2x3的变换矩阵 M,并将相应的仿射变换(可以是旋转、平移、缩放、剪切或它们的组合)应用于输入图像。2x3矩阵 M 的一般形式是: $$ M = \begin{pmatrix} \alpha & \beta & (1-\alpha) c_x - \beta c_y \ -\beta & \alpha & \beta c_x + (1-\alpha) c_y \end{pmatrix} $$ 其中变换结合了 $\alpha$ 的缩放,$\theta$ 的旋转(其中 $\alpha = scale \cdot \cos\theta$, $\beta = scale \cdot \sin\theta$),以及相对于中心 $(c_x, c_y)$ 的平移。然而,您通常不会手动构建此矩阵。相反,您会使用 cv2.getRotationMatrix2D 等辅助函数进行旋转(它包含中心和比例),或像上面所示直接定义简单的平移矩阵。然后 warpAffine 函数应用矩阵 M 定义的变换,将像素从源图像映射到目标图像,并在必要时使用插值。掌握旋转和平移对于图像几何处理非常重要。这些操作是图像对齐、机器学习中的数据增强(创建略微修改的训练图像)以及校正透视失真的构建模块。在本章后面的实践部分,您将亲自应用这些变换。