|
15 | 15 | """Preprocessing ops."""
|
16 | 16 |
|
17 | 17 | import math
|
18 |
| -from typing import Optional |
| 18 | +from typing import Optional, Tuple, Union |
19 | 19 | from six.moves import range
|
20 | 20 | import tensorflow as tf
|
21 | 21 |
|
@@ -303,6 +303,86 @@ def resize_and_crop_image_v2(image,
|
303 | 303 | return output_image, image_info
|
304 | 304 |
|
305 | 305 |
|
| 306 | +def resize_image( |
| 307 | + image: tf.Tensor, |
| 308 | + size: Union[Tuple[int, int], int], |
| 309 | + max_size: Optional[int] = None, |
| 310 | + method: tf.image.ResizeMethod = tf.image.ResizeMethod.BILINEAR): |
| 311 | + """Resize image with size and max_size. |
| 312 | +
|
| 313 | + Args: |
| 314 | + image: the image to be resized. |
| 315 | + size: if list to tuple, resize to it. If scalar, we keep the same |
| 316 | + aspect ratio and resize the short side to the value. |
| 317 | + max_size: only used when size is a scalar. When the larger side is larger |
| 318 | + than max_size after resized with size we used max_size to keep the aspect |
| 319 | + ratio instead. |
| 320 | + method: the method argument passed to tf.image.resize. |
| 321 | +
|
| 322 | + Returns: |
| 323 | + the resized image and image_info to be used for downstream processing. |
| 324 | + image_info: a 2D `Tensor` that encodes the information of the image and the |
| 325 | + applied preprocessing. It is in the format of |
| 326 | + [[original_height, original_width], [resized_height, resized_width], |
| 327 | + [y_scale, x_scale], [0, 0]], where [resized_height, resized_width] |
| 328 | + is the actual scaled image size, and [y_scale, x_scale] is the |
| 329 | + scaling factor, which is the ratio of |
| 330 | + scaled dimension / original dimension. |
| 331 | + """ |
| 332 | + |
| 333 | + def get_size_with_aspect_ratio(image_size, size, max_size=None): |
| 334 | + h = image_size[0] |
| 335 | + w = image_size[1] |
| 336 | + if max_size is not None: |
| 337 | + min_original_size = tf.cast(tf.math.minimum(w, h), dtype=tf.float32) |
| 338 | + max_original_size = tf.cast(tf.math.maximum(w, h), dtype=tf.float32) |
| 339 | + if max_original_size / min_original_size * size > max_size: |
| 340 | + size = tf.cast( |
| 341 | + tf.math.floor(max_size * min_original_size / max_original_size), |
| 342 | + dtype=tf.int32) |
| 343 | + else: |
| 344 | + size = tf.cast(size, tf.int32) |
| 345 | + |
| 346 | + else: |
| 347 | + size = tf.cast(size, tf.int32) |
| 348 | + if (w <= h and w == size) or (h <= w and h == size): |
| 349 | + return tf.stack([h, w]) |
| 350 | + |
| 351 | + if w < h: |
| 352 | + ow = size |
| 353 | + oh = tf.cast( |
| 354 | + (tf.cast(size, dtype=tf.float32) * tf.cast(h, dtype=tf.float32) / |
| 355 | + tf.cast(w, dtype=tf.float32)), |
| 356 | + dtype=tf.int32) |
| 357 | + else: |
| 358 | + oh = size |
| 359 | + ow = tf.cast( |
| 360 | + (tf.cast(size, dtype=tf.float32) * tf.cast(w, dtype=tf.float32) / |
| 361 | + tf.cast(h, dtype=tf.float32)), |
| 362 | + dtype=tf.int32) |
| 363 | + |
| 364 | + return tf.stack([oh, ow]) |
| 365 | + |
| 366 | + def get_size(image_size, size, max_size=None): |
| 367 | + if isinstance(size, (list, tuple)): |
| 368 | + return size[::-1] |
| 369 | + else: |
| 370 | + return get_size_with_aspect_ratio(image_size, size, max_size) |
| 371 | + |
| 372 | + orignal_size = tf.shape(image)[0:2] |
| 373 | + size = get_size(orignal_size, size, max_size) |
| 374 | + rescaled_image = tf.image.resize( |
| 375 | + image, tf.cast(size, tf.int32), method=method) |
| 376 | + image_scale = size / orignal_size |
| 377 | + image_info = tf.stack([ |
| 378 | + tf.cast(orignal_size, dtype=tf.float32), |
| 379 | + tf.cast(size, dtype=tf.float32), |
| 380 | + tf.cast(image_scale, tf.float32), |
| 381 | + tf.constant([0.0, 0.0], dtype=tf.float32) |
| 382 | + ]) |
| 383 | + return rescaled_image, image_info |
| 384 | + |
| 385 | + |
306 | 386 | def center_crop_image(image):
|
307 | 387 | """Center crop a square shape slice from the input image.
|
308 | 388 |
|
|
0 commit comments