James040's picture
Update app.py
ab2e507 verified
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)