-
问题:在二维空间中,假设你有一条线段,其一个端点固定在原点 (0, 0),另一个端点的初始位置为 (x1,y1) 。现在这条线段绕原点旋转了 θ 度(以弧度为单位),需要计算旋转后另一个端点的新位置 (x2,y2) 。
-
旋转后的坐标可以通过以下公式计算:
-
x 2 = x 1 c o s ( θ ) − y 1 s i n ( θ ) y 2 = x 1 s i n ( θ ) + y 1 c o n ( θ ) x_2=x_1cos(θ)−y_1sin(θ)\\ y_2=x_1sin(θ)+y_1con(θ) x2=x1cos(θ)−y1sin(θ)y2=x1sin(θ)+y1con(θ)
-
x1,y1 是初始位置的坐标。θ 是旋转的角度(以弧度为单位)。x2,y2 是旋转后的位置坐标。
-
import numpy as np from matplotlib import pyplot as plt import cv2 def rotate_point(x1, y1, theta): """ 旋转一个点 (x1, y1) 绕原点 (0, 0) 旋转 theta 弧度。 参数: x1, y1: 初始点的坐标。theta: 旋转角度(以弧度为单位)。 返回: x2, y2: 旋转后点的坐标。 """ # 将角度转换为弧度 theta_rad = np.radians(theta) x2 = x1 * np.cos(theta_rad) - y1 * np.sin(theta_rad) y2 = x1 * np.sin(theta_rad) + y1 * np.cos(theta_rad) # print("np.cos(theta_rad) , np.sin(theta_rad)",np.cos(theta_rad),np.sin(theta_rad)) # print("np.sin(theta_rad) , np.cos(theta_rad)",np.sin(theta_rad),np.cos(theta_rad)) return x2, y2 def rotate_points(points, theta): """ 批量旋转多个点,参数 points 是二维数组,每一行表示一个点的坐标。 参数 theta 是旋转角度(以弧度为单位)。 返回值是旋转后的坐标。 """ x1, y1 = points.T x2, y2 = rotate_point(x1, y1, theta) return np.vstack((x2, y2)).T def rotate_point_by_warpAffine(x1, y1, theta): """ 利用 OpenCV 的 warpAffine 函数实现旋转一个点。 参数: x1, y1: 初始点的坐标。theta: 旋转角度(以弧度为单位)。 返回: x2, y2: 旋转后点的坐标。 """ bg_width = 400 bg_height = 600 background = np.zeros((bg_height, bg_width, 3), dtype=np.uint8) background.fill(255) # 填充为白色 cv2.circle(background, (int(bg_width / 2), int(bg_height / 2)), 5, (0, 0, 0), -1) # 黑色圆点 cv2.putText(background, f'center: (0, 0)', (int(bg_width / 2), int(bg_height / 2)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) # 黑色坐标 normalize_factor = np.sqrt(x1*x1 + y1*y1) x1 = np.intp((x1/normalize_factor)* 200)+bg_width/2 y1 = np.intp((y1/normalize_factor)* 200)+bg_height/2 print("x1,y1",x1,y1) # 在背景上绘制初始点 cv2.circle(background, (int(x1), int(y1)), 5, (0, 0, 255), -1) # 红色点 cv2.putText(background, f'rotated', (int(x1), int(y1)), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) # 红色坐标 cv2.line(background, (0, int(bg_height/2)), (bg_width, int(bg_height/2)), (0, 0, 0), 1) cv2.line(background, (int(bg_width/2), 0), (int(bg_width/2), bg_height), (0, 0, 0), 1) M = cv2.getRotationMatrix2D((bg_width / 2, bg_height / 2), theta, 1) # 旋转中心, 旋转角度, 缩放比例 # 应用旋转矩阵 rotated_image = cv2.warpAffine(background, M, (bg_width, bg_height)) # opencv 与 numpy 坐标系不同,需要调整 # 在旋转后的图像上绘制旋转后的点 cv2.circle(rotated_image, (int(x1), int(y1)), 5, (0, 255, 0), -1) # 绿色点 cv2.putText(rotated_image, f'original: ({x1-bg_width/2}, {y1-bg_height/2})', (int(x1), int(y1)), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1) # 绿色坐标 # 显示图像 cv2.imshow('Rotated Point', rotated_image) cv2.waitKey(0) cv2.destroyAllWindows() def plot_rotation(x1, y1, x2, y2, theta_degrees): """ 绘制初始点和旋转后的点,并显示旋转角度。 参数:x1, y1: 初始点的坐标。x2, y2: 旋转后点的坐标。 theta_degrees: 旋转角度(以度为单位)。 """ fig, ax = plt.subplots() # 绘制初始点 ax.plot(x1, y1, 'ro', label='Initial Point') ax.text(x1, y1, f'({x1}, {y1})', fontsize=12, ha='right') # 绘制旋转后的点 ax.plot(x2, y2, 'bo', label='Rotated Point') ax.text(x2, y2, f'({x2:.2f}, {y2:.2f})', fontsize=12, ha='left') # 绘制连接线 ax.plot([0, x1], [0, y1], 'r--', label='Initial Line') ax.plot([0, x2], [0, y2], 'b--', label='Rotated Line') # 设置标题和标签 ax.set_title(f'Rotation by {theta_degrees} degrees') ax.set_xlabel('X-axis') ax.set_ylabel('Y-axis') # 添加图例 ax.legend() # 设置轴比例一致 ax.set_aspect('equal', adjustable='box') # 显示图形 plt.grid(True) plt.axhline(0, color='black', linewidth=0.5) plt.axvline(0, color='black', linewidth=0.5) plt.show() if __name__ == '__main__': # 示例 x1, y1 = 3, 4 # 初始点的坐标 theta_degrees = 45 # 旋转角度(以度为单位) # 计算旋转后的坐标 x2, y2 = rotate_point(x1, y1, theta_degrees) # 绘制图形 plot_rotation(x1, y1, x2, y2, theta_degrees) # 利用 OpenCV 的 warpAffine 函数实现旋转,通过旋转坐标系实现旋转 rotate_point_by_warpAffine(x1, y1, theta_degrees)
-
如果需要处理多个点的旋转,可以使用向量化操作来提高效率。例如,如果你有多个点,可以将它们存储在一个数组中,并一次性进行旋转计算。
-
-
cv2.warpAffine()
是 OpenCV 库中的一个函数,用于执行二维仿射变换。仿射变换是一种线性变换,可以包括平移、旋转、缩放和剪切操作。这个函数在图像处理中非常有用,可以用来对图像进行几何变换。仿射变换的原理仿射变换可以用以下公式表示:-
( x ′ y ′ ) = A ( x y ) + b \begin{pmatrix} x' \\ y' \end{pmatrix} = A \begin{pmatrix} x \\ y \end{pmatrix} + b (x′y′)=A(xy)+b
-
其中:( (x, y) ) 是原图像中的点。( (x’, y’) ) 是变换后图像中的点。( A ) 是一个 2x2 的矩阵,表示线性变换(旋转、缩放、剪切)。( b ) 是一个 2x1 的向量,表示平移。
-
-
具体来说,仿射变换可以写成:
-
( x ′ y ′ ) = ( a 11 a 12 a 21 a 22 ) ( x y ) + ( b 1 b 2 ) \begin{pmatrix} x' \\ y' \end{pmatrix} = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} + \begin{pmatrix} b_1 \\ b_2 \end{pmatrix} (x′y′)=(a11a21a12a22)(xy)+(b1b2)
-
在 OpenCV 中,仿射变换矩阵通常表示为一个 2x3 的矩阵:
-
M = ( a 11 a 12 b 1 a 21 a 22 b 2 ) M = \begin{pmatrix} a_{11} & a_{12} & b_1 \\ a_{21} & a_{22} & b_2 \end{pmatrix} M=(a11a21a12a22b1b2)
-
-
cv2.warpAffine()
函数解析,cv2.warpAffine()
函数的基本语法如下:-
cv2.warpAffine(src, M, dsize, dst=None, flags=cv2.INTER_LINEAR, borderMode=None, borderValue=None)
-
参数说明:
-
src
:输入图像。 -
M
:2x3 的仿射变换矩阵。 -
dsize
:输出图像的大小 (width, height)。 -
dst
:可选参数,输出图像。 -
flags
:插值方法,默认为cv2.INTER_LINEAR
(双线性插值)。其他选项包括cv2.INTER_NEAREST
(最近邻插值)、cv2.INTER_CUBIC
(三次样条插值)等。 -
borderMode
:边界填充模式,默认为cv2.BORDER_CONSTANT
。其他选项包括cv2.BORDER_REFLECT
、cv2.BORDER_WRAP
等。 -
borderValue
:当borderMode
为cv2.BORDER_CONSTANT
时使用的常数值。
-
-
下面是一些常见的仿射变换示例,包括平移、旋转和缩放。
import cv2 import numpy as np # 读取图像 image = cv2.imread("./image.jpg") # 定义平移矩阵 tx, ty = 50, 30 # 水平和垂直方向的平移量 M = np.float32([[1, 0, tx], [0, 1, ty]]) # 执行仿射变换 translated_image = cv2.warpAffine(image, M, (image.shape[1], image.shape[0])) # 显示结果 cv2.imshow("Translated Image", translated_image) cv2.waitKey(0) cv2.destroyAllWindows()
-
- 旋转
-
import cv2 import numpy as np # 读取图像 image = cv2.imread("./image.jpg") # 定义旋转角度 angle = 45 # 旋转角度 center = (image.shape[1] // 2, image.shape[0] // 2) # 旋转中心 scale = 1.0 # 缩放比例 # 计算旋转矩阵 M = cv2.getRotationMatrix2D(center, angle, scale) # 执行仿射变换 rotated_image = cv2.warpAffine(image, M, (image.shape[1], image.shape[0])) # 显示结果 cv2.imshow("Rotated Image", rotated_image) cv2.waitKey(0) cv2.destroyAllWindows()
-
- 缩放
-
import cv2 import numpy as np # 读取图像 image = cv2.imread("./image.jpg") # 定义缩放因子 scale_x, scale_y = 1.5, 1.5 # 水平和垂直方向的缩放因子 M = np.float32([[scale_x, 0, 0], [0, scale_y, 0]]) # 计算输出图像的大小 new_width = int(image.shape[1] * scale_x) new_height = int(image.shape[0] * scale_y) # 执行仿射变换 scaled_image = cv2.warpAffine(image, M, (new_width, new_height)) # 显示结果 cv2.imshow("Scaled Image", scaled_image) cv2.waitKey(0) cv2.destroyAllWindows()
-
-
仿射变换 包括平移、旋转、缩放和剪切操作。可以通过定义不同的变换矩阵来实现平移、旋转和缩放等操作。
-
仿射变换矩阵 是一个 2x3 的矩阵,表示线性变换和平移。
-
cv2.warpAffine()
函数用于执行仿射变换,需要输入图像、变换矩阵和输出图像的大小。 -
在更高维度的空间中进行旋转操作,可以使用旋转矩阵或四元数等方法。对于三维以上的空间,旋转通常涉及多个轴的组合。这里我们将介绍如何在高维空间中进行旋转,并提供相应的 Python 代码示例。高维空间中的旋转
-
在高维空间中,旋转可以通过多个低维旋转的组合来实现。具体来说,你可以通过将旋转分解为多个二维平面上的旋转来处理。例如,在四维空间中,你可以在 xy 平面、xz 平面、xw 平面等上分别进行旋转。
-
计算公式:假设我们有一个 n 维空间中的点 $ (\mathbf{p} = (p_1, p_2, \ldots, p_n))$,并且我们希望绕某个平面(例如 xy 平面)旋转角度 ( θ ) (\theta) (θ)。我们可以使用以下公式:
-
[ R i j ( θ ) = I + ( cos ( θ ) − 1 ) ( e i e i T + e j e j T ) + sin ( θ ) ( e i e j T − e j e i T ) ] [ R_{ij}(\theta) = I + (\cos(\theta) - 1)(\mathbf{e}_i \mathbf{e}_i^T + \mathbf{e}_j \mathbf{e}_j^T) + \sin(\theta)(\mathbf{e}_i \mathbf{e}_j^T - \mathbf{e}_j \mathbf{e}_i^T) ] [Rij(θ)=I+(cos(θ)−1)(eieiT+ejejT)+sin(θ)(eiejT−ejeiT)]
-
其中:(I) 是单位矩阵。
-
$(\mathbf{e}_i) 和 (\mathbf{e}_j) $是标准基向量。
-
$(\mathbf{e}_i \mathbf{e}_i^T) 和 (\mathbf{e}_j \mathbf{e}_j^T) $是外积。
-
( e i e j T − e j e i T ) (\mathbf{e}_i \mathbf{e}_j^T - \mathbf{e}_j \mathbf{e}_i^T) (eiejT−ejeiT)是叉积。
-
-
下面是一个示例代码,展示了如何在四维空间中绕不同的平面旋转一个点,并计算旋转后的新位置。
-
import numpy as np def rotation_matrix(n, i, j, theta): """生成绕指定平面旋转的旋转矩阵。 参数: n: 空间的维度。 i, j: 旋转平面的两个轴索引(0-based)。 theta: 旋转角度(以弧度为单位)。 返回: 旋转矩阵。 """ # 创建单位矩阵 R = np.eye(n) # 计算旋转矩阵 R[i, i] = np.cos(theta) R[j, j] = np.cos(theta) R[i, j] = -np.sin(theta) R[j, i] = np.sin(theta) return R def rotate_point(point, i, j, theta): """ 旋转一个点绕指定平面旋转。 参数: point: 初始点的坐标 (p1, p2, ..., pn)。 i, j: 旋转平面的两个轴索引(0-based)。 theta: 旋转角度(以弧度为单位)。 返回: 旋转后点的坐标 (p1', p2', ..., pn')。 """ # 将点转换为列向量 point_vector = np.array(point).reshape(-1, 1) # 生成旋转矩阵 R = rotation_matrix(len(point), i, j, theta) # 应用旋转矩阵 rotated_point_vector = np.dot(R, point_vector) # 将结果转换回行向量 rotated_point = rotated_point_vector.flatten() return rotated_point # 示例 point = (1, 2, 3, 4) # 初始点的坐标 i, j = 0, 1 # 旋转平面:xy 平面 theta_degrees = 45 # 旋转角度(以度为单位) # 将角度转换为弧度 theta_rad = np.radians(theta_degrees) # 计算旋转后的新位置 rotated_point = rotate_point(point, i, j, theta_rad) print(f"初始坐标: {point}") print(f"旋转平面: ({i}, {j})") print(f"旋转角度: {theta_degrees} 度") print(f"旋转后坐标: {rotated_point}")
-
rotation_matrix
函数根据指定的平面生成旋转矩阵。 -
rotate_point
函数使用旋转矩阵将点旋转到新的位置。 -
在示例中,我们定义了一个四维点
(1, 2, 3, 4)
,绕 xy 平面旋转 45 度,并计算旋转后的新位置。
-
-
复合旋转
-
如果你需要在多个平面上进行复合旋转,可以依次应用多个旋转矩阵。例如,在四维空间中,你可以先绕 xy 平面旋转,再绕 xz 平面旋转。
-
# 示例 point = (1, 2, 3, 4) # 初始点的坐标 theta_degrees = 45 # 旋转角度(以度为单位) theta_rad = np.radians(theta_degrees) # 绕 xy 平面旋转 rotated_point_xy = rotate_point(point, 0, 1, theta_rad) # 绕 xz 平面旋转 rotated_point_xz = rotate_point(rotated_point_xy, 0, 2, theta_rad) print(f"初始坐标: {point}") print(f"旋转角度: {theta_degrees} 度") print(f"绕 xy 平面旋转后的坐标: {rotated_point_xy}") print(f"再绕 xz 平面旋转后的坐标: {rotated_point_xz}")
-
-
旋转矩阵:在高维空间中,旋转可以通过多个低维旋转的组合来实现。每个旋转矩阵对应于一个二维平面的旋转。
-
Python 实现:通过定义旋转矩阵并应用这些矩阵,可以实现高维空间中的旋转操作。
-
复合旋转:如果需要在多个平面上进行旋转,可以依次应用多个旋转矩阵。这种方法可以扩展到任意维度的空间,并且可以灵活地处理复杂的旋转操作。
-
旋转注意点
- 都是逆时针,角度为正时。
- 角度单位 :cv2.getRotationMatrix2D 角度为度,三角函数计算偏移矩阵为弧度制
- 注意通道维度变换 PIL CV2 torch tensorflow
-
在使用如PIL (Pillow), OpenCV (cv2), PyTorch, 和 TensorFlow 这样的库处理图像时,理解通道维度的定义是非常重要的。这些库对于图像通道的默认处理方式有所不同,可以对各个工具如何处理图像通道的一般性说明:
-
PIL (Pillow),读取图像:PIL 默认以 RGB 模式读取图像,除非特别指定了其他模式(例如灰度模式)。因此,图像的数据形状通常是 (height, width, 3) 对于彩色图像。通道顺序:R, G, B。
-
OpenCV (cv2),读取图像:OpenCV 默认以 BGR 模式读取图像。这意味着图像数据形状同样是 (height, width, 3),但是颜色通道的顺序与 PIL 不同。通道顺序:B, G, R。如果需要将图像转换为RGB模式,可以使用 cv2.cvtColor(image, cv2.COLOR_BGR2RGB)。
-
PyTorch,处理图像:PyTorch 中通常期望图像数据格式为 [batch, channels, height, width]。因此,在使用 PyTorch 处理图像之前,可能需要调整图像的维度顺序。示例:如果原始图像是 (height, width, 3) 形状的 numpy 数组,则可以通过 image.transpose((2, 0, 1)) 转换为 (3, height, width) 的形式,然后作为张量输入到模型中。通道顺序:当从numpy数组创建张量时,应确保通道顺序符合预期,通常为 R, G, B。
-
import torch import numpy as np from PIL import Image # 读取图像 image = Image.open("image.jpg") image_np = np.array(image) # 转换为 PyTorch 张量,并调整维度顺序 image_tensor = torch.from_numpy(image_np).permute(2, 0, 1).float() / 255.0 print("Image shape (PyTorch):", image_tensor.shape) # 输出: (3, height, width) # 如果需要批量处理 batch_image_tensor = image_tensor.unsqueeze(0) # 增加 batch 维度 print("Batch image shape (PyTorch):", batch_image_tensor.shape) # 输出: (1, 3, height, width)
-
-
TensorFlow / Keras,处理图像:TensorFlow 或 Keras 一般采用 [batch, height, width, channels] 的格式。这与 PyTorch 的默认格式相反。示例:直接加载的图像已经是 (height, width, 3) 形状,可以直接使用。通道顺序:R, G, B。如果是通过 OpenCV 加载的图像,则需要先转换为 RGB 模式。
-
import tensorflow as tf import numpy as np from PIL import Image # 读取图像 image = Image.open("image.jpg") image_np = np.array(image) # 转换为 TensorFlow 张量 image_tensor = tf.convert_to_tensor(image_np, dtype=tf.float32) / 255.0 print("Image shape (TensorFlow):", image_tensor.shape) # 输出: (height, width, 3) # 如果需要批量处理 batch_image_tensor = tf.expand_dims(image_tensor, axis=0) # 增加 batch 维度 print("Batch image shape (TensorFlow):", batch_image_tensor.shape) # 输出: (1, height, width, 3)
-
-
Scikit-image: 使用类似于 PIL 的 RGB 顺序,并且图像通常表示为 (height, width, 3)。
-
ImageMagick: 可以灵活地处理多种颜色空间,但默认情况下也倾向于使用 RGB 顺序。
-
Dlib: 主要用于人脸检测等任务,对图像的颜色空间支持较为灵活,但同样最常见的是 RGB 顺序。
-
-
如果想从 OpenCV 读取图像并希望将其转换为 PyTorch 或 TensorFlow 格式,可以按照以下步骤进行:
-
import cv2 import torch import numpy as np # 读取图像 image = cv2.imread("image.png") print("Image shape (OpenCV):", image.shape) # 输出: (height, width, 3) # 将 BGR 转换为 RGB image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 转换为 PyTorch 张量,并调整维度顺序 image_tensor = torch.from_numpy(image_rgb).permute(2, 0, 1).float() / 255.0 print("Image shape (OpenCV to PyTorch):", image_tensor.shape) # 输出: (3, height, width) # 如果需要批量处理 batch_image_tensor = image_tensor.unsqueeze(0) # 增加 batch 维度 print("Batch image shape (OpenCV to PyTorch):", batch_image_tensor.shape) # 输出: (1, 3, height, width)
-
从 OpenCV 到 TensorFlow
-
import cv2 import tensorflow as tf import numpy as np # 读取图像 image = cv2.imread("image.jpg") # 将 BGR 转换为 RGB image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 转换为 TensorFlow 张量 image_tensor = tf.convert_to_tensor(image_rgb, dtype=tf.float32) / 255.0 print("Image shape (OpenCV to TensorFlow):", image_tensor.shape) # 输出: (height, width, 3) # 如果需要批量处理 batch_image_tensor = tf.expand_dims(image_tensor, axis=0) # 增加 batch 维度 print("Batch image shape (OpenCV to TensorFlow):", batch_image_tensor.shape) # 输出: (1, height, width, 3)
-