Files
flght-tracker/np.py

217 lines
8.1 KiB
Python

import numpy as np
import time
import math
import os
from PIL import Image, ImageDraw
import random
# --- Hilfsfunktionen ---
def normalize(v):
""" Normalisiert einen Vektor (macht seine Länge zu 1). """
norm = np.linalg.norm(v)
if norm < 1e-9:
return np.zeros_like(v)
return v / norm
def look_at_matrix(eye, target, up):
"""
Erzeugt eine View-Matrix, die Weltkoordinaten in Kamerakoordinaten transformiert.
:param eye: Position der Kamera (np.array).
:param target: Punkt, auf den die Kamera schaut (np.array).
:param up: Up-Vektor der Kamera (zeigt, wo "oben" für die Kamera ist, np.array).
:return: 4x4 View-Matrix (NumPy-Array).
"""
if np.linalg.norm(eye - target) < 1e-6:
target = eye - np.array([0.0, 0.0, 1.0]) # Standard-Blickrichtung bei Kollision
forward = normalize(eye - target)
up_normalized = normalize(up)
if abs(np.dot(up_normalized, forward)) > 0.999:
if abs(forward[1]) < 0.99:
temp_up = np.array([0.0, 1.0, 0.0])
else:
temp_up = np.array([0.0, 0.0, -1.0 if forward[1] > 0 else 1.0])
right = normalize(np.cross(temp_up, forward))
else:
right = normalize(np.cross(up_normalized, forward))
camera_up = normalize(np.cross(forward, right))
rotation = np.identity(4)
rotation[0, 0:3] = right
rotation[1, 0:3] = camera_up
rotation[2, 0:3] = forward
translation = np.identity(4)
translation[0:3, 3] = -eye
view_mat = np.dot(rotation, translation)
return view_mat
def perspective_projection(point_cam_space, fov_deg, aspect_ratio, near, far):
"""
Projiziert einen Punkt aus dem Kamera-Koordinatenraum auf eine 2D-Ebene.
Gibt normalisierte Gerätekoordinaten (NDC) zurück (-1 bis +1).
"""
if point_cam_space[2] > -near or point_cam_space[2] < -far:
return None
if abs(point_cam_space[2]) < 1e-9:
return None
f = 1.0 / math.tan(math.radians(fov_deg) / 2.0)
x_ndc = (f / aspect_ratio) * point_cam_space[0] / -point_cam_space[2]
y_ndc = f * point_cam_space[1] / -point_cam_space[2]
if not (-1 <= x_ndc <= 1 and -1 <= y_ndc <= 1):
return None
return np.array([x_ndc, y_ndc])
def ndc_to_pixel(ndc_coords, width, height):
""" Wandelt NDC-Koordinaten (-1 bis 1) in Pixelkoordinaten (0 bis width/height) um. """
pixel_x = (ndc_coords[0] + 1.0) / 2.0 * width
pixel_y = (1.0 - ndc_coords[1]) / 2.0 * height # Y invertieren
px = int(round(pixel_x))
py = int(round(pixel_y))
px = max(0, min(width - 1, px))
py = max(0, min(height - 1, py))
return px, py
# --- Simulationsparameter ---
object_start_pos = np.array([0.0, 0.0, 10.0])
# --- NEU: Fixer Zielpunkt für die Kameras ---
# Die Kameras schauen immer auf die ursprüngliche Startposition des Objekts.
# Alternativ könnte hier jeder andere feste Punkt im Raum definiert werden.
fixed_camera_target = object_start_pos.copy()
# Parameter für zufällige Bewegung
random_step_scale = 0.8
world_bounds_min = np.array([-10.0, -5.0, 5.0])
world_bounds_max = np.array([ 10.0, 5.0, 15.0])
# Kameraeinstellungen
cameras = [
{
"name": "Kamera_1_Frontal",
"pos": np.array([0.0, 0.0, 0.0]),
"target": fixed_camera_target, # Verwendet den fixen Zielpunkt
"up": np.array([0.0, 1.0, 0.0]),
"fov_deg": 60,
},
{
"name": "Kamera_2_Seitlich_Links",
"pos": np.array([-15.0, 0.0, 5.0]),
"target": fixed_camera_target, # Verwendet den fixen Zielpunkt
"up": np.array([0.0, 1.0, 0.0]),
"fov_deg": 45,
},
{
"name": "Kamera_3_Von_Oben",
"pos": np.array([0.0, 15.0, 10.0]),
"target": fixed_camera_target, # Verwendet den fixen Zielpunkt
"up": np.array([0.0, 0.0, -1.0]),
"fov_deg": 70,
}
]
# Clipping-Ebenen
near_plane = 0.1
far_plane = 100.0
simulation_duration = 10
time_step = 1
# Bildparameter
IMG_WIDTH = 320
IMG_HEIGHT = 180
OUTPUT_DIR = "./bilder"
# Ausgabeverzeichnis erstellen
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"Bilder werden in '{OUTPUT_DIR}' gespeichert.")
# --- Simulationsschleife ---
current_object_pos = object_start_pos.copy()
current_time = 0.0
print("Starte Simulation...")
while current_time <= simulation_duration:
print(f"\n--- Zeitpunkt: {current_time:.1f}s ---")
# Objektposition zufällig aktualisieren
delta_x = (random.random() - 0.5) * 2 * random_step_scale
delta_y = (random.random() - 0.5) * 2 * random_step_scale
delta_z = (random.random() - 0.5) * 2 * random_step_scale
random_displacement = np.array([delta_x, delta_y, delta_z])
current_object_pos += random_displacement
current_object_pos = np.clip(current_object_pos, world_bounds_min, world_bounds_max)
print(f"Objekt Welt-Position: ({current_object_pos[0]:.2f}, {current_object_pos[1]:.2f}, {current_object_pos[2]:.2f})")
# Objektposition in homogene Koordinaten umwandeln
object_pos_h = np.append(current_object_pos, 1.0)
# Für jede Kamera die Ansicht berechnen und Bild erzeugen
for i, cam in enumerate(cameras):
print(f"\n Kamera {i+1}: {cam['name']}")
print(f" Position: ({cam['pos'][0]:.2f}, {cam['pos'][1]:.2f}, {cam['pos'][2]:.2f})")
# --- WICHTIGE ÄNDERUNG: Verwende den *fixen* Zielpunkt ---
# Die Kamera schaut *nicht* mehr auf current_object_pos, sondern auf cam['target'],
# welches im Kamera-Dictionary auf fixed_camera_target gesetzt wurde.
current_target_for_view = cam['target'] # Dies ist jetzt immer fixed_camera_target
effective_aspect_ratio = IMG_WIDTH / IMG_HEIGHT
# Ausgabe zeigt den fixen Zielpunkt an
print(f" Schaut auf (Fixer Target): ({current_target_for_view[0]:.2f}, {current_target_for_view[1]:.2f}, {current_target_for_view[2]:.2f})")
print(f" Up-Vektor: ({cam['up'][0]:.2f}, {cam['up'][1]:.2f}, {cam['up'][2]:.2f})")
print(f" FOV: {cam['fov_deg']} Grad, Aspekt (für Projektion): {effective_aspect_ratio:.2f}")
# View-Matrix mit dem fixen Zielpunkt berechnen
view_mat = look_at_matrix(cam['pos'], current_target_for_view, cam['up'])
# Objektposition in Kamera-Koordinaten transformieren
# Dieser Schritt ist weiterhin nötig, um das *bewegliche* Objekt zu projizieren
point_in_cam_space_h = np.dot(view_mat, object_pos_h)
if point_in_cam_space_h[3] <= 1e-9:
print(" Objekt hinter oder auf der Kameraebene (w <= 0).")
projected_point_ndc = None
point_in_cam_space = np.array([np.nan, np.nan, np.nan])
else:
point_in_cam_space = point_in_cam_space_h[:3] / point_in_cam_space_h[3]
print(f" Bewegliches Objekt in Kamera-Koordinaten: ({point_in_cam_space[0]:.2f}, {point_in_cam_space[1]:.2f}, {point_in_cam_space[2]:.2f})") # Klärung der Ausgabe
# Projiziere das *bewegliche* Objekt in 2D (NDC)
projected_point_ndc = perspective_projection(
point_in_cam_space,
cam['fov_deg'],
effective_aspect_ratio,
near_plane,
far_plane
)
# Bild erstellen und speichern
img = Image.new('RGB', (IMG_WIDTH, IMG_HEIGHT), color='black')
draw = ImageDraw.Draw(img)
if projected_point_ndc is not None:
print(f" Projizierte 2D-Koordinaten des Objekts (NDC): ({projected_point_ndc[0]:.3f}, {projected_point_ndc[1]:.3f})") # Klärung der Ausgabe
px, py = ndc_to_pixel(projected_point_ndc, IMG_WIDTH, IMG_HEIGHT)
print(f" Pixelkoordinaten des Objekts (im Bild): ({px}, {py})")
draw.rectangle([(px-1, py-1), (px+1, py+1)], fill='white', outline='white')
else:
print(" Bewegliches Objekt befindet sich ausserhalb des Sichtbereichs dieser Kamera.") # Klärung der Ausgabe
timestamp_str = f"{current_time:.1f}".replace('.', '_')
filename = f"{cam['name']}_t_{timestamp_str}.png"
filepath = os.path.join(OUTPUT_DIR, filename)
img.save(filepath)
print(f" Bild gespeichert: {filepath}")
current_time += time_step
print("\nSimulation beendet.")