IMMEDIATE HIGH-IMPACT: Replace Matplotlib with Plotly WebGL ⭐⭐⭐ Why Perfect for Your Use Case: 10-100x faster rendering than matplotlib Hardware-accelerated WebGL Maintains your existing workflow Easy integration with tkinter import plotly.graph_objects as go import plotly.offline as pyo from plotly.subplots import make_subplots import webview
class OptimizedPlotlyRenderer: def init(self): self.fig = None self.webview_window = None
def create_optimized_voxel_plot(self, coords, colors, title="3D Voxel Plot"):
"""Replace matplotlib voxel plot with Plotly WebGL scatter"""
# Subsample if too many points (maintain visual fidelity)
if len(coords) > 50000:
indices = np.random.choice(len(coords), 50000, replace=False)
coords = coords[indices]
colors = colors[indices]
# Convert colors to RGB strings
rgb_colors = [f'rgb({int(c[0]*255)},{int(c[1]*255)},{int(c[2]*255)})' for c in colors]
trace = go.Scatter3d(
x=coords[:, 0],
y=coords[:, 1],
z=coords[:, 2],
mode='markers',
marker=dict(
color=rgb_colors,
size=3,
opacity=0.8,
line=dict(width=0) # Remove outlines for performance
),
hoverinfo='skip' # Disable hover for performance
)
layout = go.Layout(
title=title,
scene=dict(
xaxis=dict(title='X axis', showgrid=False),
yaxis=dict(title='Y axis', showgrid=False),
zaxis=dict(title='Z axis', showgrid=False),
bgcolor='white'
),
margin=dict(l=0, r=0, b=0, t=30),
showlegend=False
)
self.fig = go.Figure(data=[trace], layout=layout)
return self.fig
def embed_in_tkinter(self, parent_frame):
"""Embed Plotly plot in tkinter using webview"""
import tempfile
import os
# Save plot as HTML
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.html')
pyo.plot(self.fig, filename=temp_file.name, auto_open=False)
# Create webview window
self.webview_window = webview.create_window(
'Plot',
temp_file.name,
width=800,
height=600,
resizable=True
)
return self.webview_window
INTELLIGENT DATA REDUCTION SYSTEM ⭐⭐⭐
Critical for your large TIFF files:
class SmartDataReducer: def init(self): self.reduction_strategies = { 'spatial_sampling': self._spatial_sampling, 'importance_sampling': self._importance_sampling, 'adaptive_lod': self._adaptive_lod }
def reduce_for_visualization(self, mask, target_points=25000):
"""Intelligently reduce voxel data while preserving structure"""
coords = np.argwhere(mask)
if len(coords) <= target_points:
return coords
# Use spatial sampling to preserve structure
return self._spatial_sampling(coords, target_points)
def _spatial_sampling(self, coords, target_points):
"""Sample points while preserving spatial distribution"""
from sklearn.cluster import KMeans
# Cluster points and sample from each cluster
n_clusters = min(target_points, len(coords))
kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
try:
clusters = kmeans.fit_predict(coords)
# Take representative points from each cluster
sampled_coords = []
for i in range(n_clusters):
cluster_points = coords[clusters == i]
if len(cluster_points) > 0:
# Take point closest to cluster center
center = kmeans.cluster_centers_[i]
distances = np.linalg.norm(cluster_points - center, axis=1)
best_idx = np.argmin(distances)
sampled_coords.append(cluster_points[best_idx])
return np.array(sampled_coords)
except:
# Fallback to random sampling
indices = np.random.choice(len(coords), target_points, replace=False)
return coords[indices]
def _importance_sampling(self, coords, colors, target_points):
"""Sample based on color importance and spatial distribution"""
# Prioritize boundary voxels and unique colors
# Implementation for scientific data preservation
pass
. STREAMING DATA MANAGER ⭐⭐ Essential for large TIFF files:
class TIFFStreamingManager: def init(self, chunk_size=100): self.chunk_size = chunk_size self.cached_chunks = {} self.max_cache_size = 10 # Number of chunks to keep in memory
def load_tiff_streaming(self, image_path):
"""Load TIFF file with streaming to reduce memory usage"""
try:
with tiff.TiffFile(image_path) as tif:
# Get metadata first
self.total_frames = len(tif.pages)
self.image_shape = tif.pages[0].shape
self.dtype = tif.pages[0].dtype
# Load palette from first page
first_page = tif.pages[0]
if hasattr(first_page, 'colormap') and first_page.colormap is not None:
self.palette = self._extract_palette(first_page.colormap)
else:
# Extract from PIL for P mode images
img = Image.open(image_path)
if img.mode == 'P':
palette = img.getpalette()
self.palette = self._process_palette(palette)
return True
except Exception as e:
print(f"Error loading TIFF: {e}")
return False
def get_chunk(self, start_frame, end_frame, image_path):
"""Get a specific chunk of frames"""
chunk_key = (start_frame, end_frame)
if chunk_key in self.cached_chunks:
return self.cached_chunks[chunk_key]
# Load chunk from file
try:
with tiff.TiffFile(image_path) as tif:
chunk_data = []
for i in range(start_frame, min(end_frame, len(tif.pages))):
page_data = tif.pages[i].asarray()
chunk_data.append(page_data)
chunk_array = np.stack(chunk_data, axis=0)
# Cache management
if len(self.cached_chunks) >= self.max_cache_size:
# Remove oldest chunk
oldest_key = next(iter(self.cached_chunks))
del self.cached_chunks[oldest_key]
self.cached_chunks[chunk_key] = chunk_array
return chunk_array
except Exception as e:
print(f"Error loading chunk: {e}")
return None
-
BACKGROUND PROCESSING OPTIMIZATION ⭐⭐ Prevent UI freezing: class BackgroundProcessor: def init(self): self.executor = ThreadPoolExecutor(max_workers=4) self.processing_queue = Queue()
def process_color_async(self, spine_number, callback): """Process spine data in background thread""" future = self.executor.submit(self._process_spine_data, spine_number)
def on_complete(fut): try: result = fut.result() # Update UI in main thread callback(result) except Exception as e: print(f"Background processing error: {e}") future.add_done_callback(on_complete) return futuredef _process_spine_data(self, spine_number): """Process spine data without blocking UI""" # Extract mask efficiently mask = self.image_data == spine_number coords = np.argwhere(mask)
# Reduce data for visualization if len(coords) > 25000: reducer = SmartDataReducer() coords = reducer.reduce_for_visualization(mask, 25000) # Get colors colors = [] for coord in coords: pixel_value = self.image_data[tuple(coord)] if pixel_value < len(self.rgb_colors): colors.append(np.array(self.rgb_colors[pixel_value]) / 255.0) return { 'coords': coords, 'colors': np.array(colors), 'original_mask': mask, 'spine_number': spine_number } -
MODIFIED VIEWER INTEGRATION ⭐⭐⭐
def show_single_color_frame_optimized(self): """Optimized single color frame with all performance improvements""" self.clear_frames() self.single_color_frame.pack(fill=tk.BOTH, expand=True)
# Add navbar
self.add_navbar(self.single_color_frame)
# Create container for plot
plot_container = tk.Frame(self.single_color_frame)
plot_container.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Show loading indicator
loading_label = tk.Label(plot_container, text="Processing 3D visualization...",
font=("Helvetica", 14))
loading_label.pack(expand=True)
# Process in background
p_value = self.viewer.selected_p_values[self.viewer.current_color_index]
def on_processing_complete(result):
# Remove loading indicator
loading_label.destroy()
# Create optimized plot
renderer = OptimizedPlotlyRenderer()
fig = renderer.create_optimized_voxel_plot(
result['coords'],
result['colors'],
f"Spine {p_value} - 3D Visualization"
)
# Embed in tkinter (you'll need to use a web widget or export to image)
self._embed_plotly_plot(fig, plot_container)
# Create metadata panel with the result
self._create_metadata_panel_optimized(p_value, result['original_mask'])
# Start background processing
processor = BackgroundProcessor()
processor.process_color_async(p_value, on_processing_complete)