Rotation of a 2D array over an angle using rotation matrix

Learn rotation of a 2d array over an angle using rotation matrix with practical examples, diagrams, and best practices. Covers python, arrays, matrix development techniques with visual explanations.

Rotate a 2D Array by an Angle Using Rotation Matrices in Python

Hero image for Rotation of a 2D array over an angle using rotation matrix

Learn how to apply 2D rotation to array elements using fundamental linear algebra concepts and Python's NumPy library. This guide covers the mathematical principles of rotation matrices and provides practical code examples.

Rotating a 2D array, or more precisely, rotating the coordinates that define the positions of elements within a 2D space, is a common task in computer graphics, image processing, and scientific simulations. This article will guide you through the process of rotating a 2D array by a specified angle using rotation matrices, a core concept in linear algebra. We'll focus on Python, leveraging the powerful NumPy library for efficient matrix operations.

Understanding 2D Rotation

In a 2D Cartesian coordinate system, a point (x, y) can be rotated around the origin (0, 0) by an angle θ (theta) to a new point (x', y'). The mathematical transformation for this rotation is defined by a 2x2 rotation matrix. The angle θ is typically measured counter-clockwise from the positive x-axis.

graph TD
    A[Original Point (x, y)] --> B{Rotation Matrix Application}
    B --> C[New Point (x', y')]
    subgraph Rotation Matrix
        D["cos(θ)  -sin(θ)"]
        E["sin(θ)   cos(θ)"]
    end
    B -- "Multiplies by" --> D
    B -- "Multiplies by" --> E

Conceptual flow of 2D point rotation using a rotation matrix.

The rotation matrix R for an angle θ is given by:

R = [[cos(θ), -sin(θ)],
     [sin(θ),  cos(θ)]]

To find the new coordinates (x', y'), you multiply the original coordinate vector [x, y] by this rotation matrix:

[x'] = [[cos(θ), -sin(θ)]] * [x]
[y']   [[sin(θ),  cos(θ)]]   [y]

This expands to:

x' = x * cos(θ) - y * sin(θ)
y' = x * sin(θ) + y * cos(θ)

When rotating an entire array, we apply this transformation to each element's coordinates. If the rotation is not around the origin, we first translate the points so the rotation center becomes the origin, perform the rotation, and then translate them back.

Implementing Rotation in Python with NumPy

NumPy provides excellent tools for array manipulation and linear algebra, making it ideal for this task. We'll define a function that takes a 2D array (representing an image or a grid of values), an angle, and an optional center of rotation.

import numpy as np
import math

def rotate_2d_array(arr, angle_degrees, center=None):
    """
    Rotates a 2D array (or its coordinates) by a given angle around a specified center.

    Args:
        arr (np.ndarray): The input 2D array.
        angle_degrees (float): The rotation angle in degrees (counter-clockwise).
        center (tuple, optional): The (x, y) coordinates of the rotation center.
                                  If None, rotates around the array's center.

    Returns:
        np.ndarray: A new array with the rotated coordinates. The values at these
                    coordinates would typically be interpolated from the original array.
    """
    height, width = arr.shape

    # Convert angle to radians
    angle_rad = math.radians(angle_degrees)

    # Define the rotation matrix
    cos_theta = np.cos(angle_rad)
    sin_theta = np.sin(angle_rad)
    rotation_matrix = np.array([
        [cos_theta, -sin_theta],
        [sin_theta,  cos_theta]
    ])

    # Determine the center of rotation
    if center is None:
        center_x, center_y = width / 2 - 0.5, height / 2 - 0.5
    else:
        center_x, center_y = center

    # Create a grid of coordinates for the array
    y_coords, x_coords = np.indices(arr.shape)
    original_coords = np.vstack([x_coords.ravel(), y_coords.ravel()])

    # Translate coordinates so the center of rotation is the origin
    translated_coords = original_coords - np.array([[center_x], [center_y]])

    # Apply rotation
    rotated_translated_coords = np.dot(rotation_matrix, translated_coords)

    # Translate coordinates back
    rotated_coords = rotated_translated_coords + np.array([[center_x], [center_y]])

    # Reshape back to original array dimensions for x and y coordinates
    rotated_x = rotated_coords[0].reshape(arr.shape)
    rotated_y = rotated_coords[1].reshape(arr.shape)

    # Note: This function returns the *new coordinates* for each original pixel.
    # To get a rotated array with values, you would typically use interpolation
    # (e.g., scipy.ndimage.rotate or cv2.warpAffine for images).
    # For simplicity, we return the transformed coordinate grids.
    return rotated_x, rotated_y

# Example Usage:
# Create a sample 2D array
original_array = np.array([
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
    [13, 14, 15, 16]
])

print("Original Array:\n", original_array)

# Rotate by 45 degrees around its center
rotated_x_coords, rotated_y_coords = rotate_2d_array(original_array, 45)
print("\nRotated X Coordinates (45 degrees):\n", rotated_x_coords)
print("\nRotated Y Coordinates (45 degrees):\n", rotated_y_coords)

# Rotate by 90 degrees around a specific point (e.g., top-left corner (0,0))
rotated_x_coords_90, rotated_y_coords_90 = rotate_2d_array(original_array, 90, center=(0,0))
print("\nRotated X Coordinates (90 degrees, center (0,0)):\n", rotated_x_coords_90)
print("\nRotated Y Coordinates (90 degrees, center (0,0)):\n", rotated_y_coords_90)

Applying Rotation to Array Values (Interpolation)

To get a new array where the values are rotated, you need to map the original values to the new coordinate system. This often involves interpolation because the rotated coordinates might not perfectly align with integer pixel positions in the new grid. Libraries like SciPy provide convenient functions for this.

Here's an example using scipy.ndimage.rotate which internally uses similar matrix transformations and handles interpolation.

import numpy as np
from scipy.ndimage import rotate

# Create a sample 2D array
original_array = np.array([
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
    [13, 14, 15, 16]
], dtype=float) # Ensure float for interpolation

print("Original Array:\n", original_array)

# Rotate by 45 degrees, keeping the output shape the same
# `reshape=False` ensures the output array has the same dimensions as the input
# `order=1` specifies bilinear interpolation
rotated_array_45 = rotate(original_array, angle=45, reshape=False, order=1)
print("\nRotated Array (45 degrees, reshape=False):\n", rotated_array_45)

# Rotate by 90 degrees, allowing the output shape to change to fit the rotated content
# `reshape=True` (default) expands the array to fit the rotated content without cropping
rotated_array_90 = rotate(original_array, angle=90, reshape=True, order=1)
print("\nRotated Array (90 degrees, reshape=True):\n", rotated_array_90)