parent
2638433849
commit
eba62d5068
8 changed files with 534 additions and 499 deletions
@ -1,391 +0,0 @@ |
||||
#include "precomp.hpp" |
||||
#include "layers.hpp" |
||||
#include <math.h> |
||||
#include <float.h> |
||||
#include <iostream> |
||||
#include <algorithm> |
||||
using std::max; |
||||
using std::min; |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
|
||||
struct ReLUFunctor |
||||
{ |
||||
float negative_slope; |
||||
|
||||
ReLUFunctor(LayerParams ¶ms) |
||||
{ |
||||
if (params.has("negative_slope")) |
||||
negative_slope = params.get<float>("negative_slope"); |
||||
else |
||||
negative_slope = 0.f; |
||||
} |
||||
|
||||
inline float operator()(float x) |
||||
{ |
||||
return (x >= 0) ? x : negative_slope * x; |
||||
} |
||||
}; |
||||
|
||||
struct TanHFunctor |
||||
{ |
||||
TanHFunctor(LayerParams ¶ms) {} |
||||
|
||||
inline float operator()(float x) |
||||
{ |
||||
return tanh(x); |
||||
} |
||||
}; |
||||
|
||||
REGISTER_LAYER_CLASS(ReLU, ElementWiseLayer<ReLUFunctor>) |
||||
REGISTER_LAYER_CLASS(TanH, ElementWiseLayer<TanHFunctor>) |
||||
REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer) |
||||
REGISTER_LAYER_CLASS(Pooling, PoolingLayer) |
||||
REGISTER_LAYER_CLASS(InnerProduct, FullyConnectedLayer) |
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static void getKernelParams(LayerParams ¶ms, int &kernelH, int &kernelW, int &padH, int &padW, int &strideH, int &strideW) |
||||
{ |
||||
if (params.has("kernel_h") && params.has("kernel_w")) |
||||
{ |
||||
kernelH = params.get<int>("kernel_h"); |
||||
kernelW = params.get<int>("kernel_w"); |
||||
} |
||||
else if (params.has("kernel_size")) |
||||
{ |
||||
kernelH = kernelW = params.get<int>("kernel_size"); |
||||
} |
||||
else |
||||
{ |
||||
CV_Error(cv::Error::StsBadArg, "kernel_size (or kernel_h and kernel_w) not specified"); |
||||
} |
||||
|
||||
if (params.has("pad_h") && params.has("pad_w")) |
||||
{ |
||||
padH = params.get<int>("pad_h"); |
||||
padW = params.get<int>("pad_w"); |
||||
} |
||||
else |
||||
{ |
||||
padH = padW = params.get<int>("pad", 0); |
||||
} |
||||
|
||||
if (params.has("stride_h") && params.has("stride_w")) |
||||
{ |
||||
strideH = params.get<int>("stride_h"); |
||||
strideW = params.get<int>("stride_w"); |
||||
} |
||||
else |
||||
{ |
||||
strideH = strideW = params.get<int>("stride", 1); |
||||
} |
||||
|
||||
CV_Assert(kernelH > 0 && kernelW > 0 && padH >= 0 && padW >= 0 && strideH > 0 & strideW > 0); |
||||
} |
||||
|
||||
PoolingLayer::PoolingLayer(LayerParams ¶ms) |
||||
{ |
||||
if (params.has("pool")) |
||||
{ |
||||
String pool = params.get<String>("pool").toLowerCase(); |
||||
if (pool == "max") |
||||
type = MAX; |
||||
else if (pool == "ave") |
||||
type = AVE; |
||||
else if (pool == "stochastic") |
||||
type = STOCHASTIC; |
||||
else |
||||
CV_Error(cv::Error::StsBadArg, "Unknown pooling type \"" + pool + "\""); |
||||
} |
||||
else |
||||
{ |
||||
type = MAX; |
||||
} |
||||
|
||||
getKernelParams(params, kernelH, kernelW, padH, padW, strideH, strideW); |
||||
} |
||||
|
||||
void PoolingLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() > 0); |
||||
|
||||
inH = inputs[0]->cols(); |
||||
inW = inputs[0]->rows(); |
||||
computeOutputShape(inH, inW); |
||||
|
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
CV_Assert(inputs[i]->rows() == inH && inputs[i]->cols() == inW); |
||||
outputs[i].create(inputs[i]->num(), inputs[i]->channels(), pooledH, pooledW); |
||||
} |
||||
} |
||||
|
||||
void PoolingLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
for (size_t ii = 0; ii < inputs.size(); ii++) |
||||
{ |
||||
switch (type) |
||||
{ |
||||
case MAX: |
||||
maxPooling(*inputs[ii], outputs[ii]); |
||||
break; |
||||
default: |
||||
CV_Error(cv::Error::StsNotImplemented, "Not implemented"); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void PoolingLayer::maxPooling(Blob &input, Blob &output) |
||||
{ |
||||
CV_DbgAssert(output.rows() == pooledH && output.cols() == pooledW); |
||||
|
||||
for (int n = 0; n < input.num(); ++n)
|
||||
{ |
||||
for (int c = 0; c < input.channels(); ++c) |
||||
{ |
||||
float *srcData = input.ptr<float>(n, c); |
||||
float *dstData = output.ptr<float>(n, c); |
||||
|
||||
for (int ph = 0; ph < pooledH; ++ph) |
||||
{ |
||||
for (int pw = 0; pw < pooledW; ++pw) |
||||
{ |
||||
int hstart = ph * strideH - padH; |
||||
int wstart = pw * strideW - padW; |
||||
int hend = min(hstart + kernelH, inH); |
||||
int wend = min(wstart + kernelW, inW); |
||||
hstart = max(hstart, 0); |
||||
wstart = max(wstart, 0); |
||||
const int pool_index = ph * pooledW + pw; |
||||
float max_val = -FLT_MAX; |
||||
|
||||
for (int h = hstart; h < hend; ++h) |
||||
for (int w = wstart; w < wend; ++w)
|
||||
{ |
||||
const int index = h * inW + w; |
||||
if (srcData[index] > max_val) |
||||
max_val = srcData[index]; |
||||
} |
||||
|
||||
dstData[pool_index] = max_val; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void PoolingLayer::computeOutputShape(int inH, int inW) |
||||
{ |
||||
//Yeah something strange Caffe scheme-)
|
||||
pooledH = static_cast<int>(ceil(static_cast<float>(inH + 2 * padH - kernelH) / strideH)) + 1; |
||||
pooledW = static_cast<int>(ceil(static_cast<float>(inW + 2 * padW - kernelW) / strideW)) + 1; |
||||
|
||||
if (padH || padW) |
||||
{ |
||||
// If we have padding, ensure that the last pooling starts strictly
|
||||
// inside the image (instead of at the padding); otherwise clip the last.
|
||||
if ((pooledH - 1) * strideH >= inH + padH) |
||||
--pooledH; |
||||
if ((pooledW - 1) * strideW >= inW + padW) |
||||
--pooledW; |
||||
CV_Assert((pooledH - 1) * strideH < inH + padH); |
||||
CV_Assert((pooledW - 1) * strideW < inW + padW); |
||||
} |
||||
} |
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ConvolutionLayer::ConvolutionLayer(LayerParams ¶ms) |
||||
{ |
||||
getKernelParams(params, kernelH, kernelW, padH, padW, strideH, strideW); |
||||
|
||||
numOutput = params.get<int>("num_output"); |
||||
bias = params.get<bool>("bias_term", true); |
||||
group = params.get<int>("group", 1); |
||||
CV_Assert(numOutput % group == 0); |
||||
|
||||
CV_Assert(params.learnedBlobs.size() >= 1 && (!bias || params.learnedBlobs.size() >= 2)); |
||||
learnedParams.assign(params.learnedBlobs.begin(), params.learnedBlobs.begin() + (bias ? 2 : 1)); |
||||
|
||||
Blob &weightBlob = learnedParams[0]; |
||||
CV_Assert(weightBlob.cols() == kernelW && weightBlob.rows() == kernelH && weightBlob.num() == numOutput); |
||||
|
||||
if (bias) |
||||
{ |
||||
Blob &biasBlob = learnedParams[1]; |
||||
CV_Assert(biasBlob.total() == numOutput); |
||||
} |
||||
} |
||||
|
||||
void ConvolutionLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() > 0); |
||||
|
||||
Blob &weightBlob = learnedParams[0]; |
||||
|
||||
inCn = inputs[0]->channels(); |
||||
CV_Assert(inCn % group == 0 && weightBlob.channels() == inCn); |
||||
|
||||
inH = inputs[0]->rows(); |
||||
inW = inputs[0]->cols(); |
||||
computeOutputShape(inH, inW); |
||||
|
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
CV_Assert(inputs[i]->rows() == inH && inputs[i]->cols() == inW && inputs[i]->channels() == inCn); |
||||
int num = inputs[i]->num(); |
||||
|
||||
outputs[i].create(num, numOutput, outH, outW); |
||||
} |
||||
|
||||
colCn = kernelH * kernelW * inCn; |
||||
imColsMat.create(colCn, outH * outW, CV_32F); |
||||
|
||||
if (bias) |
||||
{ |
||||
biasOnesMat = Mat::ones(1, outH * outW, CV_32F); |
||||
} |
||||
} |
||||
|
||||
|
||||
template <typename Dtype> |
||||
void im2col_cpu(const Dtype* data_im, const int channels, |
||||
const int height, const int width, const int kernel_h, const int kernel_w, |
||||
const int pad_h, const int pad_w, |
||||
const int stride_h, const int stride_w, |
||||
Dtype* data_col) |
||||
{ |
||||
int height_col = (height + 2 * pad_h - kernel_h) / stride_h + 1; |
||||
int width_col = (width + 2 * pad_w - kernel_w) / stride_w + 1; |
||||
int channels_col = channels * kernel_h * kernel_w; |
||||
for (int c = 0; c < channels_col; ++c) { |
||||
int w_offset = c % kernel_w; |
||||
int h_offset = (c / kernel_w) % kernel_h; |
||||
int c_im = c / kernel_h / kernel_w; |
||||
for (int h = 0; h < height_col; ++h) { |
||||
for (int w = 0; w < width_col; ++w) { |
||||
int h_pad = h * stride_h - pad_h + h_offset; |
||||
int w_pad = w * stride_w - pad_w + w_offset; |
||||
if (h_pad >= 0 && h_pad < height && w_pad >= 0 && w_pad < width) |
||||
data_col[(c * height_col + h) * width_col + w] = |
||||
data_im[(c_im * height + h_pad) * width + w_pad]; |
||||
else |
||||
data_col[(c * height_col + h) * width_col + w] = 0; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ConvolutionLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() == outputs.size()); |
||||
|
||||
float *colPtr = imColsMat.ptr<float>(); |
||||
float *weigtsPtr = learnedParams[0].ptr<float>(); |
||||
float *biasPtr = (bias) ? learnedParams[1].ptr<float>() : NULL; |
||||
|
||||
CV_Assert(group == 1); |
||||
|
||||
for (size_t ii = 0; ii < outputs.size(); ii++) |
||||
{ |
||||
int num = inputs[ii]->num(); |
||||
|
||||
for (int n = 0; n < num; n++) |
||||
{ |
||||
float *srcImPtr = inputs[ii]->ptr<float>(n); |
||||
float *dstImPtr = outputs[ii].ptr<float>(n); |
||||
|
||||
im2col_cpu(srcImPtr, inCn, inH, inW, kernelH, kernelW, padH, padW, strideH, strideW, colPtr); |
||||
|
||||
Mat weightsMat(numOutput, colCn, CV_32F, weigtsPtr); |
||||
Mat dstIm(numOutput, outH*outW, CV_32F, dstImPtr); |
||||
|
||||
cv::gemm(weightsMat, imColsMat, 1, noArray(), 0, dstIm); |
||||
|
||||
if (bias) |
||||
{ |
||||
Mat biasMat(numOutput, 1, CV_32F, biasPtr); |
||||
cv::gemm(biasMat, biasOnesMat, 1, dstIm, 1, dstIm); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ConvolutionLayer::computeOutputShape(int inH, int inW) |
||||
{ |
||||
outH = (inH + 2 * padH - kernelH) / strideH + 1; |
||||
outW = (inW + 2 * padW - kernelW) / strideW + 1; |
||||
} |
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FullyConnectedLayer::FullyConnectedLayer(LayerParams ¶ms) |
||||
{ |
||||
numOutputs = params.get<int>("num_output"); |
||||
bias = params.get<bool>("bias_term", true); |
||||
|
||||
CV_Assert(params.learnedBlobs.size() >= 1); |
||||
CV_Assert(!bias || (params.learnedBlobs.size() >= 2 && params.learnedBlobs[1].total() == numOutputs)); |
||||
|
||||
learnedParams.resize(bias ? 2 : 1); |
||||
learnedParams[0] = params.learnedBlobs[0]; |
||||
if (bias) |
||||
{ |
||||
learnedParams[1] = params.learnedBlobs[1]; |
||||
} |
||||
} |
||||
|
||||
void FullyConnectedLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() > 0); |
||||
|
||||
inC = inputs[0]->channels(); |
||||
inH = inputs[0]->rows(); |
||||
inW = inputs[0]->cols(); |
||||
inSize = inC * inH * inW; |
||||
|
||||
CV_Assert(inSize * numOutputs == learnedParams[0].total()); |
||||
|
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
if (i != 0) |
||||
CV_Assert(inputs[i]->channels() == inC && inputs[i]->rows() == inH && inputs[i]->cols() == inW); |
||||
|
||||
outputs[i].create(inputs[i]->num(), numOutputs, 1, 1); |
||||
} |
||||
} |
||||
|
||||
void FullyConnectedLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
int M = inputs[i]->num(); |
||||
int N = numOutputs; |
||||
int K = inSize; |
||||
|
||||
Mat srcMat(M, K, CV_32F, inputs[i]->ptr<float>()); |
||||
Mat weights(K, N, CV_32F, learnedParams[0].ptr<float>()); |
||||
Mat dstMat(M, N, CV_32F, outputs[i].ptr<float>()); |
||||
|
||||
cv::gemm(srcMat, weights, 1, noArray(), 0, dstMat); |
||||
|
||||
if (bias) |
||||
{ |
||||
Mat biasOnesMat = Mat::ones(M, 1, CV_32F); |
||||
Mat biasMat(1, N, CV_32F, learnedParams[1].ptr<float>()); |
||||
cv::gemm(biasOnesMat, biasMat, 1, dstMat, 1, dstMat); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
@ -1,108 +0,0 @@ |
||||
#ifndef __OPENCV_DNN_LAYERS_HPP__ |
||||
#define __OPENCV_DNN_LAYERS_HPP__ |
||||
#include <opencv2/dnn.hpp> |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
|
||||
template<typename Func> |
||||
class ElementWiseLayer : public Layer |
||||
{ |
||||
Func func; |
||||
public: |
||||
|
||||
ElementWiseLayer(LayerParams &_params) : func(_params) {} |
||||
|
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
outputs[i] = *inputs[i]; |
||||
} |
||||
|
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() == outputs.size()); |
||||
|
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
CV_Assert(inputs[i]->ptr<float>() == outputs[i].ptr<float>()); |
||||
float *data = outputs[i].ptr<float>(); |
||||
size_t size = outputs[i].total(); |
||||
|
||||
//Vec4i shape = outputs[0].shape();
|
||||
//CV_Assert(pitch[i] == shape[i] * sizeof(float) );
|
||||
|
||||
for (size_t j = 0; j < size; j++) |
||||
data[j] = func(data[j]); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
class PoolingLayer : public Layer |
||||
{ |
||||
enum
|
||||
{ |
||||
MAX, |
||||
AVE, |
||||
STOCHASTIC |
||||
}; |
||||
|
||||
int type; |
||||
int padH, padW; |
||||
int strideH, strideW; |
||||
int kernelH, kernelW; |
||||
|
||||
int inH, inW; |
||||
int pooledH, pooledW; |
||||
|
||||
void computeOutputShape(int inH, int inW); |
||||
void maxPooling(Blob &input, Blob &output); |
||||
|
||||
public: |
||||
PoolingLayer(LayerParams ¶ms); |
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
}; |
||||
|
||||
class ConvolutionLayer : public Layer |
||||
{ |
||||
bool bias; |
||||
int numOutput, group; |
||||
int padH, padW; |
||||
int strideH, strideW; |
||||
int kernelH, kernelW; |
||||
|
||||
int inH, inW, inCn, colCn; |
||||
int outH, outW; |
||||
|
||||
Mat imColsMat, biasOnesMat; |
||||
|
||||
void computeOutputShape(int inH, int inW); |
||||
|
||||
public: |
||||
ConvolutionLayer(LayerParams ¶ms); |
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
}; |
||||
|
||||
class FullyConnectedLayer : public Layer |
||||
{ |
||||
bool bias; |
||||
int numOutputs; |
||||
|
||||
int inC, inH, inW; |
||||
size_t inSize; |
||||
|
||||
public: |
||||
FullyConnectedLayer(LayerParams ¶ms); |
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
}; |
||||
} |
||||
} |
||||
|
||||
|
||||
#endif |
@ -0,0 +1,157 @@ |
||||
#include "../precomp.hpp" |
||||
#include "layers_common.hpp" |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
//TODO: implement group parameter
|
||||
//TODO: simultaneously convolution and bias addition for cache optimization
|
||||
class ConvolutionLayer : public Layer |
||||
{ |
||||
bool bias; |
||||
int numOutput, group; |
||||
int padH, padW; |
||||
int strideH, strideW; |
||||
int kernelH, kernelW; |
||||
|
||||
int inH, inW, inCn, colCn; |
||||
int outH, outW; |
||||
|
||||
Mat imColsMat, biasOnesMat; |
||||
|
||||
void computeOutputShape(int inH, int inW); |
||||
|
||||
public: |
||||
ConvolutionLayer(LayerParams ¶ms); |
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
}; |
||||
|
||||
|
||||
REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer) |
||||
|
||||
|
||||
ConvolutionLayer::ConvolutionLayer(LayerParams ¶ms) |
||||
{ |
||||
getKernelParams(params, kernelH, kernelW, padH, padW, strideH, strideW); |
||||
|
||||
numOutput = params.get<int>("num_output"); |
||||
bias = params.get<bool>("bias_term", true); |
||||
group = params.get<int>("group", 1); |
||||
CV_Assert(numOutput % group == 0); |
||||
|
||||
CV_Assert(params.learnedBlobs.size() >= 1 && (!bias || params.learnedBlobs.size() >= 2)); |
||||
learnedParams.assign(params.learnedBlobs.begin(), params.learnedBlobs.begin() + (bias ? 2 : 1)); |
||||
|
||||
Blob &weightBlob = learnedParams[0]; |
||||
CV_Assert(weightBlob.cols() == kernelW && weightBlob.rows() == kernelH && weightBlob.num() == numOutput); |
||||
|
||||
if (bias) |
||||
{ |
||||
Blob &biasBlob = learnedParams[1]; |
||||
CV_Assert(biasBlob.total() == numOutput); |
||||
} |
||||
} |
||||
|
||||
void ConvolutionLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() > 0); |
||||
|
||||
Blob &weightBlob = learnedParams[0]; |
||||
|
||||
inCn = inputs[0]->channels(); |
||||
CV_Assert(inCn % group == 0 && weightBlob.channels() == inCn); |
||||
|
||||
inH = inputs[0]->rows(); |
||||
inW = inputs[0]->cols(); |
||||
computeOutputShape(inH, inW); |
||||
|
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
CV_Assert(inputs[i]->rows() == inH && inputs[i]->cols() == inW && inputs[i]->channels() == inCn); |
||||
int num = inputs[i]->num(); |
||||
|
||||
outputs[i].create(num, numOutput, outH, outW); |
||||
} |
||||
|
||||
colCn = kernelH * kernelW * inCn; |
||||
imColsMat.create(colCn, outH * outW, CV_32F); |
||||
|
||||
if (bias) |
||||
{ |
||||
biasOnesMat = Mat::ones(1, outH * outW, CV_32F); |
||||
} |
||||
} |
||||
|
||||
template <typename Dtype> |
||||
void im2col_cpu(const Dtype* data_im, const int channels, |
||||
const int height, const int width, const int kernel_h, const int kernel_w, |
||||
const int pad_h, const int pad_w, |
||||
const int stride_h, const int stride_w, |
||||
Dtype* data_col) |
||||
{ |
||||
int height_col = (height + 2 * pad_h - kernel_h) / stride_h + 1; |
||||
int width_col = (width + 2 * pad_w - kernel_w) / stride_w + 1; |
||||
int channels_col = channels * kernel_h * kernel_w; |
||||
for (int c = 0; c < channels_col; ++c) { |
||||
int w_offset = c % kernel_w; |
||||
int h_offset = (c / kernel_w) % kernel_h; |
||||
int c_im = c / kernel_h / kernel_w; |
||||
for (int h = 0; h < height_col; ++h) { |
||||
for (int w = 0; w < width_col; ++w) { |
||||
int h_pad = h * stride_h - pad_h + h_offset; |
||||
int w_pad = w * stride_w - pad_w + w_offset; |
||||
if (h_pad >= 0 && h_pad < height && w_pad >= 0 && w_pad < width) |
||||
data_col[(c * height_col + h) * width_col + w] = |
||||
data_im[(c_im * height + h_pad) * width + w_pad]; |
||||
else |
||||
data_col[(c * height_col + h) * width_col + w] = 0; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ConvolutionLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() == outputs.size()); |
||||
|
||||
float *colPtr = imColsMat.ptr<float>(); |
||||
float *weigtsPtr = learnedParams[0].ptr<float>(); |
||||
float *biasPtr = (bias) ? learnedParams[1].ptr<float>() : NULL; |
||||
|
||||
CV_Assert(group == 1); |
||||
|
||||
for (size_t ii = 0; ii < outputs.size(); ii++) |
||||
{ |
||||
int num = inputs[ii]->num(); |
||||
|
||||
for (int n = 0; n < num; n++) |
||||
{ |
||||
float *srcImPtr = inputs[ii]->ptr<float>(n); |
||||
float *dstImPtr = outputs[ii].ptr<float>(n); |
||||
|
||||
im2col_cpu(srcImPtr, inCn, inH, inW, kernelH, kernelW, padH, padW, strideH, strideW, colPtr); |
||||
|
||||
Mat weightsMat(numOutput, colCn, CV_32F, weigtsPtr); |
||||
Mat dstIm(numOutput, outH*outW, CV_32F, dstImPtr); |
||||
|
||||
cv::gemm(weightsMat, imColsMat, 1, noArray(), 0, dstIm); |
||||
|
||||
if (bias) |
||||
{ |
||||
Mat biasMat(numOutput, 1, CV_32F, biasPtr); |
||||
cv::gemm(biasMat, biasOnesMat, 1, dstIm, 1, dstIm); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ConvolutionLayer::computeOutputShape(int inH, int inW) |
||||
{ |
||||
outH = (inH + 2 * padH - kernelH) / strideH + 1; |
||||
outW = (inW + 2 * padW - kernelW) / strideW + 1; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,74 @@ |
||||
#include "../precomp.hpp" |
||||
#include "layers_common.hpp" |
||||
#include <math.h> |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
|
||||
template<typename Func> |
||||
class ElementWiseLayer : public Layer |
||||
{ |
||||
Func func; |
||||
public: |
||||
|
||||
ElementWiseLayer(LayerParams &_params) : func(_params) {} |
||||
|
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
outputs[i] = *inputs[i]; //no data copy
|
||||
} |
||||
|
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() == outputs.size()); |
||||
|
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
CV_Assert(inputs[i]->ptr<float>() == outputs[i].ptr<float>()); |
||||
float *data = outputs[i].ptr<float>(); |
||||
size_t size = outputs[i].total(); |
||||
|
||||
for (size_t j = 0; j < size; j++) |
||||
data[j] = func(data[j]); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
|
||||
struct ReLUFunctor |
||||
{ |
||||
float negative_slope; |
||||
|
||||
ReLUFunctor(LayerParams ¶ms) |
||||
{ |
||||
if (params.has("negative_slope")) |
||||
negative_slope = params.get<float>("negative_slope"); |
||||
else |
||||
negative_slope = 0.f; |
||||
} |
||||
|
||||
inline float operator()(float x) |
||||
{ |
||||
return (x >= 0) ? x : negative_slope * x; |
||||
} |
||||
}; |
||||
|
||||
struct TanHFunctor |
||||
{ |
||||
TanHFunctor(LayerParams ¶ms) {} |
||||
|
||||
inline float operator()(float x) |
||||
{ |
||||
return tanh(x); |
||||
} |
||||
}; |
||||
|
||||
REGISTER_LAYER_CLASS(ReLU, ElementWiseLayer<ReLUFunctor>) |
||||
REGISTER_LAYER_CLASS(TanH, ElementWiseLayer<TanHFunctor>) |
||||
|
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
#include "../precomp.hpp" |
||||
#include "layers_common.hpp" |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
//TODO: implement axis number parameter
|
||||
class FullyConnectedLayer : public Layer |
||||
{ |
||||
bool bias; |
||||
int numOutputs; |
||||
|
||||
int inC, inH, inW; |
||||
size_t inSize; |
||||
|
||||
public: |
||||
FullyConnectedLayer(LayerParams ¶ms); |
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
}; |
||||
|
||||
|
||||
REGISTER_LAYER_CLASS(InnerProduct, FullyConnectedLayer) |
||||
|
||||
|
||||
FullyConnectedLayer::FullyConnectedLayer(LayerParams ¶ms) |
||||
{ |
||||
numOutputs = params.get<int>("num_output"); |
||||
bias = params.get<bool>("bias_term", true); |
||||
|
||||
CV_Assert(params.learnedBlobs.size() >= 1); |
||||
CV_Assert(!bias || (params.learnedBlobs.size() >= 2 && params.learnedBlobs[1].total() == numOutputs)); |
||||
|
||||
learnedParams.resize(bias ? 2 : 1); |
||||
learnedParams[0] = params.learnedBlobs[0]; |
||||
if (bias) |
||||
{ |
||||
learnedParams[1] = params.learnedBlobs[1]; |
||||
} |
||||
} |
||||
|
||||
void FullyConnectedLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() > 0); |
||||
|
||||
inC = inputs[0]->channels(); |
||||
inH = inputs[0]->rows(); |
||||
inW = inputs[0]->cols(); |
||||
inSize = inC * inH * inW; |
||||
|
||||
CV_Assert(inSize * numOutputs == learnedParams[0].total()); |
||||
|
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
if (i != 0) |
||||
CV_Assert(inputs[i]->channels() == inC && inputs[i]->rows() == inH && inputs[i]->cols() == inW); |
||||
|
||||
outputs[i].create(inputs[i]->num(), numOutputs, 1, 1); |
||||
} |
||||
} |
||||
|
||||
void FullyConnectedLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
int M = inputs[i]->num(); |
||||
int N = numOutputs; |
||||
int K = inSize; |
||||
|
||||
Mat srcMat(M, K, CV_32F, inputs[i]->ptr<float>()); |
||||
Mat weights(K, N, CV_32F, learnedParams[0].ptr<float>()); |
||||
Mat dstMat(M, N, CV_32F, outputs[i].ptr<float>()); |
||||
|
||||
cv::gemm(srcMat, weights, 1, noArray(), 0, dstMat); |
||||
|
||||
if (bias) |
||||
{ |
||||
Mat biasOnesMat = Mat::ones(M, 1, CV_32F); |
||||
Mat biasMat(1, N, CV_32F, learnedParams[1].ptr<float>()); |
||||
cv::gemm(biasOnesMat, biasMat, 1, dstMat, 1, dstMat); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
#include "layers_common.hpp" |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
|
||||
void getKernelParams(LayerParams ¶ms, int &kernelH, int &kernelW, int &padH, int &padW, int &strideH, int &strideW) |
||||
{ |
||||
if (params.has("kernel_h") && params.has("kernel_w")) |
||||
{ |
||||
kernelH = params.get<int>("kernel_h"); |
||||
kernelW = params.get<int>("kernel_w"); |
||||
} |
||||
else if (params.has("kernel_size")) |
||||
{ |
||||
kernelH = kernelW = params.get<int>("kernel_size"); |
||||
} |
||||
else |
||||
{ |
||||
CV_Error(cv::Error::StsBadArg, "kernel_size (or kernel_h and kernel_w) not specified"); |
||||
} |
||||
|
||||
if (params.has("pad_h") && params.has("pad_w")) |
||||
{ |
||||
padH = params.get<int>("pad_h"); |
||||
padW = params.get<int>("pad_w"); |
||||
} |
||||
else |
||||
{ |
||||
padH = padW = params.get<int>("pad", 0); |
||||
} |
||||
|
||||
if (params.has("stride_h") && params.has("stride_w")) |
||||
{ |
||||
strideH = params.get<int>("stride_h"); |
||||
strideW = params.get<int>("stride_w"); |
||||
} |
||||
else |
||||
{ |
||||
strideH = strideW = params.get<int>("stride", 1); |
||||
} |
||||
|
||||
CV_Assert(kernelH > 0 && kernelW > 0 && padH >= 0 && padW >= 0 && strideH > 0 & strideW > 0); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
#ifndef __OPENCV_DNN_LAYERS_LAYERS_COMMON_HPP__ |
||||
#define __OPENCV_DNN_LAYERS_LAYERS_COMMON_HPP__ |
||||
#include <opencv2/dnn.hpp> |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
|
||||
void getKernelParams(LayerParams ¶ms, int &kernelH, int &kernelW, int &padH, int &padW, int &strideH, int &strideW); |
||||
|
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,153 @@ |
||||
#include "../precomp.hpp" |
||||
#include "layers_common.hpp" |
||||
#include <float.h> |
||||
#include <algorithm> |
||||
using std::max; |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace dnn |
||||
{ |
||||
class PoolingLayer : public Layer |
||||
{ |
||||
enum
|
||||
{ |
||||
MAX, |
||||
AVE, |
||||
STOCHASTIC |
||||
}; |
||||
|
||||
int type; |
||||
int padH, padW; |
||||
int strideH, strideW; |
||||
int kernelH, kernelW; |
||||
|
||||
int inH, inW; |
||||
int pooledH, pooledW; |
||||
|
||||
void computeOutputShape(int inH, int inW); |
||||
void maxPooling(Blob &input, Blob &output); |
||||
|
||||
public: |
||||
PoolingLayer(LayerParams ¶ms); |
||||
void allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs); |
||||
}; |
||||
|
||||
|
||||
REGISTER_LAYER_CLASS(Pooling, PoolingLayer) |
||||
|
||||
|
||||
PoolingLayer::PoolingLayer(LayerParams ¶ms) |
||||
{ |
||||
if (params.has("pool")) |
||||
{ |
||||
String pool = params.get<String>("pool").toLowerCase(); |
||||
if (pool == "max") |
||||
type = MAX; |
||||
else if (pool == "ave") |
||||
type = AVE; |
||||
else if (pool == "stochastic") |
||||
type = STOCHASTIC; |
||||
else |
||||
CV_Error(cv::Error::StsBadArg, "Unknown pooling type \"" + pool + "\""); |
||||
} |
||||
else |
||||
{ |
||||
type = MAX; |
||||
} |
||||
|
||||
getKernelParams(params, kernelH, kernelW, padH, padW, strideH, strideW); |
||||
} |
||||
|
||||
void PoolingLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
CV_Assert(inputs.size() > 0); |
||||
|
||||
inH = inputs[0]->cols(); |
||||
inW = inputs[0]->rows(); |
||||
computeOutputShape(inH, inW); |
||||
|
||||
outputs.resize(inputs.size()); |
||||
for (size_t i = 0; i < inputs.size(); i++) |
||||
{ |
||||
CV_Assert(inputs[i]->rows() == inH && inputs[i]->cols() == inW); |
||||
outputs[i].create(inputs[i]->num(), inputs[i]->channels(), pooledH, pooledW); |
||||
} |
||||
} |
||||
|
||||
void PoolingLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) |
||||
{ |
||||
for (size_t ii = 0; ii < inputs.size(); ii++) |
||||
{ |
||||
switch (type) |
||||
{ |
||||
case MAX: |
||||
maxPooling(*inputs[ii], outputs[ii]); |
||||
break; |
||||
default: |
||||
CV_Error(cv::Error::StsNotImplemented, "Not implemented"); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void PoolingLayer::maxPooling(Blob &input, Blob &output) |
||||
{ |
||||
CV_DbgAssert(output.rows() == pooledH && output.cols() == pooledW); |
||||
|
||||
for (int n = 0; n < input.num(); ++n) |
||||
{ |
||||
for (int c = 0; c < input.channels(); ++c) |
||||
{ |
||||
float *srcData = input.ptr<float>(n, c); |
||||
float *dstData = output.ptr<float>(n, c); |
||||
|
||||
for (int ph = 0; ph < pooledH; ++ph) |
||||
{ |
||||
for (int pw = 0; pw < pooledW; ++pw) |
||||
{ |
||||
int hstart = ph * strideH - padH; |
||||
int wstart = pw * strideW - padW; |
||||
int hend = min(hstart + kernelH, inH); |
||||
int wend = min(wstart + kernelW, inW); |
||||
hstart = max(hstart, 0); |
||||
wstart = max(wstart, 0); |
||||
const int pool_index = ph * pooledW + pw; |
||||
float max_val = -FLT_MAX; |
||||
|
||||
for (int h = hstart; h < hend; ++h) |
||||
for (int w = wstart; w < wend; ++w) |
||||
{ |
||||
const int index = h * inW + w; |
||||
if (srcData[index] > max_val) |
||||
max_val = srcData[index]; |
||||
} |
||||
|
||||
dstData[pool_index] = max_val; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void PoolingLayer::computeOutputShape(int inH, int inW) |
||||
{ |
||||
//Yeah, something strange Caffe scheme-)
|
||||
pooledH = static_cast<int>(ceil(static_cast<float>(inH + 2 * padH - kernelH) / strideH)) + 1; |
||||
pooledW = static_cast<int>(ceil(static_cast<float>(inW + 2 * padW - kernelW) / strideW)) + 1; |
||||
|
||||
if (padH || padW) |
||||
{ |
||||
// If we have padding, ensure that the last pooling starts strictly
|
||||
// inside the image (instead of at the padding); otherwise clip the last.
|
||||
if ((pooledH - 1) * strideH >= inH + padH) |
||||
--pooledH; |
||||
if ((pooledW - 1) * strideW >= inW + padW) |
||||
--pooledW; |
||||
CV_Assert((pooledH - 1) * strideH < inH + padH); |
||||
CV_Assert((pooledW - 1) * strideW < inW + padW); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue