Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import os, struct, math, tempfile
|
| 3 |
+
import numpy as np
|
| 4 |
+
from PIL import Image
|
| 5 |
+
|
| 6 |
+
# --- FLC v1.3 Logic Engine ---
|
| 7 |
+
PHI = (1.0 + 5.0**0.5) / 2.0
|
| 8 |
+
|
| 9 |
+
def fibonacci_sequence(n):
|
| 10 |
+
fibs = [1, 2]
|
| 11 |
+
while len(fibs) < n: fibs.append(fibs[-1] + fibs[-2])
|
| 12 |
+
return np.array(fibs[:n], dtype=np.int64)
|
| 13 |
+
|
| 14 |
+
def fibonacci_frequency_boundaries(n_coeffs, n_bands):
|
| 15 |
+
if n_bands < 2: return [0, n_coeffs]
|
| 16 |
+
fibs = fibonacci_sequence(n_bands).astype(np.float64)
|
| 17 |
+
w = fibs / (fibs.sum() + 1e-12)
|
| 18 |
+
cum = np.cumsum(w)
|
| 19 |
+
b = [0]
|
| 20 |
+
for i in range(n_bands - 1): b.append(int(round(n_coeffs * cum[i])))
|
| 21 |
+
b.append(n_coeffs)
|
| 22 |
+
for i in range(1, len(b)):
|
| 23 |
+
if b[i] <= b[i-1]: b[i] = b[i-1] + 1
|
| 24 |
+
return b
|
| 25 |
+
|
| 26 |
+
def dct_ortho_1d(x):
|
| 27 |
+
N = x.shape[0]
|
| 28 |
+
v = np.concatenate([x, x[::-1]])
|
| 29 |
+
V = np.fft.fft(v)
|
| 30 |
+
k = np.arange(N)
|
| 31 |
+
X = np.real(V[:N] * np.exp(-1j * np.pi * k / (2 * N)))
|
| 32 |
+
X *= 2.0
|
| 33 |
+
X[0] *= (1.0 / math.sqrt(4 * N))
|
| 34 |
+
X[1:] *= (1.0 / math.sqrt(2 * N))
|
| 35 |
+
return X
|
| 36 |
+
|
| 37 |
+
def idct_ortho_1d(X):
|
| 38 |
+
N = X.shape[0]
|
| 39 |
+
x0, xr = X[0] * math.sqrt(4 * N), X[1:] * math.sqrt(2 * N)
|
| 40 |
+
c = np.empty(N, dtype=np.complex128)
|
| 41 |
+
c[0], c[1:] = x0 / 2.0, xr / 2.0
|
| 42 |
+
k = np.arange(N)
|
| 43 |
+
c = c * np.exp(1j * np.pi * k / (2 * N))
|
| 44 |
+
V = np.zeros(2 * N, dtype=np.complex128)
|
| 45 |
+
V[:N] = c
|
| 46 |
+
V[N+1:] = np.conj(c[1:][::-1])
|
| 47 |
+
return np.fft.ifft(V).real[:N]
|
| 48 |
+
|
| 49 |
+
def hologram_spectrum_image(zints):
|
| 50 |
+
z = zints[:262144]; v = np.tanh(z / 32.0)
|
| 51 |
+
theta = (2 * math.pi / (PHI**2)) * np.arange(v.size) + 2.0 * math.pi * (v * 0.25)
|
| 52 |
+
r = 1.0 + 0.35 * np.abs(v)
|
| 53 |
+
syms = r * np.cos(theta) + 1j * r * np.sin(theta)
|
| 54 |
+
N = int(2**math.ceil(math.log2(math.sqrt(syms.size or 1))))
|
| 55 |
+
U = np.pad(syms, (0, N*N - syms.size)).reshape(N, N)
|
| 56 |
+
mag = np.log1p(np.abs(np.fft.fftshift(np.fft.fft2(U))))
|
| 57 |
+
mag = (mag - mag.min()) / (mag.max() - mag.min() + 1e-12)
|
| 58 |
+
return (mag * 255).astype(np.uint8)
|
| 59 |
+
|
| 60 |
+
def bytes_to_fib_spiral_image(data):
|
| 61 |
+
arr = np.frombuffer(data, dtype=np.uint8)[:262144]
|
| 62 |
+
fibs = [1, 1]
|
| 63 |
+
while sum(s*s for s in fibs) < arr.size: fibs.append(fibs[-1] + fibs[-2])
|
| 64 |
+
tiles, minx, miny, maxx, maxy, curr_x, curr_y = [], 0, 0, 0, 0, 0, 0
|
| 65 |
+
for i, s in enumerate(fibs):
|
| 66 |
+
d = (i-1)%4
|
| 67 |
+
if i>0:
|
| 68 |
+
if d==0: curr_x = maxx; curr_y = miny
|
| 69 |
+
elif d==1: curr_x = maxx-s; curr_y = maxy
|
| 70 |
+
elif d==2: curr_x = minx-s; curr_y = maxy-s
|
| 71 |
+
else: curr_x = minx; curr_y = miny-s
|
| 72 |
+
tiles.append((curr_x, curr_y, s))
|
| 73 |
+
minx, miny, maxx, maxy = min(minx, curr_x), min(miny, curr_y), max(maxx, curr_x+s), max(maxy, curr_y+s)
|
| 74 |
+
img, idx = np.zeros((maxy-miny, maxx-minx), dtype=np.uint8), 0
|
| 75 |
+
for x, y, s in tiles:
|
| 76 |
+
take = min(s*s, arr.size - idx)
|
| 77 |
+
if take <= 0: break
|
| 78 |
+
block = np.pad(arr[idx:idx+take], (0, s*s-take)).reshape(s, s)
|
| 79 |
+
img[img.shape[0]-(y-miny+s):img.shape[0]-(y-miny), x-minx:x-minx+s] = block
|
| 80 |
+
idx += take
|
| 81 |
+
return img
|
| 82 |
+
|
| 83 |
+
def process_flc(input_file, fidelity):
|
| 84 |
+
with open(input_file, "rb") as f: raw_data = f.read()
|
| 85 |
+
|
| 86 |
+
q_map = {"High Compression": 6, "Balanced": 12, "Near-Lossless": 24}
|
| 87 |
+
n_bands = q_map[fidelity]
|
| 88 |
+
step = 0.08 if fidelity == "High Compression" else (0.005 if fidelity == "Balanced" else 0.0001)
|
| 89 |
+
|
| 90 |
+
x = (np.frombuffer(raw_data, dtype=np.uint8).astype(float) - 127.5) / 127.5
|
| 91 |
+
block_len = 1024
|
| 92 |
+
X = np.pad(x, (0, (-x.size) % block_len)).reshape(-1, block_len)
|
| 93 |
+
|
| 94 |
+
C = np.array([dct_ortho_1d(b) for b in X])
|
| 95 |
+
bnds = fibonacci_frequency_boundaries(block_len, n_bands)
|
| 96 |
+
Q = np.zeros_like(C, dtype=np.int32)
|
| 97 |
+
for bi in range(len(bnds)-1):
|
| 98 |
+
Q[:, bnds[bi]:bnds[bi+1]] = np.round(C[:, bnds[bi]:bnds[bi+1]] / (step * (PHI**bi)))
|
| 99 |
+
|
| 100 |
+
frames = []
|
| 101 |
+
for t in range(1, n_bands + 1):
|
| 102 |
+
Q_p = np.zeros_like(Q)
|
| 103 |
+
for bi in range(t): Q_p[:, bnds[bi]:bnds[bi+1]] = Q[:, bnds[bi]:bnds[bi+1]]
|
| 104 |
+
C_p = np.zeros_like(Q_p, dtype=float)
|
| 105 |
+
for bi in range(len(bnds)-1): C_p[:, bnds[bi]:bnds[bi+1]] = Q_p[:, bnds[bi]:bnds[bi+1]] * (step * (PHI**bi))
|
| 106 |
+
|
| 107 |
+
recon = np.clip((np.array([idct_ortho_1d(B) for B in C_p]).flatten()[:len(raw_data)] * 127.5) + 127.5, 0, 255).astype(np.uint8)
|
| 108 |
+
|
| 109 |
+
h_img = Image.fromarray(hologram_spectrum_image(Q_p.flatten())).resize((256, 256))
|
| 110 |
+
s_img = Image.fromarray(bytes_to_fib_spiral_image(recon.tobytes())).resize((256, 256))
|
| 111 |
+
frame = Image.new("RGB", (512, 280), (15, 15, 25))
|
| 112 |
+
frame.paste(h_img, (0, 12)); frame.paste(s_img, (256, 12))
|
| 113 |
+
frames.append(frame)
|
| 114 |
+
|
| 115 |
+
gif_path = tempfile.mktemp(suffix=".gif")
|
| 116 |
+
frames[0].save(gif_path, save_all=True, append_images=frames[1:], duration=150, loop=0)
|
| 117 |
+
return gif_path
|
| 118 |
+
|
| 119 |
+
# --- Gradio UI ---
|
| 120 |
+
demo = gr.Interface(
|
| 121 |
+
fn=process_flc,
|
| 122 |
+
inputs=[
|
| 123 |
+
gr.File(label="Upload File"),
|
| 124 |
+
gr.Radio(["High Compression", "Balanced", "Near-Lossless"], value="Balanced", label="Fidelity Tier")
|
| 125 |
+
],
|
| 126 |
+
outputs=gr.Image(label="Holographic Unzip Sequence"),
|
| 127 |
+
title="🌀 Fibonacci Lattice Compression (FLC)",
|
| 128 |
+
description="Bio-inspired spectral compression using the Golden Ratio."
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
if __name__ == "__main__":
|
| 132 |
+
demo.launch()
|