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
|
||||
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:
|
||||
self.setup_blender_scene()
|
||||
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):
|
||||
"""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
|
||||
)
|
||||
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.button import Button
|
||||
from kivy.uix.label import Label
|
||||
@@ -250,15 +250,15 @@ class CreateAnimationScreen(Screen):
|
||||
Clock.schedule_once(lambda dt: run_google_earth_animation(), 0.5)
|
||||
|
||||
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
|
||||
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)
|
||||
layout.add_widget(label)
|
||||
layout.add_widget(progress)
|
||||
popup = Popup(
|
||||
title="Generating Blender Cinema Animation",
|
||||
title="Generating Cinema Animation",
|
||||
content=layout,
|
||||
size_hint=(0.9, None),
|
||||
size=(0, 200),
|
||||
@@ -283,39 +283,244 @@ class CreateAnimationScreen(Screen):
|
||||
Clock.schedule_once(lambda dt: popup.dismiss(), 2)
|
||||
return
|
||||
|
||||
update_status(10, "Loading GPS data into Blender...")
|
||||
# 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...")
|
||||
|
||||
# Check dependencies first
|
||||
animator = BlenderGPSAnimator(project_folder)
|
||||
animator.check_dependencies()
|
||||
# Use Blender for rendering
|
||||
animator = BlenderGPSAnimator(project_folder)
|
||||
animator.check_dependencies()
|
||||
|
||||
update_status(25, "Processing GPS coordinates...")
|
||||
gps_data = animator.load_gps_data(positions_path)
|
||||
update_status(25, "Processing GPS coordinates...")
|
||||
gps_data = animator.load_gps_data(positions_path)
|
||||
|
||||
output_video_path = os.path.join(project_folder, f"{self.project_name}_blender_cinema_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||
output_video_path = os.path.join(project_folder, f"{self.project_name}_blender_cinema_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
|
||||
|
||||
# Progress callback for the animator
|
||||
def animator_progress(progress, message):
|
||||
update_status(25 + (progress * 0.6), message) # Map 0-100% to 25-85%
|
||||
# Progress callback for the animator
|
||||
def animator_progress(progress, message):
|
||||
update_status(25 + (progress * 0.6), message) # Map 0-100% to 25-85%
|
||||
|
||||
update_status(85, "Rendering cinema-quality video...")
|
||||
success = animator.create_gps_animation(
|
||||
positions_path,
|
||||
output_video_path,
|
||||
progress_callback=animator_progress
|
||||
)
|
||||
update_status(85, "Rendering cinema-quality video...")
|
||||
success = animator.create_gps_animation(
|
||||
positions_path,
|
||||
output_video_path,
|
||||
progress_callback=animator_progress
|
||||
)
|
||||
|
||||
if success:
|
||||
update_status(100, "Blender cinema animation complete!")
|
||||
output_path = output_video_path
|
||||
else:
|
||||
raise Exception("Failed to generate Blender animation")
|
||||
if success:
|
||||
update_status(100, "Blender cinema animation complete!")
|
||||
output_path = output_video_path
|
||||
else:
|
||||
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):
|
||||
popup.dismiss()
|
||||
self.show_success_popup(
|
||||
"Blender Cinema Animation Complete!",
|
||||
f"Your cinema-quality animation has been saved to:\n{output_path}",
|
||||
"Cinema Animation Complete!",
|
||||
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
|
||||
)
|
||||
|
||||
@@ -323,9 +528,12 @@ class CreateAnimationScreen(Screen):
|
||||
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
print(f"DEBUG: Cinema animation error: {error_message}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
def show_error(dt):
|
||||
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)
|
||||
|
||||
|
||||
@@ -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