mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-22 07:06:38 +00:00
38 lines
1.8 KiB
Python
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) |