| import time |
| import jax |
| import jax.numpy as jnp |
| import jax.image |
| import dataclasses |
| import functools |
| from typing import Dict |
|
|
| |
| @dataclasses.dataclass(frozen=True) |
| class ResizeImagesOriginal: |
| height: int |
| width: int |
|
|
| def __call__(self, data: Dict) -> Dict: |
| |
| data["image"] = { |
| k: resize_with_pad(v, self.height, self.width) for k, v in data["image"].items() |
| } |
| return data |
|
|
| |
| @dataclasses.dataclass(frozen=True) |
| class ResizeImagesOptimized: |
| height: int |
| width: int |
|
|
| def __call__(self, data: Dict) -> Dict: |
| |
| data["image"] = { |
| k: batch_resize(v, self.height, self.width) for k, v in data["image"].items() |
| } |
| return data |
|
|
| @functools.partial(jax.jit, static_argnums=(1, 2, 3)) |
| def resize_with_pad( |
| images: jax.Array, |
| height: int, |
| width: int, |
| method: jax.image.ResizeMethod = jax.image.ResizeMethod.LINEAR, |
| ) -> jax.Array: |
| """Resizes an image with padding, similar to tf.image.resize_with_pad.""" |
| has_batch_dim = images.ndim == 4 |
| if not has_batch_dim: |
| images = images[None] |
| cur_height, cur_width = images.shape[1:3] |
| ratio = max(cur_width / width, cur_height / height) |
| resized_height = int(cur_height / ratio) |
| resized_width = int(cur_width / ratio) |
|
|
| resized_images = jax.image.resize( |
| images, (images.shape[0], resized_height, resized_width, images.shape[3]), method=method |
| ) |
|
|
| if images.dtype == jnp.uint8: |
| resized_images = jnp.round(resized_images).clip(0, 255).astype(jnp.uint8) |
| elif images.dtype == jnp.float32: |
| resized_images = resized_images.clip(-1.0, 1.0) |
| else: |
| raise ValueError(f"Unsupported image dtype: {images.dtype}") |
|
|
| pad_h0, remainder_h = divmod(height - resized_height, 2) |
| pad_h1 = pad_h0 + remainder_h |
| pad_w0, remainder_w = divmod(width - resized_width, 2) |
| pad_w1 = pad_w0 + remainder_w |
|
|
| padded_images = jnp.zeros((images.shape[0], height, width, images.shape[3]), dtype=images.dtype) |
| padded_images = padded_images.at[:, pad_h0:pad_h0+resized_height, pad_w0:pad_w0+resized_width, :].set(resized_images) |
|
|
| if not has_batch_dim: |
| padded_images = padded_images[0] |
|
|
| return padded_images |
|
|
| @functools.partial(jax.jit, static_argnums=(1, 2, 3)) |
| def batch_resize(images: jax.Array, height: int, width: int, method: jax.image.ResizeMethod = jax.image.ResizeMethod.LINEAR) -> jax.Array: |
| """Resize a batch of images in parallel.""" |
| return jax.vmap(resize_with_pad, in_axes=(0, None, None, None))(images, height, width, method) |
|
|
| |
| def compare_resize_methods(): |
| |
| data = { |
| "image": { |
| f"img{i}": jnp.ones((1, 256, 256, 3), dtype=jnp.uint8) for i in range(10) |
| } |
| } |
|
|
| |
| original_resize_fn = ResizeImagesOriginal(height=224, width=224) |
| start_time = time.time() |
| original_resized_data = original_resize_fn(data) |
| original_duration = time.time() - start_time |
| print(f"Original resizing time: {original_duration:.4f} seconds") |
|
|
| |
| optimized_resize_fn = ResizeImagesOptimized(height=224, width=224) |
| start_time = time.time() |
| optimized_resized_data = optimized_resize_fn(data) |
| optimized_duration = time.time() - start_time |
|
|
| import pdb; pdb.set_trace() |
| print(f"Optimized resizing time: {optimized_duration:.4f} seconds") |
|
|
| |
| compare_resize_methods() |
|
|