Spaces:
Sleeping
Sleeping
Restore edited output to original image size
Browse files- gradio_app/edit.py +11 -5
- 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
|
| 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 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
| 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:
|