Files
gps-denied-onboard/h07_image_rotation_utils.py
T
Denys Zaitsev d7e1066c60 Initial commit
2026-04-03 23:25:54 +03:00

38 lines
1.8 KiB
Python

import numpy as np
import math
import cv2
from typing import Optional, Tuple
from abc import ABC, abstractmethod
class IImageRotationUtils(ABC):
@abstractmethod
def rotate_image(self, image: np.ndarray, angle: float, center: Optional[Tuple[int, int]] = None) -> np.ndarray: pass
@abstractmethod
def calculate_rotation_from_points(self, src_points: np.ndarray, dst_points: np.ndarray) -> float: pass
@abstractmethod
def normalize_angle(self, angle: float) -> float: pass
@abstractmethod
def compute_rotation_matrix(self, angle: float, center: Tuple[int, int]) -> np.ndarray: pass
class ImageRotationUtils(IImageRotationUtils):
"""H07: Image rotation operations, angle calculations from point shifts."""
def rotate_image(self, image: np.ndarray, angle: float, center: Optional[Tuple[int, int]] = None) -> np.ndarray:
h, w = image.shape[:2]
if center is None: center = (w // 2, h // 2)
return cv2.warpAffine(image, self.compute_rotation_matrix(angle, center), (w, h))
def calculate_rotation_from_points(self, src_points: np.ndarray, dst_points: np.ndarray) -> float:
if len(src_points) == 0 or len(dst_points) == 0: return 0.0
sc, dc = np.mean(src_points, axis=0), np.mean(dst_points, axis=0)
angles = []
for s, d in zip(src_points - sc, dst_points - dc):
if np.linalg.norm(s) > 1e-3 and np.linalg.norm(d) > 1e-3:
angles.append(math.atan2(d[1], d[0]) - math.atan2(s[1], s[0]))
if not angles: return 0.0
return self.normalize_angle(math.degrees(np.mean(np.unwrap(angles))))
def normalize_angle(self, angle: float) -> float:
return angle % 360.0
def compute_rotation_matrix(self, angle: float, center: Tuple[int, int]) -> np.ndarray:
return cv2.getRotationMatrix2D(center, -angle, 1.0)