singa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wang...@apache.org
Subject incubator-singa git commit: SINGA-215 Implement Image Transformation for Image Pre-processing
Date Fri, 15 Jul 2016 08:25:29 GMT
Repository: incubator-singa
Updated Branches:
  refs/heads/dev 75616538f -> 7e050c120


SINGA-215 Implement Image Transformation for Image Pre-processing

Implement some of the image transformations (i.e., crop, resize, horizontal_mirror) for pre-processing
stage.

Image resize need to use opencv library.

Currently image transformation only support one sample input each time.

Add static function resize, crop and mirror for image transformation.

Training phase use random crop and random mirror,
Evaluation phase use central crop and no mirror.

Cannot test phases for mirror because of a random factor.

Fix bugs in test files involving memory allocation and access.


Project: http://git-wip-us.apache.org/repos/asf/incubator-singa/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-singa/commit/7e050c12
Tree: http://git-wip-us.apache.org/repos/asf/incubator-singa/tree/7e050c12
Diff: http://git-wip-us.apache.org/repos/asf/incubator-singa/diff/7e050c12

Branch: refs/heads/dev
Commit: 7e050c120197d604f256e14e5d647f4c54164f57
Parents: 7561653
Author: jixin <jixin@comp.nus.edu.sg>
Authored: Fri Jul 15 15:04:20 2016 +0800
Committer: jixin <jixin@comp.nus.edu.sg>
Committed: Fri Jul 15 15:04:25 2016 +0800

----------------------------------------------------------------------
 CMakeLists.txt                       |   1 +
 include/singa/io/transformer.h       |  89 ++++++++
 src/io/image_transformer.cc          | 356 ++++++++++++++++++++++++++++++
 src/io/jpg_decoder.cc                |  12 +-
 src/proto/io.proto                   |  21 ++
 test/singa/test_activation.cc        |   2 +
 test/singa/test_cpp_cpu.cc           |   1 +
 test/singa/test_cudnn_activation.cc  |   2 +
 test/singa/test_cudnn_softmax.cc     |   6 +
 test/singa/test_image_transformer.cc | 261 ++++++++++++++++++++++
 test/singa/test_jpg.cc               |  32 ++-
 test/singa/test_mse.cc               |   1 +
 test/singa/test_prelu.cc             |   4 +
 test/singa/test_softmax.cc           |   3 +
 14 files changed, 777 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3eade27..d3cd776 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,7 @@ OPTION(USE_PYTHON "Generate py wrappers" ON)
 INCLUDE("cmake/Dependencies.cmake")
 INCLUDE("cmake/Utils.cmake")
 ADD_DEFINITIONS(-DUSE_CMAKE)
