test non-square filters by separable convolution of Sobel operator
authorEvan Shelhamer <shelhamer@imaginarynumber.net>
Tue, 29 Jul 2014 05:43:06 +0000 (22:43 -0700)
committerEvan Shelhamer <shelhamer@imaginarynumber.net>
Tue, 29 Jul 2014 08:20:39 +0000 (01:20 -0700)
Compute the G_x kernel of the Sobel operator as a full filter and as
separable filters to check the rectangular filter output.

src/caffe/test/test_convolution_layer.cpp

index d0522b1..f3cfb68 100644 (file)
@@ -172,6 +172,102 @@ TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolutionGroup) {
   }
 }
 
+TYPED_TEST(ConvolutionLayerTest, TestSobelConvolution) {
+  // Test separable convolution by computing the Sobel operator
+  // as a single filter then comparing the result
+  // as the convolution of two rectangular filters.
+  typedef typename TypeParam::Dtype Dtype;
+  // Fill bottoms with identical Gaussian noise.
+  shared_ptr<GaussianFiller<Dtype> > filler;
+  FillerParameter filler_param;
+  filler_param.set_value(1.);
+  filler.reset(new GaussianFiller<Dtype>(filler_param));
+  filler->Fill(this->blob_bottom_);
+  this->blob_bottom_2_->CopyFrom(*this->blob_bottom_);
+  // Compute Sobel G_x operator as 3 x 3 convolution.
+  LayerParameter layer_param;
+  ConvolutionParameter* convolution_param =
+      layer_param.mutable_convolution_param();
+  convolution_param->set_kernel_size(3);
+  convolution_param->set_stride(2);
+  convolution_param->set_num_output(1);
+  convolution_param->set_bias_term(false);
+  shared_ptr<Layer<Dtype> > layer(
+      new ConvolutionLayer<Dtype>(layer_param));
+  layer->blobs().resize(1);
+  layer->blobs()[0].reset(new Blob<Dtype>(1, 3, 3, 3));
+  Dtype* weights = layer->blobs()[0]->mutable_cpu_data();
+  for (int c = 0; c < 3; ++c) {
+    int i = c * 9;  // 3 x 3 filter
+    weights[i +  0] = -1;
+    weights[i +  1] =  0;
+    weights[i +  2] =  1;
+    weights[i +  3] = -2;
+    weights[i +  4] =  0;
+    weights[i +  5] =  2;
+    weights[i +  6] = -1;
+    weights[i +  7] =  0;
+    weights[i +  8] =  1;
+  }
+  layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_));
+  layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_));
+  // Compute Sobel G_x operator as separable 3 x 1 and 1 x 3 convolutions.
+  // (1) the [1 2 1] column filter
+  vector<Blob<Dtype>*> sep_blob_bottom_vec;
+  vector<Blob<Dtype>*> sep_blob_top_vec;
+  shared_ptr<Blob<Dtype> > blob_sep(new Blob<Dtype>());
+  sep_blob_bottom_vec.push_back(this->blob_bottom_2_);
+  sep_blob_top_vec.push_back(this->blob_top_2_);
+  convolution_param->clear_kernel_size();
+  convolution_param->clear_stride();
+  convolution_param->set_kernel_h(3);
+  convolution_param->set_kernel_w(1);
+  convolution_param->set_stride_h(2);
+  convolution_param->set_stride_w(1);
+  convolution_param->set_num_output(1);
+  convolution_param->set_bias_term(false);
+  layer.reset(new ConvolutionLayer<Dtype>(layer_param));
+  layer->blobs().resize(1);
+  layer->blobs()[0].reset(new Blob<Dtype>(1, 3, 3, 1));
+  Dtype* weights_1 = layer->blobs()[0]->mutable_cpu_data();
+  for (int c = 0; c < 3; ++c) {
+    int i = c * 3;  // 3 x 1 filter
+    weights_1[i +  0] = 1;
+    weights_1[i +  1] = 2;
+    weights_1[i +  2] = 1;
+  }
+  layer->SetUp(sep_blob_bottom_vec, &(sep_blob_top_vec));
+  layer->Forward(sep_blob_bottom_vec, &(sep_blob_top_vec));
+  // (2) the [-1 0 1] row filter
+  blob_sep->CopyFrom(*this->blob_top_2_, false, true);
+  sep_blob_bottom_vec.clear();
+  sep_blob_bottom_vec.push_back(blob_sep.get());
+  convolution_param->set_kernel_h(1);
+  convolution_param->set_kernel_w(3);
+  convolution_param->set_stride_h(1);
+  convolution_param->set_stride_w(2);
+  convolution_param->set_num_output(1);
+  convolution_param->set_bias_term(false);
+  layer.reset(new ConvolutionLayer<Dtype>(layer_param));
+  layer->blobs().resize(1);
+  layer->blobs()[0].reset(new Blob<Dtype>(1, 3, 1, 3));
+  Dtype* weights_2 = layer->blobs()[0]->mutable_cpu_data();
+  for (int c = 0; c < 3; ++c) {
+    int i = c * 3;  // 1 x 3 filter
+    weights_2[i +  0] = -1;
+    weights_2[i +  1] =  0;
+    weights_2[i +  2] =  1;
+  }
+  layer->SetUp(sep_blob_bottom_vec, &(sep_blob_top_vec));
+  layer->Forward(sep_blob_bottom_vec, &(sep_blob_top_vec));
+  // Test equivalence of full and separable filters.
+  const Dtype* top_data = this->blob_top_->cpu_data();
+  const Dtype* sep_top_data = this->blob_top_2_->cpu_data();
+  for (int i = 0; i < this->blob_top_->count(); ++i) {
+    EXPECT_NEAR(top_data[i], sep_top_data[i], 1e-4);
+  }
+}
+
 TYPED_TEST(ConvolutionLayerTest, TestGradient) {
   typedef typename TypeParam::Dtype Dtype;
   LayerParameter layer_param;