Skip to content

Latest commit

 

History

History
290 lines (234 loc) · 10.6 KB

File metadata and controls

290 lines (234 loc) · 10.6 KB

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
  1. 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 future
    

    def _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
     }
    
  2. MODIFIED VIEWER INTEGRATION ⭐⭐⭐

Replace your show_single_color_frame method

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)