nimzuk commited on
Commit
89bb98f
·
verified ·
1 Parent(s): 40747a2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +17 -37
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # app.py
2
- import os, io, time, math, random
3
  from typing import Tuple
4
  import gradio as gr
5
  import numpy as np
@@ -15,19 +15,19 @@ MODEL_ID = os.getenv("MODEL_ID", "stabilityai/stable-diffusion-2-inpainting")
15
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
16
  DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
17
 
18
- OUT_DIR = "/mnt/data"
19
  os.makedirs(OUT_DIR, exist_ok=True)
20
 
21
  print(f"Loading model: {MODEL_ID} (device={DEVICE}, dtype={DTYPE})")
22
  pipe = StableDiffusionInpaintPipeline.from_pretrained(
23
  MODEL_ID,
24
  torch_dtype=DTYPE,
25
- safety_checker=None, # вы уже тестируете локально
26
  ).to(DEVICE)
27
  pipe.enable_attention_slicing()
28
  if DEVICE == "cuda":
29
  try:
30
- pipe.enable_model_cpu_offload() # экономия VRAM, если доступно
31
  except Exception:
32
  pass
33
 
@@ -38,57 +38,43 @@ def round_to_eight(x: int) -> int:
38
  return int(max(64, (x // 8) * 8))
39
 
40
  def make_square_canvas(img: Image.Image, pad_px: int, bg=(200,200,200)) -> Tuple[Image.Image, Tuple[int,int]]:
41
- """Возвращает квадратный холст + координаты вставки исходника (x0,y0)."""
42
  w, h = img.size
43
  base = max(w, h)
44
  W = base + 2*pad_px
45
  H = base + 2*pad_px
46
  canvas = Image.new("RGB", (W, H), bg)
47
- # центрируем исходник без масштабирования
48
  x0 = (W - w)//2
49
  y0 = (H - h)//2
50
  canvas.paste(img, (x0, y0))
51
  return canvas, (x0, y0)
52
 
53
  def make_ring_mask(canvas_size: Tuple[int,int], inner_rect: Tuple[int,int,int,int], feather_px:int) -> Image.Image:
54
- """Белый (генерировать) за пределами inner_rect, чёрный внутри.
55
- Дополнительно — перья по границе."""
56
  W, H = canvas_size
57
  x0,y0,x1,y1 = inner_rect
58
- mask = Image.new("L", (W,H), 255) # по умолчанию всё белое — дорисовывать
59
  draw = ImageDraw.Draw(mask)
60
- draw.rectangle([x0,y0,x1,y1], fill=0) # центр защищаем от изменений
61
  if feather_px > 0:
62
  mask = mask.filter(ImageFilter.GaussianBlur(radius=feather_px))
63
  return mask
64
 
65
  def paste_original_back(generated: Image.Image, original: Image.Image, offset: Tuple[int,int]) -> Image.Image:
66
- """Жёстко возвращаем оригинал поверх финального результата (центр не испортится)."""
67
  out = generated.copy()
68
- x0,y0 = offset
69
- out.paste(original, (x0,y0))
70
  return out
71
 
72
- def preview_frame(image: Image.Image, pad_px:int) -> Tuple[Image.Image, str]:
73
- """Готовит превью квадрата с рамкой и текстом про пиксели по сторонам."""
74
  w,h = image.size
75
  base = max(w,h)
76
  W = base + 2*pad_px
77
  H = base + 2*pad_px
78
- # уменьшаем для UI
79
- show_W, show_H = 512, 512
80
  canvas, offset = make_square_canvas(image, pad_px)
81
- # рисуем рамку (граница области дорисовки)
82
  draw = ImageDraw.Draw(canvas)
83
  x0,y0 = offset
84
  x1,y1 = x0 + w, y0 + h
85
  draw.rectangle([x0-1,y0-1,x1+1,y1+1], outline=(255, 90, 90), width=3)
86
-
87
- # ресайз для превью
88
  prev = canvas.copy()
89
- prev.thumbnail((show_W, show_H), Image.LANCZOS)
90
-
91
- # текст
92
  top = y0
93
  left = x0
94
  right = W - (x0 + w)
@@ -112,16 +98,13 @@ def outpaint_generate(
112
  if input_image is None:
113
  raise gr.Error("Сначала загрузите изображение.")
114
 
115
- # габариты и холст
116
  canvas, offset = make_square_canvas(input_image.convert("RGB"), pad_px)
117
  w, h = input_image.size
118
  x0, y0 = offset
119
  x1, y1 = x0 + w, y0 + h
120
 
121
- # маска: белое = расширять, чёрное = не трогать
122
  mask = make_ring_mask(canvas.size, (x0,y0,x1,y1), feather_px)
123
 
124
- # безопасные размеры (многократность 8, ограничение по памяти)
125
  W = round_to_eight(canvas.size[0])
126
  H = round_to_eight(canvas.size[1])
127
  MAX_SIDE = 1536 if DEVICE == "cuda" else 1024
@@ -131,7 +114,6 @@ def outpaint_generate(
131
  newH = round_to_eight(int(H*scale))
132
  canvas_small = canvas.resize((newW,newH), Image.LANCZOS)
133
  mask_small = mask.resize((newW,newH), Image.LANCZOS)
134
- # координаты центра тоже масштабируем для обратной вставки:
135
  sx = int(x0*scale); sy = int(y0*scale)
136
  sw = int(w*scale); sh = int(h*scale)
137
  inner_rect_small = (sx,sy,sx+sw,sy+sh)
@@ -140,13 +122,9 @@ def outpaint_generate(
140
  inner_rect_small = (x0,y0,x1,y1)
141
 
142
  g = torch.Generator(device=DEVICE)
143
- if seed is None or int(seed) < 0:
144
- seed_val = random.randint(0, 2**32 - 1)
145
- else:
146
- seed_val = int(seed)
147
  g.manual_seed(seed_val)
148
 
149
- # инференс
150
  with torch.autocast(device_type=DEVICE if DEVICE!="mps" else "cpu"):
151
  out = pipe(
152
  prompt=prompt,
@@ -158,14 +136,11 @@ def outpaint_generate(
158
  generator=g,
159
  ).images[0]
160
 
161
- # если генерили уменьшенно — апскейл обратно
162
  if out.size != canvas.size:
163
  out = out.resize(canvas.size, Image.LANCZOS)
164
 
165
- # возвращаем исходник поверх
166
  final = paste_original_back(out, input_image.convert("RGB"), offset)
167
 
168
- # сохраняем
169
  fname = f"outpaint_{canvas.size[0]}x{canvas.size[1]}_{int(time.time()*1000)}.png"
170
  out_path = os.path.join(OUT_DIR, fname)
171
  os.makedirs(os.path.dirname(out_path), exist_ok=True)
@@ -206,7 +181,6 @@ with gr.Blocks(css="""
206
  meta = gr.Markdown("")
207
  file_out = gr.File(label="Download PNG")
208
 
209
- # live preview
210
  def _update_preview(img, pad_px):
211
  if img is None:
212
  return None, ""
@@ -233,4 +207,10 @@ with gr.Blocks(css="""
233
  )
234
 
235
  if __name__ == "__main__":
236
- demo.launch(server_name="0.0.0.0", server_port=7860, inbrowser=False) # без ssr_mode
 
 
 
 
 
 
 
1
  # app.py
2
+ import os, time, random
3
  from typing import Tuple
4
  import gradio as gr
5
  import numpy as np
 
15
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
16
  DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
17
 
18
+ OUT_DIR = "/mnt/data" # оставляем /mnt/data
19
  os.makedirs(OUT_DIR, exist_ok=True)
20
 
21
  print(f"Loading model: {MODEL_ID} (device={DEVICE}, dtype={DTYPE})")
22
  pipe = StableDiffusionInpaintPipeline.from_pretrained(
23
  MODEL_ID,
24
  torch_dtype=DTYPE,
25
+ safety_checker=None, # для локального теста
26
  ).to(DEVICE)
27
  pipe.enable_attention_slicing()
28
  if DEVICE == "cuda":
29
  try:
30
+ pipe.enable_model_cpu_offload()
31
  except Exception:
32
  pass
33
 
 
38
  return int(max(64, (x // 8) * 8))
39
 
40
  def make_square_canvas(img: Image.Image, pad_px: int, bg=(200,200,200)) -> Tuple[Image.Image, Tuple[int,int]]:
 
41
  w, h = img.size
42
  base = max(w, h)
43
  W = base + 2*pad_px
44
  H = base + 2*pad_px
45
  canvas = Image.new("RGB", (W, H), bg)
 
46
  x0 = (W - w)//2
47
  y0 = (H - h)//2
48
  canvas.paste(img, (x0, y0))
49
  return canvas, (x0, y0)
50
 
51
  def make_ring_mask(canvas_size: Tuple[int,int], inner_rect: Tuple[int,int,int,int], feather_px:int) -> Image.Image:
 
 
52
  W, H = canvas_size
53
  x0,y0,x1,y1 = inner_rect
54
+ mask = Image.new("L", (W,H), 255)
55
  draw = ImageDraw.Draw(mask)
56
+ draw.rectangle([x0,y0,x1,y1], fill=0)
57
  if feather_px > 0:
58
  mask = mask.filter(ImageFilter.GaussianBlur(radius=feather_px))
59
  return mask
60
 
61
  def paste_original_back(generated: Image.Image, original: Image.Image, offset: Tuple[int,int]) -> Image.Image:
 
62
  out = generated.copy()
63
+ out.paste(original, offset)
 
64
  return out
65
 
66
+ def preview_frame(image: Image.Image, pad_px:int):
 
67
  w,h = image.size
68
  base = max(w,h)
69
  W = base + 2*pad_px
70
  H = base + 2*pad_px
 
 
71
  canvas, offset = make_square_canvas(image, pad_px)
 
72
  draw = ImageDraw.Draw(canvas)
73
  x0,y0 = offset
74
  x1,y1 = x0 + w, y0 + h
75
  draw.rectangle([x0-1,y0-1,x1+1,y1+1], outline=(255, 90, 90), width=3)
 
 
76
  prev = canvas.copy()
77
+ prev.thumbnail((512, 512), Image.LANCZOS)
 
 
78
  top = y0
79
  left = x0
80
  right = W - (x0 + w)
 
98
  if input_image is None:
99
  raise gr.Error("Сначала загрузите изображение.")
100
 
 
101
  canvas, offset = make_square_canvas(input_image.convert("RGB"), pad_px)
102
  w, h = input_image.size
103
  x0, y0 = offset
104
  x1, y1 = x0 + w, y0 + h
105
 
 
106
  mask = make_ring_mask(canvas.size, (x0,y0,x1,y1), feather_px)
107
 
 
108
  W = round_to_eight(canvas.size[0])
109
  H = round_to_eight(canvas.size[1])
110
  MAX_SIDE = 1536 if DEVICE == "cuda" else 1024
 
114
  newH = round_to_eight(int(H*scale))
115
  canvas_small = canvas.resize((newW,newH), Image.LANCZOS)
116
  mask_small = mask.resize((newW,newH), Image.LANCZOS)
 
117
  sx = int(x0*scale); sy = int(y0*scale)
118
  sw = int(w*scale); sh = int(h*scale)
119
  inner_rect_small = (sx,sy,sx+sw,sy+sh)
 
122
  inner_rect_small = (x0,y0,x1,y1)
123
 
124
  g = torch.Generator(device=DEVICE)
125
+ seed_val = random.randint(0, 2**32 - 1) if (seed is None or int(seed) < 0) else int(seed)
 
 
 
126
  g.manual_seed(seed_val)
127
 
 
128
  with torch.autocast(device_type=DEVICE if DEVICE!="mps" else "cpu"):
129
  out = pipe(
130
  prompt=prompt,
 
136
  generator=g,
137
  ).images[0]
138
 
 
139
  if out.size != canvas.size:
140
  out = out.resize(canvas.size, Image.LANCZOS)
141
 
 
142
  final = paste_original_back(out, input_image.convert("RGB"), offset)
143
 
 
144
  fname = f"outpaint_{canvas.size[0]}x{canvas.size[1]}_{int(time.time()*1000)}.png"
145
  out_path = os.path.join(OUT_DIR, fname)
146
  os.makedirs(os.path.dirname(out_path), exist_ok=True)
 
181
  meta = gr.Markdown("")
182
  file_out = gr.File(label="Download PNG")
183
 
 
184
  def _update_preview(img, pad_px):
185
  if img is None:
186
  return None, ""
 
207
  )
208
 
209
  if __name__ == "__main__":
210
+ # КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: разрешаем /mnt/data
211
+ demo.launch(
212
+ server_name="0.0.0.0",
213
+ server_port=7860,
214
+ inbrowser=False,
215
+ allowed_paths=["/mnt/data"]
216
+ )