Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/torch/nn.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Tompson <tompson@cims.nyu.edu>2014-06-26 19:32:57 +0400
committerJonathan Tompson <tompson@cims.nyu.edu>2014-06-26 19:32:57 +0400
commit4725c6b639f8dfc5d0440557c65e5dbc6fec1873 (patch)
treecbce1545c19a1c674c95a09ee2d9201f1adfb178
parent1310a045ebc69a9f9e8c57d07af587a6535d5ae9 (diff)
Added SpatialUpSamplingNearest module.
-rw-r--r--SpatialUpSamplingNearest.lua58
-rwxr-xr-x[-rw-r--r--]doc/convolution.md21
-rw-r--r--generic/SpatialUpSamplingNearest.c159
-rw-r--r--init.c5
-rw-r--r--init.lua1
-rw-r--r--test/test.lua64
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
diff --git a/init.c b/init.c
index 877faa9..d3062c9 100644
--- a/init.c
+++ b/init.c
@@ -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;
}
diff --git a/init.lua b/init.lua
index 91d8f11..757c9ec 100644
--- a/init.lua
+++ b/init.lua
@@ -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