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

Update app.py

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