CoolFace
Apppublic

TuringsSolutions/Fibonacci-Compressor-Gradio

sourceHugging Faceupdated 9mo agoView on Hugging Face
1likes
app.py225 linesDownload Raw Back to root
1import gradio as gr2import os, math, tempfile3import numpy as np4from PIL import Image, UnidentifiedImageError5 6# ==========================================7# FLC v1.3 Logic Engine (The "Secret Sauce")8# ==========================================9PHI = (1.0 + 5.0**0.5) / 2.010 11def fibonacci_sequence(n):12    fibs = [1, 2]13    while len(fibs) < n: fibs.append(fibs[-1] + fibs[-2])14    return np.array(fibs[:n], dtype=np.int64)15 16def fibonacci_frequency_boundaries(n_coeffs, n_bands):17    if n_bands < 2: return [0, n_coeffs]18    fibs = fibonacci_sequence(n_bands).astype(np.float64)19    w = fibs / (fibs.sum() + 1e-12)20    cum = np.cumsum(w)21    b = [0]22    for i in range(n_bands - 1): b.append(int(round(n_coeffs * cum[i])))23    b.append(n_coeffs)24    for i in range(1, len(b)):25        if b[i] <= b[i-1]: b[i] = b[i-1] + 126    return b27 28def dct_ortho_1d(x):29    N = x.shape[0]30    v = np.concatenate([x, x[::-1]])31    V = np.fft.fft(v)32    k = np.arange(N)33    X = np.real(V[:N] * np.exp(-1j * np.pi * k / (2 * N)))34    X *= 2.035    X[0] *= (1.0 / math.sqrt(4 * N))36    X[1:] *= (1.0 / math.sqrt(2 * N))37    return X38 39def idct_ortho_1d(X):40    N = X.shape[0]41    x0, xr = X[0] * math.sqrt(4 * N), X[1:] * math.sqrt(2 * N)42    c = np.empty(N, dtype=np.complex128)43    c[0], c[1:] = x0 / 2.0, xr / 2.044    k = np.arange(N)45    c = c * np.exp(1j * np.pi * k / (2 * N))46    V = np.zeros(2 * N, dtype=np.complex128)47    V[:N] = c48    V[N+1:] = np.conj(c[1:][::-1])49    return np.fft.ifft(V).real[:N]50 51# --- Visualization Helpers ---52def hologram_spectrum_image(zints):53    # Visualizes the frequency domain data as a 2D spectrum54    z = zints[:262144]; v = np.tanh(z / 32.0)55    theta = (2 * math.pi / (PHI**2)) * np.arange(v.size) + 2.0 * math.pi * (v * 0.25)56    r = 1.0 + 0.35 * np.abs(v)57    syms = r * np.cos(theta) + 1j * r * np.sin(theta)58    N = int(2**math.ceil(math.log2(math.sqrt(syms.size or 1))))59    U = np.pad(syms, (0, N*N - syms.size)).reshape(N, N)60    mag = np.log1p(np.abs(np.fft.fftshift(np.fft.fft2(U))))61    mag = (mag - mag.min()) / (mag.max() - mag.min() + 1e-12)62    return (mag * 255).astype(np.uint8)63 64def bytes_to_fib_spiral_image(data):65    # Visualizes linear data arranged on a Fibonacci spiral tiling66    arr = np.frombuffer(data, dtype=np.uint8)[:262144]67    fibs = [1, 1]68    while sum(s*s for s in fibs) < arr.size: fibs.append(fibs[-1] + fibs[-2])69    tiles, minx, miny, maxx, maxy, curr_x, curr_y = [], 0, 0, 0, 0, 0, 070    for i, s in enumerate(fibs):71        d = (i-1)%472        if i>0:73            if d == 0: curr_x = maxx; curr_y = miny74            elif d == 1: curr_x = maxx-s; curr_y = maxy75            elif d == 2: curr_x = minx-s; curr_y = maxy-s76            else: curr_x = minx; curr_y = miny-s77        tiles.append((curr_x, curr_y, s))78        minx, miny, maxx, maxy = min(minx, curr_x), min(miny, curr_y), max(maxx, curr_x+s), max(maxy, curr_y+s)79    img, idx = np.zeros((maxy-miny, maxx-minx), dtype=np.uint8), 080    for x, y, s in tiles:81        take = min(s*s, arr.size - idx)82        if take <= 0: break83        block = np.pad(arr[idx:idx+take], (0, s*s-take)).reshape(s, s)84        img[img.shape[0]-(y-miny+s):img.shape[0]-(y-miny), x-minx:x-minx+s] = block85        idx += take86    return img87 88# ==========================================89# Main Processing Logic90# ==========================================91def run_demo(input_file_wrapper, fidelity):92    # Determine input type and prepare data93    is_image = False94    orig_pil = None95    img_dims = None96    97    try:98        # Try opening as an image99        orig_pil = Image.open(input_file_wrapper.name).convert('L') # Convert to grayscale for core engine100        # Resize large images for demo performance constraint101        orig_pil.thumbnail((512, 512)) 102        img_dims = orig_pil.size # (width, height)103        raw_data = np.array(orig_pil).tobytes()104        is_image = True105    except (UnidentifiedImageError, OSError):106        # Fallback for non-image binary data107        with open(input_file_wrapper.name, "rb") as f:108            raw_data = f.read()109 110    orig_size = len(raw_data)111    112    # FLC Parameters based on user selection113    q_settings = {"High Compression (Lossy)": 6, "Balanced": 12, "Near-Lossless": 24}114    n_bands = q_settings[fidelity]115    # Aggressive steps for lower tiers to show visual difference116    step = 0.15 if fidelity == "High Compression (Lossy)" else (0.01 if fidelity == "Balanced" else 0.0001)117    118    # --- Step 1: Transform & Quantize (Compression Simulation) ---119    # Normalize data to range [-1, 1]120    x = (np.frombuffer(raw_data, dtype=np.uint8).astype(float) - 127.5) / 127.5121    block_len = 1024122    pad_len = (-x.size) % block_len123    X = np.pad(x, (0, pad_len)).reshape(-1, block_len)124    125    # Forward DCT126    C = np.array([dct_ortho_1d(b) for b in X])127    # Determine Fibonacci bands128    bnds = fibonacci_frequency_boundaries(block_len, n_bands)129    # Quantize using Phi-scaling130    Q = np.zeros_like(C, dtype=np.int32)131    for bi in range(len(bnds)-1):132        Q[:, bnds[bi]:bnds[bi+1]] = np.round(C[:, bnds[bi]:bnds[bi+1]] / (step * (PHI**bi)))133    134    # Simulated compressed size estimate (entropy estimate)135    compressed_size_est = int(np.count_nonzero(Q) * 1.5) + 512 # base overhead136    ratio = compressed_size_est / orig_size137 138    # --- Step 2: Progressive Reconstruction (Animation) ---139    frames = []140    final_recon_data = None141 142    # Iterate through bands to create progressive frames143    for t in range(1, n_bands + 1):144        # Partial quantization buffer145        Q_p = np.zeros_like(Q)146        for bi in range(t): Q_p[:, bnds[bi]:bnds[bi+1]] = Q[:, bnds[bi]:bnds[bi+1]]147        148        # Dequantize back to coefficients149        C_p = np.zeros_like(Q_p, dtype=float)150        for bi in range(len(bnds)-1):151            C_p[:, bnds[bi]:bnds[bi+1]] = Q_p[:, bnds[bi]:bnds[bi+1]] * (step * (PHI**bi))152        153        # Inverse DCT and denormalize154        recon_1d = np.clip((np.array([idct_ortho_1d(B) for B in C_p]).flatten()[:orig_size] * 127.5) + 127.5, 0, 255).astype(np.uint8)155        156        if t == n_bands:157             final_recon_data = recon_1d158 159        # Create visualization frames160        h_img = Image.fromarray(hologram_spectrum_image(Q_p.flatten())).resize((256, 256)).convert("RGB")161        s_img = Image.fromarray(bytes_to_fib_spiral_image(recon_1d.tobytes())).resize((256, 256)).convert("RGB")162        163        # Combine into one frame164        frame = Image.new("RGB", (512, 280), (15, 15, 25))165        frame.paste(h_img, (0, 12)); frame.paste(s_img, (256, 12))166        frames.append(frame)167 168    # Save animation169    gif_path = tempfile.mktemp(suffix=".gif")170    frames[0].save(gif_path, save_all=True, append_images=frames[1:], duration=120, loop=0)171    172    stats = f"Original Size: {orig_size:,} bytes\nSimulated Compressed Size: ~{compressed_size_est:,} bytes\ncompression Ratio: {ratio:.2%}"173 174    # --- Step 3: Prepare Final Comparison Images ---175    recon_pil = None176    if is_image and final_recon_data is not None:177        # Reshape 1D reconstructed data back to 2D image dimensions178        recon_pil = Image.fromarray(final_recon_data.reshape((img_dims[1], img_dims[0])))179 180    # Return results based on input type181    if is_image:182        return gif_path, stats, orig_pil, recon_pil183    else:184        # If not an image, return None for image image components so they don't display weirdly185        return gif_path, stats, None, None186 187 188# ==========================================189# Gradio UI Layout190# ==========================================191with gr.Blocks(title="FLC v1.3 | Unified Fibonacci Demo", theme=gr.themes.Soft(primary_hue="amber", neutral_hue="slate")) as demo:192    gr.Markdown("# ๐ŸŒ€ Fibonacci Lattice Compression (FLC)")193    gr.Markdown("Upload an image to see the **Golden Ratio** compress data and reconstruct it progressively.")194    195    with gr.Row():196        with gr.Column(scale=1):197            with gr.Group():198                file_input = gr.File(label="1. Upload Input (Image recommended)", file_count="single")199                radio_input = gr.Radio(["High Compression (Lossy)", "Balanced", "Near-Lossless"], value="Balanced", label="2. Select Fidelity Tier")200                run_btn = gr.Button("๐Ÿš€ Run Holographic Compression", variant="primary")201            202            stats_output = gr.Textbox(label="Compression Metrics", interactive=False, lines=4)203 204        with gr.Column(scale=2):205            gr.Markdown("### ๐ŸŽž๏ธ Progressive Reconstruction Animation")206            gr.Markdown("_Left: Frequency Hologram filling up. Right: Data organizing into Fibonacci Spiral._")207            gif_output = gr.Image(label="Animation Sequence", show_label=False)208 209    gr.Markdown("---")210    gr.Markdown("### ๐Ÿ” Visual Verification: Original vs. Reconstructed")211    gr.Markdown("_Determine if the 'Secret Sauce' maintained enough quality at the chosen compression tier._")212    213    with gr.Row():214        orig_image_output = gr.Image(label="Original Input (Grayscale)", type="pil", interactive=False)215        recon_image_output = gr.Image(label="Final Decompressed Result", type="pil", interactive=False)216 217    # Define the action218    run_btn.click(219        fn=run_demo,220        inputs=[file_input, radio_input],221        outputs=[gif_output, stats_output, orig_image_output, recon_image_output]222    )223 224if __name__ == "__main__":225    demo.launch()