旋转变换原理
旋转变换原理
旋转是仿射变换的一种,通过变换矩阵实现图像绕指定中心旋转,保持直线和平行性不变。其数学表示为:
其中:
- ( c x , c y ) (c_x, c_y) (cx,cy) 是旋转中心。
- θ \theta θ 是旋转角度(逆时针为正)。
旋转矩阵的平移项用于补偿旋转中心的偏移。推导过程如下:
-
将旋转中心移到原点:
-
旋转:
-
移回原位置:
-
合并后的矩阵(取前两行):
import numpy as np
import cv2
import matplotlib.pyplot as plt
def 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) * scale
sin = np.sin(angle_rad) * scale
if center is None:
raise ValueError("必须指定旋转中心")
cx, cy = center
# 计算平移分量
tx = cx * (1 - cos) + cy * sin
ty = cy * (1 - cos) - cx * sin
return 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 + 1
y1 = 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()