ConsistentlyInconsistentYT-.../visualize_results.py
2025-08-29 12:30:55 +02:00

526 lines
No EOL
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Visualization Script for Pixeltovoxelprojector Demo Results
==========================================================
This script loads the saved demo data and creates visualizations using matplotlib.
It provides multiple visualization modes for astronomical images, voxel grids,
and celestial sphere textures.
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from mpl_toolkits.mplot3d import Axes3D
import os
import json
import warnings
warnings.filterwarnings('ignore')
# Try to import optional dependencies
try:
import seaborn as sns
HAS_SEABORN = True
except ImportError:
HAS_SEABORN = False
print("Info: seaborn not available - using matplotlib defaults")
try:
from matplotlib.animation import FuncAnimation
HAS_ANIMATION = True
except ImportError:
HAS_ANIMATION = False
print("Info: matplotlib animation not available")
class PixeltovoxelVisualizer:
"""
A comprehensive visualizer for astronomical voxel data.
"""
def __init__(self, data_dir="./demo_output"):
"""
Initialize the visualizer with data from the specified directory.
"""
self.data_dir = data_dir
self.check_data_availability()
self.load_data()
# Set up pretty plotting style
if HAS_SEABORN:
sns.set_style("darkgrid")
plt.rcParams['figure.figsize'] = (12, 8)
plt.rcParams['font.size'] = 10
plt.rcParams['axes.labelsize'] = 12
plt.rcParams['axes.titlesize'] = 14
def check_data_availability(self):
"""Check if required data files exist."""
required_files = [
'synthetic_image.npy',
'voxel_grid.npy',
'celestial_sphere_texture.npy',
'demo_parameters.json'
]
missing_files = []
for filename in required_files:
filepath = os.path.join(self.data_dir, filename)
if not os.path.exists(filepath):
missing_files.append(filename)
if missing_files:
raise FileNotFoundError(f"Missing required data files: {missing_files}")
print(f"✓ Found all required data files in: {self.data_dir}")
def load_data(self):
"""Load all the demo data from numpy and json files."""
print("Loading demo data...")
# Load numpy arrays
self.image = np.load(os.path.join(self.data_dir, 'synthetic_image.npy'))
self.voxel_grid = np.load(os.path.join(self.data_dir, 'voxel_grid.npy'))
self.celestial_texture = np.load(os.path.join(self.data_dir, 'celestial_sphere_texture.npy'))
# Load parameters
with open(os.path.join(self.data_dir, 'demo_parameters.json'), 'r') as f:
self.params = json.load(f)
print(f"✓ Loaded image: {self.image.shape}")
print(f"✓ Loaded voxel grid: {self.voxel_grid.shape}")
print(f"✓ Loaded celestial texture: {self.celestial_texture.shape}")
def visualize_astronomical_image(self, save_path=None):
"""
Create a comprehensive visualization of the synthetic astronomical image.
"""
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Synthetic Astronomical Image Analysis', fontsize=16, fontweight='bold')
# 1. Main image
im1 = axes[0, 0].imshow(self.image, cmap='inferno', origin='lower')
axes[0, 0].set_title('Synthetic Astronomical Image')
axes[0, 0].set_xlabel('X pixel')
axes[0, 0].set_ylabel('Y pixel')
plt.colorbar(im1, ax=axes[0, 0], label='Brightness')
# 2. Image histogram
non_zero = self.image[self.image > 0]
axes[0, 1].hist(non_zero.flatten(), bins=50, alpha=0.7, color='blue', edgecolor='black')
axes[0, 1].set_title('Image Brightness Distribution')
axes[0, 1].set_xlabel('Brightness')
axes[0, 1].set_ylabel('Pixel Count')
axes[0, 1].set_yscale('log')
# 3. Bright star detection
threshold = np.mean(non_zero) + 2 * np.std(non_zero)
bright_mask = self.image > threshold
bright_coords = np.where(bright_mask)
axes[1, 0].imshow(self.image, cmap='gray', origin='lower', alpha=0.7)
if len(bright_coords[0]) > 0:
axes[1, 0].scatter(bright_coords[1], bright_coords[0],
c='red', s=10, alpha=0.8, label='Bright regions')
axes[1, 0].legend()
axes[1, 0].set_title(f'Detected Bright Regions (>{threshold:.4f})')
axes[1, 0].set_xlabel('X pixel')
axes[1, 0].set_ylabel('Y pixel')
# 4. Image statistics
axes[1, 1].axis('off')
stats_text = f"""Image Statistics:
Mean Brightness: {self.image.mean():.6f}
Max Brightness: {self.image.max():.6f}
Min Brightness: {self.image[self.image > 0].min():.6f} (non-zero)
Std Deviation: {self.image.std():.6f}
Star Detection:
Bright Regions: {np.sum(bright_mask)}
Detection Threshold: {threshold:.6f}
Total Pixels: {self.image.size}
Image Shape: {self.image.shape}
"""
axes[1, 1].text(0, 0.5, stats_text, transform=axes[1, 1].transAxes,
fontsize=12, verticalalignment='center',
bbox=dict(boxstyle="round,pad=0.5", facecolor="lightblue", alpha=0.5))
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✓ Saved astronomical image visualization to: {save_path}")
return fig
def visualize_voxel_grid(self, save_path=None, threshold_percentile=95):
"""
Create 3D visualization of the voxel grid.
"""
fig = plt.figure(figsize=(16, 8))
# Calculate threshold for visualization
if self.voxel_grid.size > 0 and np.max(self.voxel_grid) > 0:
threshold = np.percentile(self.voxel_grid[self.voxel_grid > 0], threshold_percentile)
else:
threshold = 0
# Get non-zero voxels
nonzero_mask = self.voxel_grid > threshold
if np.sum(nonzero_mask) == 0:
# No significant voxels - show summary
ax = fig.add_subplot(111)
ax.text(0.5, 0.5,
'.10f',
transform=ax.transAxes, ha='center', va='center',
fontsize=14,
bbox=dict(boxstyle="round,pad=1", facecolor="lightcoral"))
ax.set_title('Voxel Grid Analysis - No Significant Data')
ax.axis('off')
else:
# Get coordinates of non-zero voxels
coords = np.where(nonzero_mask)
values = self.voxel_grid[nonzero_mask]
# Create 3D scatter plot
ax = fig.add_subplot(121, projection='3d')
scatter = ax.scatter(coords[2], coords[1], coords[0],
c=values, cmap='plasma', alpha=0.7,
s=20, edgecolors='black', linewidth=0.5)
ax.set_xlabel('X coordinate')
ax.set_ylabel('Y coordinate')
ax.set_zlabel('Z coordinate')
ax.set_title(f'3D Voxel Grid Visualization\n(Threshold: {threshold:.2e})')
plt.colorbar(scatter, ax=ax, label='Voxel Value', shrink=0.7)
# Add 2D projections
ax2 = fig.add_subplot(222)
ax2.scatter(coords[2], coords[0], c=values, cmap='plasma', alpha=0.6, s=10)
ax2.set_xlabel('X')
ax2.set_ylabel('Z')
ax2.set_title('X-Z Projection')
ax3 = fig.add_subplot(224)
ax3.scatter(coords[1], coords[0], c=values, cmap='plasma', alpha=0.6, s=10)
ax3.set_xlabel('Y')
ax3.set_ylabel('Z')
ax3.set_title('Y-Z Projection')
ax4 = fig.add_subplot(223)
ax4.scatter(coords[1], coords[2], c=values, cmap='plasma', alpha=0.6, s=10)
ax4.set_xlabel('Y')
ax4.set_ylabel('X')
ax4.set_title('Y-X Projection (top view)')
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✓ Saved voxel grid visualization to: {save_path}")
return fig
def visualize_celestial_sphere(self, save_path=None):
"""
Visualize the celestial sphere texture with astronomical coordinates.
"""
fig = plt.figure(figsize=(14, 6))
# Create a RA/Dec grid for the texture
ra_deg = np.linspace(0, 360, self.celestial_texture.shape[1])
dec_deg = np.linspace(-90, 90, self.celestial_texture.shape[0])
# Plot the celestial sphere
ax1 = fig.add_subplot(121)
im1 = ax1.imshow(self.celestial_texture, extent=[0, 360, -90, 90],
aspect='auto', cmap='viridis', origin='lower')
ax1.set_xlabel('Right Ascension (degrees)')
ax1.set_ylabel('Declination (degrees)')
ax1.set_title('Celestial Sphere Texture')
plt.colorbar(im1, ax=ax1, label='Intensity')
# Add equatorial line
ax1.axhline(y=0, color='red', linestyle='--', alpha=0.7, label='Celestial Equator')
ax1.legend()
# Plot intensity distribution
ax2 = fig.add_subplot(222)
ax2.hist(self.celestial_texture.flatten(), bins=50, alpha=0.7,
color='blue', edgecolor='black')
ax2.set_title('Intensity Distribution')
ax2.set_xlabel('Intensity')
ax2.set_ylabel('Pixel Count')
ax2.set_yscale('log')
# Polar plot of celestial sphere
ax3 = fig.add_subplot(224, polar=True)
# Convert to polar coordinates
nonzero = self.celestial_texture[self.celestial_texture > 0]
if len(nonzero) > 0:
# Find brightest region in celestial coordinates
bright_idx = np.unravel_index(np.argmax(self.celestial_texture),
self.celestial_texture.shape)
bright_ra = ra_deg[bright_idx[1]]
bright_dec = dec_deg[bright_idx[0]]
# Convert to polar coordinates
r = 90 - bright_dec # Distance from north celestial pole
theta = np.radians(bright_ra)
ax3.scatter(theta, r, s=100, c='red', alpha=0.8, edgecolors='black')
ax3.set_title('Brightest Region\n(celestial coordinates)')
# Set up polar plot properly
ax3.set_rlim(0, 90)
ax3.set_rticks([30, 60, 90])
ax3.set_rlabel_position(135)
ax3.grid(True, alpha=0.3)
# Statistics
ax4 = fig.add_subplot(223)
ax4.axis('off')
stats_text = ".2f"f"""Celestial Sphere Stats:
Shape: {self.celestial_texture.shape}
Total Pixels: {self.celestial_texture.size}
Max Intensity: {self.celestial_texture.max():.6f}
Min Intensity: {self.celestial_texture.min():.6f}
Non-zero Pixels: {np.count_nonzero(self.celestial_texture)}
Coverage: 360° RA × 180° Dec
Resolution: {360/self.celestial_texture.shape[1]:.2f}°/pixel
"""
ax4.text(0, 0.5, stats_text, transform=ax4.transAxes,
fontsize=11, verticalalignment='center',
bbox=dict(boxstyle="round,pad=0.5", facecolor="lightyellow"))
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"✓ Saved celestial sphere visualization to: {save_path}")
return fig
def create_comprehensive_report(self, save_dir="./visualizations"):
"""
Generate a comprehensive visual report with all data visualizations.
"""
os.makedirs(save_dir, exist_ok=True)
print("\n" + "="*60)
print("GENERATING COMPREHENSIVE VISUALIZATION REPORT")
print("="*60)
# Astronomical image visualization
print("\n1. Creating astronomical image visualization...")
fig1 = self.visualize_astronomical_image()
fig1.savefig(os.path.join(save_dir, 'astronomical_image_analysis.png'),
dpi=300, bbox_inches='tight')
plt.close(fig1)
# Voxel grid visualization
print("\n2. Creating 3D voxel grid visualization...")
fig2 = self.visualize_voxel_grid()
if np.sum(self.voxel_grid > 0) > 0: # Only save if there's data
fig2.savefig(os.path.join(save_dir, 'voxel_grid_3d.png'),
dpi=300, bbox_inches='tight')
else:
print(" → Skipping voxel grid (no significant data)")
plt.close(fig2)
# Celestial sphere visualization
print("\n3. Creating celestial sphere visualization...")
fig3 = self.visualize_celestial_sphere()
fig3.savefig(os.path.join(save_dir, 'celestial_sphere_texture.png'),
dpi=300, bbox_inches='tight')
plt.close(fig3)
# Create summary plot
print("\n4. Creating summary dashboard...")
self.create_summary_dashboard(save_dir)
print(f"\n✓ All visualizations saved to: {save_dir}")
print("✓ Report generation completed!")
def create_summary_dashboard(self, save_dir):
"""
Create a summary dashboard with key metrics and small previews.
"""
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
fig.suptitle('Pixeltovoxelprojector Demo Results - Summary Dashboard',
fontsize=16, fontweight='bold')
# 1. Image preview
axes[0, 0].imshow(self.image, cmap='inferno', origin='lower')
axes[0, 0].set_title('Astronomical Image')
axes[0, 0].set_xlabel('X pixel')
axes[0, 0].set_ylabel('Y pixel')
# 2. Image histogram
non_zero = self.image[self.image > 0]
axes[1, 0].hist(non_zero.flatten(), bins=30, alpha=0.7, color='green')
axes[1, 0].set_title('Brightness Histogram')
axes[1, 0].set_xlabel('Brightness')
axes[1, 0].set_ylabel('Count')
axes[1, 0].set_yscale('log')
# 3. Voxel grid scatter
voxel_coords = np.where(self.voxel_grid > 0)
if len(voxel_coords[0]) > 0:
axes[0, 1].scatter(voxel_coords[2], voxel_coords[1], voxel_coords[0],
c=self.voxel_grid[voxel_coords], cmap='plasma',
alpha=0.6, s=5)
axes[0, 1].set_title(f'Voxel Grid ({len(voxel_coords[0])} points)')
axes[0, 1].set_xlabel('X')
axes[0, 1].set_ylabel('Y')
axes[0, 1].set_zlabel('Z')
else:
axes[0, 1].text(0.5, 0.5, 'No significant\nvoxel data',
transform=axes[0, 1].transAxes, ha='center', va='center',
fontsize=12, bbox=dict(boxstyle="round", facecolor="lightcoral"))
# 4. Celestial sphere polar plot
axes[1, 1].remove()
ax_polar = fig.add_subplot(2, 3, 5, polar=True)
if np.max(self.celestial_texture) > 0:
ax_polar.text(0, 0.5, 'Celestial\nSphere\nAvailable',
transform=ax_polar.transAxes, ha='center', va='center')
else:
ax_polar.text(0, 0.5, 'Celestial\nSphere\n(Not processed)',
transform=ax_polar.transAxes, ha='center', va='center')
ax_polar.set_title('Celestial Coverage', pad=20)
# 5. Statistics summary
axes[0, 2].axis('off')
stats_text = f"""SUMMARY STATISTICS
Image Metrics:
Shape: {self.params['image_shape']}
Mean: {self.image.mean():.4e}
Max: {self.image.max():.4e}
Std: {self.image.std():.4e}
Voxel Grid:
Shape: {self.params['voxel_shape']}
Total Voxels: {self.voxel_grid.size:,}
Non-zero: {np.count_nonzero(self.voxel_grid):,}
Max Value: {self.voxel_grid.max():.4e}
Celestial Texture:
Shape: {self.params['texture_shape']}
Total Pixels: {self.celestial_texture.size:,}
Non-zero: {np.count_nonzero(self.celestial_texture):,}
Max Value: {self.celestial_texture.max():.4e}
"""
axes[0, 2].text(0, 0.5, stats_text, transform=axes[0, 2].transAxes,
fontsize=10, verticalalignment='center',
bbox=dict(boxstyle="round,pad=0.5", facecolor="lightblue", alpha=0.7))
# 6. Processing status
axes[1, 2].axis('off')
status_text = """PROCESSING STATUS
✓ Synthetic astronomical image generated
✓ Star field simulation completed
✓ Voxel grid initialized
✓ Celestial sphere texture initialized
✓ Data export completed
Note: Actual voxel processing requires
compiled C++ library to be called.
Use: python demo_pixeltovoxel.py
"""
axes[1, 2].text(0, 0.5, status_text, transform=axes[1, 2].transAxes,
fontsize=11, verticalalignment='center',
bbox=dict(boxstyle="round,pad=0.5", facecolor="lightgreen", alpha=0.7))
plt.tight_layout()
fig.savefig(os.path.join(save_dir, 'summary_dashboard.png'),
dpi=300, bbox_inches='tight')
plt.close(fig)
def show_interactive_voxel_slice(self):
"""
Create an interactive voxel grid slice viewer.
Requires matplotlib animation support.
"""
if not HAS_ANIMATION:
print("Matplotlib animation not available. Skipping interactive viewer.")
return
fig, ax = plt.subplots(1, 1, figsize=(10, 8))
def update_frame(frame):
ax.clear()
slice_data = self.voxel_grid[:, :, frame]
im = ax.imshow(slice_data, cmap='plasma', origin='lower')
ax.set_title(f'Voxel Grid Slice Z={frame}')
ax.set_xlabel('X coordinate')
ax.set_ylabel('Y coordinate')
plt.colorbar(im, ax=ax, label='Intensity')
return [im]
# Create animation
num_frames = self.voxel_grid.shape[2]
if num_frames > 1:
anim = FuncAnimation(fig, update_frame, frames=num_frames,
interval=200, blit=False)
print("Interactive voxel slice viewer created!")
return anim
else:
print("No animation needed - only one slice available.")
return None
def main():
"""
Main function to run all visualizations.
"""
print("Pixeltovoxelprojector - Results Visualization")
print("=" * 50)
try:
# Create visualizer
visualizer = PixeltovoxelVisualizer()
# Generate comprehensive report
visualizer.create_comprehensive_report()
# Try to show individual visualizations
print("\n" + "="*50)
print("INDIVIDUAL VISUALIZATIONS AVAILABLE:")
print("1. Astronomical image analysis")
print("2. 3D voxel grid visualization")
print("3. Celestial sphere texture")
print("4. Summary dashboard")
print("="*50)
# Optional: Show interactive visualization
if HAS_ANIMATION and input("\nShow interactive voxel slice viewer? (y/N): ").lower().startswith('y'):
anim = visualizer.show_interactive_voxel_slice()
if anim:
plt.show()
else:
print("\nVisualization complete! Check the ./visualizations/ directory for all images.")
except FileNotFoundError as e:
print(f"Error: {e}")
print("\nMake sure you've run the demo script first:")
print("python demo_pixeltovoxel.py")
except Exception as e:
print(f"Visualization error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()