Bootstrap

构建旋转变换矩阵对二维到高维空间的线段点进行旋转

  • 问题:在二维空间中,假设你有一条线段,其一个端点固定在原点 (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 (xy)=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} (xy)=(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_REFLECTcv2.BORDER_WRAP 等。

    • borderValue:当 borderModecv2.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()
    
      1. 旋转
    • 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()
      
      1. 缩放
    • 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(θ)(eiejTejeiT)]

    • 其中:(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) (eiejTejeiT)是叉积。

  • 下面是一个示例代码,展示了如何在四维空间中绕不同的平面旋转一个点,并计算旋转后的新位置。

    • 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)
      

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;