← 返回信息流
技术博客Hugging Face Blog·2026/4/1

使用 Gradio 后端构建任意自定义前端

原标题:Any Custom Frontend with Gradio's Backend

速览

Gradio 不再局限于默认界面,允许开发者使用 HTML、CSS 和 JavaScript 构建完全自定义的前端。这一功能通过简单的 API 调用即可与现有的 Gradio 后端连接。它极大地提升了用户界面的灵活性,满足了复杂交互和个性化设计的需求。

AI 深度解读

深度解读:使用 gradio.Server 实现任意前端与 Gradio 后端的完美融合

背景

Gradio 长期以来一直是构建机器学习演示应用的首选工具,其核心优势在于通过 gr.Blocksgr.HTML 等组件,让开发者能够利用自定义 HTML、CSS 和 JavaScript 在 Gradio 内部构建丰富且交互式的界面。这种灵活性解锁了许多可能性,但在面对更复杂的应用场景时,它依然存在局限性。

许多开发者希望完全使用自己的前端框架(如 React、Svelte,甚至是原生 HTML/JS)来构建应用,同时又能享受 Gradio 生态系统带来的强大后端能力,包括请求队列系统、API 基础设施、MCP(Model Context Protocol)支持以及 Hugging Face Spaces 上的 ZeroGPU 自动分配功能。

然而,传统的 Gradio 组件系统难以表达复杂的 UI 逻辑。以 Hugging Face 上的一个示例应用 Text Behind Image 为例,该应用要求用户上传图片,利用 ML 模型去除背景,然后将风格化的文本放置在前景主体和背景之间,营造出文本位于人物或物体后方的视觉效果。

这一需求涉及:

  • 具有分层渲染功能的拖拽画布(背景 → 文本 → 前景)。
  • 包含滑块的控制面板,用于调整字体大小、粗细、字间距、颜色、透明度、描边、阴影、3D 挤出、透视变换等数十个参数。
  • 运行背景移除模型的后端 ML 端点,并返回透明 PNG 图像。
  • 客户端的 PNG 导出功能。

这是一个完整的前端 Web 应用,无法通过标准的 Gradio 组件来构建。但开发者依然渴望获得 Gradio 的后端力量:队列管理、并发控制、ZeroGPU 支持,以及在 HF Spaces 上托管时无需处理基础设施的麻烦。

gradio.Server 正是为了解决这一痛点而生,它彻底改变了 Gradio 和 Hugging Face Spaces 的可能性边界。

核心内容

gradio.Server 基于 FastAPI 扩展而来。它不仅保留了 FastAPI 的全部强大功能(如自定义路由、中间件、文件上传、任意响应类型),还在其上叠加了 Gradio 的 API 引擎,包括请求队列、SSE(Server-Sent Events)流式传输、并发控制以及 gradio_client 兼容性。

1. 后端实现:极简的代码与强大的引擎

Text Behind Image 应用中,后端代码仅约 50 行 Python。其核心逻辑如下:

  • 模型加载与 GPU 管理:模型(BiRefNet)在启动时加载,@spaces.GPU 装饰器负责处理 ZeroGPU 的自动分配。
  • API 端点定义:使用 @app.api() 装饰器定义业务逻辑。
  • 路由服务:使用标准的 FastAPI 路由 @app.get("/") 提供前端 HTML 页面。
import os
import torch
from PIL import Image
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
from gradio import Server
from gradio.data_classes import FileData
from fastapi.responses import HTMLResponse
import spaces

# 设置高精度矩阵乘法
torch.set_float32_matmul_precision("high")

# 加载 BiRefNet 模型
birefnet = AutoModelForImageSegmentation.from_pretrained(
    "ZhengPeng7/BiRefNet", trust_remote_code=True
)
birefnet.to("cuda")
birefnet.float()

# 图像预处理变换
transform_image = transforms.Compose([
    transforms.Resize((1024, 1024)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])

app = Server()

@spaces.GPU
def segment(image: Image.Image) -> Image.Image:
    """运行 BiRefNet 分割以生成透明遮罩。"""
    image_size = image.size
    input_images = transform_image(image).unsqueeze(0).to("cuda")
    with torch.no_grad():
        preds = birefnet(input_images)[-1].sigmoid().cpu()
        pred = preds[0].squeeze()
        mask = transforms.ToPILImage()(pred).resize(image_size)
        image.putalpha(mask)
    return image

@app.api(name="remove_background")
def remove_background(image_path: FileData) -> FileData:
    """从图像中移除背景。返回透明 PNG。"""
    im = Image.open(image_path["path"]).convert("RGB")
    result = segment(im)
    out_path = image_path["path"].rsplit(".", 1)[0] + ".png"
    result.save(out_path)
    return FileData(path=out_path)

@app.get("/", response_class=HTMLResponse)
async def homepage():
    html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
    with open(html_path, "r", encoding="utf-8") as f:
        return f.read()

app.launch(show_error=True)

2. 为什么使用 @app.api() 而不是普通的 FastAPI 路由?

如果这是一个普通的 FastAPI 应用,开发者会使用 @app.post() 定义背景移除的路由。这在单用户或少量并发下工作正常,但当两个用户同时访问时,如果没有并发管理,两个请求会争夺 GPU 资源,导致应用崩溃或返回错误数据。

@app.api() 解决了这个问题:

  • 队列引擎:它将函数包裹在 Gradio 的队列引擎中,请求被序列化,并发得到控制。
  • ZeroGPU 支持:在 ZeroGPU Spaces 上,GPU 分配通过 @spaces.GPU 自动处理。
  • 客户端兼容性:任何 @app.api() 端点都可以通过 gradio_client 调用,使得其他应用或脚本可以以编程方式使用该 Space。
from gradio_client import Client, handle_file

client = Client("ysharma/text-behind-image")
result = client.predict(
    image_path=handle_file("photo.jpg"),
    api_name="/remove_background"
)

与此同时,@app.get("/") 是一个标准的 FastAPI 路由,用于提供 HTML 页面。由于 Server 本身就是 FastAPI 应用,两者可以自然地共存。

3. 前端实现:纯 HTML/CSS/JS

前端是一个自包含的约 1300 行的 Web 应用,无需 React,无需构建步骤,无需打包器。它仅使用原生 HTML,包含:

  • 三层画布:背景图像 → 文本层 → 前景(透明 PNG),通过 CSS z-index 堆叠。
  • 拖拽定位:使用指针事件实现文本的拖拽定位。
  • 控制面板:包含 20 多个参数,如字体族(25+ 种字体)、大小、粗细、间距、颜色、透明度、背景填充、描边、阴影、3D 挤出深度和角度、旋转、倾斜以及完整的 CSS 透视变换。
  • 客户端导出:使用 <canvas> 合成在客户端导出 PNG。

前端通过 Gradio JS Client 与后端通信,这是关键所在:

import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";

const client = await Client.connect(window.location.origin);
const result = await client.predict("/remove_background", {
    image_path: handle_file(file),
});

foregroundLayer.src = result.data[0].url; // 透明 PNG

通过使用 Gradio JS 客户端而不是原始的 fetch() 调用,前端请求经过 Gradio 的队列。这意味着并发得到管理,GPU 请求不会冲突,并且可以向用户显示队列位置或进度。其余所有操作(文本渲染、图层合成、导出)均在浏览器中完成。

关键要点

  • 解耦前后端gradio.Server 允许开发者将任意前端框架(React, Svelte, 原生 JS)与
查看原文 →huggingface.co