return _param.kernel_size();
}
+uint32_t PoolingSpec::vertical_stride(void) const
+{
+ if (_param.has_stride_h())
+ {
+ return _param.stride_h();
+ }
+
+ if (_param.has_stride())
+ {
+ return _param.stride();
+ }
+
+ return 1;
+}
+
+uint32_t PoolingSpec::horizontal_stride(void) const
+{
+ if (_param.has_stride_w())
+ {
+ return _param.stride_w();
+ }
+
+ if (_param.has_stride())
+ {
+ return _param.stride();
+ }
+
+ return 1;
+}
+
nncc::core::ADT::tensor::Shape PoolingSpec::ofm_shape(void) const
{
nncc::core::ADT::tensor::Shape res;
res.dim(1) = _ifm_shape.dim(1);
// H and W are derived from IFM, Window, and Padding
- res.dim(2) = _ifm_shape.dim(2) - window_height() + 1;
- res.dim(3) = _ifm_shape.dim(3) - window_width() + 1;
+ // TODO Remove the following asserts
+ assert((_ifm_shape.dim(2) - window_height()) % vertical_stride() == 0);
+ assert((_ifm_shape.dim(3) - window_width()) % horizontal_stride() == 0);
+ res.dim(2) = (_ifm_shape.dim(2) - window_height()) / vertical_stride() + 1;
+ res.dim(3) = (_ifm_shape.dim(3) - window_width()) / horizontal_stride() + 1;
return res;
}
ASSERT_EQ(expected, obtained);
}
}
+
+TEST_F(PoolingSpecTest, stride_for_all)
+{
+ const tensor::Shape ifm_shape{1, 3, 15, 15};
+
+ ::caffe::NetParameter param;
+ {
+ SequentialBuilder builder{¶m};
+
+ builder.addInputLayer(ifm_shape);
+
+ // clang-format off
+ const char *prototxt = STRING(
+ name : "pool"
+ type : "Pooling"
+ bottom : "data"
+ top : "pool"
+ pooling_param {
+ pool: MAX
+ kernel_size : 3
+ stride: 2
+ }
+ );
+ // clang-format on
+
+ builder.addLayer(prototxt);
+ }
+
+ ::caffe::Net<float> net{param};
+
+ PoolingSpec spec{param.layer(1).pooling_param()};
+
+ spec.ifm_shape(ifm_shape);
+
+ ASSERT_EQ(spec.vertical_stride(), 2);
+ ASSERT_EQ(spec.horizontal_stride(), 2);
+
+ // Check 'ofm_shape'
+ {
+ auto expected = as_tensor_shape(net.blob_by_name("pool")->shape());
+ auto obtained = spec.ofm_shape();
+
+ ASSERT_EQ(expected, obtained);
+ }
+}