Ammmob commited on
Commit
2beb04d
·
1 Parent(s): ee797d5

Restore edited output to original image size

Browse files
Files changed (2) hide show
  1. gradio_app/edit.py +11 -5
  2. pixelsmile/utils/image.py +113 -0
gradio_app/edit.py CHANGED
@@ -11,7 +11,7 @@ from gradio_app.config import (
11
  from gradio_app.logging_utils import setup_logging
12
  from gradio_app.pipeline import load_lora
13
  from pixelsmile.linear_conditioning import compute_text_embeddings
14
- from pixelsmile.utils.image import resize
15
 
16
 
17
  logger = setup_logging()
@@ -39,8 +39,7 @@ def prepare_input_image(image: Image.Image) -> Image.Image:
39
  raise gr.Error("Please upload an input image.")
40
  if not isinstance(image, Image.Image):
41
  image = Image.fromarray(image)
42
- image = image.convert("RGB")
43
- return resize(image, (DEFAULT_WIDTH, DEFAULT_HEIGHT), DEFAULT_RESIZE_MODE)
44
 
45
 
46
  def run_edit(
@@ -70,7 +69,12 @@ def run_edit(
70
  pipe = load_lora(weight_version)
71
  if progress is not None:
72
  progress(0.35, desc="Preparing input image...")
73
- input_image = prepare_input_image(image)
 
 
 
 
 
74
  logger.info("Input image prepared at size: %s", input_image.size)
75
  edit_condition = build_edit_condition(subject, expression, float(scale))
76
  logger.info("Edit condition: %s", edit_condition)
@@ -107,4 +111,6 @@ def run_edit(
107
  logger.info("Pipeline inference finished")
108
  if progress is not None:
109
  progress(0.95, desc="Finalizing output...")
110
- return output.images[0]
 
 
 
11
  from gradio_app.logging_utils import setup_logging
12
  from gradio_app.pipeline import load_lora
13
  from pixelsmile.linear_conditioning import compute_text_embeddings
14
+ from pixelsmile.utils.image import build_edit_image_bundle, restore_edited_image
15
 
16
 
17
  logger = setup_logging()
 
39
  raise gr.Error("Please upload an input image.")
40
  if not isinstance(image, Image.Image):
41
  image = Image.fromarray(image)
42
+ return image.convert("RGB")
 
43
 
44
 
45
  def run_edit(
 
69
  pipe = load_lora(weight_version)
70
  if progress is not None:
71
  progress(0.35, desc="Preparing input image...")
72
+ source_image = prepare_input_image(image)
73
+ input_image, restore_meta = build_edit_image_bundle(
74
+ source_image,
75
+ (DEFAULT_WIDTH, DEFAULT_HEIGHT),
76
+ DEFAULT_RESIZE_MODE,
77
+ )
78
  logger.info("Input image prepared at size: %s", input_image.size)
79
  edit_condition = build_edit_condition(subject, expression, float(scale))
80
  logger.info("Edit condition: %s", edit_condition)
 
111
  logger.info("Pipeline inference finished")
112
  if progress is not None:
113
  progress(0.95, desc="Finalizing output...")
114
+ restored = restore_edited_image(output.images[0], restore_meta)
115
+ logger.info("Restored edited image to original size: %s", restored.size)
116
+ return restored
pixelsmile/utils/image.py CHANGED
@@ -145,6 +145,119 @@ def resize(
145
  else:
146
  raise ValueError(f"Resize mode error: {resize_mode}")
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  def scale_fun(x: float) -> float:
149
  y = max(0.0, min(1.0, x))
150
  if x <= 0.6:
 
145
  else:
146
  raise ValueError(f"Resize mode error: {resize_mode}")
147
 
148
+
149
+ def build_edit_image_bundle(
150
+ image: Image.Image,
151
+ target_size: tuple,
152
+ resize_mode: str,
153
+ box: list = None,
154
+ vertical_bias: float = 0.5,
155
+ ):
156
+ original = image.convert("RGB")
157
+ original_size = original.size
158
+ target_w, target_h = target_size
159
+
160
+ if resize_mode == "direct":
161
+ edited_input = original.resize((target_w, target_h), resample=Image.LANCZOS)
162
+ meta = {
163
+ "mode": "direct",
164
+ "original_size": original_size,
165
+ "target_size": target_size,
166
+ }
167
+ return edited_input, meta
168
+
169
+ if resize_mode == "padding":
170
+ w, h = original.size
171
+ scale = min(target_w / w, target_h / h)
172
+ new_w = int(w * scale)
173
+ new_h = int(h * scale)
174
+ resized = original.resize((new_w, new_h), resample=Image.LANCZOS)
175
+
176
+ canvas = Image.new("RGB", (target_w, target_h))
177
+ pad_left = (target_w - new_w) // 2
178
+ pad_top = (target_h - new_h) // 2
179
+ canvas.paste(resized, (pad_left, pad_top))
180
+
181
+ meta = {
182
+ "mode": "padding",
183
+ "original_size": original_size,
184
+ "target_size": target_size,
185
+ "content_box": (pad_left, pad_top, pad_left + new_w, pad_top + new_h),
186
+ }
187
+ return canvas, meta
188
+
189
+ if resize_mode == "crop":
190
+ w, h = original.size
191
+ scale = max(target_w / w, target_h / h)
192
+ new_w = int(w * scale)
193
+ new_h = int(h * scale)
194
+ resized = original.resize((new_w, new_h), resample=Image.LANCZOS)
195
+
196
+ if box:
197
+ nx1, ny1, nx2, ny2 = [c * scale for c in box]
198
+ box_cx = (nx1 + nx2) / 2
199
+ box_cy = (ny1 + ny2) / 2
200
+ box_w = nx2 - nx1
201
+ box_h = ny2 - ny1
202
+
203
+ left = box_cx - target_w / 2
204
+ if box_h <= target_h:
205
+ min_top_to_contain = ny2 - target_h
206
+ max_top_to_contain = ny1
207
+ top = max_top_to_contain - (
208
+ max_top_to_contain - min_top_to_contain
209
+ ) * vertical_bias
210
+ else:
211
+ top = box_cy - target_h / 2
212
+ else:
213
+ left = (new_w - target_w) / 2
214
+ top = (new_h - target_h) * vertical_bias
215
+
216
+ left = max(0, min(left, new_w - target_w))
217
+ top = max(0, min(top, new_h - target_h))
218
+ left = int(left)
219
+ top = int(top)
220
+ right = left + target_w
221
+ bottom = top + target_h
222
+
223
+ edited_input = resized.crop((left, top, right, bottom))
224
+ meta = {
225
+ "mode": "crop",
226
+ "original_size": original_size,
227
+ "target_size": target_size,
228
+ "resized_size": (new_w, new_h),
229
+ "crop_box": (left, top, right, bottom),
230
+ "resized_background": resized,
231
+ }
232
+ return edited_input, meta
233
+
234
+ raise ValueError(f"Resize mode error: {resize_mode}")
235
+
236
+
237
+ def restore_edited_image(
238
+ edited_image: Image.Image,
239
+ meta: dict,
240
+ ):
241
+ mode = meta["mode"]
242
+ original_size = tuple(meta["original_size"])
243
+
244
+ if mode == "direct":
245
+ return edited_image.resize(original_size, resample=Image.LANCZOS)
246
+
247
+ if mode == "padding":
248
+ left, top, right, bottom = meta["content_box"]
249
+ content = edited_image.crop((left, top, right, bottom))
250
+ return content.resize(original_size, resample=Image.LANCZOS)
251
+
252
+ if mode == "crop":
253
+ canvas = meta["resized_background"].copy()
254
+ left, top, right, bottom = meta["crop_box"]
255
+ pasted = edited_image.resize((right - left, bottom - top), resample=Image.LANCZOS)
256
+ canvas.paste(pasted, (left, top))
257
+ return canvas.resize(original_size, resample=Image.LANCZOS)
258
+
259
+ raise ValueError(f"Unsupported restore mode: {mode}")
260
+
261
  def scale_fun(x: float) -> float:
262
  y = max(0.0, min(1.0, x))
263
  if x <= 0.6: