Skip to content

Commit ffe3d1b

Browse files
Frank Chentensorflower-gardener
Frank Chen
authored andcommitted
Add resize_images_preserve_aspect_ratio function.
PiperOrigin-RevId: 200242751
1 parent d820151 commit ffe3d1b

File tree

3 files changed

+109
-2
lines changed

3 files changed

+109
-2
lines changed

tensorflow/python/ops/image_ops_impl.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,8 @@ class ResizeMethod(object):
921921
def resize_images(images,
922922
size,
923923
method=ResizeMethod.BILINEAR,
924-
align_corners=False):
924+
align_corners=False,
925+
preserve_aspect_ratio=False):
925926
"""Resize `images` to `size` using the specified `method`.
926927
927928
Resized images will be distorted if their original aspect ratio is not
@@ -953,6 +954,10 @@ def resize_images(images,
953954
align_corners: bool. If True, the centers of the 4 corner pixels of the
954955
input and output tensors are aligned, preserving the values at the
955956
corner pixels. Defaults to `False`.
957+
preserve_aspect_ratio: Whether to preserve the aspect ratio. If this is set,
958+
then `images` will be resized to a size that fits in `size` while
959+
preserving the aspect ratio of the original image. Scales up the image if
960+
`size` is bigger than the current size of the `image`. Defaults to False.
956961
957962
Raises:
958963
ValueError: if the shape of `images` is incompatible with the
@@ -991,6 +996,28 @@ def resize_images(images,
991996
new_height_const = size_const_as_shape[0].value
992997
new_width_const = size_const_as_shape[1].value
993998

999+
if preserve_aspect_ratio:
1000+
# Get the current shapes of the image, even if dynamic.
1001+
_, current_height, current_width, _ = _ImageDimensions(images, rank=4)
1002+
1003+
# do the computation to find the right scale and height/width.
1004+
scale_factor_height = (math_ops.to_float(new_height_const) /
1005+
math_ops.to_float(current_height))
1006+
scale_factor_width = (math_ops.to_float(new_width_const) /
1007+
math_ops.to_float(current_width))
1008+
scale_factor = math_ops.minimum(scale_factor_height, scale_factor_width)
1009+
scaled_height_const = math_ops.to_int32(scale_factor *
1010+
math_ops.to_float(current_height))
1011+
scaled_width_const = math_ops.to_int32(scale_factor *
1012+
math_ops.to_float(current_width))
1013+
1014+
# NOTE: Reset the size and other constants used later.
1015+
size = ops.convert_to_tensor([scaled_height_const, scaled_width_const],
1016+
dtypes.int32, name='size')
1017+
size_const_as_shape = tensor_util.constant_value_as_shape(size)
1018+
new_height_const = size_const_as_shape[0].value
1019+
new_width_const = size_const_as_shape[1].value
1020+
9941021
# If we can determine that the height and width will be unmodified by this
9951022
# transformation, we avoid performing the resize.
9961023
if all(x is not None

tensorflow/python/ops/image_ops_test.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,6 +2511,86 @@ def testNameScope(self):
25112511
y = image_ops.resize_images(single_image, [55, 66])
25122512
self.assertTrue(y.op.name.startswith("resize_images"))
25132513

2514+
def _ResizeImageCall(self, x, max_h, max_w, preserve_aspect_ratio,
2515+
use_tensor_inputs):
2516+
if use_tensor_inputs:
2517+
target_max = ops.convert_to_tensor([max_h, max_w])
2518+
x_tensor = array_ops.placeholder(x.dtype, shape=[None] * x.ndim)
2519+
feed_dict = {x_tensor: x}
2520+
else:
2521+
target_max = [max_h, max_w]
2522+
x_tensor = x
2523+
feed_dict = {}
2524+
2525+
y = image_ops.resize_images(x_tensor, target_max,
2526+
preserve_aspect_ratio=preserve_aspect_ratio)
2527+
2528+
with self.test_session(use_gpu=True):
2529+
return y.eval(feed_dict=feed_dict)
2530+
2531+
def _assertResizeEqual(self, x, x_shape, y, y_shape,
2532+
preserve_aspect_ratio=True,
2533+
use_tensor_inputs_options=None):
2534+
use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
2535+
target_height, target_width, _ = y_shape
2536+
x = np.array(x).reshape(x_shape)
2537+
y = np.array(y).reshape(y_shape)
2538+
2539+
for use_tensor_inputs in use_tensor_inputs_options:
2540+
y_tf = self._ResizeImageCall(x, target_height, target_width,
2541+
preserve_aspect_ratio, use_tensor_inputs)
2542+
self.assertAllClose(y, y_tf)
2543+
2544+
def _assertResizeCheckShape(self, x, x_shape, target_shape,
2545+
y_shape, preserve_aspect_ratio=True,
2546+
use_tensor_inputs_options=None):
2547+
use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
2548+
target_height, target_width = target_shape
2549+
x = np.array(x).reshape(x_shape)
2550+
y = np.zeros(y_shape)
2551+
2552+
for use_tensor_inputs in use_tensor_inputs_options:
2553+
y_tf = self._ResizeImageCall(x, target_height, target_width,
2554+
preserve_aspect_ratio, use_tensor_inputs)
2555+
self.assertShapeEqual(y, ops.convert_to_tensor(y_tf))
2556+
2557+
def testPreserveAspectRatioMultipleImages(self):
2558+
x_shape = [10, 100, 100, 10]
2559+
x = np.random.uniform(size=x_shape)
2560+
2561+
self._assertResizeCheckShape(x, x_shape, [250, 250], [10, 250, 250, 10],
2562+
preserve_aspect_ratio=False)
2563+
2564+
def testPreserveAspectRatioNoOp(self):
2565+
x_shape = [10, 10, 10]
2566+
x = np.random.uniform(size=x_shape)
2567+
2568+
self._assertResizeEqual(x, x_shape, x, x_shape)
2569+
2570+
def testPreserveAspectRatioSmaller(self):
2571+
x_shape = [100, 100, 10]
2572+
x = np.random.uniform(size=x_shape)
2573+
2574+
self._assertResizeCheckShape(x, x_shape, [75, 50], [50, 50, 10])
2575+
2576+
def testPreserveAspectRatioSmallerMultipleImages(self):
2577+
x_shape = [10, 100, 100, 10]
2578+
x = np.random.uniform(size=x_shape)
2579+
2580+
self._assertResizeCheckShape(x, x_shape, [75, 50], [10, 50, 50, 10])
2581+
2582+
def testPreserveAspectRatioLarger(self):
2583+
x_shape = [100, 100, 10]
2584+
x = np.random.uniform(size=x_shape)
2585+
2586+
self._assertResizeCheckShape(x, x_shape, [150, 200], [150, 150, 10])
2587+
2588+
def testPreserveAspectRatioSameRatio(self):
2589+
x_shape = [1920, 1080, 3]
2590+
x = np.random.uniform(size=x_shape)
2591+
2592+
self._assertResizeCheckShape(x, x_shape, [3840, 2160], [3840, 2160, 3])
2593+
25142594

25152595
class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase):
25162596

tensorflow/tools/api/golden/tensorflow.image.pbtxt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ tf_module {
174174
}
175175
member_method {
176176
name: "resize_images"
177-
argspec: "args=[\'images\', \'size\', \'method\', \'align_corners\'], varargs=None, keywords=None, defaults=[\'0\', \'False\'], "
177+
argspec: "args=[\'images\', \'size\', \'method\', \'align_corners\', \'preserve_aspect_ratio\'], varargs=None, keywords=None, defaults=[\'0\', \'False\', \'False\'], "
178178
}
179179
member_method {
180180
name: "resize_nearest_neighbor"

0 commit comments

Comments
 (0)