diff options
author | Jonathan Tompson <tompson@cims.nyu.edu> | 2014-06-26 19:32:57 +0400 |
---|---|---|
committer | Jonathan Tompson <tompson@cims.nyu.edu> | 2014-06-26 19:32:57 +0400 |
commit | 4725c6b639f8dfc5d0440557c65e5dbc6fec1873 (patch) | |
tree | cbce1545c19a1c674c95a09ee2d9201f1adfb178 | |
parent | 1310a045ebc69a9f9e8c57d07af587a6535d5ae9 (diff) |
Added SpatialUpSamplingNearest module.
-rw-r--r-- | SpatialUpSamplingNearest.lua | 58 | ||||
-rwxr-xr-x[-rw-r--r--] | doc/convolution.md | 21 | ||||
-rw-r--r-- | generic/SpatialUpSamplingNearest.c | 159 | ||||
-rw-r--r-- | init.c | 5 | ||||
-rw-r--r-- | init.lua | 1 | ||||
-rw-r--r-- | test/test.lua | 64 |
6 files changed, 308 insertions, 0 deletions
diff --git a/SpatialUpSamplingNearest.lua b/SpatialUpSamplingNearest.lua new file mode 100644 index 0000000..8288250 --- /dev/null +++ b/SpatialUpSamplingNearest.lua @@ -0,0 +1,58 @@ +local SpatialUpSamplingNearest, parent = torch.class('nn.SpatialUpSamplingNearest', 'nn.Module')
+
+--[[
+Applies a 2D up-sampling over an input image composed of several input planes.
+
+The upsampling is done using the simple nearest neighbor technique.
+
+The Y and X dimensions are assumed to be the last 2 tensor dimensions. For
+instance, if the tensor is 4D, then dim 3 is the y dimension and dim 4 is the x.
+
+owidth = width*scale_factor
+oheight = height*scale_factor
+--]]
+
+function SpatialUpSamplingNearest:__init(scale)
+ parent.__init(self)
+
+ self.scale_factor = scale
+ if self.scale_factor < 1 then
+ error('scale_factor must be greater than 1')
+ end
+ if math.floor(self.scale_factor) ~= self.scale_factor then
+ error('scale_factor must be integer')
+ end
+ self.inputSize = torch.LongStorage(4)
+ self.outputSize = torch.LongStorage(4)
+ self.usage = nil
+end
+
+function SpatialUpSamplingNearest:updateOutput(input)
+ if input:dim() ~= 4 and input:dim() ~= 3 then
+ error('SpatialUpSamplingNearest only support 3D or 4D tensors')
+ end
+ -- Copy the input size
+ local xdim = input:dim()
+ local ydim = input:dim() - 1
+ for i = 1, input:dim() do
+ self.inputSize[i] = input:size(i)
+ self.outputSize[i] = input:size(i)
+ end
+ self.outputSize[ydim] = self.outputSize[ydim] * self.scale_factor
+ self.outputSize[xdim] = self.outputSize[xdim] * self.scale_factor
+ -- Resize the output if needed
+ if input:dim() == 3 then
+ self.output:resize(self.outputSize[1], self.outputSize[2],
+ self.outputSize[3])
+ else
+ self.output:resize(self.outputSize)
+ end
+ input.nn.SpatialUpSamplingNearest_updateOutput(self, input)
+ return self.output
+end
+
+function SpatialUpSamplingNearest:updateGradInput(input, gradOutput)
+ self.gradInput:resizeAs(input)
+ input.nn.SpatialUpSamplingNearest_updateGradInput(self, input, gradOutput)
+ return self.gradInput
+end
diff --git a/doc/convolution.md b/doc/convolution.md index 91bda8c..5571b19 100644..100755 --- a/doc/convolution.md +++ b/doc/convolution.md @@ -396,6 +396,27 @@ output[i][j][k] = bias[k] + weight[k] sum_{s=1}^kW sum_{t=1}^kH input[dW*(i-1)+s)][dH*(j-1)+t][k] ``` +<a name="nn.SpatialUpSamplingNearest"/> +### SpatialUpSamplingNearest ### + +```lua +module = nn.SpatialUpSamplingNearest(scale) +``` + +Applies a 2D up-sampling over an input image composed of several input planes. The `input` tensor in +`forward(input)` is expected to be a 3D or 4D tensor (i.e. for 4D: `nBatchPlane x nInputPlane x height x width`). The number of output planes will be the same. The v dimension is assumed to be the second last dimension (i.e. for 4D it will be the 3rd dim), and the u dimension is assumed to be the last dimension. + +The parameters are the following: + * `scale`: The upscale ratio. Must be a positive integer
+
+The up-scaling method is simple nearest neighbor, ie: + +```lua +output(u,v) = input(floor((u-1)/scale)+1, floor((v-1)/scale)+1) +``` + +Where `u` and `v` are index from 1 (as per lua convention). There are no learnable parameters. + <a name="nn.SpatialZeroPadding"/> ### SpatialZeroPadding ### diff --git a/generic/SpatialUpSamplingNearest.c b/generic/SpatialUpSamplingNearest.c new file mode 100644 index 0000000..5b6dbd3 --- /dev/null +++ b/generic/SpatialUpSamplingNearest.c @@ -0,0 +1,159 @@ +#ifndef TH_GENERIC_FILE
+#define TH_GENERIC_FILE "generic/SpatialUpSamplingNearest.c"
+#else
+
+static int nn_(SpatialUpSamplingNearest_updateOutput)(lua_State *L)
+{
+ // get all params
+ THTensor *input = luaT_checkudata(L, 2, torch_Tensor);
+ int scale_factor = luaT_getfieldcheckint(L, 1, "scale_factor");
+ int dW = scale_factor;
+ int dH = scale_factor;
+ int xDim = input->nDimension-2;
+ int yDim = input->nDimension-1;
+ THTensor *output = luaT_getfieldcheckudata(L, 1, "output", torch_Tensor);
+
+ // dims
+ int idim = input->nDimension; // Gauranteed to be between 3 and 5
+ int osz0 = output->size[0];
+ int osz1 = output->size[1];
+ int osz2 = output->size[2];
+ int osz3 = 1;
+ if (idim > 3) {
+ osz3 = output->size[3];
+ }
+
+ // get strides
+ long *is = input->stride;
+ long *os = output->stride;
+
+ // get raw pointers
+ real *pin = THTensor_(data)(input);
+ real *pout = THTensor_(data)(output);
+
+ // perform the upsampling
+ int i0, i1, i2, i3, isrc, idst;
+ int iout[4]; // Output indices
+ int iin[4]; // Input indices
+
+ for (i0 = 0; i0 < osz0; i0++) {
+ iout[0] = i0;
+ iin[0] = i0;
+ for (i1 = 0; i1 < osz1; i1++) {
+ iout[1] = i1;
+ iin[1] = i1;
+ for (i2 = 0; i2 < osz2; i2++) {
+ iout[2] = i2;
+ iin[2] = i2;
+ for (i3 = 0; i3 < osz3; i3++) {
+ iout[3] = i3;
+ iin[3] = i3;
+
+ // set the indices for the upsampled dimensions
+ iin[xDim] = iout[xDim] / dW;
+ iin[yDim] = iout[yDim] / dH;
+
+ idst = i0*os[0] + i1*os[1] + i2*os[2];
+ isrc = iin[0]*is[0] + iin[1]*is[1] + iin[2]*is[2];
+ if (idim > 3) {
+ idst += i3*os[3];
+ isrc += iin[3]*is[3];
+ }
+
+ pout[idst] = pin[isrc];
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static int nn_(SpatialUpSamplingNearest_updateGradInput)(lua_State *L)
+{
+ // get all params
+ //THTensor *input = luaT_checkudata(L,2, torch_Tensor);
+ THTensor *gradOutput = luaT_checkudata(L,3, torch_Tensor);
+ THTensor *gradInput = luaT_getfieldcheckudata(L,1, "gradInput", torch_Tensor);
+
+ int scale_factor = luaT_getfieldcheckint(L, 1, "scale_factor");
+ int dW = scale_factor;
+ int dH = scale_factor;
+ int xDim = gradInput->nDimension-2;
+ int yDim = gradInput->nDimension-1;
+
+ // dims
+ int idim = gradInput->nDimension; // Gauranteed to be between 3 and 5
+ int isz0 = gradInput->size[0];
+ int isz1 = gradInput->size[1];
+ int isz2 = gradInput->size[2];
+ int isz3 = 1;
+ if (idim > 3) {
+ isz3 = gradInput->size[3];
+ }
+
+ // get strides
+ long *is = gradInput->stride;
+ long *os = gradOutput->stride;
+
+ // get raw pointers
+ real *pin = THTensor_(data)(gradInput);
+ real *pout = THTensor_(data)(gradOutput);
+
+ // perform the upsampling
+ int i0, i1, i2, i3, isrc, idst, x, y;
+ int iin[4]; // Input indices
+ int iout[4]; // Output indices
+
+ THTensor_(zero)(gradInput);
+
+ for (i0 = 0; i0 < isz0; i0++) {
+ iin[0] = i0;
+ iout[0] = i0;
+ for (i1 = 0; i1 < isz1; i1++) {
+ iin[1] = i1;
+ iout[1] = i1;
+ for (i2 = 0; i2 < isz2; i2++) {
+ iin[2] = i2;
+ iout[2] = i2;
+ for (i3 = 0; i3 < isz3; i3++) {
+ iin[3] = i3;
+ iout[3] = i3;
+
+ idst = i0*is[0] + i1*is[1] + i2*is[2];
+ if (idim > 3) {
+ idst += i3*is[3];
+ }
+
+ // Now accumulate the gradients from gradOutput
+ for (y = 0; y < dH; y++) {
+ for (x = 0; x < dW; x++) {
+ iout[xDim] = dW * iin[xDim] + x;
+ iout[yDim] = dH * iin[yDim] + y;
+ isrc = iout[0]*os[0] + iout[1]*os[1] + iout[2]*os[2];
+ if (idim > 3) {
+ isrc += iout[3]*os[3];
+ }
+ pin[idst] += pout[isrc];
+ }
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static const struct luaL_Reg nn_(SpatialUpSamplingNearest__) [] = {
+ {"SpatialUpSamplingNearest_updateOutput", nn_(SpatialUpSamplingNearest_updateOutput)},
+ {"SpatialUpSamplingNearest_updateGradInput", nn_(SpatialUpSamplingNearest_updateGradInput)},
+ {NULL, NULL}
+};
+
+static void nn_(SpatialUpSamplingNearest_init)(lua_State *L)
+{
+ luaT_pushmetatable(L, torch_Tensor);
+ luaT_registeratname(L, nn_(SpatialUpSamplingNearest__), "nn");
+ lua_pop(L,1);
+}
+
+#endif
@@ -107,6 +107,9 @@ #include "generic/L1Cost.c" #include "THGenerateFloatTypes.h" +#include "generic/SpatialUpSamplingNearest.c" +#include "THGenerateFloatTypes.h" + LUA_EXTERNC DLL_EXPORT int luaopen_libnn(lua_State *L); int luaopen_libnn(lua_State *L) @@ -149,6 +152,7 @@ int luaopen_libnn(lua_State *L) nn_FloatMultiMarginCriterion_init(L); nn_FloatMultiLabelMarginCriterion_init(L); nn_FloatL1Cost_init(L); + nn_FloatSpatialUpSamplingNearest_init(L); nn_DoubleMin_init(L); nn_DoubleMax_init(L); @@ -184,6 +188,7 @@ int luaopen_libnn(lua_State *L) nn_DoubleMultiMarginCriterion_init(L); nn_DoubleMultiLabelMarginCriterion_init(L); nn_DoubleL1Cost_init(L); + nn_DoubleSpatialUpSamplingNearest_init(L); return 1; } @@ -76,6 +76,7 @@ include('SpatialSubtractiveNormalization.lua') include('SpatialDivisiveNormalization.lua') include('SpatialContrastiveNormalization.lua') include('SpatialZeroPadding.lua') +include('SpatialUpSamplingNearest.lua') include('VolumetricConvolution.lua') include('VolumetricMaxPooling.lua') diff --git a/test/test.lua b/test/test.lua index 5db941a..7a23c5e 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1886,6 +1886,70 @@ function nntest.View() "Error in minibatch nElement") end +-- Define a test for SpatialUpSamplingCuda +function nntest.SpatialUpSamplingNearest() + local scale = torch.random(2,4) + for dim = 3,4 do + local m = nn.SpatialUpSamplingNearest(scale) + + -- Create a randomly sized dimD vector + local shape = {} + for i = 1, dim do + table.insert(shape, torch.random(2, 2+dim-1)) + end + + -- Check that the gradient is correct by using finite elements + local input = torch.Tensor(unpack(shape)):zero() + + local err = jac.testJacobian(m, input) + mytester:assertlt(err, precision, ' error on state ') + + local ferr, berr = jac.testIO(m, input) + mytester:asserteq(ferr, 0, torch.typename(m)..' - i/o forward err ') + mytester:asserteq(berr, 0, torch.typename(m)..' - i/o backward err ') + + -- Also check that the forward prop is correct. + input = torch.rand(unpack(shape)) + local output = m:forward(input) + + local feat + local nfeats + if input:dim() == 3 then + nfeats = shape[1] + feat = {0} + else + feat = {0, 0} + nfeats = shape[1] * shape[2] + end + feat[#feat+1] = 0 -- ydim + feat[#feat+1] = 0 -- xdim + local xdim = input:dim() + local ydim = input:dim()-1 + local err = 0 + for f = 1, nfeats do + if input:dim() == 4 then + feat[1] = math.floor((f-1) / shape[1]) + 1 + feat[2] = math.mod((f-1), shape[2]) + 1 + else + feat[1] = f + end + for y = 1, input:size(ydim) * scale do + for x = 1, input:size(xdim) * scale do + feat[ydim] = y + feat[xdim] = x + local oval = output[feat] + feat[ydim] = math.floor((y-1)/scale)+1 + feat[xdim] = math.floor((x-1)/scale)+1 + local ival = input[feat] + err = math.max(err, math.abs(oval-ival)) + end + end + end + + mytester:assertlt(err, precision, ' fprop is incorrect ') + end +end + mytester:add(nntest) if not nn then |