i0switch commited on
Commit
3063fd0
·
verified ·
1 Parent(s): 3ba6815

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +178 -0
  2. requirements.txt +30 -0
app.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py — BRA v7 (AIGaming repo) × InstantID × ZeroGPU
2
+ # 2025-06-22
3
+
4
+ ##############################################################################
5
+ # 0. diffusers-0.27 互換: cached_download() パッチ
6
+ ##############################################################################
7
+ from huggingface_hub import hf_hub_download
8
+ import huggingface_hub as _hf
9
+ if not hasattr(_hf, "cached_download"):
10
+ _hf.cached_download = hf_hub_download
11
+
12
+ ##############################################################################
13
+ # 1. ライブラリ
14
+ ##############################################################################
15
+ import os, io, base64, subprocess, traceback
16
+ from pathlib import Path
17
+ from typing import Optional
18
+ import numpy as np
19
+ import torch, gradio as gr, spaces
20
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException
21
+ from PIL import Image
22
+ from diffusers import (
23
+ StableDiffusionControlNetPipeline,
24
+ ControlNetModel,
25
+ DPMSolverMultistepScheduler,
26
+ )
27
+ from diffusers.loaders import AttnProcsLayers
28
+ from insightface.app import FaceAnalysis
29
+ from realesrgan import RealESRGANer
30
+
31
+ ##############################################################################
32
+ # 2. キャッシュパス
33
+ ##############################################################################
34
+ ROOT = Path("/data") if Path("/data").exists() else Path.home() / ".cache/instantid"
35
+ MODELS = ROOT / "models"; LORA = ROOT / "lora"; UPSCALE = ROOT / "realesrgan"
36
+ for p in (MODELS, LORA, UPSCALE): p.mkdir(parents=True, exist_ok=True)
37
+
38
+ ##############################################################################
39
+ # 3. モデル ID / ファイル
40
+ ##############################################################################
41
+ # --- BRA v7 (公開) ---
42
+ BRA_REPO = "AIGaming/beautiful_realistic_asians"
43
+ BRA_FILE = "beautifulRealistic_v7.safetensors"
44
+ BRA_REV = "801a9b1999dd7018e58a1e2b432fdccd3d1d723d" # 固定 revision
45
+
46
+ # --- IP-Adapter 本体 & LoRA ---
47
+ IP_REPO, IP_BIN = "h94/IP-Adapter", "models/ip-adapter-plus-face_sd15.bin"
48
+ LORA_REPO,IP_LORA = "h94/IP-Adapter-FaceID", "ip-adapter-faceid-plusv2_sd15_lora.safetensors"
49
+
50
+ # --- ControlNet (MediaPipe Face) ---
51
+ CN_REPO, CN_SUBF = "CrucibleAI/ControlNetMediaPipeFace", "diffusion_sd15"
52
+
53
+ # --- Real-ESRGAN ---
54
+ ESRGAN_REPO, ESRGAN_FILE = "aimagelab/realesrgan", "RealESRGAN_x4plus.pth"
55
+
56
+ ##############################################################################
57
+ # 4. HF Hub ダウンロード
58
+ ##############################################################################
59
+ def dl(repo: str, file: str, sub: str | None = None, rev: str | None = None) -> Path:
60
+ return Path(hf_hub_download(repo, file, subfolder=sub,
61
+ revision=rev, cache_dir=str(MODELS)))
62
+
63
+ ##############################################################################
64
+ # 5. グローバル
65
+ ##############################################################################
66
+ pipe: Optional[StableDiffusionControlNetPipeline] = None
67
+ face_analyser: Optional[FaceAnalysis] = None
68
+ upsampler: Optional[RealESRGANer] = None
69
+
70
+ ##############################################################################
71
+ # 6. 初期化
72
+ ##############################################################################
73
+ def init():
74
+ global pipe, face_analyser, upsampler
75
+ if pipe is not None:
76
+ return
77
+ print("[INIT] downloading models…")
78
+
79
+ # 6-1 BRA v7
80
+ bra_ckpt = dl(BRA_REPO, BRA_FILE, rev=BRA_REV)
81
+
82
+ # 6-2 ControlNet
83
+ cn = ControlNetModel.from_pretrained(
84
+ CN_REPO, subfolder=CN_SUBF, torch_dtype=torch.float16,
85
+ cache_dir=str(MODELS)
86
+ )
87
+
88
+ # 6-3 Pipeline from .safetensors + ControlNet
89
+ pipe_ = StableDiffusionControlNetPipeline.from_single_file(
90
+ bra_ckpt, controlnet=cn, torch_dtype=torch.float16,
91
+ safety_checker=None
92
+ )
93
+ pipe_.scheduler = DPMSolverMultistepScheduler.from_config(pipe_.scheduler.config)
94
+
95
+ # 6-4 IP-Adapter
96
+ ip_lora = dl(LORA_REPO, IP_LORA)
97
+
98
+ ### 最終修正 ### subfolder引数に空文字列""を渡し、TypeErrorを回避する
99
+ pipe_.load_ip_adapter(IP_REPO, "", weight_name=IP_BIN, cache_dir=str(MODELS))
100
+
101
+ AttnProcsLayers(pipe_.unet.attn_processors).load_lora_weights(
102
+ ip_lora, adapter_name="ip_faceid", safe_load=True
103
+ )
104
+ pipe_.set_adapters(["ip_faceid"], adapter_weights=[0.6])
105
+ pipe_.to("cuda"); pipe_ = pipe_
106
+
107
+ pipe = pipe_
108
+ face_analyser = FaceAnalysis(
109
+ name="buffalo_l", root=str(MODELS), providers=["CUDAExecutionProvider"]
110
+ ); face_analyser.prepare(ctx_id=0, det_size=(640,640))
111
+
112
+ esr = dl(ESRGAN_REPO, ESRGAN_FILE)
113
+ upsampler = RealESRGANer(scale=4, model_path=str(esr), half=True,
114
+ tile=512, tile_pad=10, pre_pad=0, gpu_id=0)
115
+ print("[INIT] ready.")
116
+
117
+ ##############################################################################
118
+ # 7. プロンプト
119
+ ##############################################################################
120
+ BASE = "(masterpiece:1.2), best quality, ultra-realistic, RAW photo, 8k, cinematic lighting, textured skin, "
121
+ NEG = "verybadimagenegative_v1.3, ng_deepnegative_v1_75t, (worst quality:2), (low quality:2), lowres, blurry, bad anatomy, bad hands, extra digits, watermark, signature"
122
+
123
+ ##############################################################################
124
+ # 8. 生成コア
125
+ ##############################################################################
126
+ @spaces.GPU(duration=60)
127
+ def generate(face: Image.Image, subj: str, add: str, neg: str,
128
+ cfg: float, ipw: float, steps: int, w: int, h: int,
129
+ up: bool, upf: int, progress=gr.Progress(track_tqdm=True)):
130
+ if pipe is None:
131
+ init()
132
+ if len(face_analyser.get(np.array(face))) == 0:
133
+ raise ValueError("顔が検出できません。他の画像でお試しください。")
134
+ pipe.set_adapters(["ip_faceid"], adapter_weights=[ipw])
135
+ img = pipe(prompt=BASE+subj+", "+add,
136
+ negative_prompt=NEG+", "+neg,
137
+ num_inference_steps=steps, guidance_scale=cfg,
138
+ image=face, width=w, height=h).images[0]
139
+ if up:
140
+ upsampler.scale = int(upf)
141
+ img, _ = upsampler.enhance(np.array(img)); img = Image.fromarray(img)
142
+ return img
143
+
144
+ ##############################################################################
145
+ # 9. Gradio UI
146
+ ##############################################################################
147
+ with gr.Blocks(title="BRA v7 × InstantID (ZeroGPU)") as demo:
148
+ gr.Markdown("## BRA v7 × InstantID")
149
+ with gr.Row():
150
+ f = gr.Image(type="pil", label="Face ID"); s = gr.Textbox(label="被写体説明")
151
+ ap = gr.Textbox(label="追加プロンプト"); ng = gr.Textbox(label="追加ネガ")
152
+ with gr.Row():
153
+ cf = gr.Slider(1,20,7.5,0.5,"CFG"); ip = gr.Slider(0.1,1.0,0.6,0.05,"IP-Adapter Weight")
154
+ with gr.Row():
155
+ st = gr.Slider(10,50,30,1,"Steps"); W = gr.Slider(512,1024,768,64,"W"); H = gr.Slider(512,1024,768,64,"H")
156
+ with gr.Row():
157
+ up = gr.Checkbox(label="Real-ESRGAN"); upf = gr.Radio([4,8], value=4, label="アップスケール")
158
+ btn = gr.Button("Generate"); out = gr.Image(type="pil", label="Result")
159
+ btn.click(generate, [f,s,ap,ng,cf,ip,st,W,H,up,upf], out, show_progress=True)
160
+
161
+ ##############################################################################
162
+ # 10. FastAPI
163
+ ##############################################################################
164
+ app = FastAPI()
165
+
166
+ @app.post("/api/generate")
167
+ async def api_gen(subj: str=Form(...), cfg: float=Form(7.5), stp: int=Form(30),
168
+ ipw: float=Form(0.6), W: int=Form(768), H: int=Form(768),
169
+ file: UploadFile=File(...)):
170
+ img = Image.open(io.BytesIO(await file.read())).convert("RGB")
171
+ res = generate(img, subj, "", "", cfg, ipw, stp, W, H, False, 4)
172
+ buf = io.BytesIO(); res.save(buf,"PNG")
173
+ return {"image":"data:image/png;base64,"+base64.b64encode(buf.getvalue()).decode()}
174
+
175
+ ##############################################################################
176
+ # 11. Launch
177
+ ##############################################################################
178
+ demo.queue(default_concurrency_limit=2).launch(share=False)
requirements.txt ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GPU wheels は ZeroGPU が自動で解決するため extra-index は不要
2
+
3
+ gradio[mcp,oauth]==5.34.2
4
+ spaces==0.37.1
5
+ huggingface-hub>=0.30,<0.40
6
+
7
+ # --- PyTorch スタック(ZeroGPU 公認) ---
8
+ torch==2.4.1 # “+cu118” を外す
9
+ torchvision==0.19.1
10
+ xformers==0.0.28.post1 # 2.4.1 対応ビルド
11
+
12
+ # --- 生成 AI ---
13
+ diffusers==0.30.3
14
+ accelerate==0.29.3
15
+ transformers==4.41.1
16
+ peft==0.11.1
17
+ safetensors>=0.5.3
18
+ numpy==1.26.4
19
+
20
+ # --- CV / 画像処理 ---
21
+ opencv-python-headless
22
+ Pillow
23
+ insightface
24
+ basicsr
25
+ realesrgan
26
+ onnxruntime-gpu
27
+
28
+ # --- API グルー ---
29
+ fastapi
30
+ python-multipart