| @@ -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}) | |||
| @@ -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<Mat>& bottom_blobs, std::vector<Mat>& 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.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| top_w += bottom_blob.w; | |||
| } | |||
| Mat& top_blob = top_blobs[0]; | |||
| top_blob.create(top_w); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| float* outptr = top_blob; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| int w = bottom_blob.w; | |||
| const float* ptr = bottom_blob; | |||
| for (int i=0; i<w; i++) | |||
| { | |||
| outptr[i] = ptr[i]; | |||
| } | |||
| outptr += w; | |||
| } | |||
| return 0; | |||
| } | |||
| if (dims == 2 && dim == 0) | |||
| { | |||
| // concat image | |||
| int w = bottom_blobs[0].w; | |||
| // total height | |||
| int top_h = 0; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| top_h += bottom_blob.h; | |||
| } | |||
| Mat& top_blob = top_blobs[0]; | |||
| top_blob.create(w, top_h); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| float* outptr = top_blob; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| int size = w * bottom_blob.h; | |||
| const float* ptr = bottom_blob; | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i]; | |||
| } | |||
| outptr += size; | |||
| } | |||
| return 0; | |||
| } | |||
| if (dims == 2 && dim == 1) | |||
| { | |||
| // interleave image row | |||
| int h = bottom_blobs[0].h; | |||
| // total width | |||
| int top_w = 0; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| top_w += bottom_blob.w; | |||
| } | |||
| Mat& top_blob = top_blobs[0]; | |||
| top_blob.create(top_w, h); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int i=0; i<h; i++) | |||
| { | |||
| float* outptr = top_blob.row(i); | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| const float* ptr = bottom_blob.row(i); | |||
| for (int j=0; j<bottom_blob.w; j++) | |||
| { | |||
| outptr[j] = ptr[j]; | |||
| } | |||
| outptr += bottom_blob.w; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| if (dims == 3 && dim == 0) | |||
| { | |||
| // concat dim | |||
| int w = bottom_blobs[0].w; | |||
| int h = bottom_blobs[0].h; | |||
| // total channels | |||
| int top_channels = 0; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| top_channels += bottom_blob.c; | |||
| } | |||
| Mat& top_blob = top_blobs[0]; | |||
| top_blob.create(w, h, top_channels); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| int q = 0; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| int channels = bottom_blob.c; | |||
| int size = bottom_blob.cstep * channels; | |||
| const float* ptr = bottom_blob; | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i]; | |||
| } | |||
| q += channels; | |||
| } | |||
| return 0; | |||
| } | |||
| if (dims == 3 && dim == 1) | |||
| { | |||
| // interleave dim height | |||
| int w = bottom_blobs[0].w; | |||
| int channels = bottom_blobs[0].c; | |||
| // total height | |||
| int top_h = 0; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| top_h += bottom_blob.h; | |||
| } | |||
| Mat& top_blob = top_blobs[0]; | |||
| top_blob.create(w, top_h, channels); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| float* outptr = top_blob.channel(q); | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| int size = bottom_blob.w * bottom_blob.h; | |||
| const float* ptr = bottom_blob.channel(q); | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i]; | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| if (dims == 3 && dim == 2) | |||
| { | |||
| // interleave dim width | |||
| int h = bottom_blobs[0].h; | |||
| int channels = bottom_blobs[0].c; | |||
| // total height | |||
| int top_w = 0; | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| top_w += bottom_blob.w; | |||
| } | |||
| Mat& top_blob = top_blobs[0]; | |||
| top_blob.create(top_w, h, channels); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i=0; i<h; i++) | |||
| { | |||
| for (size_t b=0; b<bottom_blobs.size(); b++) | |||
| { | |||
| const Mat& bottom_blob = bottom_blobs[b]; | |||
| const float* ptr = bottom_blob.channel(q).row(i); | |||
| for (int j=0; j<bottom_blob.w; j++) | |||
| { | |||
| outptr[j] = ptr[j]; | |||
| } | |||
| outptr += bottom_blob.w; | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| return 0; | |||
| } | |||
| } // namespace ncnn | |||
| @@ -0,0 +1,43 @@ | |||
| // 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_CONCATV2_H | |||
| #define LAYER_CONCATV2_H | |||
| #include "layer.h" | |||
| namespace ncnn { | |||
| class ConcatV2 : public Layer | |||
| { | |||
| public: | |||
| ConcatV2(); | |||
| #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<Mat>& bottom_blobs, std::vector<Mat>& top_blobs) const; | |||
| public: | |||
| int dim; | |||
| }; | |||
| } // namespace ncnn | |||
| #endif // LAYER_CONCATV2_H | |||
| @@ -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 <math.h> | |||
| 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<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| float ssum = 0.f; | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| ssum += ptr[i] * ptr[i]; | |||
| } | |||
| square_sum_ptr[q] = ssum; | |||
| } | |||
| // sum + eps | |||
| float ssum = eps; | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| ssum += square_sum_ptr[q]; | |||
| } | |||
| // 1 / sqrt(ssum) | |||
| float a = 1.f / sqrt(ssum); | |||
| if (channel_shared) | |||
| { | |||
| float scale = a * scale_data.data[0]; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i] * scale; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| float* outptr = top_blob.channel(q); | |||
| float scale = a * scale_data.data[q]; | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i] * scale; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // square sum, 1 / sqrt(ssum) | |||
| Mat square_sum_blob; | |||
| square_sum_blob.create(w, h); | |||
| if (square_sum_blob.empty()) | |||
| return -100; | |||
| float* ssptr = square_sum_blob; | |||
| if (channel_shared) | |||
| { | |||
| float scale = scale_data.data[0]; | |||
| #pragma omp parallel for | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| float ssum = eps; | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| ssum += ptr[i] * ptr[i]; | |||
| } | |||
| ssptr[i] = 1.f / sqrt(ssum) * scale; | |||
| } | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i] * ssptr[i]; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| #pragma omp parallel for | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| float ssum = eps; | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| ssum += ptr[i] * ptr[i]; | |||
| } | |||
| ssptr[i] = 1.f / sqrt(ssum); | |||
| } | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| float* outptr = top_blob.channel(q); | |||
| float scale = scale_data.data[q]; | |||
| for (int i=0; i<size; i++) | |||
| { | |||
| outptr[i] = ptr[i] * ssptr[i] * scale; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| } // namespace ncnn | |||
| @@ -0,0 +1,51 @@ | |||
| // 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_NORMALIZE_H | |||
| #define LAYER_NORMALIZE_H | |||
| #include "layer.h" | |||
| namespace ncnn { | |||
| class Normalize : public Layer | |||
| { | |||
| public: | |||
| Normalize(); | |||
| #if NCNN_STDIO | |||
| #if NCNN_STRING | |||
| virtual int load_param(FILE* paramfp); | |||
| #endif // NCNN_STRING | |||
| virtual int load_param_bin(FILE* paramfp); | |||
| virtual int load_model(FILE* binfp); | |||
| #endif // NCNN_STDIO | |||
| virtual int load_param(const unsigned char*& mem); | |||
| virtual int load_model(const unsigned char*& mem); | |||
| virtual int forward(const Mat& bottom_blob, Mat& top_blob) const; | |||
| public: | |||
| // param | |||
| int across_spatial; | |||
| int channel_shared; | |||
| float eps; | |||
| int scale_data_size; | |||
| Mat scale_data; | |||
| }; | |||
| } // namespace ncnn | |||
| #endif // LAYER_NORMALIZE_H | |||
| @@ -0,0 +1,189 @@ | |||
| // 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 "permute.h" | |||
| namespace ncnn { | |||
| DEFINE_LAYER_CREATOR(Permute) | |||
| Permute::Permute() | |||
| { | |||
| one_blob_only = true; | |||
| support_inplace = false; | |||
| } | |||
| #if NCNN_STDIO | |||
| #if NCNN_STRING | |||
| int Permute::load_param(FILE* paramfp) | |||
| { | |||
| int nscan = fscanf(paramfp, "%d", &order_type); | |||
| if (nscan != 1) | |||
| { | |||
| fprintf(stderr, "Permute load_param failed %d\n", nscan); | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| #endif // NCNN_STRING | |||
| int Permute::load_param_bin(FILE* paramfp) | |||
| { | |||
| fread(&order_type, sizeof(int), 1, paramfp); | |||
| return 0; | |||
| } | |||
| #endif // NCNN_STDIO | |||
| int Permute::load_param(const unsigned char*& mem) | |||
| { | |||
| order_type = *(int*)(mem); | |||
| mem += 4; | |||
| return 0; | |||
| } | |||
| int Permute::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; | |||
| // order_type | |||
| // 0 = w h c | |||
| // 1 = h w c | |||
| // 2 = w c h | |||
| // 3 = c w h | |||
| // 4 = h c w | |||
| // 5 = c h w | |||
| if (order_type == 0) | |||
| { | |||
| top_blob = bottom_blob; | |||
| } | |||
| else if (order_type == 1) | |||
| { | |||
| top_blob.create(h, w, channels); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<channels; q++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(q); | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i = 0; i < w; i++) | |||
| { | |||
| for (int j = 0; j < h; j++) | |||
| { | |||
| outptr[i*h + j] = ptr[j*w + i]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else if (order_type == 2) | |||
| { | |||
| top_blob.create(w, channels, h); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<h; q++) | |||
| { | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i = 0; i < channels; i++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(i).row(q); | |||
| for (int j = 0; j < w; j++) | |||
| { | |||
| outptr[i*w + j] = ptr[j]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else if (order_type == 3) | |||
| { | |||
| top_blob.create(channels, w, h); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<h; q++) | |||
| { | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i = 0; i < w; i++) | |||
| { | |||
| for (int j = 0; j < channels; j++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(j).row(q); | |||
| outptr[i*channels + j] = ptr[i]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else if (order_type == 4) | |||
| { | |||
| top_blob.create(h, channels, w); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<w; q++) | |||
| { | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i = 0; i < channels; i++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(i); | |||
| for (int j = 0; j < h; j++) | |||
| { | |||
| outptr[i*channels + j] = ptr[j*w + q]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else if (order_type == 5) | |||
| { | |||
| top_blob.create(channels, h, w); | |||
| if (top_blob.empty()) | |||
| return -100; | |||
| #pragma omp parallel for | |||
| for (int q=0; q<w; q++) | |||
| { | |||
| float* outptr = top_blob.channel(q); | |||
| for (int i = 0; i < h; i++) | |||
| { | |||
| for (int j = 0; j < channels; j++) | |||
| { | |||
| const float* ptr = bottom_blob.channel(j); | |||
| outptr[i*channels + j] = ptr[i*w + q]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| } // namespace ncnn | |||
| @@ -0,0 +1,43 @@ | |||
| // 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_PERMUTE_H | |||
| #define LAYER_PERMUTE_H | |||
| #include "layer.h" | |||
| namespace ncnn { | |||
| class Permute : public Layer | |||
| { | |||
| public: | |||
| Permute(); | |||
| #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 Mat& bottom_blob, Mat& top_blob) const; | |||
| public: | |||
| int order_type; | |||
| }; | |||
| } // namespace ncnn | |||
| #endif // LAYER_PERMUTE_H | |||
| @@ -0,0 +1,321 @@ | |||
| // 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 "priorbox.h" | |||
| #include <math.h> | |||
| 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<num_min_size; i++) | |||
| { | |||
| int nscan = fscanf(paramfp, "%f", &min_sizes_ptr[i]); | |||
| if (nscan != 1) | |||
| { | |||
| fprintf(stderr, "PriorBox load_param failed %d\n", nscan); | |||
| return -1; | |||
| } | |||
| } | |||
| max_sizes.create(num_max_size); | |||
| if (max_sizes.empty()) | |||
| return -100; | |||
| float* max_sizes_ptr = max_sizes; | |||
| for (int i=0; i<num_max_size; i++) | |||
| { | |||
| int nscan = fscanf(paramfp, "%f", &max_sizes_ptr[i]); | |||
| if (nscan != 1) | |||
| { | |||
| fprintf(stderr, "PriorBox load_param failed %d\n", nscan); | |||
| return -1; | |||
| } | |||
| } | |||
| aspect_ratios.create(num_aspect_ratio); | |||
| if (aspect_ratios.empty()) | |||
| return -100; | |||
| float* aspect_ratios_ptr = aspect_ratios; | |||
| for (int i=0; i<num_aspect_ratio; i++) | |||
| { | |||
| int nscan = fscanf(paramfp, "%f", &aspect_ratios_ptr[i]); | |||
| if (nscan != 1) | |||
| { | |||
| fprintf(stderr, "PriorBox load_param failed %d\n", nscan); | |||
| return -1; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| #endif // NCNN_STRING | |||
| int PriorBox::load_param_bin(FILE* paramfp) | |||
| { | |||
| fread(&num_min_size, sizeof(int), 1, paramfp); | |||
| fread(&num_max_size, sizeof(int), 1, paramfp); | |||
| fread(&num_aspect_ratio, sizeof(int), 1, paramfp); | |||
| fread(&variances[0], sizeof(float), 1, paramfp); | |||
| fread(&variances[1], sizeof(float), 1, paramfp); | |||
| fread(&variances[2], sizeof(float), 1, paramfp); | |||
| fread(&variances[3], sizeof(float), 1, paramfp); | |||
| fread(&flip, sizeof(int), 1, paramfp); | |||
| fread(&clip, sizeof(int), 1, paramfp); | |||
| fread(&image_width, sizeof(int), 1, paramfp); | |||
| fread(&image_height, sizeof(int), 1, paramfp); | |||
| fread(&step_width, sizeof(float), 1, paramfp); | |||
| fread(&step_height, sizeof(float), 1, paramfp); | |||
| fread(&offset, sizeof(float), 1, paramfp); | |||
| min_sizes.create(num_min_size); | |||
| if (min_sizes.empty()) | |||
| return -100; | |||
| float* min_sizes_ptr = min_sizes; | |||
| fread(min_sizes_ptr, sizeof(float), num_min_size, paramfp); | |||
| max_sizes.create(num_max_size); | |||
| if (max_sizes.empty()) | |||
| return -100; | |||
| float* max_sizes_ptr = max_sizes; | |||
| fread(max_sizes_ptr, sizeof(float), num_max_size, paramfp); | |||
| aspect_ratios.create(num_aspect_ratio); | |||
| if (aspect_ratios.empty()) | |||
| return -100; | |||
| float* aspect_ratios_ptr = aspect_ratios; | |||
| fread(aspect_ratios_ptr, sizeof(float), num_aspect_ratio, paramfp); | |||
| return 0; | |||
| } | |||
| #endif // NCNN_STDIO | |||
| int PriorBox::load_param(const unsigned char*& mem) | |||
| { | |||
| num_min_size = *(int*)(mem); | |||
| mem += 4; | |||
| num_max_size = *(int*)(mem); | |||
| mem += 4; | |||
| num_aspect_ratio = *(int*)(mem); | |||
| mem += 4; | |||
| variances[0] = *(float*)(mem); | |||
| mem += 4; | |||
| variances[1] = *(float*)(mem); | |||
| mem += 4; | |||
| variances[2] = *(float*)(mem); | |||
| mem += 4; | |||
| variances[3] = *(float*)(mem); | |||
| mem += 4; | |||
| flip = *(int*)(mem); | |||
| mem += 4; | |||
| clip = *(int*)(mem); | |||
| mem += 4; | |||
| image_width = *(int*)(mem); | |||
| mem += 4; | |||
| image_height = *(int*)(mem); | |||
| mem += 4; | |||
| step_width = *(float*)(mem); | |||
| mem += 4; | |||
| step_height = *(float*)(mem); | |||
| mem += 4; | |||
| offset = *(float*)(mem); | |||
| mem += 4; | |||
| min_sizes = Mat(num_min_size, (float*)mem); | |||
| mem += num_min_size * sizeof(float); | |||
| max_sizes = Mat(num_max_size, (float*)mem); | |||
| mem += num_max_size * sizeof(float); | |||
| aspect_ratios = Mat(num_aspect_ratio, (float*)mem); | |||
| mem += num_aspect_ratio * sizeof(float); | |||
| return 0; | |||
| } | |||
| #include <stdio.h> | |||
| int PriorBox::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& 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 | |||
| @@ -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<Mat>& bottom_blobs, std::vector<Mat>& 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 | |||
| @@ -14,6 +14,7 @@ | |||
| #include <stdio.h> | |||
| #include <limits.h> | |||
| #include <math.h> | |||
| #include <fstream> | |||
| #include <set> | |||
| @@ -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<float> 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<prior_box_param.aspect_ratio_size(); j++) | |||
| { | |||
| float ar = prior_box_param.aspect_ratio(j); | |||
| if (fabs(ar - 1.) < 1e-6) { | |||
| num_aspect_ratio--; | |||
| } | |||
| } | |||
| float variances[4] = {0.1f, 0.1f, 0.1f, 0.1f}; | |||
| if (prior_box_param.variance_size() == 4) | |||
| { | |||
| variances[0] = prior_box_param.variance(0); | |||
| variances[1] = prior_box_param.variance(1); | |||
| variances[2] = prior_box_param.variance(2); | |||
| variances[3] = prior_box_param.variance(3); | |||
| } | |||
| else if (prior_box_param.variance_size() == 1) | |||
| { | |||
| variances[0] = prior_box_param.variance(0); | |||
| variances[1] = prior_box_param.variance(0); | |||
| variances[2] = prior_box_param.variance(0); | |||
| variances[3] = prior_box_param.variance(0); | |||
| } | |||
| int flip = prior_box_param.has_flip() ? prior_box_param.flip() : 1; | |||
| int clip = prior_box_param.has_clip() ? prior_box_param.clip() : 0; | |||
| int image_width = -233; | |||
| int image_height = -233; | |||
| if (prior_box_param.has_img_size()) | |||
| { | |||
| image_width = prior_box_param.img_size(); | |||
| image_height = prior_box_param.img_size(); | |||
| } | |||
| else if (prior_box_param.has_img_w() && prior_box_param.has_img_h()) | |||
| { | |||
| image_width = prior_box_param.img_w(); | |||
| image_height = prior_box_param.img_h(); | |||
| } | |||
| float step_width = -233; | |||
| float step_height = -233; | |||
| if (prior_box_param.has_step()) | |||
| { | |||
| step_width = prior_box_param.step(); | |||
| step_height = prior_box_param.step(); | |||
| } | |||
| else if (prior_box_param.has_step_w() && prior_box_param.has_step_h()) | |||
| { | |||
| step_width = prior_box_param.step_w(); | |||
| step_height = prior_box_param.step_h(); | |||
| } | |||
| fprintf(pp, " %d %d %d %f %f %f %f %d %d %d %d %f %f %f", prior_box_param.min_size_size(), | |||
| prior_box_param.max_size_size(), num_aspect_ratio, | |||
| variances[0], variances[1], variances[2], variances[3], | |||
| flip, clip, image_width, image_height, | |||
| step_width, step_height, prior_box_param.offset()); | |||
| for (int j=0; j<prior_box_param.min_size_size(); j++) | |||
| { | |||
| fprintf(pp, " %f", prior_box_param.min_size(j)); | |||
| } | |||
| for (int j=0; j<prior_box_param.max_size_size(); j++) | |||
| { | |||
| fprintf(pp, " %f", prior_box_param.max_size(j)); | |||
| } | |||
| for (int j=0; j<prior_box_param.aspect_ratio_size(); j++) | |||
| { | |||
| float ar = prior_box_param.aspect_ratio(j); | |||
| if (fabs(ar - 1.) < 1e-6) { | |||
| continue; | |||
| } | |||
| fprintf(pp, " %f", ar); | |||
| } | |||
| } | |||
| else if (layer.type() == "Proposal") | |||
| { | |||
| const caffe::PythonParameter& python_param = layer.python_param(); | |||