From 8b47d90f5d89cd0ef38d06960e4f06f7bb7dd383 Mon Sep 17 00:00:00 2001 From: dengkaipeng Date: Wed, 7 Nov 2018 11:17:19 +0800 Subject: [PATCH] add 'actual_shape' attribute. test=develop --- paddle/fluid/API.spec | 6 +- paddle/fluid/operators/interpolate_op.cc | 6 +- python/paddle/fluid/layers/nn.py | 124 +++++++++++++++--- .../tests/unittests/test_interpolate_op.py | 40 +++++- 4 files changed, 148 insertions(+), 28 deletions(-) diff --git a/paddle/fluid/API.spec b/paddle/fluid/API.spec index 65436cdd988..1948c6ecc91 100644 --- a/paddle/fluid/API.spec +++ b/paddle/fluid/API.spec @@ -118,10 +118,10 @@ paddle.fluid.layers.label_smooth ArgSpec(args=['label', 'prior_dist', 'epsilon', paddle.fluid.layers.roi_pool ArgSpec(args=['input', 'rois', 'pooled_height', 'pooled_width', 'spatial_scale'], varargs=None, keywords=None, defaults=(1, 1, 1.0)) paddle.fluid.layers.roi_align ArgSpec(args=['input', 'rois', 'pooled_height', 'pooled_width', 'spatial_scale', 'sampling_ratio', 'name'], varargs=None, keywords=None, defaults=(1, 1, 1.0, -1, None)) paddle.fluid.layers.dice_loss ArgSpec(args=['input', 'label', 'epsilon'], varargs=None, keywords=None, defaults=(1e-05,)) -paddle.fluid.layers.image_resize ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'resample'], varargs=None, keywords=None, defaults=(None, None, None, 'BILINEAR')) +paddle.fluid.layers.image_resize ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'resample', 'actual_shape'], varargs=None, keywords=None, defaults=(None, None, None, 'BILINEAR', None)) paddle.fluid.layers.image_resize_short ArgSpec(args=['input', 'out_short_len', 'resample'], varargs=None, keywords=None, defaults=('BILINEAR',)) -paddle.fluid.layers.resize_bilinear ArgSpec(args=['input', 'out_shape', 'scale', 'name'], varargs=None, keywords=None, defaults=(None, None, None)) -paddle.fluid.layers.resize_nearest ArgSpec(args=['input', 'out_shape', 'scale', 'name'], varargs=None, keywords=None, defaults=(None, None, None)) +paddle.fluid.layers.resize_bilinear ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape'], varargs=None, keywords=None, defaults=(None, None, None, None)) +paddle.fluid.layers.resize_nearest ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape'], varargs=None, keywords=None, defaults=(None, None, None, None)) paddle.fluid.layers.gather ArgSpec(args=['input', 'index'], varargs=None, keywords=None, defaults=None) paddle.fluid.layers.scatter ArgSpec(args=['input', 'index', 'updates', 'name'], varargs=None, keywords=None, defaults=(None,)) paddle.fluid.layers.sequence_scatter ArgSpec(args=['input', 'index', 'updates', 'name'], varargs=None, keywords=None, defaults=(None,)) diff --git a/paddle/fluid/operators/interpolate_op.cc b/paddle/fluid/operators/interpolate_op.cc index e2000d0e0c4..8f979e05d31 100644 --- a/paddle/fluid/operators/interpolate_op.cc +++ b/paddle/fluid/operators/interpolate_op.cc @@ -40,11 +40,13 @@ class InterpolateOp : public framework::OperatorWithKernel { int out_w = ctx->Attrs().Get("out_w"); PADDLE_ENFORCE_EQ(dim_x.size(), 4, "X's dimension must be 4"); - if (ctx->HasInput("OutSize")) { + if (ctx->HasInput("OutSize") && ctx->IsRuntime()) { auto out_size_dim = ctx->GetInputDim("OutSize"); PADDLE_ENFORCE_EQ(out_size_dim.size(), 1, "OutSize's dimension size must be 1"); PADDLE_ENFORCE_EQ(out_size_dim[0], 2, "OutSize's dim[0] must be 2"); + ctx->ShareLoD("X", "Out"); + return; } std::vector dim_out({dim_x[0], dim_x[1], out_h, out_w}); ctx->SetOutputDim("Out", framework::make_ddim(dim_out)); @@ -86,7 +88,7 @@ class InterpolateOpMaker : public framework::OpProtoAndCheckerMaker { interpolation. Nearest neighbor interpolation is to perform nearest neighbor interpolation - in bot the 3rd dimention(in height direction) and the 4th dimention(in width + in both the 3rd dimention(in height direction) and the 4th dimention(in width direction) on input tensor. Bilinear interpolation is an extension of linear interpolation for diff --git a/python/paddle/fluid/layers/nn.py b/python/paddle/fluid/layers/nn.py index 3b65825b966..46ce401b17f 100644 --- a/python/paddle/fluid/layers/nn.py +++ b/python/paddle/fluid/layers/nn.py @@ -5575,7 +5575,8 @@ def image_resize(input, out_shape=None, scale=None, name=None, - resample='BILINEAR'): + resample='BILINEAR', + actual_shape=None): """ **Resize a Batch of Images** @@ -5600,25 +5601,50 @@ def image_resize(input, Default: None name(str|None): A name for this layer(optional). If set None, the layer will be named automatically. - resample(str): The resample method. It can only be 'BILINEAR' currently. + resample(str): The resample method. It supports 'BILINEAR' and 'NEAREST' + currently. Default: 'BILINEAR' + actual_shape(Variable): An optional input to specify output shape + dynamically. If provided, image resize + according to this given shape rather than + :attr:`out_shape` and :attr:`scale` specifying + shape. That is to say actual_shape has the + highest priority. It is recommended to use + actual_shape instead of :attr:`out_shape` if you + want to specify output shape dynamically. When + using actual_shape to specify output shape, one of + :attr:`out_shape` and :attr:`scale` should also be + set, otherwise errors would be occured in graph + constructing stage. + Default: None Returns: Variable: The output is a 4-D tensor of the shape (num_batches, channls, out_h, out_w). + Raises: + TypeError: out_shape should be a list or tuple or Variable. + TypeError: actual_shape should either be Variable or None. + ValueError: The 'resample' of image_resize can only be 'BILINEAR' + or 'NEAREST' currently. + ValueError: One of out_shape and scale must not be None. + ValueError: out_shape length should be 2. + Examples: .. code-block:: python out = fluid.layers.image_resize(input, out_shape=[12, 12]) """ - resample_methods = {'BILINEAR': 'bilinear', 'NEAREST': 'nearest'} + resample_methods = { + 'BILINEAR': 'bilinear', + 'NEAREST': 'nearest', + } if resample not in resample_methods: raise ValueError( - "The 'resample' of image_resize can only be 'BILINEAR' and 'NEAREST' currently." + "The 'resample' of image_resize can only be 'BILINEAR' or 'NEAREST' currently." ) if out_shape is None and scale is None: - raise ValueError("One of out_shape and scale must not be None") + raise ValueError("One of out_shape and scale must not be None.") helper = LayerHelper('interpolate', **locals()) dtype = helper.input_dtype() @@ -5629,19 +5655,28 @@ def image_resize(input, out_w = 0 inputs = {"X": input} if out_shape is not None: - if not (_is_list_or_turple_(out_shape) and - len(out_shape) == 2) and not isinstance(out_shape, Variable): - raise ValueError('out_shape should be a list or tuple or variable') - if _is_list_or_turple_(out_shape): - out_shape = list(map(int, out_shape)) - out_h = out_shape[0] - out_w = out_shape[1] - else: + if isinstance(out_shape, Variable): + warnings.warn("out_shape as Variable type is deprecated, \ + it is recommended to use actual_shape instead of \ + out_shape to specify output shape dynamically.") inputs['OutSize'] = out_shape + elif not (_is_list_or_turple_(out_shape)): + raise TypeError("out_shape should be a list or tuple or Variable.") + elif len(out_shape) != 2: + raise ValueError("out_shape length should be 2.") + + out_shape = list(map(int, out_shape)) + out_h = out_shape[0] + out_w = out_shape[1] else: out_h = int(input.shape[2] * scale) out_w = int(input.shape[3] * scale) + if isinstance(actual_shape, Variable): + inputs["OutSize"] = actual_shape + elif actual_shape is not None: + raise TypeError("actual_shape should either be Variable or None.") + out = helper.create_variable_for_type_inference(dtype) helper.append_op( type='interpolate', @@ -5656,9 +5691,24 @@ def image_resize(input, @templatedoc(op_type="interpolate") -def resize_bilinear(input, out_shape=None, scale=None, name=None): +def resize_bilinear(input, + out_shape=None, + scale=None, + name=None, + actual_shape=None): """ - ${comment} + Resize input by performing bilinear interpolation based on given + output shape which specified by actual_shape, out_shape and scale + in priority order. + + Bilinear interpolation is an extension of linear interpolation for + interpolating functions of two variables (e.g. H-direction and + W-direction in this op) on a rectilinear 2D grid. The key idea is + to perform linear interpolation first in one direction, and then + again in the other direction. + + For details of bilinear interpolation, please refer to Wikipedia: + https://en.wikipedia.org/wiki/Bilinear_interpolation Args: input(${x_type}): ${x_comment}. @@ -5670,18 +5720,41 @@ def resize_bilinear(input, out_shape=None, scale=None, name=None): a higher priority than scale. Default: None. name(str|None): The output variable name. + actual_shape(Variable): An optional input to specify output shape + dynamically. If provided, image resize + according to this given shape rather than + :attr:`out_shape` and :attr:`scale` specifying + shape. That is to say actual_shape has the + highest priority. It is recommended to use + actual_shape instead of :attr:`out_shape` if you + want to specify output shape dynamically. When + using actual_shape to specify output shape, one of + :attr:`out_shape` and :attr:`scale` should also be + set, otherwise errors would be occured in graph + constructing stage. + Default: None Returns: ${out_comment}. """ - return image_resize(input, out_shape, scale, name, 'BILINEAR') + return image_resize(input, out_shape, scale, name, 'BILINEAR', actual_shape) @templatedoc(op_type="interpolate") -def resize_nearest(input, out_shape=None, scale=None, name=None): +def resize_nearest(input, + out_shape=None, + scale=None, + name=None, + actual_shape=None): """ - ${comment} + Resize input by performing nearest neighbor interpolation in both the + 3rd dimention(in height direction) and the 4th dimention(in width + direction) based on given output shape which specified by actual_shape, + out_shape and scale in priority order. + + For details of nearest neighbor interpolation, please refer to Wikipedia: + https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation Args: input(${x_type}): ${x_comment}. @@ -5693,12 +5766,25 @@ def resize_nearest(input, out_shape=None, scale=None, name=None): a higher priority than scale. Default: None. name(str|None): The output variable name. + actual_shape(Variable): An optional input to specify output shape + dynamically. If provided, image resize + according to this given shape rather than + :attr:`out_shape` and :attr:`scale` specifying + shape. That is to say actual_shape has the + highest priority. It is recommended to use + actual_shape instead of :attr:`out_shape` if you + want to specify output shape dynamically. When + using actual_shape to specify output shape, one of + :attr:`out_shape` and :attr:`scale` should also be + set, otherwise errors would be occured in graph + constructing stage. + Default: None Returns: ${out_comment}. """ - return image_resize(input, out_shape, scale, name, 'NEAREST') + return image_resize(input, out_shape, scale, name, 'NEAREST', actual_shape) def image_resize_short(input, out_short_len, resample='BILINEAR'): diff --git a/python/paddle/fluid/tests/unittests/test_interpolate_op.py b/python/paddle/fluid/tests/unittests/test_interpolate_op.py index dd3bf5fd5c9..9748d094cda 100644 --- a/python/paddle/fluid/tests/unittests/test_interpolate_op.py +++ b/python/paddle/fluid/tests/unittests/test_interpolate_op.py @@ -20,11 +20,18 @@ from op_test import OpTest import paddle.fluid.core as core -def nearest_neighbor_interp_np(X, out_h, out_w, out_size=None): +def nearest_neighbor_interp_np(X, + out_h, + out_w, + out_size=None, + actual_shape=None): """nearest neighbor interpolation implement in shape [N, C, H, W]""" if out_size is not None: out_h = out_size[0] out_w = out_size[1] + if actual_shape is not None: + out_h = actual_shape[0] + out_w = actual_shape[1] n, c, in_h, in_w = X.shape ratio_h = ratio_w = 0.0 @@ -43,11 +50,14 @@ def nearest_neighbor_interp_np(X, out_h, out_w, out_size=None): return out.astype(X.dtype) -def bilinear_interp_np(input, out_h, out_w, out_size): +def bilinear_interp_np(input, out_h, out_w, out_size=None, actual_shape=None): """bilinear interpolation implement in shape [N, C, H, W]""" if out_size is not None: out_h = out_size[0] out_w = out_size[1] + if actual_shape is not None: + out_h = actual_shape[0] + out_w = actual_shape[1] batch_size, channel, in_h, in_w = input.shape if out_h > 1: ratio_h = (in_h - 1.0) / (out_h - 1.0) @@ -86,15 +96,18 @@ INTERPOLATE_FUNCS = { class TestInterpolateOp(OpTest): def setUp(self): self.out_size = None + self.actual_shape = None self.init_test_case() self.op_type = "interpolate" input_np = np.random.random(self.input_shape).astype("float32") output_np = INTERPOLATE_FUNCS[self.interp_method]( - input_np, self.out_h, self.out_w, self.out_size) + input_np, self.out_h, self.out_w, self.out_size, self.actual_shape) self.inputs = {'X': input_np} if self.out_size is not None: self.inputs['OutSize'] = self.out_size + if self.actual_shape is not None: + self.inputs['OutSize'] = self.actual_shape self.attrs = { 'out_h': self.out_h, 'out_w': self.out_w, @@ -167,6 +180,15 @@ class TestBilinearInterpCase6(TestInterpolateOp): self.out_size = np.array([65, 129]).astype("int32") +class TestBilinearInterpActualShape(TestInterpolateOp): + def init_test_case(self): + self.interp_method = 'bilinear' + self.input_shape = [3, 2, 32, 16] + self.out_h = 64 + self.out_w = 32 + self.out_size = np.array([66, 40]).astype("int32") + + class TestBilinearInterpBigScale(TestInterpolateOp): def init_test_case(self): self.interp_method = 'bilinear' @@ -179,12 +201,13 @@ class TestBilinearInterpBigScale(TestInterpolateOp): class TestInterpolateOpUint8(OpTest): def setUp(self): self.out_size = None + self.actual_shape = None self.init_test_case() self.op_type = "interpolate" input_np = np.random.randint( low=0, high=256, size=self.input_shape).astype("uint8") output_np = INTERPOLATE_FUNCS[self.interp_method]( - input_np, self.out_h, self.out_w, self.out_size) + input_np, self.out_h, self.out_w, self.out_size, self.actual_shape) self.inputs = {'X': input_np} if self.out_size is not None: self.inputs['OutSize'] = self.out_size @@ -273,6 +296,15 @@ class TestNearestNeighborInterpCase6(TestInterpolateOp): self.out_size = np.array([65, 129]).astype("int32") +class TestNearestNeighborInterpActualShape(TestInterpolateOp): + def init_test_case(self): + self.interp_method = 'nearest' + self.input_shape = [3, 2, 32, 16] + self.out_h = 64 + self.out_w = 32 + self.out_size = np.array([66, 40]).astype("int32") + + class TestNearestNeighborInterpBigScale(TestInterpolateOp): def init_test_case(self): self.interp_method = 'nearest' -- GitLab