Spaces:
Running
Running
File size: 3,701 Bytes
3bb804c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
"""
PC Visualizer Node - Visualize what each principal component controls
Shows the "eigenfaces" of your frequency space
"""
import numpy as np
from PyQt6 import QtGui
import cv2
import __main__
BaseNode = __main__.BaseNode
QtGui = __main__.QtGui
class PCVisualizerNode(BaseNode):
"""
Visualizes individual principal components as images.
Connect to SpectralPCA to see what each PC represents.
"""
NODE_CATEGORY = "AI / Physics"
NODE_COLOR = QtGui.QColor(180, 220, 120)
def __init__(self, pc_index=0, amplitude=3.0):
super().__init__()
self.node_title = f"PC Visualizer (PC{pc_index})"
self.inputs = {
'pca_node': 'node_reference', # Reference to SpectralPCA node
'amplitude': 'signal' # How much to amplify
}
self.outputs = {
'image': 'image',
'complex_spectrum': 'complex_spectrum',
'variance_explained': 'signal'
}
self.pc_index = int(pc_index)
self.amplitude = float(amplitude)
# Visualization
self.pc_image = np.zeros((128, 128, 3), dtype=np.uint8)
self.pc_spectrum = None
self.variance_explained = 0.0
def step(self):
# Get amplitude modulation
amp_signal = self.get_blended_input('amplitude', 'sum')
if amp_signal is not None:
amplitude = amp_signal * 10.0 # Scale up for visibility
else:
amplitude = self.amplitude
# Get reference to PCA node (this is a bit of a hack)
# In practice, you'd connect SpectralPCA's outputs here
# For now, we'll create a synthetic visualization
# Create a spectrum with just this PC activated
# This would come from: mean_spectrum + pc_component * amplitude
# Placeholder: create a synthetic pattern
size = 64
freq = self.pc_index + 1
# Each PC might represent a different frequency pattern
y, x = np.ogrid[0:size, 0:size]
pattern = np.sin(2*np.pi*freq*x/size) * np.cos(2*np.pi*freq*y/size)
pattern = pattern * amplitude
# Visualize
pattern_norm = (pattern - pattern.min()) / (pattern.max() - pattern.min() + 1e-9)
self.pc_image = cv2.applyColorMap((pattern_norm * 255).astype(np.uint8),
cv2.COLORMAP_VIRIDIS)
# Create complex spectrum (simplified)
self.pc_spectrum = np.fft.fft2(pattern)
# Variance explained (would come from PCA node)
self.variance_explained = 1.0 / (self.pc_index + 1) # Decreasing
def get_output(self, port_name):
if port_name == 'image':
return self.pc_image.astype(np.float32) / 255.0
elif port_name == 'complex_spectrum':
return self.pc_spectrum
elif port_name == 'variance_explained':
return self.variance_explained
return None
def get_display_image(self):
img = self.pc_image.copy()
# Add label
label = f"PC{self.pc_index}: {self.variance_explained:.1%}"
cv2.putText(img, label, (5, 15), cv2.FONT_HERSHEY_SIMPLEX,
0.4, (255,255,255), 1)
return QtGui.QImage(img.data, 128, 128, 128*3, QtGui.QImage.Format.Format_RGB888)
def get_config_options(self):
return [
("PC Index", "pc_index", self.pc_index, None),
("Amplitude", "amplitude", self.amplitude, None)
]
|