Spaces:
Runtime error
Runtime error
| """ | |
| logos/baker.py - The SPCW Encoder (Fractal Compression) | |
| """ | |
| import numpy as np | |
| from .fractal_engine import ContextAction | |
| class BreadBaker: | |
| """ | |
| Encodes image tiles into Prime Atoms using Quadtree Decomposition. | |
| """ | |
| def __init__(self, variance_threshold=30.0, min_block_size=8): | |
| self.variance_threshold = variance_threshold | |
| self.min_block_size = min_block_size | |
| def bake(self, tile: np.ndarray, prefix_path: list = None) -> list: | |
| """ | |
| Compress a tile into atom definitions. | |
| Returns list of dicts: {'path_bits': [], 'payload': bytes} | |
| """ | |
| self.atoms = [] | |
| if prefix_path is None: | |
| prefix_path = [] | |
| self._recursive_decompose(tile, level=0, path_bits=prefix_path) | |
| return self.atoms | |
| def _recursive_decompose(self, block: np.ndarray, level: int, path_bits: list): | |
| h, w = block.shape[:2] | |
| if h == 0 or w == 0: return | |
| mean_val = np.mean(block, axis=(0, 1)) | |
| variance = np.var(block) | |
| # Stop condition: Low variance OR too small OR max depth | |
| # Max local depth 5 (512 -> 16px blocks) | |
| is_leaf = (variance < self.variance_threshold) or (w <= self.min_block_size) or (level >= 5) | |
| if is_leaf: | |
| self._emit_atom(mean_val, path_bits) | |
| else: | |
| # Subdivide | |
| half_h, half_w = h // 2, w // 2 | |
| # TL, TR, BL, BR | |
| self._recursive_decompose(block[:half_h, :half_w], level + 1, path_bits + [0b00]) | |
| self._recursive_decompose(block[:half_h, half_w:], level + 1, path_bits + [0b01]) | |
| self._recursive_decompose(block[half_h:, :half_w], level + 1, path_bits + [0b10]) | |
| self._recursive_decompose(block[half_h:, half_w:], level + 1, path_bits + [0b11]) | |
| def _emit_atom(self, rgb_mean, path_bits): | |
| r, g, b = int(rgb_mean[0]), int(rgb_mean[1]), int(rgb_mean[2]) | |
| matrix_key = (b << 16) | (g << 8) | r | |
| # --- ATOM 1: WARM UP (Harmonic Resonance) --- | |
| # We must raise heat_state from 0 to 1 so that GF4 multiplication (Dissolution) | |
| # has a non-zero scalar. 0 * Input = 0 (No Change). | |
| # ContextAction.PERSIST (00) increments heat. | |
| warm_action = ContextAction.PERSIST | |
| warm_ctrl = (warm_action.value << 6) & 0xC0 | |
| warm_payload = bytes([warm_ctrl]) | |
| self.atoms.append({ | |
| "path_bits": path_bits, | |
| "payload": warm_payload | |
| }) | |
| # --- ATOM 2: STRUCTURE (Matrix Key) --- | |
| # Pack payload compatible with LogoFractalEngine vector decoder | |
| # We pack matrix_key (RGB) into the vector slots | |
| action = ContextAction.CHANGE_1 | |
| control_byte = (action.value << 6) & 0xC0 | |
| payload_bytes = bytearray() | |
| payload_bytes.append(control_byte) | |
| # Convert RGB integer to 2-bit vector sequence | |
| vector = [(matrix_key >> (i*2)) & 0b11 for i in range(16)] | |
| # Pack vector into 4 bytes | |
| # Note: reconstruction expects this exact packing to retrieve matrix_key | |
| # Byte order matters. We pack vector[0..3] into first byte, etc. | |
| # This aligns with `_payload_to_vector` in fractal_engine. | |
| # Re-verify _payload_to_vector in fractal_engine: | |
| # for byte in payload[:8]: | |
| # append((byte >> 6) & 0b11) ... | |
| # So we pack vector[0] into MSB of byte 0? | |
| # No, `_payload_to_vector` extracts MSB first: `(byte >> 6) & 0b11`. | |
| # So we should pack vector[0] at MSB. | |
| current_vector_idx = 0 | |
| for _ in range(4): # 4 bytes to hold 16 pairs | |
| b_val = 0 | |
| if current_vector_idx < 16: b_val |= (vector[current_vector_idx] & 0b11) << 6 | |
| if current_vector_idx+1 < 16: b_val |= (vector[current_vector_idx+1] & 0b11) << 4 | |
| if current_vector_idx+2 < 16: b_val |= (vector[current_vector_idx+2] & 0b11) << 2 | |
| if current_vector_idx+3 < 16: b_val |= (vector[current_vector_idx+3] & 0b11) | |
| payload_bytes.append(b_val) | |
| current_vector_idx += 4 | |
| self.atoms.append({ | |
| "path_bits": path_bits, | |
| "payload": bytes(payload_bytes) | |
| }) | |