Spaces:
Running
Running
| """ | |
| iFFT Cochlea Node - Reconstructs an image from a complex spectrum. | |
| Based on the hardwired iFFTCochleaNode from anttis_perception_laboratory.py | |
| Requires: pip install scipy | |
| Place this file in the 'nodes' folder | |
| """ | |
| import numpy as np | |
| from PyQt6 import QtGui | |
| import cv2 | |
| # --- !! CRITICAL IMPORT BLOCK !! --- | |
| import __main__ | |
| BaseNode = __main__.BaseNode | |
| QtGui = __main__.QtGui | |
| PA_INSTANCE = getattr(__main__, "PA_INSTANCE", None) | |
| # ----------------------------------- | |
| try: | |
| from scipy.fft import irfft | |
| SCIPY_AVAILABLE = True | |
| except ImportError: | |
| SCIPY_AVAILABLE = False | |
| print("Warning: iFFTCochleaNode requires 'scipy'.") | |
| print("Please run: pip install scipy") | |
| class iFFTCochleaNode(BaseNode): | |
| """ | |
| Performs an Inverse Real FFT on a complex spectrum (from FFTCochleaNode) | |
| to reconstruct a 2D image. | |
| """ | |
| NODE_CATEGORY = "Transform" | |
| NODE_COLOR = QtGui.QColor(200, 100, 60) | |
| def __init__(self, height=120, width=160): | |
| super().__init__() | |
| self.node_title = "iFFT Cochlea" | |
| self.inputs = {'complex_spectrum': 'complex_spectrum'} | |
| self.outputs = {'image': 'image'} | |
| self.h, self.w = height, width | |
| self.reconstructed_img = np.zeros((self.h, self.w), dtype=np.float32) | |
| def step(self): | |
| if not SCIPY_AVAILABLE: | |
| return | |
| complex_spec = self.get_blended_input('complex_spectrum', 'mean') | |
| if complex_spec is not None and complex_spec.ndim == 2: | |
| try: | |
| # Perform inverse real FFT | |
| img = irfft(complex_spec, axis=1).astype(np.float32) | |
| # Resize to target output size (just in case) | |
| self.reconstructed_img = cv2.resize(img, (self.w, self.h)) | |
| # Normalize for viewing (0-1) | |
| min_v, max_v = np.min(self.reconstructed_img), np.max(self.reconstructed_img) | |
| if (max_v - min_v) > 1e-6: | |
| self.reconstructed_img = (self.reconstructed_img - min_v) / (max_v - min_v) | |
| else: | |
| self.reconstructed_img.fill(0.5) | |
| except Exception as e: | |
| print(f"iFFT Error: {e}") | |
| self.reconstructed_img.fill(0.0) | |
| else: | |
| # Fade to black if no input | |
| self.reconstructed_img *= 0.9 | |
| def get_output(self, port_name): | |
| if port_name == 'image': | |
| return self.reconstructed_img | |
| return None | |
| def get_display_image(self): | |
| img_u8 = (np.clip(self.reconstructed_img, 0, 1) * 255).astype(np.uint8) | |
| img_u8 = np.ascontiguousarray(img_u8) | |
| return QtGui.QImage(img_u8.data, self.w, self.h, self.w, QtGui.QImage.Format.Format_Grayscale8) | |
| def get_config_options(self): | |
| return [ | |
| ("Height", "height", self.h, None), | |
| ("Width", "width", self.w, None) | |
| ] |