update
This commit is contained in:
@@ -25,14 +25,6 @@ QPushButton* makeAlgoButton(const QString& title, QWidget* parent) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QString BlackholeResolveDialog::defaultModelInpaintPrompt() {
|
|
||||||
return QStringLiteral(
|
|
||||||
"中国画局部,需去除人物并只补背景:请严格依据掩膜外已见的笔墨皴擦、设色层次、留白与纸绢肌理,"
|
|
||||||
"在掩膜内自然延展,补全为山石、坡岸、云水、苔点或空灵留白等,与外缘笔法、墨色与气韵连贯衔接;"
|
|
||||||
"禁止出现人物轮廓、肢体、衣褶、五官、手足、发丝、肤色、投影及去人后的模糊晕带、鬼影边缘与色块台阶,"
|
|
||||||
"整体须像原画从未画入人物一样自然,不留修补感。");
|
|
||||||
}
|
|
||||||
|
|
||||||
BlackholeResolveDialog::BlackholeResolveDialog(const QString& blackholeName, QWidget* parent)
|
BlackholeResolveDialog::BlackholeResolveDialog(const QString& blackholeName, QWidget* parent)
|
||||||
: QDialog(parent),
|
: QDialog(parent),
|
||||||
m_blackholeName(blackholeName) {
|
m_blackholeName(blackholeName) {
|
||||||
@@ -142,8 +134,7 @@ void BlackholeResolveDialog::buildDetailPage() {
|
|||||||
pLay->setSpacing(8);
|
pLay->setSpacing(8);
|
||||||
|
|
||||||
m_promptEdit = new QPlainTextEdit(panel);
|
m_promptEdit = new QPlainTextEdit(panel);
|
||||||
m_promptEdit->setPlainText(BlackholeResolveDialog::defaultModelInpaintPrompt());
|
m_promptEdit->setPlaceholderText(QStringLiteral("可选"));
|
||||||
m_promptEdit->setPlaceholderText(QStringLiteral("可按画面增删;若全部删空,请求时仍使用内置默认描述"));
|
|
||||||
m_promptEdit->setMinimumHeight(72);
|
m_promptEdit->setMinimumHeight(72);
|
||||||
pLay->addWidget(m_promptEdit);
|
pLay->addWidget(m_promptEdit);
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ public:
|
|||||||
Algorithm selectedAlgorithm() const { return m_selectedAlgorithm; }
|
Algorithm selectedAlgorithm() const { return m_selectedAlgorithm; }
|
||||||
QString promptText() const;
|
QString promptText() const;
|
||||||
|
|
||||||
/// 模型补全(LaMa 等)内置正向提示:中国画去人物、补背景纹理,无人物残留。
|
|
||||||
[[nodiscard]] static QString defaultModelInpaintPrompt();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void buildSelectPage();
|
void buildSelectPage();
|
||||||
void buildDetailPage();
|
void buildDetailPage();
|
||||||
|
|||||||
@@ -5050,21 +5050,13 @@ void MainWindow::showBlackholeContextMenu(const QPoint& globalPos, const QString
|
|||||||
client->setBaseUrl(QUrl(base));
|
client->setBaseUrl(QUrl(base));
|
||||||
|
|
||||||
QString immediateErr;
|
QString immediateErr;
|
||||||
QString prompt = dlg.promptText();
|
const QString prompt = dlg.promptText();
|
||||||
if (prompt.isEmpty()) {
|
|
||||||
prompt = BlackholeResolveDialog::defaultModelInpaintPrompt();
|
|
||||||
}
|
|
||||||
QNetworkReply* reply = client->inpaintAsync(
|
QNetworkReply* reply = client->inpaintAsync(
|
||||||
cropPng,
|
cropPng,
|
||||||
maskPng,
|
maskPng,
|
||||||
// LaMa(物体移除 / 擦除)
|
QStringLiteral("flux_fill"),
|
||||||
QStringLiteral("lama"),
|
|
||||||
prompt,
|
prompt,
|
||||||
// 抑制人物与去人常见伪影(中英混合便于通用后端)
|
QString(),
|
||||||
QStringLiteral(
|
|
||||||
"person, human, face, body, hands, feet, portrait, silhouette, skin, clothing folds, hair, "
|
|
||||||
"shadow, ghosting, blur halo, seam, banding, watermark, text, logo, signature, jpeg artifacts"),
|
|
||||||
// 去人物场景:strength 太低容易“保留人物残影”;适当提高更干净
|
|
||||||
0.72,
|
0.72,
|
||||||
1024,
|
1024,
|
||||||
&immediateErr);
|
&immediateErr);
|
||||||
@@ -5077,7 +5069,7 @@ void MainWindow::showBlackholeContextMenu(const QPoint& globalPos, const QString
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto* task = new CancelableTaskDialog(QStringLiteral("黑洞修复"),
|
auto* task = new CancelableTaskDialog(QStringLiteral("黑洞修复"),
|
||||||
QStringLiteral("正在请求后端模型补全,请稍候……"),
|
QStringLiteral("补全中…"),
|
||||||
this);
|
this);
|
||||||
task->setAttribute(Qt::WA_DeleteOnClose, true);
|
task->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
connect(task, &CancelableTaskDialog::canceled, this, [reply, task]() {
|
connect(task, &CancelableTaskDialog::canceled, this, [reply, task]() {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
HF_TOKEN=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
|
||||||
@@ -15,7 +15,7 @@ router = APIRouter(tags=["inpaint"])
|
|||||||
@router.post("/inpaint")
|
@router.post("/inpaint")
|
||||||
def inpaint(req: InpaintRequest) -> Dict[str, Any]:
|
def inpaint(req: InpaintRequest) -> Dict[str, Any]:
|
||||||
try:
|
try:
|
||||||
model_name = req.model_name or "sdxl_inpaint"
|
model_name = req.model_name or "flux_fill"
|
||||||
pil = b64_to_pil_image(req.image_b64).convert("RGB")
|
pil = b64_to_pil_image(req.image_b64).convert("RGB")
|
||||||
|
|
||||||
if req.mask_b64:
|
if req.mask_b64:
|
||||||
@@ -24,13 +24,17 @@ def inpaint(req: InpaintRequest) -> Dict[str, Any]:
|
|||||||
mask = default_half_mask(pil)
|
mask = default_half_mask(pil)
|
||||||
|
|
||||||
predictor = get_inpaint_predictor(model_name)
|
predictor = get_inpaint_predictor(model_name)
|
||||||
|
call_kw: Dict[str, Any] = {"strength": req.strength, "max_side": req.max_side}
|
||||||
|
if req.guidance_scale is not None:
|
||||||
|
call_kw["guidance_scale"] = req.guidance_scale
|
||||||
|
if req.num_inference_steps is not None:
|
||||||
|
call_kw["num_inference_steps"] = req.num_inference_steps
|
||||||
out = predictor(
|
out = predictor(
|
||||||
pil,
|
pil,
|
||||||
mask,
|
mask,
|
||||||
req.prompt or "",
|
req.prompt or "",
|
||||||
req.negative_prompt or "",
|
req.negative_prompt or "",
|
||||||
strength=req.strength,
|
**call_kw,
|
||||||
max_side=req.max_side,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
out_dir = OUTPUT_DIR / "inpaint"
|
out_dir = OUTPUT_DIR / "inpaint"
|
||||||
|
|||||||
@@ -43,19 +43,19 @@ def get_models() -> Dict[str, Any]:
|
|||||||
"mask2former": {"name": "Mask2Former (not implemented)"},
|
"mask2former": {"name": "Mask2Former (not implemented)"},
|
||||||
},
|
},
|
||||||
"inpaint": {
|
"inpaint": {
|
||||||
"copy": {"name": "Copy (no-op)", "params": []},
|
"flux_fill": {
|
||||||
"sdxl_inpaint": {
|
"name": "FLUX.1 Fill (default)",
|
||||||
"name": "SDXL Inpaint",
|
"params": [{"id": "prompt", "label": "prompt", "optional": True}],
|
||||||
"params": [
|
|
||||||
{"id": "prompt", "label": "提示词", "optional": True},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
"lama": {"name": "LaMa (Erase / Remove Object)", "params": []},
|
"sdxl_inpaint": {
|
||||||
|
"name": "sdxl_inpaint",
|
||||||
|
"params": [{"id": "prompt", "label": "prompt", "optional": True}],
|
||||||
|
},
|
||||||
|
"lama": {"name": "lama", "params": []},
|
||||||
|
"copy": {"name": "copy (debug)", "params": []},
|
||||||
"controlnet": {
|
"controlnet": {
|
||||||
"name": "ControlNet Inpaint (canny)",
|
"name": "controlnet",
|
||||||
"params": [
|
"params": [{"id": "prompt", "label": "prompt", "optional": True}],
|
||||||
{"id": "prompt", "label": "提示词", "optional": True},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"animation": {
|
"animation": {
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ class InpaintRequest(ImageInput):
|
|||||||
negative_prompt: Optional[str] = Field("", description="负向 prompt")
|
negative_prompt: Optional[str] = Field("", description="负向 prompt")
|
||||||
mask_b64: Optional[str] = Field(None, description="mask PNG base64(可选)")
|
mask_b64: Optional[str] = Field(None, description="mask PNG base64(可选)")
|
||||||
max_side: int = Field(1024, ge=128, le=2048)
|
max_side: int = Field(1024, ge=128, le=2048)
|
||||||
|
guidance_scale: Optional[float] = Field(None, ge=0.0, le=200.0, description="扩散 CFG;不传则各后端默认")
|
||||||
|
num_inference_steps: Optional[int] = Field(None, ge=1, le=150, description="采样步数;不传则各后端默认")
|
||||||
|
|
||||||
|
|
||||||
class AnimateRequest(BaseModel):
|
class AnimateRequest(BaseModel):
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ def get_inpaint_predictor(model_name: str):
|
|||||||
_inpaint_cache[model_name] = _copy
|
_inpaint_cache[model_name] = _copy
|
||||||
return _copy
|
return _copy
|
||||||
|
|
||||||
|
if model_name == "flux_fill":
|
||||||
|
pred, _ = build_inpaint_predictor(UnifiedInpaintConfig(backend=InpaintBackend.FLUX_FILL))
|
||||||
|
_inpaint_cache[model_name] = pred
|
||||||
|
return pred
|
||||||
|
|
||||||
if model_name == "sdxl_inpaint":
|
if model_name == "sdxl_inpaint":
|
||||||
pred, _ = build_inpaint_predictor(UnifiedInpaintConfig(backend=InpaintBackend.SDXL_INPAINT))
|
pred, _ = build_inpaint_predictor(UnifiedInpaintConfig(backend=InpaintBackend.SDXL_INPAINT))
|
||||||
_inpaint_cache[model_name] = pred
|
_inpaint_cache[model_name] = pred
|
||||||
|
|||||||
+16
-7
@@ -86,10 +86,17 @@ class InpaintModelInfo(TypedDict):
|
|||||||
family: str
|
family: str
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
backend: str # "sdxl_inpaint" | "controlnet"
|
backend: str # "flux_fill" | "sdxl_inpaint" | "controlnet"
|
||||||
|
|
||||||
|
|
||||||
INPAINT_MODELS: List[InpaintModelInfo] = [
|
INPAINT_MODELS: List[InpaintModelInfo] = [
|
||||||
|
{
|
||||||
|
"id": "flux_fill",
|
||||||
|
"family": "FLUX",
|
||||||
|
"name": "FLUX.1 Fill",
|
||||||
|
"description": "FLUX Fill 专用补全",
|
||||||
|
"backend": "flux_fill",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "sdxl_inpaint",
|
"id": "sdxl_inpaint",
|
||||||
"family": "SDXL",
|
"family": "SDXL",
|
||||||
@@ -100,8 +107,8 @@ INPAINT_MODELS: List[InpaintModelInfo] = [
|
|||||||
{
|
{
|
||||||
"id": "controlnet",
|
"id": "controlnet",
|
||||||
"family": "ControlNet",
|
"family": "ControlNet",
|
||||||
"name": "ControlNet (placeholder)",
|
"name": "SD1.5 + ControlNet Inpaint",
|
||||||
"description": "ControlNet 补全/控制生成(当前统一封装暂未实现)。",
|
"description": "runway inpaint + v11p inpaint ControlNet。",
|
||||||
"backend": "controlnet",
|
"backend": "controlnet",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -159,10 +166,12 @@ class DepthConfig:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class InpaintConfig:
|
class InpaintConfig:
|
||||||
# 统一补全默认后端
|
# 统一补全默认后端(与 model key 对齐;实际路由以请求的 model_name 为准)
|
||||||
backend: Literal["sdxl_inpaint", "controlnet"] = "sdxl_inpaint"
|
backend: Literal["flux_fill", "sdxl_inpaint", "controlnet"] = "flux_fill"
|
||||||
# SDXL Inpaint 的基础模型(可写 HuggingFace model id 或本地目录)
|
# FLUX Fill(gated:须在 Hugging Face 同意条款,并建议设置环境变量 HF_TOKEN)
|
||||||
sdxl_base_model: str = "stabilityai/stable-diffusion-xl-base-1.0"
|
flux_fill_model: str = "black-forest-labs/FLUX.1-Fill-dev"
|
||||||
|
# SDXL:须为专用 inpaint 权重(勿用 xl-base 当补全底模)
|
||||||
|
sdxl_base_model: str = "diffusers/stable-diffusion-xl-1.0-inpainting-0.1"
|
||||||
# ControlNet Inpaint 基础模型与 controlnet 权重
|
# ControlNet Inpaint 基础模型与 controlnet 权重
|
||||||
controlnet_base_model: str = "runwayml/stable-diffusion-inpainting"
|
controlnet_base_model: str = "runwayml/stable-diffusion-inpainting"
|
||||||
controlnet_model: str = "lllyasviel/control_v11p_sd15_inpaint"
|
controlnet_model: str = "lllyasviel/control_v11p_sd15_inpaint"
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ def get_sdxl_base_model_from_app(app_cfg: AppConfig | None = None) -> str:
|
|||||||
return app_cfg.inpaint.sdxl_base_model
|
return app_cfg.inpaint.sdxl_base_model
|
||||||
|
|
||||||
|
|
||||||
|
def get_flux_fill_model_from_app(app_cfg: AppConfig | None = None) -> str:
|
||||||
|
if app_cfg is None:
|
||||||
|
app_cfg = load_app_config()
|
||||||
|
return app_cfg.inpaint.flux_fill_model
|
||||||
|
|
||||||
|
|
||||||
def get_controlnet_base_model_from_app(app_cfg: AppConfig | None = None) -> str:
|
def get_controlnet_base_model_from_app(app_cfg: AppConfig | None = None) -> str:
|
||||||
if app_cfg is None:
|
if app_cfg is None:
|
||||||
app_cfg = load_app_config()
|
app_cfg = load_app_config()
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
统一的补全(Inpaint)模型加载入口。
|
统一的补全(Inpaint)模型加载入口。
|
||||||
|
|
||||||
当前支持:
|
当前支持:
|
||||||
|
- FLUX Fill(diffusers FluxFillPipeline,如 FLUX.1-Fill-dev)
|
||||||
- SDXL Inpaint(diffusers AutoPipelineForInpainting)
|
- SDXL Inpaint(diffusers AutoPipelineForInpainting)
|
||||||
- ControlNet(Stable Diffusion + ControlNet Inpaint)
|
- ControlNet(Stable Diffusion + ControlNet Inpaint)
|
||||||
- LaMa(torchscript big-lama,本地封装)
|
- LaMa(torchscript big-lama,本地封装)
|
||||||
@@ -11,7 +12,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -21,10 +22,12 @@ from config_loader import (
|
|||||||
get_sdxl_base_model_from_app,
|
get_sdxl_base_model_from_app,
|
||||||
get_controlnet_base_model_from_app,
|
get_controlnet_base_model_from_app,
|
||||||
get_controlnet_model_from_app,
|
get_controlnet_model_from_app,
|
||||||
|
get_flux_fill_model_from_app,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InpaintBackend(str, Enum):
|
class InpaintBackend(str, Enum):
|
||||||
|
FLUX_FILL = "flux_fill"
|
||||||
SDXL_INPAINT = "sdxl_inpaint"
|
SDXL_INPAINT = "sdxl_inpaint"
|
||||||
CONTROLNET = "controlnet"
|
CONTROLNET = "controlnet"
|
||||||
LAMA = "lama"
|
LAMA = "lama"
|
||||||
@@ -36,6 +39,7 @@ class UnifiedInpaintConfig:
|
|||||||
device: str | None = None
|
device: str | None = None
|
||||||
# SDXL base model (HF id 或本地目录),不填则用 config.py 的默认值
|
# SDXL base model (HF id 或本地目录),不填则用 config.py 的默认值
|
||||||
sdxl_base_model: str | None = None
|
sdxl_base_model: str | None = None
|
||||||
|
flux_fill_model: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -100,7 +104,7 @@ def _make_sdxl_inpaint_predictor(
|
|||||||
- 输出:PIL RGB 结果图
|
- 输出:PIL RGB 结果图
|
||||||
"""
|
"""
|
||||||
import torch
|
import torch
|
||||||
from diffusers import AutoPipelineForText2Image, AutoPipelineForInpainting
|
from diffusers import AutoPipelineForInpainting
|
||||||
|
|
||||||
app_cfg = load_app_config()
|
app_cfg = load_app_config()
|
||||||
base_model = cfg.sdxl_base_model or get_sdxl_base_model_from_app(app_cfg)
|
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
|
torch_dtype = torch.float16 if device == "cuda" else torch.float32
|
||||||
|
|
||||||
pipe_t2i = AutoPipelineForText2Image.from_pretrained(
|
pipe = AutoPipelineForInpainting.from_pretrained(
|
||||||
base_model,
|
base_model,
|
||||||
torch_dtype=torch_dtype,
|
torch_dtype=torch_dtype,
|
||||||
variant="fp16" if device == "cuda" else None,
|
variant="fp16" if device == "cuda" else None,
|
||||||
use_safetensors=True,
|
use_safetensors=True,
|
||||||
).to(device)
|
).to(device)
|
||||||
pipe = AutoPipelineForInpainting.from_pipe(pipe_t2i).to(device)
|
|
||||||
|
|
||||||
# 省显存设置(尽量不改变输出语义)
|
# 省显存设置(尽量不改变输出语义)
|
||||||
# 注意:CPU offload 会明显变慢,但能显著降低显存占用。
|
# 注意:CPU offload 会明显变慢,但能显著降低显存占用。
|
||||||
@@ -149,6 +152,7 @@ def _make_sdxl_inpaint_predictor(
|
|||||||
guidance_scale: float = 7.5,
|
guidance_scale: float = 7.5,
|
||||||
num_inference_steps: int = 30,
|
num_inference_steps: int = 30,
|
||||||
max_side: int = 1024,
|
max_side: int = 1024,
|
||||||
|
**_: Any,
|
||||||
) -> Image.Image:
|
) -> Image.Image:
|
||||||
image = image.convert("RGB")
|
image = image.convert("RGB")
|
||||||
# diffusers 要求 mask 为单通道,白色区域为需要重绘
|
# diffusers 要求 mask 为单通道,白色区域为需要重绘
|
||||||
@@ -257,6 +261,7 @@ def _make_controlnet_predictor(_: UnifiedInpaintConfig):
|
|||||||
num_inference_steps: int = 30,
|
num_inference_steps: int = 30,
|
||||||
controlnet_conditioning_scale: float = 1.0,
|
controlnet_conditioning_scale: float = 1.0,
|
||||||
max_side: int = 768,
|
max_side: int = 768,
|
||||||
|
**_: Any,
|
||||||
) -> Image.Image:
|
) -> Image.Image:
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -316,6 +321,22 @@ def _make_controlnet_predictor(_: UnifiedInpaintConfig):
|
|||||||
return _predict
|
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):
|
def _make_lama_predictor(cfg: UnifiedInpaintConfig):
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
@@ -340,7 +361,7 @@ def _make_lama_predictor(cfg: UnifiedInpaintConfig):
|
|||||||
max_side: int = 1024,
|
max_side: int = 1024,
|
||||||
**_kwargs,
|
**_kwargs,
|
||||||
) -> Image.Image:
|
) -> Image.Image:
|
||||||
# LaMa 不使用 prompt/negative_prompt,也不使用 strength/steps 等扩散参数
|
# big-lama:仅 (RGB, mask);无文本条件。
|
||||||
_ = (strength, guidance_scale, num_inference_steps, max_side)
|
_ = (strength, guidance_scale, num_inference_steps, max_side)
|
||||||
return lama(image, mask)
|
return lama(image, mask)
|
||||||
|
|
||||||
@@ -355,6 +376,9 @@ def build_inpaint_predictor(
|
|||||||
"""
|
"""
|
||||||
cfg = cfg or UnifiedInpaintConfig()
|
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:
|
if cfg.backend == InpaintBackend.SDXL_INPAINT:
|
||||||
return _make_sdxl_inpaint_predictor(cfg), InpaintBackend.SDXL_INPAINT
|
return _make_sdxl_inpaint_predictor(cfg), InpaintBackend.SDXL_INPAINT
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
"""
|
|
||||||
source .venv/bin/activate # 仓库根 hfut-bishe/.venv
|
|
||||||
cd python_server && uvicorn app.main:app --host 0.0.0.0 --port 8000
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
from app.main import app
|
from app.main import app
|
||||||
|
|
||||||
__all__ = ["app"]
|
__all__ = ["app"]
|
||||||
|
|||||||
Reference in New Issue
Block a user