Source code for miop.matrices_metadata
# Copyright (c) 2025, Maxime Paschoud.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
#
# (http://opensource.org/licenses/BSD-3-Clause)
#
# __author__ = "Maxime Paschoud, ETHZ: CMBM"
#
import numpy as np
from pipeline import DAGNode
[docs]
class CameraMvmtFromMetadata(DAGNode):
"""
Computes camera intrinsics and extrinsics (rotation matrices) from metadata.
Attributes
----------
rotation_matrices : list of list of np.ndarray
Rotation matrices for each image in each face.
camera_position : list of dict
List of dictionaries for each face containing:
- 'rotations': array of 3x3 rotation matrices (extrinsics)
- 'intrinsics': placeholder list for intrinsics (scaling and shear [ones, [1., 0.]])
"""
def __init__(self):
"""
Initializes the camera movement estimation object with empty attributes.
"""
super().__init__()
self.rotation_matrices = None
self.camera_position = None
[docs]
def eval(self, img_collection):
"""
Computes camera extrinsics (rotation matrices) for each image in the image collection
using the tilt angle and inferred tilt axis from metadata.
Parameters
----------
img_collection : ImageCollection
The collection of images with metadata from which to compute rotations.
Returns
-------
list of dict
One dictionary per face, containing:
- 'rotations': np.ndarray of shape (N, 3, 3), rotation matrices for N images
- 'intrinsics': placeholder value ([ones, [1., 0.]])
"""
rot_mat = []
camera_pos = []
for face in img_collection.faces:
img = face[0][0]
axis = self.choose_axis(img.rotation_z)
rot_mat.append([self.get_rotation_matrix(img.tilt, axis)])
for pair in face:
img = pair[1]
m = self.get_rotation_matrix(img.tilt, axis)
rot_mat[-1].append(m)
camera_pos.append({'rotations': np.asarray(rot_mat[-1]), 'intrinsics': [np.ones(len(face) + 1), [1.,0]]})
self.rotation_matrices = rot_mat
self.camera_position = camera_pos
return [camera_pos]
[docs]
def choose_axis(self, rot_z):
"""
Determines the tilt axis based on rotation in the (x, y) plane.
Parameters
----------
rot_z : float
Rotation angle in the (x, y) plane in radians.
Returns
-------
list of float
A 3D unit vector representing the axis of rotation, one of:
- [1., 0., 0.] (default: horizontal)
- [-1., 0., 0.] (rot_z ≈ 180°)
- [0., 1., 0.] (rot_z ≈ -90°)
- [0., -1., 0.] (rot_z ≈ 90°)
"""
if np.isclose(rot_z, 90 * np.pi/180):
axis = [0., -1., 0.]
elif np.isclose(rot_z, 180 * np.pi/180):
axis = [-1., 0., 0.]
elif np.isclose(rot_z, -90 * np.pi/180):
axis = [0., 1., 0.]
else:
axis = [1., 0., 0.]
return axis
[docs]
def get_rotation_matrix(self, angle, axis):
"""
Computes a 3x3 rotation matrix given an angle and rotation axis using Rodrigues' formula.
Parameters
----------
angle : float
Rotation angle in radians.
axis : list of float
3D axis of rotation (e.g., [1., 0., 0.] for X-axis).
Returns
-------
np.ndarray
A 3x3 rotation matrix representing the rotation about the given axis.
"""
axis = np.asarray(axis)
axis = axis / np.linalg.norm(axis)
K = np.array([[0, -axis[2], axis[1]],
[axis[2], 0, -axis[0]],
[-axis[1], axis[0], 0]])
# Rotation matrix calculation using Rodrigues' formula
I = np.eye(3)
rotation_matrix = I + np.sin(angle) * K + (1 - np.cos(angle)) * np.dot(K, K)
return rotation_matrix