Spaces:
Running on Zero
Running on Zero
File size: 5,051 Bytes
cfd987c e2e4ca3 1c02f7a cfd987c 1c02f7a e2e4ca3 1c02f7a e2e4ca3 1c02f7a e2e4ca3 1c02f7a e2e4ca3 cfd987c e2e4ca3 cfd987c e2e4ca3 cfd987c e2e4ca3 cfd987c e2e4ca3 cfd987c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | """
UltraSharp V2 — 图像超分辨率 Gradio 应用
==========================================
## 模型来源
默认从 Kim2091/UltraSharpV2 公开仓库下载 4x-UltraSharpV2.pth,
自动缓存到 ~/.cache/huggingface/hub/,无需手动上传。
## 可选环境变量
MODEL_REPO_ID 覆盖默认仓库(默认 Kim2091/UltraSharpV2)
MODEL_FILENAME 覆盖默认文件名(默认 4x-UltraSharpV2.pth)
HF_ENDPOINT 镜像站,如 https://hf-mirror.com(国内加速)
HF_TOKEN 私有仓库的 token(公开仓库无需设置)
## 本地运行
python app.py
# 国内镜像: HF_ENDPOINT=https://hf-mirror.com python app.py
## 部署到 HuggingFace Space
1. 在 Space 设置中将 Hardware 选为 ZeroGPU
2. 无需设置 Secrets(模型来自公开仓库)
3. 如需国内镜像,添加 Secret: HF_ENDPOINT = https://hf-mirror.com
"""
import os
import asyncio
import asyncio.base_events
import gradio as gr
from model_loader import UltraSharpV2
# ---------------------------------------------------------------------------
# 修复 Python 3.12 asyncio 事件循环 GC 时的 "Invalid file descriptor: -1" 报错
#
# 根因: Gradio / spaces 在 import 阶段会创建临时事件循环,这些循环被 GC
# 回收时 __del__ → close() → _close_self_pipe() 尝试对已关闭的 socket
# (fd=-1) 执行 _remove_reader,触发 ValueError。属于 CPython 3.12 的
# 已知问题,对功能无害但日志很吵。此处 patch __del__ 静默吞掉该异常。
# ---------------------------------------------------------------------------
_orig_loop_del = asyncio.base_events.BaseEventLoop.__del__
def _safe_loop_del(self):
try:
_orig_loop_del(self)
except Exception:
pass
asyncio.base_events.BaseEventLoop.__del__ = _safe_loop_del
# ---------------------------------------------------------------------------
# ZeroGPU 兼容层
# ---------------------------------------------------------------------------
try:
import spaces
_zerogpu = spaces.GPU(duration=120) # 最长 GPU 占用 120s
IN_ZEROGPU = bool(os.environ.get("SPACES_ZERO_GPU"))
except ImportError:
spaces = None
_zerogpu = None
IN_ZEROGPU = False
def _gpu(fn):
"""安全地应用 @spaces.GPU 装饰器(本地开发时退化为无操作)。"""
return _zerogpu(fn) if _zerogpu is not None else fn
# ---------------------------------------------------------------------------
# 模型:始终在 CPU 上加载(ZeroGPU 启动时 GPU 不可用)
# ---------------------------------------------------------------------------
model = UltraSharpV2(device="cpu")
# ---------------------------------------------------------------------------
# 推理参数(RTX PRO 6000 Blackwell / 48GB — 无需省显存)
# ---------------------------------------------------------------------------
_TILE_SIZE = 1024
_TILE_OVERLAP = 48
# ---------------------------------------------------------------------------
# 推理函数(生成器模式 — ZeroGPU 硬性要求)
# ---------------------------------------------------------------------------
@_gpu
def on_upscale(image, target_scale):
if image is None:
yield None, "请先上传图片"
return
model.to_cuda()
try:
result, elapsed = model.upscale(
image, _TILE_SIZE, _TILE_OVERLAP, float(target_scale)
)
finally:
model.to_cpu()
yield result, f"耗时: {elapsed:.2f}s"
# ---------------------------------------------------------------------------
# Gradio UI
# ---------------------------------------------------------------------------
with gr.Blocks(title="UltraSharp V2") as demo:
device_display = "ZeroGPU" if IN_ZEROGPU else model.device.upper()
gr.Markdown("# UltraSharp V2 - 图像超分辨率")
gr.Markdown(f"**运行设备**: {device_display} | **模型原生倍率**: {model.scale}x")
with gr.Row():
with gr.Column(scale=1):
input_img = gr.Image(label="输入图片", type="pil", height=400)
target_scale = gr.Slider(
label="放大倍率",
minimum=1.0,
maximum=4.0,
value=4.0,
step=0.05,
info="> 模型原生倍率时, 输出先 4x 推理再 Lanczos 缩放",
)
with gr.Column(scale=1):
run_btn = gr.Button("开始推理", variant="primary")
output_img = gr.Image(label="推理结果", height=400)
status = gr.Textbox(label="状态", interactive=False)
run_btn.click(
fn=on_upscale,
inputs=[input_img, target_scale],
outputs=[output_img, status],
)
# ---------------------------------------------------------------------------
# ZeroGPU 必须启用 queue(默认并发 1,队列上限 10)
# ---------------------------------------------------------------------------
demo.queue(max_size=10, default_concurrency_limit=1)
if __name__ == "__main__":
demo.launch()
|