blanchon commited on
Commit
4788158
·
1 Parent(s): 02fd27c
Files changed (1) hide show
  1. app.py +91 -63
app.py CHANGED
@@ -80,45 +80,105 @@ def make_example(image_path: Path, mask_path: Path) -> EditorValue:
80
  }
81
 
82
 
83
- def remove_padding(image, original_size):
84
- original_width, original_height = original_size
85
- left = max((image.width - original_width) // 2, 0)
86
- top = max((image.height - original_height) // 2, 0)
87
- right = left + original_width
88
- bottom = top + original_height
89
- return image.crop((left, top, right, bottom))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
 
92
  def adjust_bbox_to_divisible_16(
93
- x_min, y_min, x_max, y_max, width, height, padding=MASK_CONTEXT_PADDING
94
- ):
95
- # Add padding
 
 
 
 
 
 
96
  x_min = max(x_min - padding, 0)
97
  y_min = max(y_min - padding, 0)
98
  x_max = min(x_max + padding, width)
99
  y_max = min(y_max + padding, height)
100
 
101
- # Calculate current bbox width and height
102
- bbox_width = x_max - x_min
103
- bbox_height = y_max - y_min
104
-
105
  # Ensure bbox dimensions are divisible by 16
106
- if bbox_width % 16 != 0:
107
- adjustment = 16 - (bbox_width % 16)
108
- x_min = max(x_min - adjustment // 2, 0)
109
- x_max = min(x_max + adjustment // 2, width)
110
-
111
- if bbox_height % 16 != 0:
112
- adjustment = 16 - (bbox_height % 16)
113
- y_min = max(y_min - adjustment // 2, 0)
114
- y_max = min(y_max + adjustment // 2, height)
115
-
116
- # Ensure bbox is still within bounds
 
117
  x_min = max(x_min, 0)
118
  y_min = max(y_min, 0)
119
  x_max = min(x_max, width)
120
  y_max = min(y_max, height)
121
 
 
 
 
 
122
  return x_min, y_min, x_max, y_max
123
 
124
 
@@ -176,18 +236,9 @@ def infer(
176
  mask_bbox_x_max,
177
  mask_bbox_y_max,
178
  ))
179
- room_image_cropped = ImageOps.pad(
180
- room_image_cropped,
181
- (bbox_longest_side, bbox_longest_side),
182
- # White padding
183
- color=(255, 255, 255),
184
- centering=(0.5, 0.5),
185
- )
186
- room_image_cropped = ImageOps.fit(
187
  room_image_cropped,
188
  (max_dimension, max_dimension),
189
- method=Image.Resampling.BICUBIC,
190
- centering=(0.5, 0.5),
191
  )
192
 
193
  room_mask_cropped = room_mask.crop((
@@ -196,30 +247,17 @@ def infer(
196
  mask_bbox_x_max,
197
  mask_bbox_y_max,
198
  ))
199
- room_mask_cropped.save("room_mask_croppedv1.png")
200
- room_mask_cropped = ImageOps.pad(
201
  room_mask_cropped,
202
  (max_dimension, max_dimension),
203
- # White padding
204
- color=(255, 255, 255),
205
- centering=(0.5, 0.5),
206
- )
207
- room_mask_cropped = ImageOps.fit(
208
- room_mask_cropped,
209
- (max_dimension, max_dimension),
210
- method=Image.Resampling.BICUBIC,
211
- centering=(0.5, 0.5),
212
  )
213
 
214
  room_image_cropped.save("room_image_cropped.png")
215
  room_mask_cropped.save("room_mask_cropped.png")
216
 
217
- furniture_image = ImageOps.pad(
218
  furniture_image_input,
219
  (max_dimension, max_dimension),
220
- # White padding
221
- color=(255, 255, 255),
222
- centering=(0.5, 0.5),
223
  )
224
 
225
  furniture_mask = Image.new("RGB", (max_dimension, max_dimension), (255, 255, 255))
@@ -271,19 +309,9 @@ def infer(
271
  for image in results_images:
272
  final_image = room_image.copy()
273
 
274
- # Downscale back to the bbox_longest_side
275
- image_generated = image.crop((
276
- max_dimension,
277
- 0,
278
- max_dimension * 2,
279
- max_dimension,
280
- ))
281
- image_generated = image_generated.resize(
282
- (bbox_longest_side, bbox_longest_side), Image.Resampling.BICUBIC
283
- )
284
- # Crop back to the bbox (remove the padding)
285
- image_generated = remove_padding(
286
- image_generated,
287
  (
288
  mask_bbox_x_max - mask_bbox_x_min,
289
  mask_bbox_y_max - mask_bbox_y_min,
 
80
  }
81
 
82
 
83
+ def pad(
84
+ image: Image.Image,
85
+ size: tuple[int, int],
86
+ method: int = Image.Resampling.BICUBIC,
87
+ color: str | int | tuple[int, ...] | None = None,
88
+ centering: tuple[float, float] = (1, 1),
89
+ ) -> tuple[Image.Image, tuple[int, int]]:
90
+ resized = ImageOps.contain(image, size, method)
91
+ resized_size = resized.size
92
+ if resized_size == size:
93
+ out = resized
94
+ else:
95
+ out = Image.new(image.mode, size, color)
96
+ if resized.palette:
97
+ palette = resized.getpalette()
98
+ if palette is not None:
99
+ out.putpalette(palette)
100
+ if resized.width != size[0]:
101
+ x = round((size[0] - resized.width) * max(0, max(centering[0], 1)))
102
+ out.paste(resized, (x, 0))
103
+ else:
104
+ y = round((size[1] - resized.height) * max(0, max(centering[1], 1)))
105
+ out.paste(resized, (0, y))
106
+ return out, resized_size
107
+
108
+
109
+ def unpad(
110
+ padded_image: Image.Image,
111
+ padded_size: tuple[int, int],
112
+ original_size: tuple[int, int],
113
+ centering: tuple[float, float] = (1, 1),
114
+ method: int = Image.Resampling.BICUBIC,
115
+ ) -> Image.Image:
116
+ """
117
+ Remove the padding added by the `pad` function to recover the original resized image.
118
+
119
+ Args:
120
+ padded_image (Image.Image): The padded image.
121
+ padded_size (tuple[int, int]): The original size of the resized image before padding.
122
+ centering (tuple[float, float]): The centering used during padding (x, y), defaults to (1, 1).
123
+
124
+ Returns:
125
+ Image.Image: The cropped image matching the original resized dimensions.
126
+
127
+ """
128
+ width, height = padded_image.size
129
+ padded_width, padded_height = padded_size
130
+
131
+ # Calculate the cropping box based on centering
132
+ left = round((width - padded_width) * centering[0])
133
+ top = round((height - padded_height) * centering[1])
134
+ right = left + padded_width
135
+ bottom = top + padded_height
136
+
137
+ # Crop the image to remove the padding
138
+ cropped_image = padded_image.crop((left, top, right, bottom))
139
+
140
+ # Resize the cropped image to match the original size
141
+ resized_image = cropped_image.resize(original_size, method)
142
+ return resized_image
143
 
144
 
145
  def adjust_bbox_to_divisible_16(
146
+ x_min: int,
147
+ y_min: int,
148
+ x_max: int,
149
+ y_max: int,
150
+ width: int,
151
+ height: int,
152
+ padding: int = MASK_CONTEXT_PADDING,
153
+ ) -> tuple[int, int, int, int]:
154
+ # Add context padding
155
  x_min = max(x_min - padding, 0)
156
  y_min = max(y_min - padding, 0)
157
  x_max = min(x_max + padding, width)
158
  y_max = min(y_max + padding, height)
159
 
 
 
 
 
160
  # Ensure bbox dimensions are divisible by 16
161
+ def make_divisible_16(val_min, val_max, max_limit):
162
+ size = val_max - val_min
163
+ if size % 16 != 0:
164
+ adjustment = 16 - (size % 16)
165
+ val_min = max(val_min - adjustment // 2, 0)
166
+ val_max = min(val_max + adjustment // 2, max_limit)
167
+ return val_min, val_max
168
+
169
+ x_min, x_max = make_divisible_16(x_min, x_max, width)
170
+ y_min, y_max = make_divisible_16(y_min, y_max, height)
171
+
172
+ # Re-check divisibility after bounds adjustment
173
  x_min = max(x_min, 0)
174
  y_min = max(y_min, 0)
175
  x_max = min(x_max, width)
176
  y_max = min(y_max, height)
177
 
178
+ # Final divisibility check (in case constraints pushed it off again)
179
+ x_min, x_max = make_divisible_16(x_min, x_max, width)
180
+ y_min, y_max = make_divisible_16(y_min, y_max, height)
181
+
182
  return x_min, y_min, x_max, y_max
183
 
184
 
 
236
  mask_bbox_x_max,
237
  mask_bbox_y_max,
238
  ))
239
+ room_image_cropped, room_image_cropped_size = pad(
 
 
 
 
 
 
 
240
  room_image_cropped,
241
  (max_dimension, max_dimension),
 
 
242
  )
243
 
244
  room_mask_cropped = room_mask.crop((
 
247
  mask_bbox_x_max,
248
  mask_bbox_y_max,
249
  ))
250
+ room_mask_cropped, _ = pad(
 
251
  room_mask_cropped,
252
  (max_dimension, max_dimension),
 
 
 
 
 
 
 
 
 
253
  )
254
 
255
  room_image_cropped.save("room_image_cropped.png")
256
  room_mask_cropped.save("room_mask_cropped.png")
257
 
258
+ furniture_image, _ = pad(
259
  furniture_image_input,
260
  (max_dimension, max_dimension),
 
 
 
261
  )
262
 
263
  furniture_mask = Image.new("RGB", (max_dimension, max_dimension), (255, 255, 255))
 
309
  for image in results_images:
310
  final_image = room_image.copy()
311
 
312
+ image_generated = unpad(
313
+ image,
314
+ room_image_cropped_size,
 
 
 
 
 
 
 
 
 
 
315
  (
316
  mask_bbox_x_max - mask_bbox_x_min,
317
  mask_bbox_y_max - mask_bbox_y_min,