Compare commits
3 Commits
911143dfc5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7df9de12ce | |||
| 9f8c1c27dc | |||
| 1d0dc05a7b |
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,3 +1,39 @@
|
|||||||
# Ignore the virtual environment folder
|
# Ignore the virtual environment folder
|
||||||
track/
|
track/
|
||||||
resurces/projects/
|
|
||||||
|
# Ignore Python cache files
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
|
||||||
|
# Ignore project data and generated files
|
||||||
|
resources/projects/
|
||||||
|
resources/trip_archive/
|
||||||
|
resources/credentials.enc
|
||||||
|
resources/key.key
|
||||||
|
resources/server_settings.enc
|
||||||
|
|
||||||
|
# Ignore generated videos and frames
|
||||||
|
*.mp4
|
||||||
|
*.avi
|
||||||
|
*.mov
|
||||||
|
*.webm
|
||||||
|
cinema_frames/
|
||||||
|
progressive_frames/
|
||||||
|
|
||||||
|
# Ignore test files and temporary files
|
||||||
|
test_*.py
|
||||||
|
*.tmp
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Ignore IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Ignore OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|||||||
Binary file not shown.
@@ -1,45 +0,0 @@
|
|||||||
# Project Cleanup Summary
|
|
||||||
|
|
||||||
## What Was Cleaned Up
|
|
||||||
|
|
||||||
### Moved to `junk_files/`
|
|
||||||
- Documentation files (*.md) that were cluttering the root directory
|
|
||||||
- `3D_VIDEO_DOCUMENTATION.md`
|
|
||||||
- `PAUSE_EDIT_IMPROVEMENTS.md`
|
|
||||||
- `PROJECT_MODERNIZATION_SUMMARY.md`
|
|
||||||
- `TEST_MODE_DOCUMENTATION.md`
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- All `__pycache__` directories and compiled Python bytecode files
|
|
||||||
- Duplicate and test files that were no longer needed
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fixed typo in requirements.txt (`reqirements.txt` was corrected to `requirements.txt`)
|
|
||||||
- Ensured proper import structure (app uses `py_scripts.video_3d_generator` correctly)
|
|
||||||
|
|
||||||
## Current Clean Structure
|
|
||||||
```
|
|
||||||
traccar_animation/
|
|
||||||
├── .git/ # Git repository files
|
|
||||||
├── .gitignore # Git ignore rules
|
|
||||||
├── config.py # Application configuration
|
|
||||||
├── main.py # Main application entry point
|
|
||||||
├── traccar.kv # Kivy UI layout file
|
|
||||||
├── requirements.txt # Python dependencies (fixed)
|
|
||||||
├── py_scripts/ # Python modules
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── utils.py
|
|
||||||
│ ├── video_3d_generator.py
|
|
||||||
│ └── webview.py
|
|
||||||
├── screens/ # Kivy screen modules
|
|
||||||
├── resources/ # Application resources
|
|
||||||
├── track/ # Virtual environment
|
|
||||||
└── junk_files/ # Non-essential files moved here
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
- ✅ Utils module imports correctly
|
|
||||||
- ✅ Video 3D generator module imports correctly
|
|
||||||
- ✅ No duplicate files remain
|
|
||||||
- ✅ All dependencies properly listed in requirements.txt
|
|
||||||
- ✅ Clean project structure maintained
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Complete video generation from existing frames
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
from moviepy import ImageSequenceClip
|
|
||||||
|
|
||||||
def create_video_from_frames():
|
|
||||||
frames_folder = "/home/pi/Desktop/traccar_animation/resources/projects/day 2/frames"
|
|
||||||
output_path = "/home/pi/Desktop/traccar_animation/resources/projects/day 2/advanced_3d_animation.mp4"
|
|
||||||
|
|
||||||
# Get all frame files
|
|
||||||
frame_files = glob.glob(os.path.join(frames_folder, "frame_*.png"))
|
|
||||||
frame_files.sort() # Ensure correct order
|
|
||||||
|
|
||||||
if not frame_files:
|
|
||||||
print("No frames found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"Found {len(frame_files)} frames")
|
|
||||||
print("Creating video...")
|
|
||||||
|
|
||||||
# Create video clip
|
|
||||||
clip = ImageSequenceClip(frame_files, fps=30)
|
|
||||||
|
|
||||||
# Write video file
|
|
||||||
clip.write_videofile(
|
|
||||||
output_path,
|
|
||||||
codec='libx264',
|
|
||||||
bitrate='8000k',
|
|
||||||
audio=False,
|
|
||||||
temp_audiofile=None,
|
|
||||||
remove_temp=True
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"Video created successfully: {output_path}")
|
|
||||||
return output_path
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
create_video_from_frames()
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for Google Earth-style flythrough animation
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append('/home/pi/Desktop/traccar_animation')
|
|
||||||
|
|
||||||
from py_scripts.advanced_3d_generator import Advanced3DGenerator
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def test_google_earth_animation():
|
|
||||||
"""Test the new Google Earth flythrough animation"""
|
|
||||||
|
|
||||||
# Find a project with GPS data
|
|
||||||
projects_folder = "/home/pi/Desktop/traccar_animation/resources/projects"
|
|
||||||
|
|
||||||
if not os.path.exists(projects_folder):
|
|
||||||
print("Projects folder not found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Look for projects
|
|
||||||
projects = [d for d in os.listdir(projects_folder) if os.path.isdir(os.path.join(projects_folder, d))]
|
|
||||||
|
|
||||||
if not projects:
|
|
||||||
print("No projects found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Use the first project found
|
|
||||||
project_name = projects[0]
|
|
||||||
project_folder = os.path.join(projects_folder, project_name)
|
|
||||||
positions_file = os.path.join(project_folder, "positions.json")
|
|
||||||
|
|
||||||
if not os.path.exists(positions_file):
|
|
||||||
print(f"No positions.json found in project {project_name}")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"Testing Google Earth animation with project: {project_name}")
|
|
||||||
|
|
||||||
# Create generator
|
|
||||||
generator = Advanced3DGenerator(project_folder)
|
|
||||||
|
|
||||||
# Check dependencies
|
|
||||||
try:
|
|
||||||
generator.check_dependencies()
|
|
||||||
print("✅ All dependencies available")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Dependency error: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Generate Google Earth-style animation
|
|
||||||
output_video = os.path.join(project_folder, f"{project_name}_google_earth_test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
|
||||||
|
|
||||||
def progress_callback(progress, message):
|
|
||||||
print(f"Progress: {progress:.1f}% - {message}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
print("Starting Google Earth flythrough generation...")
|
|
||||||
success = generator.generate_3d_animation(
|
|
||||||
positions_file,
|
|
||||||
output_video,
|
|
||||||
style='google_earth',
|
|
||||||
progress_callback=progress_callback
|
|
||||||
)
|
|
||||||
|
|
||||||
if success and os.path.exists(output_video):
|
|
||||||
print(f"✅ SUCCESS! Google Earth flythrough created: {output_video}")
|
|
||||||
|
|
||||||
# Get file size
|
|
||||||
file_size = os.path.getsize(output_video) / (1024 * 1024) # MB
|
|
||||||
print(f"📹 Video size: {file_size:.1f} MB")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("❌ Failed to create video")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error during generation: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_google_earth_animation()
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for the improved Relive-style GPS animation
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
# Add the project directory to the path
|
|
||||||
sys.path.append('/home/pi/Desktop/traccar_animation')
|
|
||||||
|
|
||||||
from py_scripts.advanced_3d_generator import Advanced3DGenerator
|
|
||||||
|
|
||||||
def test_relive_animation():
|
|
||||||
"""Test the new Relive-style animation"""
|
|
||||||
|
|
||||||
# Find a project with GPS data
|
|
||||||
resources_folder = "/home/pi/Desktop/traccar_animation/resources"
|
|
||||||
projects_folder = os.path.join(resources_folder, "projects")
|
|
||||||
|
|
||||||
if not os.path.exists(projects_folder):
|
|
||||||
print("No projects folder found")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Look for projects with positions.json
|
|
||||||
for project_name in os.listdir(projects_folder):
|
|
||||||
project_path = os.path.join(projects_folder, project_name)
|
|
||||||
positions_file = os.path.join(project_path, "positions.json")
|
|
||||||
|
|
||||||
if os.path.exists(positions_file):
|
|
||||||
print(f"🎬 Testing Relive-style animation with project: {project_name}")
|
|
||||||
|
|
||||||
# Check if positions file has data
|
|
||||||
try:
|
|
||||||
with open(positions_file, 'r') as f:
|
|
||||||
positions = json.load(f)
|
|
||||||
|
|
||||||
if len(positions) < 5:
|
|
||||||
print(f"❌ Project {project_name} has only {len(positions)} GPS points - skipping")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"📍 Found {len(positions)} GPS points")
|
|
||||||
|
|
||||||
# Create generator
|
|
||||||
generator = Advanced3DGenerator(project_path)
|
|
||||||
|
|
||||||
# Progress callback
|
|
||||||
def progress_callback(progress, message):
|
|
||||||
print(f"Progress: {progress:.1f}% - {message}")
|
|
||||||
|
|
||||||
# Generate animation
|
|
||||||
output_video = os.path.join(project_path, f"relive_animation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
|
||||||
|
|
||||||
print(f"🚀 Starting Relive-style animation generation...")
|
|
||||||
success = generator.generate_3d_animation(
|
|
||||||
positions_file,
|
|
||||||
output_video,
|
|
||||||
style='advanced',
|
|
||||||
progress_callback=progress_callback
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
print(f"✅ SUCCESS! Relive-style animation created: {output_video}")
|
|
||||||
print(f"📁 You can find your video at: {output_video}")
|
|
||||||
else:
|
|
||||||
print("❌ Failed to generate animation")
|
|
||||||
|
|
||||||
return # Exit after first successful project
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error testing project {project_name}: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print("❌ No suitable projects found for testing")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🎬 Testing Improved Relive-Style GPS Animation")
|
|
||||||
print("=" * 50)
|
|
||||||
test_relive_animation()
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,7 +24,8 @@ class BlenderGPSAnimator:
|
|||||||
if BLENDER_AVAILABLE:
|
if BLENDER_AVAILABLE:
|
||||||
self.setup_blender_scene()
|
self.setup_blender_scene()
|
||||||
else:
|
else:
|
||||||
raise ImportError("Blender (bpy) is not available. Please install Blender with Python API access.")
|
# Don't raise error here, let the caller handle the check
|
||||||
|
pass
|
||||||
|
|
||||||
def check_dependencies(self):
|
def check_dependencies(self):
|
||||||
"""Check if Blender dependencies are available"""
|
"""Check if Blender dependencies are available"""
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
gAAAAABob4-usKjps0vEVupB8FIJ3tKqoOeedzOUpt16NICbpi1ejKoSwqDvH7eIAPaZCOkfbPC6gjJGTo2yxt4BOPg1yzg_-Xanpl5iL1Y2mIRxWag-5cWhDNiqZo3bEZMqZ3M875O-
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
wetp_PNG9CC5432-W9H3rUbaqIurwldZxHlOgori5kY=
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
gAAAAABobK2fcNGeWyfPJzYnOl_HWl8TdQfRDb5teUXH9Kpjmme0TUVA3Dy7wm2MuMEGsPBTWBm8XfaX8daIwu6iDV6o8G07XZ_A0RoMqx3xWiYUbX63ovYy8qITIpMqbt0dayYigDSPmdr_8pcqko6ik-ctfdg4SkGH1gRXb5yuacnzezLr3KcHMh833PkbTO6WiUYPCwaivEMTVHUxL5YORiLRGu4E3lS_WDPo7kv53khtUI9b7vWJOOUFXcelM2vF3iHI3EkXCWrO2Qpm22nC44b-yCnZvYzx7g-WHZDNfG6CA1KXbcyhouxR4b7502iofpEAN5sizLFuyOWIOBdVphblIkRd1qdq6fVmt0IMeoaMpNPNuDKJqMDLuAU05wXDWbGXei6YU6rs6YJgpGOfNdv8A_sKKJBrh5QVE2kZ2GE0Ysqpnw2Yfj_jsMBpdh-bBs6UDwcI
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -10,7 +10,7 @@ from py_scripts.utils import (
|
|||||||
process_preview_util, optimize_route_entries_util
|
process_preview_util, optimize_route_entries_util
|
||||||
)
|
)
|
||||||
from py_scripts.advanced_3d_generator import NavigationAnimationGenerator
|
from py_scripts.advanced_3d_generator import NavigationAnimationGenerator
|
||||||
from py_scripts.blender_animator import BlenderGPSAnimator
|
# BlenderGPSAnimator imported conditionally when needed
|
||||||
from kivy.uix.popup import Popup
|
from kivy.uix.popup import Popup
|
||||||
from kivy.uix.button import Button
|
from kivy.uix.button import Button
|
||||||
from kivy.uix.label import Label
|
from kivy.uix.label import Label
|
||||||
@@ -250,15 +250,15 @@ class CreateAnimationScreen(Screen):
|
|||||||
Clock.schedule_once(lambda dt: run_google_earth_animation(), 0.5)
|
Clock.schedule_once(lambda dt: run_google_earth_animation(), 0.5)
|
||||||
|
|
||||||
def generate_blender_animation(self):
|
def generate_blender_animation(self):
|
||||||
"""Generate cinema-quality animation using Blender"""
|
"""Generate cinema-quality animation using Blender (or fallback to advanced 3D)"""
|
||||||
# Show processing popup
|
# Show processing popup
|
||||||
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||||
label = Label(text="Initializing Blender rendering pipeline...")
|
label = Label(text="Initializing cinema rendering pipeline...")
|
||||||
progress = ProgressBar(max=100, value=0)
|
progress = ProgressBar(max=100, value=0)
|
||||||
layout.add_widget(label)
|
layout.add_widget(label)
|
||||||
layout.add_widget(progress)
|
layout.add_widget(progress)
|
||||||
popup = Popup(
|
popup = Popup(
|
||||||
title="Generating Blender Cinema Animation",
|
title="Generating Cinema Animation",
|
||||||
content=layout,
|
content=layout,
|
||||||
size_hint=(0.9, None),
|
size_hint=(0.9, None),
|
||||||
size=(0, 200),
|
size=(0, 200),
|
||||||
@@ -283,9 +283,13 @@ class CreateAnimationScreen(Screen):
|
|||||||
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if Blender is available
|
||||||
|
try:
|
||||||
|
from py_scripts.blender_animator import BLENDER_AVAILABLE, BlenderGPSAnimator
|
||||||
|
if BLENDER_AVAILABLE:
|
||||||
update_status(10, "Loading GPS data into Blender...")
|
update_status(10, "Loading GPS data into Blender...")
|
||||||
|
|
||||||
# Check dependencies first
|
# Use Blender for rendering
|
||||||
animator = BlenderGPSAnimator(project_folder)
|
animator = BlenderGPSAnimator(project_folder)
|
||||||
animator.check_dependencies()
|
animator.check_dependencies()
|
||||||
|
|
||||||
@@ -311,11 +315,212 @@ class CreateAnimationScreen(Screen):
|
|||||||
else:
|
else:
|
||||||
raise Exception("Failed to generate Blender animation")
|
raise Exception("Failed to generate Blender animation")
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ImportError("Blender not available")
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# Fallback to advanced 3D animation with cinema-quality settings
|
||||||
|
update_status(10, "Blender not available - using advanced 3D cinema mode...")
|
||||||
|
|
||||||
|
# Import here to avoid startup delays
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('Agg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from mpl_toolkits.mplot3d import Axes3D
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
# Load GPS data
|
||||||
|
with open(positions_path, 'r') as f:
|
||||||
|
positions = json.load(f)
|
||||||
|
|
||||||
|
if len(positions) < 2:
|
||||||
|
update_status(0, "Error: Need at least 2 GPS points")
|
||||||
|
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||||
|
return
|
||||||
|
|
||||||
|
update_status(20, "Processing GPS coordinates for cinema rendering...")
|
||||||
|
|
||||||
|
# Extract coordinates
|
||||||
|
lats = np.array([pos['latitude'] for pos in positions])
|
||||||
|
lons = np.array([pos['longitude'] for pos in positions])
|
||||||
|
alts = np.array([pos.get('altitude', 0) for pos in positions])
|
||||||
|
timestamps = [pos.get('fixTime', '') for pos in positions]
|
||||||
|
|
||||||
|
# Convert to relative coordinates
|
||||||
|
lat_center = np.mean(lats)
|
||||||
|
lon_center = np.mean(lons)
|
||||||
|
alt_min = np.min(alts)
|
||||||
|
|
||||||
|
x = (lons - lon_center) * 111320 * np.cos(np.radians(lat_center))
|
||||||
|
y = (lats - lat_center) * 110540
|
||||||
|
z = alts - alt_min
|
||||||
|
|
||||||
|
update_status(30, "Creating cinema-quality frames...")
|
||||||
|
|
||||||
|
# Cinema settings - higher quality
|
||||||
|
frames_folder = os.path.join(project_folder, "cinema_frames")
|
||||||
|
os.makedirs(frames_folder, exist_ok=True)
|
||||||
|
|
||||||
|
fps = 24 # Cinema standard
|
||||||
|
total_frames = min(len(positions), 200) # Limit for reasonable processing time
|
||||||
|
points_per_frame = max(1, len(positions) // total_frames)
|
||||||
|
|
||||||
|
frame_files = []
|
||||||
|
|
||||||
|
# Generate cinema-quality frames
|
||||||
|
for frame_idx in range(total_frames):
|
||||||
|
current_progress = 30 + (frame_idx / total_frames) * 50
|
||||||
|
update_status(current_progress, f"Rendering cinema frame {frame_idx + 1}/{total_frames}...")
|
||||||
|
|
||||||
|
end_point = min((frame_idx + 1) * points_per_frame, len(positions))
|
||||||
|
|
||||||
|
# Create high-quality 3D plot
|
||||||
|
plt.style.use('dark_background') # Cinema-style dark theme
|
||||||
|
fig = plt.figure(figsize=(16, 12), dpi=150) # Higher resolution
|
||||||
|
ax = fig.add_subplot(111, projection='3d')
|
||||||
|
|
||||||
|
# Plot route with cinema styling
|
||||||
|
if end_point > 1:
|
||||||
|
# Gradient effect for completed route
|
||||||
|
colors = np.linspace(0, 1, end_point)
|
||||||
|
ax.scatter(x[:end_point], y[:end_point], z[:end_point],
|
||||||
|
c=colors, cmap='plasma', s=30, alpha=0.8)
|
||||||
|
ax.plot(x[:end_point], y[:end_point], z[:end_point],
|
||||||
|
color='cyan', linewidth=3, alpha=0.9)
|
||||||
|
|
||||||
|
# Current position with glow effect
|
||||||
|
if end_point > 0:
|
||||||
|
current_idx = end_point - 1
|
||||||
|
# Multiple layers for glow effect
|
||||||
|
for size, alpha in [(200, 0.3), (150, 0.5), (100, 0.8)]:
|
||||||
|
ax.scatter(x[current_idx], y[current_idx], z[current_idx],
|
||||||
|
c='yellow', s=size, alpha=alpha, marker='o')
|
||||||
|
|
||||||
|
# Trail effect
|
||||||
|
trail_start = max(0, current_idx - 10)
|
||||||
|
if current_idx > trail_start:
|
||||||
|
trail_alpha = np.linspace(0.3, 1.0, current_idx - trail_start + 1)
|
||||||
|
for i, alpha in enumerate(trail_alpha):
|
||||||
|
idx = trail_start + i
|
||||||
|
ax.scatter(x[idx], y[idx], z[idx],
|
||||||
|
c='orange', s=60, alpha=alpha)
|
||||||
|
|
||||||
|
# Remaining route preview
|
||||||
|
if end_point < len(positions):
|
||||||
|
ax.plot(x[end_point:], y[end_point:], z[end_point:],
|
||||||
|
color='gray', linewidth=1, alpha=0.4, linestyle='--')
|
||||||
|
|
||||||
|
# Cinema-style labels and styling
|
||||||
|
ax.set_xlabel('East-West (m)', color='white', fontsize=14)
|
||||||
|
ax.set_ylabel('North-South (m)', color='white', fontsize=14)
|
||||||
|
ax.set_zlabel('Elevation (m)', color='white', fontsize=14)
|
||||||
|
|
||||||
|
# Progress and time info
|
||||||
|
progress_percent = (end_point / len(positions)) * 100
|
||||||
|
timestamp_str = timestamps[end_point-1] if end_point > 0 else "Start"
|
||||||
|
ax.set_title(f'CINEMA GPS JOURNEY\nProgress: {progress_percent:.1f}% • Point {end_point}/{len(positions)} • {timestamp_str}',
|
||||||
|
color='white', fontsize=16, pad=20, weight='bold')
|
||||||
|
|
||||||
|
# Consistent view with cinematic angle
|
||||||
|
margin = max(np.ptp(x), np.ptp(y)) * 0.15
|
||||||
|
ax.set_xlim(np.min(x) - margin, np.max(x) + margin)
|
||||||
|
ax.set_ylim(np.min(y) - margin, np.max(y) + margin)
|
||||||
|
ax.set_zlim(np.min(z) - 20, np.max(z) + 20)
|
||||||
|
|
||||||
|
# Dynamic camera movement for cinematic effect
|
||||||
|
azim = 45 + (frame_idx * 0.5) % 360 # Slowly rotating view
|
||||||
|
ax.view_init(elev=25, azim=azim)
|
||||||
|
|
||||||
|
# Cinema-style grid
|
||||||
|
ax.grid(True, alpha=0.2, color='white')
|
||||||
|
ax.xaxis.pane.fill = False
|
||||||
|
ax.yaxis.pane.fill = False
|
||||||
|
ax.zaxis.pane.fill = False
|
||||||
|
|
||||||
|
# Make pane edges more subtle
|
||||||
|
ax.xaxis.pane.set_edgecolor('gray')
|
||||||
|
ax.yaxis.pane.set_edgecolor('gray')
|
||||||
|
ax.zaxis.pane.set_edgecolor('gray')
|
||||||
|
ax.xaxis.pane.set_alpha(0.1)
|
||||||
|
ax.yaxis.pane.set_alpha(0.1)
|
||||||
|
ax.zaxis.pane.set_alpha(0.1)
|
||||||
|
|
||||||
|
# Save high-quality frame
|
||||||
|
frame_path = os.path.join(frames_folder, f"cinema_frame_{frame_idx:04d}.png")
|
||||||
|
try:
|
||||||
|
plt.savefig(frame_path, dpi=150, bbox_inches='tight',
|
||||||
|
facecolor='black', edgecolor='none', format='png')
|
||||||
|
plt.close(fig)
|
||||||
|
|
||||||
|
if os.path.exists(frame_path) and os.path.getsize(frame_path) > 1024:
|
||||||
|
test_frame = cv2.imread(frame_path)
|
||||||
|
if test_frame is not None:
|
||||||
|
frame_files.append(frame_path)
|
||||||
|
if frame_idx == 0:
|
||||||
|
h, w, c = test_frame.shape
|
||||||
|
update_status(current_progress, f"Cinema quality: {w}x{h} at {fps} FPS")
|
||||||
|
except Exception as frame_error:
|
||||||
|
update_status(current_progress, f"Error creating frame {frame_idx}: {str(frame_error)}")
|
||||||
|
plt.close(fig)
|
||||||
|
continue
|
||||||
|
|
||||||
|
plt.style.use('default') # Reset style
|
||||||
|
|
||||||
|
# Create cinema video
|
||||||
|
if not frame_files:
|
||||||
|
raise Exception("No valid cinema frames were generated")
|
||||||
|
|
||||||
|
update_status(80, f"Creating cinema video from {len(frame_files)} frames...")
|
||||||
|
|
||||||
|
output_video_path = os.path.join(project_folder, f"{self.project_name}_cinema_3d_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||||
|
|
||||||
|
# Cinema video creation with higher quality
|
||||||
|
first_frame = cv2.imread(frame_files[0])
|
||||||
|
height, width, layers = first_frame.shape
|
||||||
|
|
||||||
|
# Try to create high-quality video
|
||||||
|
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
||||||
|
video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
|
||||||
|
|
||||||
|
if video_writer.isOpened():
|
||||||
|
for i, frame_file in enumerate(frame_files):
|
||||||
|
frame = cv2.imread(frame_file)
|
||||||
|
if frame is not None:
|
||||||
|
video_writer.write(frame)
|
||||||
|
|
||||||
|
if i % 10 == 0:
|
||||||
|
progress = 80 + (i / len(frame_files)) * 8
|
||||||
|
update_status(progress, f"Encoding cinema frame {i+1}/{len(frame_files)}")
|
||||||
|
|
||||||
|
video_writer.release()
|
||||||
|
|
||||||
|
if os.path.exists(output_video_path) and os.path.getsize(output_video_path) > 1024:
|
||||||
|
update_status(90, "Cinema video created successfully")
|
||||||
|
output_path = output_video_path
|
||||||
|
else:
|
||||||
|
raise Exception("Cinema video creation failed")
|
||||||
|
else:
|
||||||
|
raise Exception("Could not initialize cinema video writer")
|
||||||
|
|
||||||
|
# Clean up frames
|
||||||
|
for frame_file in frame_files:
|
||||||
|
try:
|
||||||
|
os.remove(frame_file)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
os.rmdir(frames_folder)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
update_status(100, "Cinema animation complete!")
|
||||||
|
|
||||||
def show_success(dt):
|
def show_success(dt):
|
||||||
popup.dismiss()
|
popup.dismiss()
|
||||||
self.show_success_popup(
|
self.show_success_popup(
|
||||||
"Blender Cinema Animation Complete!",
|
"Cinema Animation Complete!",
|
||||||
f"Your cinema-quality animation has been saved to:\n{output_path}",
|
f"Your cinema-quality animation has been saved to:\n{output_path}\n\nNote: Blender was not available, so advanced 3D cinema mode was used instead.",
|
||||||
output_path
|
output_path
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -323,9 +528,12 @@ class CreateAnimationScreen(Screen):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = str(e)
|
error_message = str(e)
|
||||||
|
print(f"DEBUG: Cinema animation error: {error_message}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
def show_error(dt):
|
def show_error(dt):
|
||||||
popup.dismiss()
|
popup.dismiss()
|
||||||
self.show_error_popup("Blender Animation Error", error_message)
|
self.show_error_popup("Cinema Animation Error", error_message)
|
||||||
|
|
||||||
Clock.schedule_once(show_error, 0)
|
Clock.schedule_once(show_error, 0)
|
||||||
|
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for Google Earth-style flythrough animation
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append('/home/pi/Desktop/traccar_animation')
|
|
||||||
|
|
||||||
from py_scripts.advanced_3d_generator import NavigationAnimationGenerator
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def test_google_earth_animation():
|
|
||||||
"""Test the new Google Earth flythrough animation"""
|
|
||||||
|
|
||||||
# Find a project with GPS data
|
|
||||||
projects_folder = "/home/pi/Desktop/traccar_animation/resources/projects"
|
|
||||||
|
|
||||||
if not os.path.exists(projects_folder):
|
|
||||||
print("Projects folder not found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Look for projects
|
|
||||||
projects = [d for d in os.listdir(projects_folder) if os.path.isdir(os.path.join(projects_folder, d))]
|
|
||||||
|
|
||||||
if not projects:
|
|
||||||
print("No projects found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Use the first project found
|
|
||||||
project_name = projects[0]
|
|
||||||
project_folder = os.path.join(projects_folder, project_name)
|
|
||||||
positions_file = os.path.join(project_folder, "positions.json")
|
|
||||||
|
|
||||||
if not os.path.exists(positions_file):
|
|
||||||
print(f"No positions.json found in project {project_name}")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"Testing Google Earth animation with project: {project_name}")
|
|
||||||
|
|
||||||
# Create generator
|
|
||||||
generator = NavigationAnimationGenerator(project_folder)
|
|
||||||
|
|
||||||
# Check dependencies
|
|
||||||
try:
|
|
||||||
generator.check_dependencies()
|
|
||||||
print("✅ All dependencies available")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Dependency error: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Generate Google Earth-style animation
|
|
||||||
output_video = os.path.join(project_folder, f"{project_name}_google_earth_test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
|
||||||
|
|
||||||
def progress_callback(progress, message):
|
|
||||||
print(f"Progress: {progress:.1f}% - {message}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
print("Starting Google Earth flythrough generation...")
|
|
||||||
# Generate frames
|
|
||||||
frame_paths = generator.generate_frames(
|
|
||||||
positions_file,
|
|
||||||
style='google_earth',
|
|
||||||
progress_callback=progress_callback
|
|
||||||
)
|
|
||||||
|
|
||||||
if frame_paths and len(frame_paths) > 0:
|
|
||||||
print(f"✅ Generated {len(frame_paths)} frames")
|
|
||||||
|
|
||||||
# Create video
|
|
||||||
success = generator.create_video(
|
|
||||||
frame_paths,
|
|
||||||
output_video,
|
|
||||||
progress_callback=progress_callback
|
|
||||||
)
|
|
||||||
|
|
||||||
if success and os.path.exists(output_video):
|
|
||||||
print(f"✅ SUCCESS! Google Earth flythrough created: {output_video}")
|
|
||||||
|
|
||||||
# Get file size
|
|
||||||
file_size = os.path.getsize(output_video) / (1024 * 1024) # MB
|
|
||||||
print(f"📹 Video size: {file_size:.1f} MB")
|
|
||||||
|
|
||||||
# Clean up frames
|
|
||||||
generator.cleanup_frames()
|
|
||||||
else:
|
|
||||||
print("❌ Failed to create video from frames")
|
|
||||||
else:
|
|
||||||
print("❌ Failed to generate frames")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error during generation: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_google_earth_animation()
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for progressive 3D animation function
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append('/home/pi/Desktop/traccar_animation')
|
|
||||||
|
|
||||||
def test_progressive_3d_animation():
|
|
||||||
"""Test the progressive 3D animation dependencies"""
|
|
||||||
|
|
||||||
print("Testing progressive 3D animation dependencies...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Test matplotlib with 3D
|
|
||||||
import matplotlib
|
|
||||||
matplotlib.use('Agg') # Non-interactive backend
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from mpl_toolkits.mplot3d import Axes3D
|
|
||||||
print("✅ Matplotlib with 3D support available")
|
|
||||||
|
|
||||||
# Test OpenCV (instead of MoviePy)
|
|
||||||
import cv2
|
|
||||||
print("✅ OpenCV available for video creation")
|
|
||||||
|
|
||||||
# Test numpy
|
|
||||||
import numpy as np
|
|
||||||
print("✅ NumPy available")
|
|
||||||
|
|
||||||
# Test basic 3D plot creation
|
|
||||||
fig = plt.figure(figsize=(8, 6))
|
|
||||||
ax = fig.add_subplot(111, projection='3d')
|
|
||||||
|
|
||||||
# Create simple test data
|
|
||||||
x = np.array([0, 1, 2, 3, 4])
|
|
||||||
y = np.array([0, 1, 0, 1, 0])
|
|
||||||
z = np.array([0, 0, 1, 1, 2])
|
|
||||||
|
|
||||||
ax.plot(x, y, z, 'b-', linewidth=2)
|
|
||||||
ax.scatter(x, y, z, c='red', s=50)
|
|
||||||
ax.set_xlabel('X')
|
|
||||||
ax.set_ylabel('Y')
|
|
||||||
ax.set_zlabel('Z')
|
|
||||||
ax.set_title('Test 3D Plot')
|
|
||||||
|
|
||||||
# Save test plot
|
|
||||||
test_path = '/tmp/test_3d_plot.png'
|
|
||||||
plt.savefig(test_path, dpi=100, bbox_inches='tight')
|
|
||||||
plt.close(fig)
|
|
||||||
|
|
||||||
if os.path.exists(test_path):
|
|
||||||
print("✅ 3D plot creation and saving works")
|
|
||||||
os.remove(test_path)
|
|
||||||
else:
|
|
||||||
print("❌ Failed to create 3D plot")
|
|
||||||
|
|
||||||
except ImportError as e:
|
|
||||||
print(f"❌ Import error: {e}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("🎉 All dependencies for progressive 3D animation are working!")
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_progressive_3d_animation()
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for progressive 3D animation with debugging
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append('/home/pi/Desktop/traccar_animation')
|
|
||||||
|
|
||||||
def test_progressive_animation_debug():
|
|
||||||
"""Test the progressive animation with a simple GPS dataset"""
|
|
||||||
|
|
||||||
print("Testing progressive 3D animation with debug output...")
|
|
||||||
|
|
||||||
# Find a project with GPS data
|
|
||||||
projects_folder = "/home/pi/Desktop/traccar_animation/resources/projects"
|
|
||||||
|
|
||||||
if not os.path.exists(projects_folder):
|
|
||||||
print("❌ Projects folder not found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Look for projects
|
|
||||||
projects = [d for d in os.listdir(projects_folder) if os.path.isdir(os.path.join(projects_folder, d))]
|
|
||||||
|
|
||||||
if not projects:
|
|
||||||
print("❌ No projects found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Use the first project found
|
|
||||||
project_name = projects[0]
|
|
||||||
project_folder = os.path.join(projects_folder, project_name)
|
|
||||||
positions_file = os.path.join(project_folder, "positions.json")
|
|
||||||
|
|
||||||
if not os.path.exists(positions_file):
|
|
||||||
print(f"❌ No positions.json found in project {project_name}")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"✅ Testing with project: {project_name}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Import the animation generation code
|
|
||||||
import json
|
|
||||||
import matplotlib
|
|
||||||
matplotlib.use('Agg')
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from mpl_toolkits.mplot3d import Axes3D
|
|
||||||
import numpy as np
|
|
||||||
import cv2
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
# Load GPS data
|
|
||||||
with open(positions_file, 'r') as f:
|
|
||||||
positions = json.load(f)
|
|
||||||
|
|
||||||
print(f"✅ Loaded {len(positions)} GPS points")
|
|
||||||
|
|
||||||
if len(positions) < 2:
|
|
||||||
print("❌ Need at least 2 GPS points")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Test creating just 3 frames
|
|
||||||
test_frames_folder = os.path.join(project_folder, "test_frames")
|
|
||||||
os.makedirs(test_frames_folder, exist_ok=True)
|
|
||||||
|
|
||||||
# Extract coordinates
|
|
||||||
lats = np.array([pos['latitude'] for pos in positions[:10]]) # Just first 10 points
|
|
||||||
lons = np.array([pos['longitude'] for pos in positions[:10]])
|
|
||||||
alts = np.array([pos.get('altitude', 0) for pos in positions[:10]])
|
|
||||||
|
|
||||||
# Convert to relative coordinates
|
|
||||||
lat_center = np.mean(lats)
|
|
||||||
lon_center = np.mean(lons)
|
|
||||||
alt_min = np.min(alts)
|
|
||||||
|
|
||||||
x = (lons - lon_center) * 111320 * np.cos(np.radians(lat_center))
|
|
||||||
y = (lats - lat_center) * 110540
|
|
||||||
z = alts - alt_min
|
|
||||||
|
|
||||||
print(f"✅ Processed coordinates: x={x.min():.1f} to {x.max():.1f}, y={y.min():.1f} to {y.max():.1f}")
|
|
||||||
|
|
||||||
frame_files = []
|
|
||||||
|
|
||||||
# Create 3 test frames
|
|
||||||
for frame_idx in range(3):
|
|
||||||
end_point = (frame_idx + 1) * 3
|
|
||||||
end_point = min(end_point, len(x))
|
|
||||||
|
|
||||||
print(f"Creating frame {frame_idx + 1}, showing {end_point} points...")
|
|
||||||
|
|
||||||
# Create 3D plot
|
|
||||||
fig = plt.figure(figsize=(10, 8), dpi=100)
|
|
||||||
ax = fig.add_subplot(111, projection='3d')
|
|
||||||
|
|
||||||
# Plot route up to current point
|
|
||||||
if end_point > 1:
|
|
||||||
ax.plot(x[:end_point], y[:end_point], z[:end_point], 'b-', linewidth=2)
|
|
||||||
ax.scatter(x[:end_point], y[:end_point], z[:end_point], c='blue', s=20)
|
|
||||||
|
|
||||||
# Current position
|
|
||||||
if end_point > 0:
|
|
||||||
current_idx = end_point - 1
|
|
||||||
ax.scatter(x[current_idx], y[current_idx], z[current_idx],
|
|
||||||
c='red', s=100, marker='o')
|
|
||||||
|
|
||||||
# Remaining route
|
|
||||||
if end_point < len(x):
|
|
||||||
ax.plot(x[end_point:], y[end_point:], z[end_point:],
|
|
||||||
'lightgray', linewidth=1, alpha=0.3)
|
|
||||||
|
|
||||||
ax.set_xlabel('East-West (m)')
|
|
||||||
ax.set_ylabel('North-South (m)')
|
|
||||||
ax.set_zlabel('Elevation (m)')
|
|
||||||
ax.set_title(f'Test Frame {frame_idx + 1} - Point {end_point}/{len(x)}')
|
|
||||||
|
|
||||||
# Set view
|
|
||||||
margin = max(np.ptp(x), np.ptp(y)) * 0.1 if np.ptp(x) > 0 else 100
|
|
||||||
ax.set_xlim(np.min(x) - margin, np.max(x) + margin)
|
|
||||||
ax.set_ylim(np.min(y) - margin, np.max(y) + margin)
|
|
||||||
ax.set_zlim(np.min(z) - 10, np.max(z) + 10)
|
|
||||||
ax.view_init(elev=20, azim=45)
|
|
||||||
ax.grid(True)
|
|
||||||
|
|
||||||
# Save frame
|
|
||||||
frame_path = os.path.join(test_frames_folder, f"test_frame_{frame_idx:03d}.png")
|
|
||||||
try:
|
|
||||||
plt.savefig(frame_path, dpi=100, bbox_inches='tight',
|
|
||||||
facecolor='white', edgecolor='none', format='png')
|
|
||||||
plt.close(fig)
|
|
||||||
|
|
||||||
# Check frame
|
|
||||||
if os.path.exists(frame_path):
|
|
||||||
file_size = os.path.getsize(frame_path)
|
|
||||||
print(f"✅ Frame {frame_idx + 1} saved: {file_size} bytes")
|
|
||||||
|
|
||||||
# Test OpenCV reading
|
|
||||||
test_img = cv2.imread(frame_path)
|
|
||||||
if test_img is not None:
|
|
||||||
h, w, c = test_img.shape
|
|
||||||
print(f"✅ Frame {frame_idx + 1} readable by OpenCV: {w}x{h}")
|
|
||||||
frame_files.append(frame_path)
|
|
||||||
else:
|
|
||||||
print(f"❌ Frame {frame_idx + 1} not readable by OpenCV")
|
|
||||||
else:
|
|
||||||
print(f"❌ Frame {frame_idx + 1} not created")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error creating frame {frame_idx + 1}: {e}")
|
|
||||||
plt.close(fig)
|
|
||||||
|
|
||||||
print(f"Created {len(frame_files)} valid frames")
|
|
||||||
|
|
||||||
# Test video creation
|
|
||||||
if frame_files:
|
|
||||||
output_video = os.path.join(project_folder, f"test_progressive_{datetime.now().strftime('%H%M%S')}.mp4")
|
|
||||||
|
|
||||||
# Read first frame for dimensions
|
|
||||||
first_frame = cv2.imread(frame_files[0])
|
|
||||||
height, width, layers = first_frame.shape
|
|
||||||
|
|
||||||
print(f"Video dimensions: {width}x{height}")
|
|
||||||
|
|
||||||
# Create video
|
|
||||||
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
|
||||||
video_writer = cv2.VideoWriter(output_video, fourcc, 2.0, (width, height))
|
|
||||||
|
|
||||||
if video_writer.isOpened():
|
|
||||||
for i, frame_file in enumerate(frame_files):
|
|
||||||
frame = cv2.imread(frame_file)
|
|
||||||
if frame is not None:
|
|
||||||
video_writer.write(frame)
|
|
||||||
print(f"✅ Added frame {i+1} to video")
|
|
||||||
else:
|
|
||||||
print(f"❌ Could not read frame {i+1}")
|
|
||||||
|
|
||||||
video_writer.release()
|
|
||||||
|
|
||||||
if os.path.exists(output_video):
|
|
||||||
file_size = os.path.getsize(output_video)
|
|
||||||
print(f"✅ Video created: {output_video} ({file_size} bytes)")
|
|
||||||
else:
|
|
||||||
print("❌ Video file not created")
|
|
||||||
else:
|
|
||||||
print("❌ Could not open video writer")
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
for frame_file in frame_files:
|
|
||||||
try:
|
|
||||||
os.remove(frame_file)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
os.rmdir(test_frames_folder)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_progressive_animation_debug()
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for video creation functionality
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
sys.path.append('/home/pi/Desktop/traccar_animation')
|
|
||||||
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
import matplotlib
|
|
||||||
matplotlib.use('Agg')
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
def test_video_creation():
|
|
||||||
"""Test video creation with sample frames"""
|
|
||||||
|
|
||||||
print("Testing video creation functionality...")
|
|
||||||
|
|
||||||
# Create test directory
|
|
||||||
test_dir = "/tmp/video_test"
|
|
||||||
os.makedirs(test_dir, exist_ok=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Create sample frames
|
|
||||||
frame_files = []
|
|
||||||
for i in range(10):
|
|
||||||
# Create a simple test plot
|
|
||||||
fig, ax = plt.subplots(figsize=(8, 6))
|
|
||||||
|
|
||||||
# Simple animation - moving dot
|
|
||||||
x = np.linspace(0, 10, 100)
|
|
||||||
y = np.sin(x + i * 0.5)
|
|
||||||
|
|
||||||
ax.plot(x, y, 'b-', linewidth=2)
|
|
||||||
ax.scatter([i], [np.sin(i * 0.5)], c='red', s=100)
|
|
||||||
ax.set_xlim(0, 10)
|
|
||||||
ax.set_ylim(-2, 2)
|
|
||||||
ax.set_title(f'Test Frame {i+1}/10')
|
|
||||||
ax.grid(True)
|
|
||||||
|
|
||||||
# Save frame
|
|
||||||
frame_path = os.path.join(test_dir, f"frame_{i:03d}.png")
|
|
||||||
plt.savefig(frame_path, dpi=100, bbox_inches='tight')
|
|
||||||
plt.close(fig)
|
|
||||||
|
|
||||||
frame_files.append(frame_path)
|
|
||||||
print(f"Created frame {i+1}/10")
|
|
||||||
|
|
||||||
print(f"Created {len(frame_files)} test frames")
|
|
||||||
|
|
||||||
# Test video creation with different codecs
|
|
||||||
codecs_to_test = [
|
|
||||||
('mp4v', '.mp4'),
|
|
||||||
('XVID', '.avi'),
|
|
||||||
('MJPG', '.avi')
|
|
||||||
]
|
|
||||||
|
|
||||||
for codec, ext in codecs_to_test:
|
|
||||||
try:
|
|
||||||
output_path = os.path.join(test_dir, f"test_video_{codec}{ext}")
|
|
||||||
|
|
||||||
# Read first frame for dimensions
|
|
||||||
first_frame = cv2.imread(frame_files[0])
|
|
||||||
if first_frame is None:
|
|
||||||
print(f"❌ Could not read first frame")
|
|
||||||
continue
|
|
||||||
|
|
||||||
height, width, layers = first_frame.shape
|
|
||||||
print(f"Frame dimensions: {width}x{height}")
|
|
||||||
|
|
||||||
# Create video writer
|
|
||||||
fourcc = cv2.VideoWriter_fourcc(*codec)
|
|
||||||
video_writer = cv2.VideoWriter(output_path, fourcc, 5.0, (width, height))
|
|
||||||
|
|
||||||
if not video_writer.isOpened():
|
|
||||||
print(f"❌ Could not open video writer with {codec}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Write frames
|
|
||||||
frames_written = 0
|
|
||||||
for frame_file in frame_files:
|
|
||||||
frame = cv2.imread(frame_file)
|
|
||||||
if frame is not None:
|
|
||||||
video_writer.write(frame)
|
|
||||||
frames_written += 1
|
|
||||||
|
|
||||||
video_writer.release()
|
|
||||||
|
|
||||||
# Check result
|
|
||||||
if os.path.exists(output_path):
|
|
||||||
file_size = os.path.getsize(output_path)
|
|
||||||
if file_size > 1024: # At least 1KB
|
|
||||||
print(f"✅ {codec} video created: {output_path} ({file_size} bytes, {frames_written} frames)")
|
|
||||||
else:
|
|
||||||
print(f"❌ {codec} video too small: {file_size} bytes")
|
|
||||||
else:
|
|
||||||
print(f"❌ {codec} video not created")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error with {codec}: {e}")
|
|
||||||
|
|
||||||
# Check OpenCV version and capabilities
|
|
||||||
print(f"\nOpenCV version: {cv2.__version__}")
|
|
||||||
print(f"OpenCV build info available: {hasattr(cv2, 'getBuildInformation')}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# Clean up
|
|
||||||
for frame_file in frame_files:
|
|
||||||
try:
|
|
||||||
os.remove(frame_file)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
os.rmdir(test_dir)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_video_creation()
|
|
||||||
Reference in New Issue
Block a user