From 78400d17da78d8a0949077187ef7e65542bf50ff Mon Sep 17 00:00:00 2001 From: nihui Date: Thu, 5 Oct 2017 17:16:27 +0800 Subject: [PATCH] implement ssd Normalize Permute PriorBox, introduce ConcatV2 for interleave --- src/CMakeLists.txt | 4 + src/layer/concatv2.cpp | 298 +++++++++++++++++++++++++++++++++++++ src/layer/concatv2.h | 43 ++++++ src/layer/normalize.cpp | 245 ++++++++++++++++++++++++++++++ src/layer/normalize.h | 51 +++++++ src/layer/permute.cpp | 189 +++++++++++++++++++++++ src/layer/permute.h | 43 ++++++ src/layer/priorbox.cpp | 321 ++++++++++++++++++++++++++++++++++++++++ src/layer/priorbox.h | 58 ++++++++ tools/caffe2ncnn.cpp | 171 ++++++++++++++++++++- 10 files changed, 1422 insertions(+), 1 deletion(-) create mode 100644 src/layer/concatv2.cpp create mode 100644 src/layer/concatv2.h create mode 100644 src/layer/normalize.cpp create mode 100644 src/layer/normalize.h create mode 100644 src/layer/permute.cpp create mode 100644 src/layer/permute.h create mode 100644 src/layer/priorbox.cpp create mode 100644 src/layer/priorbox.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d39d9e521..8c77b24e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -131,6 +131,10 @@ ncnn_add_layer(ConvolutionDepthWise) ncnn_add_layer(Padding) ncnn_add_layer(Squeeze) ncnn_add_layer(ExpandDims) +ncnn_add_layer(Normalize) +ncnn_add_layer(Permute) +ncnn_add_layer(PriorBox) +ncnn_add_layer(ConcatV2) add_library(ncnn STATIC ${ncnn_SRCS}) diff --git a/src/layer/concatv2.cpp b/src/layer/concatv2.cpp new file mode 100644 index 000000000..0b2a6f729 --- /dev/null +++ b/src/layer/concatv2.cpp @@ -0,0 +1,298 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "concatv2.h" + +namespace ncnn { + +DEFINE_LAYER_CREATOR(ConcatV2) + +ConcatV2::ConcatV2() +{ +} + +#if NCNN_STDIO +#if NCNN_STRING +int ConcatV2::load_param(FILE* paramfp) +{ + int nscan = fscanf(paramfp, "%d", &dim); + if (nscan != 1) + { + fprintf(stderr, "ConcatV2 load_param failed %d\n", nscan); + return -1; + } + + return 0; +} +#endif // NCNN_STRING +int ConcatV2::load_param_bin(FILE* paramfp) +{ + fread(&dim, sizeof(int), 1, paramfp); + + return 0; +} +#endif // NCNN_STDIO + +int ConcatV2::load_param(const unsigned char*& mem) +{ + dim = *(int*)(mem); + mem += 4; + + return 0; +} + +int ConcatV2::forward(const std::vector& bottom_blobs, std::vector& top_blobs) const +{ + int dims = bottom_blobs[0].dims; + + if (dims == 1) // dim == 0 + { + // concat vector + // total length + int top_w = 0; + for (size_t b=0; b& bottom_blobs, std::vector& top_blobs) const; + +public: + int dim; +}; + +} // namespace ncnn + +#endif // LAYER_CONCATV2_H diff --git a/src/layer/normalize.cpp b/src/layer/normalize.cpp new file mode 100644 index 000000000..bc5375ca0 --- /dev/null +++ b/src/layer/normalize.cpp @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "normalize.h" +#include + +namespace ncnn { + +DEFINE_LAYER_CREATOR(Normalize) + +Normalize::Normalize() +{ + one_blob_only = true; + support_inplace = false; +} + +#if NCNN_STDIO +#if NCNN_STRING +int Normalize::load_param(FILE* paramfp) +{ + int nscan = fscanf(paramfp, "%d %d %f %d", + &across_spatial, &channel_shared, &eps, &scale_data_size); + if (nscan != 4) + { + fprintf(stderr, "Normalize load_param failed %d\n", nscan); + return -1; + } + + return 0; +} +#endif // NCNN_STRING +int Normalize::load_param_bin(FILE* paramfp) +{ + fread(&across_spatial, sizeof(int), 1, paramfp); + + fread(&channel_shared, sizeof(int), 1, paramfp); + + fread(&eps, sizeof(float), 1, paramfp); + + fread(&scale_data_size, sizeof(int), 1, paramfp); + + return 0; +} + +int Normalize::load_model(FILE* binfp) +{ + int nread; + + scale_data.create(1, scale_data_size); + nread = fread(scale_data, scale_data_size * sizeof(float), 1, binfp); + if (nread != 1) + { + fprintf(stderr, "Normalize read scale_data failed %d\n", nread); + return -1; + } + + return 0; +} +#endif // NCNN_STDIO + +int Normalize::load_param(const unsigned char*& mem) +{ + across_spatial = *(int*)(mem); + mem += 4; + + channel_shared = *(int*)(mem); + mem += 4; + + eps = *(float*)(mem); + mem += 4; + + scale_data_size = *(float*)(mem); + mem += 4; + + return 0; +} + +int Normalize::load_model(const unsigned char*& mem) +{ + scale_data = Mat(1, scale_data_size, (float*)mem); + mem += scale_data_size * sizeof(float); + + return 0; +} + +int Normalize::forward(const Mat& bottom_blob, Mat& top_blob) const +{ + int w = bottom_blob.w; + int h = bottom_blob.h; + int channels = bottom_blob.c; + int size = w * h; + + top_blob.create(w, h, channels); + if (top_blob.empty()) + return -100; + + if (across_spatial) + { + // square + Mat square_sum_blob; + square_sum_blob.create(channels); + if (square_sum_blob.empty()) + return -100; + + float* square_sum_ptr = square_sum_blob; + #pragma omp parallel for + for (int q=0; q + +namespace ncnn { + +DEFINE_LAYER_CREATOR(PriorBox) + +PriorBox::PriorBox() +{ + one_blob_only = false; + support_inplace = false; +} + +#if NCNN_STDIO +#if NCNN_STRING +int PriorBox::load_param(FILE* paramfp) +{ + int nscan = fscanf(paramfp, "%d %d %d %f %f %f %f %d %d %d %d %f %f %f", + &num_min_size, &num_max_size, &num_aspect_ratio, + &variances[0], &variances[1], &variances[2], &variances[3], + &flip, &clip, &image_width, &image_height, + &step_width, &step_height, &offset); + if (nscan != 14) + { + fprintf(stderr, "PriorBox load_param failed %d\n", nscan); + return -1; + } + + min_sizes.create(num_min_size); + if (min_sizes.empty()) + return -100; + float* min_sizes_ptr = min_sizes; + for (int i=0; i +int PriorBox::forward(const std::vector& bottom_blobs, std::vector& top_blobs) const +{ + int w = bottom_blobs[0].w; + int h = bottom_blobs[0].h; + + int image_w = image_width; + int image_h = image_height; + if (image_w == -233) + image_w = bottom_blobs[1].w; + if (image_h == -233) + image_h = bottom_blobs[1].h; + + float step_w = step_width; + float step_h = step_height; + if (step_w == -233) + step_w = (float)image_w / w; + if (step_h == -233) + step_h = (float)image_h / h; + + int num_prior = num_min_size * num_aspect_ratio + num_min_size + num_max_size; + if (flip) + num_prior += num_min_size * num_aspect_ratio; + + Mat& top_blob = top_blobs[0]; + top_blob.create(4 * w * h * num_prior, 2); + + #pragma omp parallel for + for (int i = 0; i < h; i++) + { + float* box = top_blob.data + i * w * num_prior * 4; + + float center_x = offset * step_w; + float center_y = offset * step_h + i * step_h; + + for (int j = 0; j < w; j++) + { + float box_w; + float box_h; + + for (int k = 0; k < num_min_size; k++) + { + float min_size = min_sizes.data[k]; + + // min size box + box_w = box_h = min_size; + + box[0] = (center_x - box_w * 0.5f) / image_w; + box[1] = (center_y - box_h * 0.5f) / image_h; + box[2] = (center_x + box_w * 0.5f) / image_w; + box[3] = (center_y + box_h * 0.5f) / image_h; + + box += 4; + + if (num_max_size > 0) + { + float max_size = max_sizes.data[k]; + + // max size box + box_w = box_h = sqrt(min_size * max_size); + + box[0] = (center_x - box_w * 0.5f) / image_w; + box[1] = (center_y - box_h * 0.5f) / image_h; + box[2] = (center_x + box_w * 0.5f) / image_w; + box[3] = (center_y + box_h * 0.5f) / image_h; + + box += 4; + } + + // all aspect_ratios + for (int p = 0; p < num_aspect_ratio; p++) + { + float ar = aspect_ratios[p]; + + box_w = min_size * sqrt(ar); + box_h = min_size / sqrt(ar); + + box[0] = (center_x - box_w * 0.5f) / image_w; + box[1] = (center_y - box_h * 0.5f) / image_h; + box[2] = (center_x + box_w * 0.5f) / image_w; + box[3] = (center_y + box_h * 0.5f) / image_h; + + box += 4; + + if (flip) + { + box[0] = (center_x - box_h * 0.5f) / image_h; + box[1] = (center_y - box_w * 0.5f) / image_w; + box[2] = (center_x + box_h * 0.5f) / image_h; + box[3] = (center_y + box_w * 0.5f) / image_w; + + box += 4; + } + } + } + + center_x += step_w; + } + + center_y += step_h; + } + + if (clip) + { + float* box = top_blob; + for (int i = 0; i < top_blob.w; i++) + { + box[i] = std::min(std::max(box[i], 0.f), 1.f); + } + } + + // set variance + float* var = top_blob.row(1); + for (int i = 0; i < top_blob.w / 4; i++) + { + var[0] = variances[0]; + var[1] = variances[1]; + var[2] = variances[2]; + var[3] = variances[3]; + + var += 4; + } + + return 0; +} + +} // namespace ncnn diff --git a/src/layer/priorbox.h b/src/layer/priorbox.h new file mode 100644 index 000000000..17e640cde --- /dev/null +++ b/src/layer/priorbox.h @@ -0,0 +1,58 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 LAYER_PRIORBOX_H +#define LAYER_PRIORBOX_H + +#include "layer.h" + +namespace ncnn { + +class PriorBox : public Layer +{ +public: + PriorBox(); + +#if NCNN_STDIO +#if NCNN_STRING + virtual int load_param(FILE* paramfp); +#endif // NCNN_STRING + virtual int load_param_bin(FILE* paramfp); +#endif // NCNN_STDIO + virtual int load_param(const unsigned char*& mem); + + virtual int forward(const std::vector& bottom_blobs, std::vector& top_blobs) const; + +public: + int num_min_size; + int num_max_size; + int num_aspect_ratio; + float variances[4]; + + int flip; + int clip; + int image_width; + int image_height; + float step_width; + float step_height; + float offset; + + Mat min_sizes; + Mat max_sizes; + Mat aspect_ratios; +}; + +} // namespace ncnn + +#endif // LAYER_PRIORBOX_H diff --git a/tools/caffe2ncnn.cpp b/tools/caffe2ncnn.cpp index 9d9e5ccd5..d2aeae2d3 100644 --- a/tools/caffe2ncnn.cpp +++ b/tools/caffe2ncnn.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -319,7 +320,15 @@ int main(int argc, char** argv) // layer definition line, repeated // [type] [name] [bottom blob count] [top blob count] [bottom blobs] [top blobs] [layer specific params] - if (layer.type() == "Convolution") + if (layer.type() == "Concat") + { + const caffe::ConcatParameter& concat_param = layer.concat_param(); + if (concat_param.axis() != 1) + fprintf(pp, "%-16s", "ConcatV2"); + else + fprintf(pp, "%-16s", "Concat"); + } + else if (layer.type() == "Convolution") { const caffe::ConvolutionParameter& convolution_param = layer.convolution_param(); if (convolution_param.group() != 1) @@ -426,6 +435,15 @@ int main(int argc, char** argv) std::vector zeros(mean_blob.data_size(), 0.f); fwrite(zeros.data(), sizeof(float), zeros.size(), bp);// bias } + else if (layer.type() == "Concat") + { + const caffe::ConcatParameter& concat_param = layer.concat_param(); + if (concat_param.axis() != 1) + { + int dim = concat_param.axis() >= 1 ? concat_param.axis() - 1 : 0; + fprintf(pp, " %d", dim); + } + } else if (layer.type() == "Convolution") { const caffe::LayerParameter& binlayer = net.layer(netidx); @@ -641,6 +659,77 @@ int main(int argc, char** argv) const caffe::MemoryDataParameter& memory_data_param = layer.memory_data_param(); fprintf(pp, " %d %d %d", memory_data_param.channels(), memory_data_param.width(), memory_data_param.height()); } + else if (layer.type() == "Normalize") + { + const caffe::LayerParameter& binlayer = net.layer(netidx); + const caffe::BlobProto& scale_blob = binlayer.blobs(0); + const caffe::NormalizeParameter& norm_param = layer.norm_param(); + fprintf(pp, " %d %d %f %d", norm_param.across_spatial(), norm_param.channel_shared(), norm_param.eps(), scale_blob.data_size()); + + fwrite(scale_blob.data().data(), sizeof(float), scale_blob.data_size(), bp); + } + else if (layer.type() == "Permute") + { + const caffe::PermuteParameter& permute_param = layer.permute_param(); + int order_size = permute_param.order_size(); + int order_type = 0; + if (order_size == 0) + order_type = 0; + if (order_size == 1) + { + int order0 = permute_param.order(0); + if (order0 == 0) + order_type = 0; + // permute with N not supported + } + if (order_size == 2) + { + int order0 = permute_param.order(0); + int order1 = permute_param.order(1); + if (order0 == 0) + { + if (order1 == 1) // 0 1 2 3 + order_type = 0; + else if (order1 == 2) // 0 2 1 3 + order_type = 2; + else if (order1 == 3) // 0 3 1 2 + order_type = 4; + } + // permute with N not supported + } + if (order_size == 3 || order_size == 4) + { + int order0 = permute_param.order(0); + int order1 = permute_param.order(1); + int order2 = permute_param.order(2); + if (order0 == 0) + { + if (order1 == 1) + { + if (order2 == 2) // 0 1 2 3 + order_type = 0; + if (order2 == 3) // 0 1 3 2 + order_type = 1; + } + else if (order1 == 2) + { + if (order2 == 1) // 0 2 1 3 + order_type = 2; + if (order2 == 3) // 0 2 3 1 + order_type = 3; + } + else if (order1 == 3) + { + if (order2 == 1) // 0 3 1 2 + order_type = 4; + if (order2 == 2) // 0 3 2 1 + order_type = 5; + } + } + // permute with N not supported + } + fprintf(pp, " %d", order_type); + } else if (layer.type() == "Pooling") { const caffe::PoolingParameter& pooling_param = layer.pooling_param(); @@ -659,6 +748,86 @@ int main(int argc, char** argv) fprintf(pp, " %d", slope_blob.data_size()); fwrite(slope_blob.data().data(), sizeof(float), slope_blob.data_size(), bp); } + else if (layer.type() == "PriorBox") + { + const caffe::PriorBoxParameter& prior_box_param = layer.prior_box_param(); + + int num_aspect_ratio = prior_box_param.aspect_ratio_size(); + for (int j=0; j