This commit is contained in:
2026-05-14 18:37:18 +08:00
parent 5e6d8046e1
commit 7a2ffc91a2
15 changed files with 200 additions and 56 deletions
+2
View File
@@ -1 +1,3 @@
from .flux_fill.loader import make_flux_fill_predictor
__all__ = ["make_flux_fill_predictor"]
@@ -0,0 +1,3 @@
from .loader import make_flux_fill_predictor
__all__ = ["make_flux_fill_predictor"]
@@ -0,0 +1,109 @@
from __future__ import annotations
import os
from typing import Any, Callable
import numpy as np
from PIL import Image
def make_flux_fill_predictor(
model_id: str,
device: str | None = None,
) -> Callable[[Image.Image, Image.Image, str, str], Image.Image]:
import torch
from diffusers import FluxFillPipeline
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
if device == "cuda" and torch.cuda.is_bf16_supported():
torch_dtype = torch.bfloat16
elif device == "cuda":
torch_dtype = torch.float16
else:
torch_dtype = torch.float32
hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
pipe = FluxFillPipeline.from_pretrained(
model_id,
torch_dtype=torch_dtype,
token=hf_token,
)
if device == "cuda":
try:
pipe.enable_model_cpu_offload()
except Exception:
try:
pipe.enable_vae_tiling()
except Exception:
pass
pipe.to("cuda")
else:
pipe.to(device)
def _predict(
image: Image.Image,
mask: Image.Image,
prompt: str,
negative_prompt: str = "",
strength: float = 1.0,
guidance_scale: float = 30.0,
num_inference_steps: int = 28,
max_side: int = 1024,
max_sequence_length: int = 512,
**_: Any,
) -> Image.Image:
_ = negative_prompt
image = image.convert("RGB")
mask = mask.convert("L")
orig_w, orig_h = image.size
run_w, run_h = orig_w, orig_h
if max(orig_w, orig_h) > max_side:
scale = max_side / float(max(orig_w, orig_h))
run_w = int(round(orig_w * scale))
run_h = int(round(orig_h * scale))
run_w = max(8, run_w - (run_w % 8))
run_h = max(8, run_h - (run_h % 8))
if (run_w, run_h) != (orig_w, orig_h):
image_run = image.resize((run_w, run_h), resample=Image.BICUBIC)
mask_run = mask.resize((run_w, run_h), resample=Image.NEAREST)
else:
image_run = image
mask_run = mask
p = (prompt or "").strip()
if not p:
p = "Chinese ink painting background, natural texture, seamless fill, no figures"
st = float(strength)
st = min(1.0, max(0.05, st))
if device == "cuda":
try:
torch.cuda.empty_cache()
except Exception:
pass
out = pipe(
prompt=p,
image=image_run,
mask_image=mask_run,
height=run_h,
width=run_w,
strength=st,
guidance_scale=float(guidance_scale),
num_inference_steps=int(num_inference_steps),
max_sequence_length=int(max_sequence_length),
).images[0]
out = out.convert("RGB")
if out.size != (orig_w, orig_h):
out = out.resize((orig_w, orig_h), resample=Image.BICUBIC)
return out
return _predict
+29 -5
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
统一的补全(Inpaint)模型加载入口。
当前支持:
- FLUX Filldiffusers FluxFillPipeline,如 FLUX.1-Fill-dev
- SDXL Inpaintdiffusers AutoPipelineForInpainting
- ControlNetStable Diffusion + ControlNet Inpaint
- LaMatorchscript big-lama,本地封装)
@@ -11,7 +12,7 @@ from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Callable
from typing import Any, Callable
import numpy as np
from PIL import Image
@@ -21,10 +22,12 @@ from config_loader import (
get_sdxl_base_model_from_app,
get_controlnet_base_model_from_app,
get_controlnet_model_from_app,
get_flux_fill_model_from_app,
)
class InpaintBackend(str, Enum):
FLUX_FILL = "flux_fill"
SDXL_INPAINT = "sdxl_inpaint"
CONTROLNET = "controlnet"
LAMA = "lama"
@@ -36,6 +39,7 @@ class UnifiedInpaintConfig:
device: str | None = None
# SDXL base model (HF id 或本地目录),不填则用 config.py 的默认值
sdxl_base_model: str | None = None
flux_fill_model: str | None = None
@dataclass
@@ -100,7 +104,7 @@ def _make_sdxl_inpaint_predictor(
- 输出:PIL RGB 结果图
"""
import torch
from diffusers import AutoPipelineForText2Image, AutoPipelineForInpainting
from diffusers import AutoPipelineForInpainting
app_cfg = load_app_config()
base_model = cfg.sdxl_base_model or get_sdxl_base_model_from_app(app_cfg)
@@ -112,13 +116,12 @@ def _make_sdxl_inpaint_predictor(
torch_dtype = torch.float16 if device == "cuda" else torch.float32
pipe_t2i = AutoPipelineForText2Image.from_pretrained(
pipe = AutoPipelineForInpainting.from_pretrained(
base_model,
torch_dtype=torch_dtype,
variant="fp16" if device == "cuda" else None,
use_safetensors=True,
).to(device)
pipe = AutoPipelineForInpainting.from_pipe(pipe_t2i).to(device)
# 省显存设置(尽量不改变输出语义)
# 注意:CPU offload 会明显变慢,但能显著降低显存占用。
@@ -149,6 +152,7 @@ def _make_sdxl_inpaint_predictor(
guidance_scale: float = 7.5,
num_inference_steps: int = 30,
max_side: int = 1024,
**_: Any,
) -> Image.Image:
image = image.convert("RGB")
# diffusers 要求 mask 为单通道,白色区域为需要重绘
@@ -257,6 +261,7 @@ def _make_controlnet_predictor(_: UnifiedInpaintConfig):
num_inference_steps: int = 30,
controlnet_conditioning_scale: float = 1.0,
max_side: int = 768,
**_: Any,
) -> Image.Image:
import cv2
import numpy as np
@@ -316,6 +321,22 @@ def _make_controlnet_predictor(_: UnifiedInpaintConfig):
return _predict
def _make_flux_fill_predictor(cfg: UnifiedInpaintConfig):
import torch
from .flux_fill.loader import make_flux_fill_predictor as _flux_fill_factory
app_cfg = load_app_config()
model_id = cfg.flux_fill_model or get_flux_fill_model_from_app(app_cfg)
device = cfg.device
if device is None:
device = app_cfg.inpaint.device
device = "cuda" if device.startswith("cuda") and torch.cuda.is_available() else "cpu"
return _flux_fill_factory(model_id=model_id, device=device)
def _make_lama_predictor(cfg: UnifiedInpaintConfig):
import torch
@@ -340,7 +361,7 @@ def _make_lama_predictor(cfg: UnifiedInpaintConfig):
max_side: int = 1024,
**_kwargs,
) -> Image.Image:
# LaMa 不使用 prompt/negative_prompt,也不使用 strength/steps 等扩散参数
# big-lama:仅 (RGB, mask);无文本条件。
_ = (strength, guidance_scale, num_inference_steps, max_side)
return lama(image, mask)
@@ -355,6 +376,9 @@ def build_inpaint_predictor(
"""
cfg = cfg or UnifiedInpaintConfig()
if cfg.backend == InpaintBackend.FLUX_FILL:
return _make_flux_fill_predictor(cfg), InpaintBackend.FLUX_FILL
if cfg.backend == InpaintBackend.SDXL_INPAINT:
return _make_sdxl_inpaint_predictor(cfg), InpaintBackend.SDXL_INPAINT