+#message(STATUS "${SINGA_INCLUDE_DIR}")
 
 CONFIGURE_FILE (
     "${PROJECT_SOURCE_DIR}/cmake/Templates/singa_config.h.in"

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/include/singa/io/transformer.h
----------------------------------------------------------------------
diff --git a/include/singa/io/transformer.h b/include/singa/io/transformer.h
new file mode 100644
index 0000000..d9a9263
--- /dev/null
+++ b/include/singa/io/transformer.h
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SINGA_IO_TRANSFORMER_H_
+#define SINGA_IO_TRANSFORMER_H_
+
+#include <vector>
+#include <string>
+#include "singa/core/tensor.h"
+#include "singa/proto/io.pb.h"
+#include "singa/proto/model.pb.h"
+
+namespace singa {
+
+/// Base apply class that does data transformations in pre-processing stage.
+class Transformer {
+ public:
+  Transformer() {}
+  virtual ~Transformer() {}
+
+  virtual void Setup(const TransformerConf& conf) {}
+
+  virtual Tensor Apply(int flag, Tensor& input) = 0;
+};
+
+class ImageTransformer: public Transformer {
+ public:
+  void Setup(const TransformerConf& conf) override {
+    featurewise_center_ = conf.featurewise_center();
+    featurewise_std_norm_ = conf.featurewise_std_norm();
+    resize_height_ = conf.resize_height();
+    resize_width_ = conf.resize_width();
+    rescale_ = conf.rescale();
+    horizontal_mirror_ = conf.horizontal_mirror();
+    image_dim_order_ = conf.image_dim_order();
+    
+    /// if crop_shape not contain 2 elements, ignore crop option.
+    if (conf.crop_shape_size() == 2)
+      crop_shape_ = {conf.crop_shape(0), conf.crop_shape(1)};      
+  }
+
+  Tensor Apply(int flag, Tensor& input) override;
+
+  const bool featurewise_center() const { return featurewise_center_; }
+  const bool featurewise_std_norm() const { return featurewise_std_norm_; }
+  const bool horizontal_mirror() const { return horizontal_mirror_; }
+  const int resize_height() const { return resize_height_; }
+  const int resize_width() const { return resize_width_; }
+  const float rescale() const { return rescale_; }
+  const Shape crop_shape() const { return crop_shape_; }
+  const string image_dim_order() const { return image_dim_order_; }
+
+ private:
+  bool featurewise_center_ = false;
+  bool featurewise_std_norm_ = false;
+  bool horizontal_mirror_ = false;
+  int resize_height_ = 0;
+  int resize_width_ = 0;
+  float rescale_ = 0.f;
+  Shape crop_shape_ = {};
+  std::string image_dim_order_ = "CHW";
+};
+
+#ifdef USE_OPENCV
+Tensor resize(Tensor& input, const size_t resize_height, 
+         const size_t resize_width, const string& image_dim_order);
+#endif
+Tensor crop(Tensor& input, const size_t crop_height, 
+             const size_t crop_width, const size_t crop_h_offset, 
+             const size_t crop_w_offset, const string& image_dim_order);
+Tensor mirror(Tensor& input, const bool horizontal_mirror, 
+             const bool vertical_mirror, const string& image_dim_order);
+} // namespace singa
+#endif  // SINGA_IO_TRANSFORMER_H_

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/src/io/image_transformer.cc
----------------------------------------------------------------------
diff --git a/src/io/image_transformer.cc b/src/io/image_transformer.cc
new file mode 100644
index 0000000..f233ad3
--- /dev/null
+++ b/src/io/image_transformer.cc
@@ -0,0 +1,356 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "singa/io/transformer.h"
+#include <time.h>
+
+#ifdef USE_OPENCV
+#include <opencv2/highgui/highgui.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+#endif
+
+namespace singa {
+
+  Tensor ImageTransformer::Apply(int flag, Tensor& input) {
+    CHECK_LE(input.nDim(), 4u);
+    CHECK_GE(input.nDim(), 2u);
+    CHECK_EQ(input.data_type(), kFloat32) << "Data type " << input.data_type()
+      << " is invalid for an raw image";
+    srand(time(NULL));
+    /// TODO
+    /// currently only consider one sample each time
+
+    /// resize image using opencv resize
+    Tensor temp1;
+#ifdef USE_OPENCV
+    temp1 = resize(input, resize_height_, resize_width_, image_dim_order_);
+#else
+    temp1 = input;
+#endif
+
+    /// crop
+    Tensor temp2;
+    size_t height = 0, width = 0;
+    if (input.nDim() >= 3u) {
+      if (image_dim_order_ == "CHW")
+        height = temp1.shape(input.nDim() - 2), width = temp1.shape(input.nDim() - 1);
+      else if (image_dim_order_ == "HWC")
+        height = temp1.shape(input.nDim() - 3), width = temp1.shape(input.nDim() - 2);
+      else
+        LOG(FATAL) << "Unknow dimension order for images " << image_dim_order_
+               << " Only support 'HWC' and 'CHW'";
+    } else /// input is 2D gray image
+      height = temp1.shape(0), width = temp1.shape(1);
+
+    if (crop_shape_.size() == 2) {
+      if (flag == kTrain) { 
+        /// random crop
+        if (crop_shape_[0] > height || crop_shape_[0] > width)
+          LOG(FATAL) << "Crop size larger than the size of raw image";
+        size_t crop_h_offset = rand() % ((height - crop_shape_[0]) / 2), 
+               crop_w_offset = rand() % ((width - crop_shape_[1]) / 2);
+        temp2 = crop(temp1, crop_shape_[0], crop_shape_[1], 
+                  crop_h_offset, crop_w_offset, image_dim_order_);
+      } else if (flag == kEval) {
+        /// central crop
+        size_t crop_h_offset = (height - crop_shape_[0]) / 2,
+               crop_w_offset = (width - crop_shape_[1]) / 2;
+        temp2 = crop(temp1, crop_shape_[0], crop_shape_[1], 
+                  crop_h_offset, crop_w_offset, image_dim_order_); 
+      }
+    }
+    else temp2 = temp1;
+
+    /// mirror
+    Tensor output;
+    if ((flag == kTrain) && (rand() % 2))
+        output = mirror(temp2, true, false, image_dim_order_);
+    else output = temp2;
+    return output;
+  }
+
+#ifdef USE_OPENCV
+  Tensor resize(Tensor& input, const size_t resize_height, 
+               const size_t resize_width, const string& image_dim_order) {
+    CHECK_LE(input.nDim(), 4u);
+    CHECK_GE(input.nDim(), 2u);
+    if (!resize_height || !resize_width) return input;
+    Tensor output;
+    cv::Mat mat;
+    const auto* in = input.data<float>();
+    if (input.nDim() == 4u) {
+      /// TODO
+      /// batch based resize
+      LOG(FATAL) << "Not implemented";
+    } else if (input.nDim() == 3u) {
+      if (image_dim_order == "CHW") {
+        size_t height = input.shape(1), width = input.shape(2),
+               channel = input.shape(0);
+        if (channel == 3u) {
+          mat = cv::Mat(height, width, CV_32FC3, cv::Scalar(0, 0, 0));
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              for (size_t k = 0; k < channel; k++)
+                mat.at<cv::Vec3f>(i, j)[k] = in[k * height * width + i * width + j];
+        }
+        else if (channel == 1u) {
+          mat = cv::Mat(height, width, CV_32FC1);
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+                mat.at<cv::Vec<float, 1>>(i, j)[0] = in[i * width + j];
+        }
+        else LOG(FATAL) << "Invalid channel size: " << channel;
+      } else if (image_dim_order == "HWC") {
+        size_t height = input.shape(0), width = input.shape(1),
+               channel = input.shape(2);
+        if (channel == 3u) {
+          mat = cv::Mat(height, width, CV_32FC3, cv::Scalar(0, 0, 0));
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              for (size_t k = 0; k < channel; k++)
+                mat.at<cv::Vec3f>(i, j)[k] =
+                  in[i * width * channel + j * channel + k];
+        } else if (channel == 1u) { /// 2D gray image
+          mat = cv::Mat(height, width, CV_32FC1);
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              mat.at<cv::Vec<float, 1>>(i, j)[0] = in[i * width + j];
+        } else LOG(FATAL) << "Invalid channel size: " << channel;
+      } else {
+        LOG(FATAL) << "Unknow dimension order for images " << image_dim_order
+                   << " Only support 'HWC' and 'CHW'";
+      }
+    } else { /// 2D gray image
+      size_t height = input.shape(0), width = input.shape(1);
+      mat = cv::Mat(height, width, CV_32FC1);
+      for (size_t i = 0; i < height; i++)
+        for (size_t j = 0; j < width; j++)
+          mat.at<cv::Vec<float, 1>>(i, j)[0] = in[i * width + j];
+    }
+    cv::Size size(resize_width, resize_height);
+    cv::Mat resized;
+    cv::resize(mat, resized, size);
+    CHECK_EQ(resized.size().height, resize_height);
+    CHECK_EQ(resized.size().width, resize_width);
+    size_t new_size = resize_height * resize_width * resized.channels();
+    float* out = new float[new_size];
+    if (input.nDim() == 4u) {
+      /// TODO
+      /// batch based resize
+      LOG(FATAL) << "Not implemented";
+    } else if (input.nDim() == 3u) {
+      if (image_dim_order == "CHW") {
+        size_t height = resize_height, width = resize_width,
+           channel = input.shape(0);
+        if (channel == 3u) {
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              for (size_t k = 0; k < channel; k++)
+                out[k * height * width + i * width + j] = resized.at<cv::Vec3f>(i,
j)[k];
+        } else { /// 2D gray image
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              out[i * width + j] = resized.at<cv::Vec<float, 1>>(i, j)[0];
+        }
+        Tensor temp(Shape{channel, height, width});
+        temp.CopyDataFromHostPtr<float>(out, new_size);
+        output = temp;
+      } else {
+        size_t height = resize_height, width = resize_width,
+           channel = input.shape(2);
+        if (channel == 3u) {
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              for (size_t k = 0; k < channel; k++)
+                out[i * width * channel + j * channel + k] = resized.at<cv::Vec3f>(i,
j)[k];
+        } else { /// 1 channel
+          for (size_t i = 0; i < height; i++)
+            for (size_t j = 0; j < width; j++)
+              out[i * width + j] = resized.at<cv::Vec<float, 1>>(i, j)[0];
+        }
+        Tensor temp(Shape{height, width, channel}); 
+        temp.CopyDataFromHostPtr<float>(out, new_size);
+        output = temp;
+      }
+    } else { /// 2D gray image
+      size_t height = resize_height, width = resize_width;
+      for (size_t i = 0; i < height; i++)
+        for (size_t j = 0; j < width; j++)
+          out[i * width + j] = resized.at<cv::Vec<float, 1>>(i, j)[0];
+      Tensor temp(Shape{height, width});
+      temp.CopyDataFromHostPtr<float>(out, new_size);
+      output = temp;
+    }
+    delete[] out;
+    return output;
+  }
+#endif
+
+  Tensor crop(Tensor& input, const size_t crop_height, const size_t crop_width, 
+             const size_t crop_h_offset, const size_t crop_w_offset, 
+             const string& image_dim_order) {
+    CHECK_LE(input.nDim(), 4u);
+    CHECK_GE(input.nDim(), 2u);
+
+    Tensor output;
+    const float* in = input.data<float>();
+    size_t out_idx = 0, in_idx = 0;
+    if (input.nDim() == 4u) {
+      /// TODO
+      LOG(FATAL) << "Not implemented";
+    } else if (input.nDim() == 3u) {
+      if (image_dim_order == "CHW") {
+        size_t height = input.shape(1), width = input.shape(2),
+            channel = input.shape(0); 
+        CHECK_LE(crop_height + crop_h_offset, height);
+        CHECK_LE(crop_width + crop_w_offset, width);
+        float* out = new float[crop_height * crop_width * channel];
+        for (size_t c = 0; c < channel; c++) {
+          for (size_t h = 0; h < crop_height; h++) {
+            for (size_t w = 0; w < crop_width; w++) {
+              in_idx = (c * height + crop_h_offset + h) * width + crop_w_offset + w;
+              out_idx = (c * crop_height + h) * crop_width + w;
+              out[out_idx] = in[in_idx];
+            }
+          }
+        }
+        output.Reshape(Shape{channel, crop_height, crop_width});
+        output.CopyDataFromHostPtr<float>(out, crop_height * crop_width * channel);
+        delete[] out;
+      } else if (image_dim_order == "HWC") {
+        size_t height = input.shape(0), width = input.shape(1), 
+               channel = input.shape(2); 
+        CHECK_LE(crop_height + crop_h_offset, height);
+        CHECK_LE(crop_width + crop_w_offset, width);
+        float* out = new float[crop_height * crop_width * channel];
+        for (size_t c = 0; c < channel; c++) {
+          for (size_t h = 0; h < crop_height; h++) {
+            for (size_t w = 0; w < crop_width; w++) {
+              in_idx = ((crop_h_offset + h) * width + crop_w_offset + w) * channel + c;
+              out_idx = (h * crop_width + w) * channel + c;
+              out[out_idx] = in[in_idx];
+            }
+          }
+        }
+        output.Reshape(Shape{crop_height, crop_width, channel});
+        output.CopyDataFromHostPtr<float>(out, crop_height * crop_width * channel);
+        delete[] out;
+      } else {
+        LOG(FATAL) << "Unknow dimension order for images " << image_dim_order
+                   << " Only support 'HWC' and 'CHW'";
+      }
+    } else { /// 2D gray image
+      size_t height = input.shape(0), width = input.shape(1); 
+      CHECK_LE(crop_height + crop_h_offset, height);
+      CHECK_LE(crop_width + crop_w_offset, width);
+      float* out = new float[crop_height * crop_width];
+      for (size_t h = 0; h < crop_height; h++) {
+        for (size_t w = 0; w < crop_width; w++) {
+          in_idx = (crop_h_offset + h) * width + crop_w_offset + w;
+          out_idx = h * crop_width + w;
+          out[out_idx] = in[in_idx];
+        }
+      }
+      output.Reshape(Shape{crop_height, crop_width});
+      output.CopyDataFromHostPtr<float>(out, crop_height * crop_width);
+      delete[] out;
+    }
+    return output;
+  }
+
+  Tensor mirror(Tensor& input, const bool horizontal_mirror,
+             const bool vertical_mirror, const string& image_dim_order) {
+    CHECK_LE(input.nDim(), 4u);
+    CHECK_GE(input.nDim(), 2u);
+    if (!horizontal_mirror && !vertical_mirror) return input;
+
+    Tensor output;
+    const float* in = input.data<float>();
+    size_t out_idx = 0, in_idx = 0;
+    if (input.nDim() == 4u) {
+      /// TODO
+      LOG(FATAL) << "Not implemented";
+    } else if (input.nDim() == 3u) {
+      if (image_dim_order == "CHW") {
+        size_t height = input.shape(1), width = input.shape(2),
+            channel = input.shape(0);
+        float* out = new float[height * width * channel];
+        for (size_t c = 0; c < channel; c++) {
+          for (size_t h = 0; h < height; h++) {
+            for (size_t w = 0; w < width; w++) {
+              in_idx = (c * height + h) * width + w;
+              if (horizontal_mirror && vertical_mirror)
+                out_idx = (c * height + (height - 1 - h)) * width + (width - 1 - w);
+              else if (horizontal_mirror)
+                out_idx = (c * height + h) * width + (width - 1 - w);
+              else /// only do vertical mirror
+                out_idx = (c * height + (height - 1 - h)) * width + w;
+              out[out_idx] = in[in_idx];
+            }
+          }
+        }
+        output.Reshape(Shape{channel, height, width});
+        output.CopyDataFromHostPtr<float>(out, height * width * channel);
+        delete[] out;
+      } else if (image_dim_order == "HWC") {
+        size_t height = input.shape(0), width = input.shape(1),
+            channel = input.shape(2);
+        float* out = new float[height * width * channel];
+        for (size_t c = 0; c < channel; c++) {
+          for (size_t h = 0; h < height; h++) {
+            for (size_t w = 0; w < width; w++) {
+              in_idx = (h * width + w) * channel + c;
+              if (horizontal_mirror && vertical_mirror)
+                out_idx = ((height - 1 - h) * width + (width - 1 - w)) * channel + c;
+              else if (horizontal_mirror)
+                out_idx = (h * width + (width - 1 - w)) * channel + c;
+              else /// only do vertical mirror
+                out_idx = ((height - 1 - h) * width + w) * channel + c;
+              out[out_idx] = in[in_idx];
+            }
+          }
+        }
+        output.Reshape(Shape{height, width, channel});
+        output.CopyDataFromHostPtr<float>(out, height * width * channel);
+        delete[] out;
+      } else {
+        LOG(FATAL) << "Unknow dimension order for images " << image_dim_order
+                   << " Only support 'HWC' and 'CHW'";
+      }
+    } else { /// 2D gray image
+      size_t height = input.shape(0), width = input.shape(1);
+      float* out = new float[height * width];
+      for (size_t h = 0; h < height; h++) {
+        for (size_t w = 0; w < width; w++) {
+          in_idx = h * width + w;
+          if (horizontal_mirror && vertical_mirror)
+            out_idx = (height - 1 - h) * width + (width - 1 - w);
+          else if (horizontal_mirror)
+            out_idx = h * width + (width - 1 - w);
+          else /// only do vertical mirror
+            out_idx = (height - 1 - h) * width + w;
+          out[out_idx] = in[in_idx];
+        }
+      }
+      output.Reshape(Shape{height, width});
+      output.CopyDataFromHostPtr<float>(out, height * width);
+      delete[] out;
+    }
+    return output;
+  }
+} // namespace singa

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/src/io/jpg_decoder.cc
----------------------------------------------------------------------
diff --git a/src/io/jpg_decoder.cc b/src/io/jpg_decoder.cc
index f885537..b778b98 100644
--- a/src/io/jpg_decoder.cc
+++ b/src/io/jpg_decoder.cc
@@ -34,11 +34,11 @@ std::vector<Tensor> JPGDecoder::Decode(std::string value) {
 
   // decode image
   cv::Mat mat = cv::imdecode(cv::Mat(pixel), CV_LOAD_IMAGE_COLOR);
-  size_t height = mat.rows, width = mat.cols, channel = mat.channels();
+  size_t height = mat.size().height, width = mat.size().width, channel = mat.channels();
   Shape shape(record.shape().begin(), record.shape().end());
-  CHECK_EQ(shape[0], height);
-  CHECK_EQ(shape[1], width);
-  CHECK_EQ(shape[2], channel);
+  //CHECK_EQ(shape[0], height);
+  //CHECK_EQ(shape[1], width);
+  //CHECK_EQ(shape[2], channel);
   Tensor image(shape);
 
   float* data = new float[image.Size()];
@@ -61,10 +61,10 @@ std::vector<Tensor> JPGDecoder::Decode(std::string value) {
   }
   image.CopyDataFromHostPtr<float>(data, image.Size());
   output.push_back(image);
-  delete data;
+  delete[] data;
 
   if (record.label_size()) {
-    Tensor label(Shape{1}, &defaultDevice, kInt);
+    Tensor label(Shape{1}, kInt);
     int labelid = record.label(0);
     label.CopyDataFromHostPtr(&labelid, 1);
     output.push_back(label);

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/src/proto/io.proto
----------------------------------------------------------------------
diff --git a/src/proto/io.proto b/src/proto/io.proto
index 96280e5..f349f74 100644
--- a/src/proto/io.proto
+++ b/src/proto/io.proto
@@ -30,6 +30,27 @@ message DecoderConf {
   optional bool has_label = 3 [default = true];
 }
 
+message TransformerConf {
+  optional bool featurewise_center = 1 [default = false];
+  optional bool samplewise_center = 2 [default = false];
+  optional bool featurewise_std_norm = 3 [default = false];
+  optional bool samplewise_std_norm = 4 [default = false];
+  optional bool zca_whitening = 5 [default = false];
+  optional int32 rotation_range = 6 [default = 0];
+  /// crop_shape should exactly contain 2 elements in order,
+  /// i.e., crop_height, crop_width.
+  repeated uint32 crop_shape = 7 [packed = true];
+  optional int32 resize_height = 8 [default = 0];
+  optional int32 resize_width = 9 [default = 0];
+  optional bool horizontal_mirror = 10 [default = false];
+  optional bool vertical_mirror = 11 [default = false];
+  /// if input tensor is 4D or 3D,
+  /// supported shape of inputs are "CHW" and "HWC".
+  /// if input tensor is 2D, this field will be ignored.
+  optional string image_dim_order = 12 [default = "CHW"];
+  optional float rescale = 13 [default = 0];
+}
+
 message ImageRecord {
   repeated int32 shape = 1;
   repeated int32 label = 2;

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_activation.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_activation.cc b/test/singa/test_activation.cc
index 4b76d68..001c49c 100644
--- a/test/singa/test_activation.cc
+++ b/test/singa/test_activation.cc
@@ -81,6 +81,7 @@ TEST(Activation, Forward) {
     EXPECT_FLOAT_EQ(y[0], yptr[0]);
     EXPECT_FLOAT_EQ(y[4], yptr[4]);
     EXPECT_FLOAT_EQ(y[5], yptr[5]);
+    delete[] y;
   }
 }
 
@@ -130,5 +131,6 @@ TEST(Activation, Backward) {
     EXPECT_FLOAT_EQ(dx[0], xptr[0]);
     EXPECT_FLOAT_EQ(dx[4], xptr[4]);
     EXPECT_FLOAT_EQ(dx[5], xptr[5]);
+    delete[] dx;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_cpp_cpu.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_cpp_cpu.cc b/test/singa/test_cpp_cpu.cc
index 0ef6aac..35bd108 100644
--- a/test/singa/test_cpp_cpu.cc
+++ b/test/singa/test_cpp_cpu.cc
@@ -46,6 +46,7 @@ TEST(CppCPU, Exec) {
       z = x + y;
       }, {b}, {b}, false);
   EXPECT_EQ(x + y, z);
+  dev.FreeBlock(b);
 }
 
 TEST(CppCPU, CopyData) {

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_cudnn_activation.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_cudnn_activation.cc b/test/singa/test_cudnn_activation.cc
index a9ac1a3..9279d6c 100644
--- a/test/singa/test_cudnn_activation.cc
+++ b/test/singa/test_cudnn_activation.cc
@@ -79,6 +79,7 @@ TEST(TCudnnActivation, Forward) {
     EXPECT_FLOAT_EQ(y[0], yptr[0]);
     EXPECT_FLOAT_EQ(y[4], yptr[4]);
     EXPECT_FLOAT_EQ(y[5], yptr[5]);
+    delete[] y;
   }
 }
 
@@ -127,6 +128,7 @@ TEST(TCudnnActivation, Backward) {
     for (size_t i = 0; i < n; i++) {
       EXPECT_NEAR(dx[i], xptr[i], 1e-7);
     }
+    delete[] dx;
   }
 }
 #endif  // USE_CUDNN

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_cudnn_softmax.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_cudnn_softmax.cc b/test/singa/test_cudnn_softmax.cc
index a7cbe9d..2b88843 100644
--- a/test/singa/test_cudnn_softmax.cc
+++ b/test/singa/test_cudnn_softmax.cc
@@ -63,6 +63,7 @@ TEST(CudnnSoftmax, Forward1D) {
   for (size_t i = 0; i < n; i++) sigma += exp(x[i]);
   for (size_t i = 0; i < n; i++) y[i] = exp(x[i]) / sigma;
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(y[i], yptr[i]);
+  delete[] y;
 }
 
 TEST(CudnnSoftmax, Backward1D) {
@@ -96,6 +97,7 @@ TEST(CudnnSoftmax, Backward1D) {
   for (size_t i = 0; i < n; i++) sigma += grad[i] * yptr[i];
   for (size_t i = 0; i < n; i++) dx[i] = (grad[i] - sigma) * yptr[i];
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(dx[i], xptr[i]);
+  delete[] dx;
 }
 
 TEST(CudnnSoftmax, Forward2D) {
@@ -124,6 +126,8 @@ TEST(CudnnSoftmax, Forward2D) {
   for (size_t i = 0; i < n; i++) sigma[i / c] += exp(x[i]);
   for (size_t i = 0; i < n; i++) y[i] = exp(x[i]) / sigma[i / c];
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(y[i], yptr[i]);
+  delete[] y;
+  delete[] sigma;
 }
 
 TEST(CudnnSoftmax, Backward2D) {
@@ -159,5 +163,7 @@ TEST(CudnnSoftmax, Backward2D) {
   for (size_t i = 0; i < n; i++) sigma[i / c] += grad[i] * yptr[i];
   for (size_t i = 0; i < n; i++) dx[i] = (grad[i] - sigma[i / c]) * yptr[i];
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(dx[i], xptr[i]);
+  delete[] dx;
+  delete[] sigma;
 }
 #endif  // USE_CUDNN

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_image_transformer.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_image_transformer.cc b/test/singa/test_image_transformer.cc
new file mode 100644
index 0000000..92eb1a6
--- /dev/null
+++ b/test/singa/test_image_transformer.cc
@@ -0,0 +1,261 @@
+/************************************************************
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*************************************************************/
+
+#include "singa/io/transformer.h"
+#include "gtest/gtest.h"
+#include <time.h>
+#include <iostream>
+
+// decide whether to use opencv
+// #include "singa/singa_config.h"
+
+#ifdef USE_OPENCV
+#include <opencv2/highgui/highgui.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+#endif
+
+using singa::Shape;
+TEST(ImageTransformer, Setup) {
+  singa::ImageTransformer img_transformer;
+
+  singa::TransformerConf conf;
+  conf.set_resize_height(256);
+  conf.set_resize_width(256);
+  conf.set_horizontal_mirror(true);
+  conf.set_image_dim_order("HWC");
+  conf.add_crop_shape(224u);
+  conf.add_crop_shape(200u);
+
+  img_transformer.Setup(conf);
+  EXPECT_EQ(256, img_transformer.resize_height());
+  EXPECT_EQ(256, img_transformer.resize_width());
+  EXPECT_EQ(true, img_transformer.horizontal_mirror());
+  EXPECT_EQ("HWC", img_transformer.image_dim_order());
+  EXPECT_EQ(224u, img_transformer.crop_shape()[0]);
+  EXPECT_EQ(200u, img_transformer.crop_shape()[1]);
+}
+
+TEST(ImageTransformer, Apply3D) {
+  size_t n = 180;
+  float* x = new float[n];
+  size_t channel = 3, height = 6, width = 10;
+  singa::Tensor in(singa::Shape{height, width, channel});
+  srand(time(NULL));
+  for (size_t i = 0; i < n; i++) x[i] = (float)(rand() % 256);
+  in.CopyDataFromHostPtr<float>(x, n);
+  size_t resize_height = 4, resize_width = 6;
+
+  singa::ImageTransformer img_transformer;
+  singa::TransformerConf conf;
+  conf.set_resize_height(resize_height);
+  conf.set_resize_width(resize_width);
+  conf.set_horizontal_mirror(false);
+  conf.set_image_dim_order("HWC");
+  conf.add_crop_shape(2u);
+  conf.add_crop_shape(3u);
+  img_transformer.Setup(conf);
+
+  singa::Tensor out = img_transformer.Apply(singa::kEval, in);
+  EXPECT_EQ(2u, out.shape(0));
+  EXPECT_EQ(3u, out.shape(1));
+  const float* y = out.data<float>();
+#ifdef USE_OPENCV
+  cv::Mat mat(height, width, CV_32FC3, cv::Scalar(0, 0, 0));
+  for (size_t i = 0; i < height; i++)
+    for (size_t j = 0; j < width; j++)
+      for (size_t k = 0; k < channel; k++)
+        mat.at<cv::Vec3f>(i, j)[k] = x[i * width * channel + j * channel + k];
+  cv::Size size(resize_width, resize_height);
+  cv::Mat resized;
+  cv::resize(mat, resized, size);
+  EXPECT_EQ(resize_height, resized.size().height);
+  EXPECT_EQ(resize_width, resized.size().width);
+  size_t new_size = resize_height * resize_width * channel;
+  float* xt = new float[new_size];
+  for (size_t i = 0; i < resize_height; i++)
+    for (size_t j = 0; j < resize_width; j++)
+      for (size_t k = 0; k < channel; k++)
+        xt[i * resize_width * channel + j * channel + k] = resized.at<cv::Vec3f>(i,
j)[k];
+  for (size_t c = 0; c < 3; c++)
+    for (size_t h = 0; h < 2; h++)
+      for (size_t w = 0; w < 3; w++){
+        //size_t in_idx = (c * height + 1 + h) * width + 1 + w,
+        //    out_idx = (c * 2 + h) * 3 + w;
+        // test for HWC
+        size_t in_idx = ((h + 1) * resize_width + 1 + w) * channel + c,
+              out_idx = (h * 3 + w) * channel + c;
+        EXPECT_EQ(xt[in_idx], y[out_idx]);
+      }
+  delete[] xt;
+#else
+  for (size_t c = 0; c < 3; c++)
+    for (size_t h = 0; h < 2; h++)
+      for (size_t w = 0; w < 3; w++){
+        //size_t in_idx = (c * height + 2 + h) * width + 3 + w,
+        //    out_idx = (c * 2 + h) * 3 + w;
+        // test for HWC
+        size_t in_idx = ((h + 2) * width + 3 + w) * channel + c,
+              out_idx = (h * 3 + w) * channel + c;
+        EXPECT_EQ(x[in_idx], y[out_idx]);
+      }
+#endif
+  delete[] x;
+}
+
+TEST(ImageTransformer, Apply2D) {
+  size_t n = 60;
+  float* x = new float[n];
+  size_t height = 6, width = 10;
+  singa::Tensor in(singa::Shape{height, width});
+  srand(time(NULL));
+  for (size_t i = 0; i < n; i++) x[i] = (float)(rand() % 256);
+  in.CopyDataFromHostPtr<float>(x, n);
+  size_t resize_height = 4, resize_width = 6;
+
+  singa::ImageTransformer img_transformer;
+  singa::TransformerConf conf;
+  conf.set_resize_height(resize_height);
+  conf.set_resize_width(resize_width);
+  conf.set_horizontal_mirror(false);
+  conf.set_image_dim_order("HWC");
+  conf.add_crop_shape(2u);
+  conf.add_crop_shape(3u);
+  img_transformer.Setup(conf);
+
+  singa::Tensor out = img_transformer.Apply(singa::kEval, in);
+  EXPECT_EQ(2u, out.shape(0));
+  EXPECT_EQ(3u, out.shape(1));
+  const float* y = out.data<float>();
+#ifdef USE_OPENCV
+  cv::Mat mat(height, width, CV_32FC1, cv::Scalar(0, 0, 0));
+  for (size_t i = 0; i < height; i++)
+    for (size_t j = 0; j < width; j++)
+      mat.at<cv::Vec<float, 1>>(i, j)[0] = x[i * width + j];
+  cv::Size size(resize_width, resize_height);
+  cv::Mat resized;
+  cv::resize(mat, resized, size);
+  EXPECT_EQ(resize_height, resized.size().height);
+  EXPECT_EQ(resize_width, resized.size().width);
+  size_t new_size = resize_height * resize_width;
+  float* xt = new float[new_size];
+  for (size_t i = 0; i < resize_height; i++)
+    for (size_t j = 0; j < resize_width; j++)
+        xt[i * resize_width + j] = resized.at<cv::Vec<float, 1>>(i, j)[0];
+
+  for (size_t h = 0; h < 2; h++)
+    for (size_t w = 0; w < 3; w++){
+      size_t in_idx = (h + 1) * resize_width + 1 + w,
+            out_idx = h * 3 + w;
+      EXPECT_EQ(xt[in_idx], y[out_idx]);
+    }
+  delete[] xt;
+#else
+  for (size_t h = 0; h < 2; h++)
+    for (size_t w = 0; w < 3; w++){
+      size_t in_idx = (h + 2) * width + 3 + w,
+            out_idx = h * 3 + w;
+      EXPECT_EQ(x[in_idx], y[out_idx]);
+    }
+#endif
+  delete[] x;
+}
+
+#ifdef USE_OPENCV
+TEST(ImageTransformer, Resize) {
+  size_t n = 180;
+  float* x = new float[n];
+  size_t channel = 3, height = 6, width = 10;
+  singa::Tensor in(singa::Shape{height, width, channel});
+  srand(time(NULL));
+  for (size_t i = 0; i < n; i++) x[i] = (float)(rand() % 256);
+  in.CopyDataFromHostPtr<float>(x, n);
+  size_t resize_height = 4, resize_width = 5;
+  singa::Tensor out = singa::resize(in, resize_height, resize_width, "HWC");
+  const float* y = out.data<float>();
+
+  cv::Mat mat(height, width, CV_32FC3, cv::Scalar(0, 0, 0));
+  for (size_t i = 0; i < height; i++)
+    for (size_t j = 0; j < width; j++)
+      for (size_t k = 0; k < channel; k++)
+        mat.at<cv::Vec3f>(i, j)[k] = x[i * width * channel + j * channel + k];
+  cv::Size size(resize_width, resize_height);
+  cv::Mat resized;
+  cv::resize(mat, resized, size);
+  EXPECT_EQ(resize_height, resized.size().height);
+  EXPECT_EQ(resize_width, resized.size().width);
+  size_t new_size = resize_height * resize_width * channel;
+  float* xt = new float[new_size];
+  for (size_t i = 0; i < resize_height; i++)
+    for (size_t j = 0; j < resize_width; j++)
+      for (size_t k = 0; k < channel; k++)
+        xt[i * resize_width * channel + j * channel + k] = resized.at<cv::Vec3f>(i,
j)[k];
+
+  for (size_t i = 0; i < new_size; i++) EXPECT_EQ(xt[i], y[i]);
+  delete[] x;
+  delete[] xt;
+}
+#endif
+
+TEST(ImageTransformer, Crop) {
+  size_t n = 180;
+  float* x = new float[n];
+  size_t channel = 3, height = 6, width = 10;
+  singa::Tensor in(singa::Shape{channel, height, width});
+  srand(time(NULL));
+  for (size_t i = 0; i < n; i++) x[i] = (float)(rand() % 256);
+  in.CopyDataFromHostPtr<float>(x, n);
+  size_t crop_height = 3, crop_width = 4,
+         crop_h_offset = 2, crop_w_offset = 5;
+  singa::Tensor out = singa::crop(in, crop_height, crop_width,
+                         crop_h_offset, crop_w_offset, "CHW");
+
+  const float* y = out.data<float>();
+  for (size_t h = 0; h < crop_height; h++)
+    for (size_t w = 0; w < crop_width; w++)
+      for (size_t c = 0; c < channel; c++) {
+        size_t out_idx = c * crop_height * crop_width + h * crop_width + w;
+        size_t in_idx = c * height * width + (h + crop_h_offset) 
+                 * width + w + crop_w_offset;
+        EXPECT_EQ(x[in_idx], y[out_idx]);
+      }
+  delete[] x;
+}
+
+TEST(ImageTransformer, Mirror) {
+  size_t n = 30;
+  float* x = new float[n];
+  size_t channel = 3, height = 2, width = 5;
+  singa::Tensor in(singa::Shape{height, width, channel});
+  srand(time(NULL));
+  for (size_t i = 0; i < n; i++) x[i] = (float)(rand() % 256);
+  in.CopyDataFromHostPtr<float>(x, n);
+  singa::Tensor out = singa::mirror(in, true, false, "HWC");
+
+  const float* y = out.data<float>();
+  for (size_t h = 0; h < height; h++)
+    for (size_t w = 0; w < width; w++)
+      for (size_t c = 0; c < channel; c++) {
+        size_t out_idx = h * width * channel + (width - 1 - w) * channel + c;
+        size_t in_idx = h * width * channel + w * channel + c;
+        EXPECT_EQ(x[in_idx], y[out_idx]);
+      }
+  delete[] x;
+}

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_jpg.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_jpg.cc b/test/singa/test_jpg.cc
index f60546a..95ee01d 100644
--- a/test/singa/test_jpg.cc
+++ b/test/singa/test_jpg.cc
@@ -35,9 +35,19 @@ TEST(Decoder, Decode) {
 
   // initial random seed
   srand(time(NULL));
+ 
+  singa::EncoderConf encoder_conf;
+  encoder_conf.set_image_dim_order("HWC");
+  encoder.Setup(encoder_conf);
+  EXPECT_EQ("HWC", encoder.image_dim_order());
 
-  size_t height = 40, width = 30;
-  size_t nheight = 256, nwidth = 256, channel = 3;
+  singa::DecoderConf decoder_conf;
+  decoder_conf.set_image_dim_order("HWC");
+  decoder.Setup(decoder_conf);
+  EXPECT_EQ("HWC", decoder.image_dim_order());
+
+  size_t height = 4, width = 2;
+  size_t nheight = 4, nwidth = 2, channel = 3;
   size_t total = nheight * nwidth * channel;
   cv::Mat image(height, width, CV_8UC3);
   for (size_t i = 0; i < height; i++)
@@ -48,8 +58,8 @@ TEST(Decoder, Decode) {
   cv::Mat transformed;
   cv::Size size(nwidth, nheight);
   cv::resize(image, transformed, size);
-  EXPECT_EQ(static_cast<int>(nwidth), transformed.cols);
-  EXPECT_EQ(static_cast<int>(nheight), transformed.rows);
+  EXPECT_EQ(static_cast<int>(nwidth), transformed.size().width);
+  EXPECT_EQ(static_cast<int>(nheight), transformed.size().height);
   EXPECT_EQ(static_cast<int>(channel), transformed.channels());
 
   unsigned char* buff = transformed.data;
@@ -67,7 +77,7 @@ TEST(Decoder, Decode) {
   const int* in_label = input[1].data<int>();
   EXPECT_EQ(2, in_label[0]);
   EXPECT_EQ(2u, input.size());
-
+ 
   std::string tmp = encoder.Encode(input);
   std::vector<Tensor> output = decoder.Decode(tmp);
   EXPECT_EQ(2u, output.size());
@@ -77,8 +87,14 @@ TEST(Decoder, Decode) {
   const int* out_label = output[1].data<int>();
   EXPECT_EQ(raw_label, out_label[0]);
   // opencv imencode will have some information loss
-  // const float* out_pixel = output[0].data<const float*>();
-  // for(size_t i = 0; i < total; i++)
-  //  EXPECT_LE(fabs(in_pixel[i]-out_pixel[i]), 10.f);
+  /*const float* out_pixel = output[0].data<const float>();
+  cv::Mat out(height, width, CV_8UC3);
+  for (size_t i = 0; i < height; i++)
+    for (size_t j = 0; j < width; j++)
+      for (size_t k = 0; k < channel; k++)
+        out.at<cv::Vec3b>(i, j)[k] = 
+            out_pixel[i * width * channel + j * channel + k];
+  for(size_t i = 0; i < total; i++)
+    EXPECT_LE(fabs(in_pixel[i]-out_pixel[i]), 10.f);*/
 }
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_mse.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_mse.cc b/test/singa/test_mse.cc
index 640caf4..a0ab1a1 100644
--- a/test/singa/test_mse.cc
+++ b/test/singa/test_mse.cc
@@ -87,6 +87,7 @@ TEST_F(TestMSE, CudaForward) {
   }
 	p.ToHost();
 	t.ToHost();
+  delete mse;
 }
 
 TEST_F(TestMSE, CudaBackward) {

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_prelu.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_prelu.cc b/test/singa/test_prelu.cc
index dbf5ca6..dbb7cde 100644
--- a/test/singa/test_prelu.cc
+++ b/test/singa/test_prelu.cc
@@ -77,6 +77,7 @@ TEST(PReLU, ForwardCPU) {
     }
   }
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(y[i], yptr[i]);
+  delete[] y;
 }
 
 TEST(PReLU, BackwardCPU) {
@@ -135,6 +136,7 @@ TEST(PReLU, BackwardCPU) {
   }
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(dx[i], xptr[i]);
   for (size_t i = 0; i < params; i++) EXPECT_FLOAT_EQ(da[i], aptr[i]);
+  delete[] dx;
 }
 
 #ifdef USE_CUDA
@@ -178,6 +180,7 @@ TEST(PReLU, ForwardGPU) {
     }
   }
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(y[i], yptr[i]);
+  delete[] y;
 }
 
 TEST(PReLU, BackwardGPU) {
@@ -241,5 +244,6 @@ TEST(PReLU, BackwardGPU) {
   }
   for (size_t i = 0; i < n; i++) EXPECT_FLOAT_EQ(dx[i], xptr[i]);
   for (size_t i = 0; i < params; i++) EXPECT_FLOAT_EQ(da[i], aptr[i]);
+  delete[] dx;
 }
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/7e050c12/test/singa/test_softmax.cc
----------------------------------------------------------------------
diff --git a/test/singa/test_softmax.cc b/test/singa/test_softmax.cc
index f4a3bd7..00b8378 100644
--- a/test/singa/test_softmax.cc
+++ b/test/singa/test_softmax.cc
@@ -60,6 +60,7 @@ TEST(Softmax, Forward) {
     for (size_t j = 0; j < col; j++) {
       EXPECT_FLOAT_EQ(yptr[i * col + j], exp(x[i * col + j]) / sigma[i]);
     }
+  delete[] sigma;
 }
 
 TEST(Softmax, Backward) {
@@ -96,5 +97,7 @@ TEST(Softmax, Backward) {
   EXPECT_FLOAT_EQ(dx[0], xptr[0]);
   EXPECT_FLOAT_EQ(dx[4], xptr[4]);
   EXPECT_FLOAT_EQ(dx[5], xptr[5]);
+  delete[] dx;
+  delete[] sigma;
 }
 #endif



Mime
View raw message