旋转变换原理
旋转是仿射变换的一种,通过变换矩阵实现图像绕指定中心旋转,保持直线和平行性不变。其数学表示为:
其中:
- ( c x , c y ) (c_x, c_y) (cx,cy) 是旋转中心。
- θ \theta θ 是旋转角度(逆时针为正)。
旋转矩阵的平移项用于补偿旋转中心的偏移。推导过程如下:
-
将旋转中心移到原点:
-
旋转:
-
移回原位置:
-
合并后的矩阵(取前两行):
import numpy as np
import cv2
import matplotlib.pyplot as pltdef get_rotation_matrix(angle, center=None, scale=1.0):"""生成正确的2x3旋转仿射矩阵参数:angle: 旋转角度(度数,逆时针为正)center: (cx, cy)旋转中心坐标scale: 缩放比例返回:2x3仿射变换矩阵"""angle_rad = np.deg2rad(angle)cos = np.cos(angle_rad) * scalesin = np.sin(angle_rad) * scaleif center is None:raise ValueError("必须指定旋转中心")cx, cy = center# 计算平移分量tx = cx * (1 - cos) + cy * sinty = cy * (1 - cos) - cx * sinreturn np.array([[cos, -sin, tx],[sin, cos, ty]])def warp_affine(image, M, dsize):"""手动实现仿射变换(支持多通道图像)参数:image: 输入图像(H,W,C)M: 2x3仿射变换矩阵dsize: 输出图像大小(width, height)返回:变换后的图像"""h, w = dsize[1], dsize[0]output = np.zeros((h, w, image.shape[2]), dtype=image.dtype)# 构造逆变换矩阵M_inv = np.vstack([M, [0, 0, 1]])M_inv = np.linalg.inv(M_inv)[:2]# 生成目标图像网格坐标y, x = np.indices((h, w))coords = np.stack([x, y, np.ones_like(x)], axis=-1) # (H,W,3)# 计算原图坐标src_coords = np.dot(coords, M_inv.T) # (H,W,2)# 双线性插值x_src = src_coords[:, :, 0]y_src = src_coords[:, :, 1]# 计算四个邻近点坐标x0 = np.floor(x_src).astype(int)y0 = np.floor(y_src).astype(int)x1 = x0 + 1y1 = y0 + 1# 处理边界x0 = np.clip(x0, 0, image.shape[1]-1)y0 = np.clip(y0, 0, image.shape[0]-1)x1 = np.clip(x1, 0, image.shape[1]-1)y1 = np.clip(y1, 0, image.shape[0]-1)# 计算权重wa = (x1 - x_src) * (y1 - y_src)wb = (x_src - x0) * (y1 - y_src)wc = (x1 - x_src) * (y_src - y0)wd = (x_src - x0) * (y_src - y0)# 对每个通道进行插值for c in range(image.shape[2]):output[:, :, c] = (wa * image[y0, x0, c] + wb * image[y0, x1, c] + wc * image[y1, x0, c] + wd * image[y1, x1, c])return output.astype(np.uint8)# 测试代码
if __name__ == "__main__":# 读取图像img = cv2.imread('example.jpg')img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 设置旋转参数angle = 45 # 旋转45度h, w = img.shape[:2]center = (w//2, h//2) # 图像中心# 生成旋转矩阵M = get_rotation_matrix(angle, center)# 手动实现变换rotated_manual = warp_affine(img, M, (w, h))# 使用OpenCV实现对比rotated_cv = cv2.warpAffine(img, M, (w, h))# 显示结果plt.figure(figsize=(12, 6))plt.subplot(131), plt.imshow(img), plt.title('Original')plt.subplot(132), plt.imshow(rotated_manual), plt.title('Manual Rotation')plt.subplot(133), plt.imshow(rotated_cv), plt.title('OpenCV Rotation')plt.show()