Spaces:
Sleeping
Sleeping
| import os | |
| import math | |
| import numpy as np | |
| import onnxruntime as ort | |
| from PIL import Image | |
| import gradio as gr | |
| from huggingface_hub import hf_hub_download | |
| import shutil | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1) Auto-Download Ultra-Fast Model | |
| MODEL_DIR = "model" | |
| os.makedirs(MODEL_DIR, exist_ok=True) | |
| MODEL_X4_PATH = os.path.join(MODEL_DIR, "realesr-general-x4v3.onnx") | |
| def fetch_models(): | |
| """Downloads the 4.8MB ultra-lightweight model from an open repository.""" | |
| if not os.path.exists(MODEL_X4_PATH): | |
| print("Downloading 4.8MB High-Speed Real-ESRGAN model...") | |
| downloaded_path = hf_hub_download( | |
| repo_id="OwlMaster/AllFilesRope", | |
| filename="realesr-general-x4v3.onnx" | |
| ) | |
| shutil.copy(downloaded_path, MODEL_X4_PATH) | |
| print("Model downloaded successfully!") | |
| fetch_models() | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 2) Efficient CPU Session | |
| sess_opts = ort.SessionOptions() | |
| sess_opts.intra_op_num_threads = 2 | |
| sess_opts.inter_op_num_threads = 2 | |
| session_x4 = None | |
| def load_model(): | |
| global session_x4 | |
| if session_x4 is None: | |
| session_x4 = ort.InferenceSession(MODEL_X4_PATH, sess_options=sess_opts, providers=["CPUExecutionProvider"]) | |
| return session_x4 | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 3) Adaptive Tiling Engine | |
| def process_tensor(session, tensor_np): | |
| input_name = session.get_inputs()[0].name | |
| patch_nchw = np.transpose(tensor_np, (2, 0, 1))[np.newaxis, ...] | |
| out_nchw = session.run(None, {input_name: patch_nchw})[0] | |
| out_nchw = np.squeeze(out_nchw, axis=0) | |
| return np.transpose(out_nchw, (1, 2, 0)) | |
| def dynamic_upscale(input_img: Image.Image, target_scale: int, progress=gr.Progress()): | |
| session = load_model() | |
| base_model_scale = 4 | |
| img_rgb = input_img.convert("RGB") | |
| arr = np.array(img_rgb).astype(np.float32) / 255.0 | |
| h_orig, w_orig, _ = arr.shape | |
| # 16GB RAM can safely process up to ~1500x1500px in one shot without tiling. | |
| MAX_NO_TILE_DIM = 1500 | |
| if max(h_orig, w_orig) <= MAX_NO_TILE_DIM: | |
| progress(0.4, desc="RAM check passed. Processing full image instantly...") | |
| out_arr = process_tensor(session, arr) | |
| else: | |
| # For huge images, we use large 800px tiles to minimize loop overhead | |
| tile_size = 800 | |
| tiles_h = math.ceil(h_orig / tile_size) | |
| tiles_w = math.ceil(w_orig / tile_size) | |
| pad_h = tiles_h * tile_size - h_orig | |
| pad_w = tiles_w * tile_size - w_orig | |
| arr_padded = np.pad(arr, ((0, pad_h), (0, pad_w), (0, 0)), mode="reflect") | |
| out_h = tiles_h * tile_size * base_model_scale | |
| out_w = tiles_w * tile_size * base_model_scale | |
| out_arr = np.zeros((out_h, out_w, 3), dtype=np.float32) | |
| total_tiles = tiles_h * tiles_w | |
| current_tile = 0 | |
| for i in range(tiles_h): | |
| for j in range(tiles_w): | |
| current_tile += 1 | |
| progress(current_tile / total_tiles, desc=f"AI Processing tile {current_tile}/{total_tiles}...") | |
| y0, x0 = i * tile_size, j * tile_size | |
| tile = arr_padded[y0:y0+tile_size, x0:x0+tile_size, :] | |
| up_tile = process_tensor(session, tile) | |
| oy0, ox0 = i * tile_size * base_model_scale, j * tile_size * base_model_scale | |
| out_arr[oy0:oy0 + tile_size * base_model_scale, ox0:ox0 + tile_size * base_model_scale, :] = up_tile | |
| out_arr = out_arr[0:h_orig * base_model_scale, 0:w_orig * base_model_scale, :] | |
| # Finalize Image | |
| progress(0.8, desc="Finalizing output...") | |
| out_arr = np.clip(out_arr, 0.0, 1.0) | |
| final_pil = Image.fromarray((out_arr * 255.0).round().astype(np.uint8)) | |
| # Downscale if the user requested 2x (from the 4x native output) | |
| if target_scale != 4: | |
| progress(0.9, desc=f"Refining output to {target_scale}x...") | |
| target_size = (w_orig * target_scale, h_orig * target_scale) | |
| final_pil = final_pil.resize(target_size, resample=Image.LANCZOS) | |
| return final_pil | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 4) UI Setup | |
| def upscale_2x(img, prog=gr.Progress()): return dynamic_upscale(img, 2, prog) if img else None | |
| def upscale_4x(img, prog=gr.Progress()): return dynamic_upscale(img, 4, prog) if img else None | |
| def upscale_6x(img, prog=gr.Progress()): return dynamic_upscale(img, 6, prog) if img else None | |
| css = """ | |
| #x2-btn { background-color: #d1fae5 !important; color: black !important; } | |
| #x4-btn { background-color: #bfdbfe !important; color: black !important; } | |
| #x6-btn { background-color: #fef08a !important; color: black !important; } | |
| """ | |
| with gr.Blocks(title="Speed-Optimized CPU Upscaler") as demo: | |
| gr.Markdown("# β‘ Adaptive CPU Upscaler\nUpload an image. Great For Anime/Cartoon/Text , Very Fast . This app dynamically allocates RAM to bypass tiling for smaller images, running significantly faster on the Free Tier.") | |
| with gr.Row(): inp_image = gr.Image(type="pil", label="Source Image") | |
| with gr.Row(): | |
| btn_x2 = gr.Button("Upscale (Γ2)", elem_id="x2-btn") | |
| btn_x4 = gr.Button("Standard AI (Γ4)", elem_id="x4-btn") | |
| btn_x6 = gr.Button("Hybrid High-Res (Γ6)", elem_id="x6-btn") | |
| out_preview = gr.Image(type="pil", label="Upscaled Result") | |
| btn_x2.click(fn=upscale_2x, inputs=inp_image, outputs=out_preview) | |
| btn_x4.click(fn=upscale_4x, inputs=inp_image, outputs=out_preview) | |
| btn_x6.click(fn=upscale_6x, inputs=inp_image, outputs=out_preview) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860, css=css) |