1 // Tencent is pleased to support the open source community by making ncnn available.
2 //
3 // Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
4 //
5 // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // https://opensource.org/licenses/BSD-3-Clause
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14
15 #ifdef _MSC_VER
16 #define _CRT_SECURE_NO_DEPRECATE
17 #endif
18
19 #include "caffe.pb.h"
20
21 #include <algorithm>
22 #include <fstream>
23 #include <google/protobuf/io/coded_stream.h>
24 #include <google/protobuf/io/zero_copy_stream_impl.h>
25 #include <google/protobuf/message.h>
26 #include <google/protobuf/text_format.h>
27 #include <limits.h>
28 #include <limits>
29 #include <map>
30 #include <math.h>
31 #include <set>
32 #include <stdio.h>
33
read_proto_from_text(const char * filepath,google::protobuf::Message * message)34 static bool read_proto_from_text(const char* filepath, google::protobuf::Message* message)
35 {
36 std::ifstream fs(filepath, std::ifstream::in);
37 if (!fs.is_open())
38 {
39 fprintf(stderr, "open failed %s\n", filepath);
40 return false;
41 }
42
43 google::protobuf::io::IstreamInputStream input(&fs);
44 bool success = google::protobuf::TextFormat::Parse(&input, message);
45
46 fs.close();
47
48 return success;
49 }
50
read_proto_from_binary(const char * filepath,google::protobuf::Message * message)51 static bool read_proto_from_binary(const char* filepath, google::protobuf::Message* message)
52 {
53 std::ifstream fs(filepath, std::ifstream::in | std::ifstream::binary);
54 if (!fs.is_open())
55 {
56 fprintf(stderr, "open failed %s\n", filepath);
57 return false;
58 }
59
60 google::protobuf::io::IstreamInputStream input(&fs);
61 google::protobuf::io::CodedInputStream codedstr(&input);
62
63 #if GOOGLE_PROTOBUF_VERSION >= 3011000
64 codedstr.SetTotalBytesLimit(INT_MAX);
65 #else
66 codedstr.SetTotalBytesLimit(INT_MAX, INT_MAX / 2);
67 #endif
68
69 bool success = message->ParseFromCodedStream(&codedstr);
70
71 fs.close();
72
73 return success;
74 }
75
main(int argc,char ** argv)76 int main(int argc, char** argv)
77 {
78 if (!(argc == 3 || argc == 5))
79 {
80 fprintf(stderr, "Usage: %s [caffeproto] [caffemodel] [ncnnparam] [ncnnbin]\n", argv[0]);
81 return -1;
82 }
83
84 const char* caffeproto = argv[1];
85 const char* caffemodel = argv[2];
86 const char* ncnn_prototxt = argc == 5 ? argv[3] : "ncnn.param";
87 const char* ncnn_modelbin = argc == 5 ? argv[4] : "ncnn.bin";
88
89 caffe::NetParameter proto;
90 caffe::NetParameter net;
91
92 // load
93 bool s0 = read_proto_from_text(caffeproto, &proto);
94 if (!s0)
95 {
96 fprintf(stderr, "read_proto_from_text failed\n");
97 return -1;
98 }
99
100 bool s1 = read_proto_from_binary(caffemodel, &net);
101 if (!s1)
102 {
103 fprintf(stderr, "read_proto_from_binary failed\n");
104 return -1;
105 }
106
107 FILE* pp = fopen(ncnn_prototxt, "wb");
108 FILE* bp = fopen(ncnn_modelbin, "wb");
109
110 // magic
111 fprintf(pp, "7767517\n");
112
113 // rename mapping for identical bottom top style
114 std::map<std::string, std::string> blob_name_decorated;
115
116 // bottom blob reference
117 std::map<std::string, int> bottom_reference;
118
119 // global definition line
120 // [layer count] [blob count]
121 int layer_count = proto.layer_size();
122 std::set<std::string> blob_names;
123 for (int i = 0; i < layer_count; i++)
124 {
125 const caffe::LayerParameter& layer = proto.layer(i);
126
127 for (int j = 0; j < layer.bottom_size(); j++)
128 {
129 std::string blob_name = layer.bottom(j);
130 if (blob_name_decorated.find(blob_name) != blob_name_decorated.end())
131 {
132 blob_name = blob_name_decorated[blob_name];
133 }
134
135 blob_names.insert(blob_name);
136
137 if (bottom_reference.find(blob_name) == bottom_reference.end())
138 {
139 bottom_reference[blob_name] = 1;
140 }
141 else
142 {
143 bottom_reference[blob_name] = bottom_reference[blob_name] + 1;
144 }
145 }
146
147 if (layer.bottom_size() == 1 && layer.top_size() == 1 && layer.bottom(0) == layer.top(0))
148 {
149 std::string blob_name = layer.top(0) + "_" + layer.name();
150 blob_name_decorated[layer.top(0)] = blob_name;
151 blob_names.insert(blob_name);
152 }
153 else
154 {
155 for (int j = 0; j < layer.top_size(); j++)
156 {
157 std::string blob_name = layer.top(j);
158 blob_names.insert(blob_name);
159 }
160 }
161 }
162 // remove bottom_reference entry with reference equals to one
163 int splitncnn_blob_count = 0;
164 std::map<std::string, int>::iterator it = bottom_reference.begin();
165 while (it != bottom_reference.end())
166 {
167 if (it->second == 1)
168 {
169 bottom_reference.erase(it++);
170 }
171 else
172 {
173 splitncnn_blob_count += it->second;
174 // fprintf(stderr, "%s %d\n", it->first.c_str(), it->second);
175 ++it;
176 }
177 }
178 fprintf(pp, "%d %d\n", int(layer_count + bottom_reference.size()), int(blob_names.size() + splitncnn_blob_count));
179
180 // populate
181 blob_name_decorated.clear();
182 int internal_split = 0;
183 for (int i = 0; i < layer_count; i++)
184 {
185 const caffe::LayerParameter& layer = proto.layer(i);
186
187 // layer definition line, repeated
188 // [type] [name] [bottom blob count] [top blob count] [bottom blobs] [top blobs] [layer specific params]
189 if (layer.type() == "BN")
190 {
191 fprintf(pp, "%-16s", "Scale");
192 }
193 else if (layer.type() == "Convolution")
194 {
195 const caffe::ConvolutionParameter& convolution_param = layer.convolution_param();
196 if (convolution_param.group() != 1)
197 fprintf(pp, "%-16s", "ConvolutionDepthWise");
198 else
199 fprintf(pp, "%-16s", "Convolution");
200 }
201 else if (layer.type() == "ConvolutionDepthwise" || layer.type() == "DepthwiseConvolution")
202 {
203 fprintf(pp, "%-16s", "ConvolutionDepthWise");
204 }
205 else if (layer.type() == "Deconvolution")
206 {
207 const caffe::ConvolutionParameter& convolution_param = layer.convolution_param();
208 if (convolution_param.group() != 1)
209 fprintf(pp, "%-16s", "DeconvolutionDepthWise");
210 else
211 fprintf(pp, "%-16s", "Deconvolution");
212 }
213 else if (layer.type() == "MemoryData")
214 {
215 fprintf(pp, "%-16s", "Input");
216 }
217 else if (layer.type() == "Python")
218 {
219 const caffe::PythonParameter& python_param = layer.python_param();
220 std::string python_layer_name = python_param.layer();
221 if (python_layer_name == "ProposalLayer")
222 fprintf(pp, "%-16s", "Proposal");
223 else
224 fprintf(pp, "%-16s", python_layer_name.c_str());
225 }
226 else if (layer.type() == "ReLU6")
227 {
228 fprintf(pp, "%-16s", "Clip");
229 }
230 else if (layer.type() == "Silence")
231 {
232 fprintf(pp, "%-16s", "Noop");
233 }
234 else
235 {
236 fprintf(pp, "%-16s", layer.type().c_str());
237 }
238 fprintf(pp, " %-16s %d %d", layer.name().c_str(), layer.bottom_size(), layer.top_size());
239
240 for (int j = 0; j < layer.bottom_size(); j++)
241 {
242 std::string blob_name = layer.bottom(j);
243 if (blob_name_decorated.find(layer.bottom(j)) != blob_name_decorated.end())
244 {
245 blob_name = blob_name_decorated[layer.bottom(j)];
246 }
247
248 if (bottom_reference.find(blob_name) != bottom_reference.end())
249 {
250 int refidx = bottom_reference[blob_name] - 1;
251 bottom_reference[blob_name] = refidx;
252
253 char splitsuffix[256];
254 sprintf(splitsuffix, "_splitncnn_%d", refidx);
255 blob_name = blob_name + splitsuffix;
256 }
257
258 fprintf(pp, " %s", blob_name.c_str());
259 }
260
261 // decorated
262 if (layer.bottom_size() == 1 && layer.top_size() == 1 && layer.bottom(0) == layer.top(0))
263 {
264 std::string blob_name = layer.top(0) + "_" + layer.name();
265 blob_name_decorated[layer.top(0)] = blob_name;
266
267 fprintf(pp, " %s", blob_name.c_str());
268 }
269 else
270 {
271 for (int j = 0; j < layer.top_size(); j++)
272 {
273 std::string blob_name = layer.top(j);
274 fprintf(pp, " %s", blob_name.c_str());
275 }
276 }
277
278 // find blob binary by layer name
279 int netidx;
280 for (netidx = 0; netidx < net.layer_size(); netidx++)
281 {
282 if (net.layer(netidx).name() == layer.name())
283 {
284 break;
285 }
286 }
287
288 // layer specific params
289 if (layer.type() == "BatchNorm")
290 {
291 const caffe::LayerParameter& binlayer = net.layer(netidx);
292
293 const caffe::BlobProto& mean_blob = binlayer.blobs(0);
294 const caffe::BlobProto& var_blob = binlayer.blobs(1);
295 fprintf(pp, " 0=%d", (int)mean_blob.data_size());
296
297 const caffe::BatchNormParameter& batch_norm_param = layer.batch_norm_param();
298 float eps = batch_norm_param.eps();
299
300 std::vector<float> ones(mean_blob.data_size(), 1.f);
301 fwrite(ones.data(), sizeof(float), ones.size(), bp); // slope
302
303 if (binlayer.blobs_size() < 3)
304 {
305 fwrite(mean_blob.data().data(), sizeof(float), mean_blob.data_size(), bp);
306 float tmp;
307 for (int j = 0; j < var_blob.data_size(); j++)
308 {
309 tmp = var_blob.data().data()[j] + eps;
310 fwrite(&tmp, sizeof(float), 1, bp);
311 }
312 }
313 else
314 {
315 float scale_factor = binlayer.blobs(2).data().data()[0] == 0 ? 0 : 1 / binlayer.blobs(2).data().data()[0];
316 // premultiply scale_factor to mean and variance
317 float tmp;
318 for (int j = 0; j < mean_blob.data_size(); j++)
319 {
320 tmp = mean_blob.data().data()[j] * scale_factor;
321 fwrite(&tmp, sizeof(float), 1, bp);
322 }
323 for (int j = 0; j < var_blob.data_size(); j++)
324 {
325 tmp = var_blob.data().data()[j] * scale_factor + eps;
326 fwrite(&tmp, sizeof(float), 1, bp);
327 }
328 }
329
330 std::vector<float> zeros(mean_blob.data_size(), 0.f);
331 fwrite(zeros.data(), sizeof(float), zeros.size(), bp); // bias
332 }
333 else if (layer.type() == "BN")
334 {
335 const caffe::LayerParameter& binlayer = net.layer(netidx);
336
337 const caffe::BlobProto& scale_blob = binlayer.blobs(0);
338 const caffe::BlobProto& shift_blob = binlayer.blobs(1);
339 fprintf(pp, " 0=%d", (int)scale_blob.data_size());
340 fprintf(pp, " 1=1");
341
342 fwrite(scale_blob.data().data(), sizeof(float), scale_blob.data_size(), bp);
343 fwrite(shift_blob.data().data(), sizeof(float), shift_blob.data_size(), bp);
344 }
345 else if (layer.type() == "Concat")
346 {
347 const caffe::ConcatParameter& concat_param = layer.concat_param();
348 int axis = concat_param.axis() - 1;
349 fprintf(pp, " 0=%d", axis);
350 }
351 else if (layer.type() == "Convolution" || layer.type() == "ConvolutionDepthwise" || layer.type() == "DepthwiseConvolution")
352 {
353 const caffe::LayerParameter& binlayer = net.layer(netidx);
354
355 const caffe::BlobProto& weight_blob = binlayer.blobs(0);
356 const caffe::ConvolutionParameter& convolution_param = layer.convolution_param();
357 fprintf(pp, " 0=%d", convolution_param.num_output());
358 if (convolution_param.has_kernel_w() && convolution_param.has_kernel_h())
359 {
360 fprintf(pp, " 1=%d", convolution_param.kernel_w());
361 fprintf(pp, " 11=%d", convolution_param.kernel_h());
362 }
363 else
364 {
365 fprintf(pp, " 1=%d", convolution_param.kernel_size(0));
366 }
367 fprintf(pp, " 2=%d", convolution_param.dilation_size() != 0 ? convolution_param.dilation(0) : 1);
368 if (convolution_param.has_stride_w() && convolution_param.has_stride_h())
369 {
370 fprintf(pp, " 3=%d", convolution_param.stride_w());
371 fprintf(pp, " 13=%d", convolution_param.stride_h());
372 }
373 else
374 {
375 fprintf(pp, " 3=%d", convolution_param.stride_size() != 0 ? convolution_param.stride(0) : 1);
376 }
377 if (convolution_param.has_pad_w() && convolution_param.has_pad_h())
378 {
379 fprintf(pp, " 4=%d", convolution_param.pad_w());
380 fprintf(pp, " 14=%d", convolution_param.pad_h());
381 }
382 else
383 {
384 fprintf(pp, " 4=%d", convolution_param.pad_size() != 0 ? convolution_param.pad(0) : 0);
385 }
386 fprintf(pp, " 5=%d", convolution_param.bias_term());
387 fprintf(pp, " 6=%d", weight_blob.data_size());
388
389 int num_group = 1;
390 if (layer.type() == "ConvolutionDepthwise" || layer.type() == "DepthwiseConvolution")
391 {
392 num_group = convolution_param.num_output();
393 }
394 else
395 {
396 num_group = convolution_param.group();
397 }
398
399 if (num_group != 1)
400 {
401 fprintf(pp, " 7=%d", num_group);
402 }
403
404 for (int j = 0; j < binlayer.blobs_size(); j++)
405 {
406 int quantize_tag = 0;
407 const caffe::BlobProto& blob = binlayer.blobs(j);
408
409 // we will not quantize the bias values
410 if (j == 0)
411 {
412 // write quantize tag first
413 fwrite(&quantize_tag, sizeof(int), 1, bp);
414
415 // write original data
416 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
417 }
418 else
419 {
420 // write original data
421 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
422 }
423 }
424 }
425 else if (layer.type() == "Crop")
426 {
427 const caffe::CropParameter& crop_param = layer.crop_param();
428 int num_offset = crop_param.offset_size();
429 if (num_offset == 1)
430 {
431 int offset = crop_param.offset(0);
432 int axis = crop_param.axis() - 1;
433 if (axis == 0)
434 {
435 fprintf(pp, " 0=%d", offset);
436 fprintf(pp, " 1=%d", offset);
437 fprintf(pp, " 2=%d", offset);
438 }
439 else if (axis == 1)
440 {
441 fprintf(pp, " 0=%d", offset);
442 fprintf(pp, " 1=%d", offset);
443 }
444 else if (axis == 2)
445 {
446 fprintf(pp, " 0=%d", offset);
447 }
448 }
449 else if (num_offset == 2)
450 {
451 int woffset = crop_param.offset(1);
452 int hoffset = crop_param.offset(0);
453 fprintf(pp, " 0=%d", woffset);
454 fprintf(pp, " 1=%d", hoffset);
455 }
456 else if (num_offset == 3)
457 {
458 int woffset = crop_param.offset(2);
459 int hoffset = crop_param.offset(1);
460 int coffset = crop_param.offset(0);
461 fprintf(pp, " 0=%d", woffset);
462 fprintf(pp, " 1=%d", hoffset);
463 fprintf(pp, " 2=%d", coffset);
464 }
465 }
466 else if (layer.type() == "Deconvolution")
467 {
468 const caffe::LayerParameter& binlayer = net.layer(netidx);
469
470 const caffe::BlobProto& weight_blob = binlayer.blobs(0);
471 const caffe::ConvolutionParameter& convolution_param = layer.convolution_param();
472 fprintf(pp, " 0=%d", convolution_param.num_output());
473 if (convolution_param.has_kernel_w() && convolution_param.has_kernel_h())
474 {
475 fprintf(pp, " 1=%d", convolution_param.kernel_w());
476 fprintf(pp, " 11=%d", convolution_param.kernel_h());
477 }
478 else
479 {
480 fprintf(pp, " 1=%d", convolution_param.kernel_size(0));
481 }
482 fprintf(pp, " 2=%d", convolution_param.dilation_size() != 0 ? convolution_param.dilation(0) : 1);
483 if (convolution_param.has_stride_w() && convolution_param.has_stride_h())
484 {
485 fprintf(pp, " 3=%d", convolution_param.stride_w());
486 fprintf(pp, " 13=%d", convolution_param.stride_h());
487 }
488 else
489 {
490 fprintf(pp, " 3=%d", convolution_param.stride_size() != 0 ? convolution_param.stride(0) : 1);
491 }
492 if (convolution_param.has_pad_w() && convolution_param.has_pad_h())
493 {
494 fprintf(pp, " 4=%d", convolution_param.pad_w());
495 fprintf(pp, " 14=%d", convolution_param.pad_h());
496 }
497 else
498 {
499 fprintf(pp, " 4=%d", convolution_param.pad_size() != 0 ? convolution_param.pad(0) : 0);
500 }
501 fprintf(pp, " 5=%d", convolution_param.bias_term());
502 fprintf(pp, " 6=%d", weight_blob.data_size());
503
504 int group = convolution_param.group();
505 if (group != 1)
506 {
507 fprintf(pp, " 7=%d", group);
508 }
509
510 int quantized_weight = 0;
511 fwrite(&quantized_weight, sizeof(int), 1, bp);
512
513 int maxk = 0;
514 if (convolution_param.has_kernel_w() && convolution_param.has_kernel_h())
515 {
516 maxk = convolution_param.kernel_w() * convolution_param.kernel_h();
517 }
518 else
519 {
520 maxk = convolution_param.kernel_size(0) * convolution_param.kernel_size(0);
521 }
522 for (int g = 0; g < group; g++)
523 {
524 // reorder weight from inch-outch to outch-inch
525 int num_output = convolution_param.num_output() / group;
526 int num_input = weight_blob.data_size() / maxk / num_output / group;
527 const float* weight_data_ptr = weight_blob.data().data() + g * maxk * num_output * num_input;
528 for (int k = 0; k < num_output; k++)
529 {
530 for (int j = 0; j < num_input; j++)
531 {
532 fwrite(weight_data_ptr + (j * num_output + k) * maxk, sizeof(float), maxk, bp);
533 }
534 }
535 }
536
537 for (int j = 1; j < binlayer.blobs_size(); j++)
538 {
539 const caffe::BlobProto& blob = binlayer.blobs(j);
540 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
541 }
542 }
543 else if (layer.type() == "DetectionOutput")
544 {
545 const caffe::DetectionOutputParameter& detection_output_param = layer.detection_output_param();
546 const caffe::NonMaximumSuppressionParameter& nms_param = detection_output_param.nms_param();
547 fprintf(pp, " 0=%d", detection_output_param.num_classes());
548 fprintf(pp, " 1=%e", nms_param.nms_threshold());
549 fprintf(pp, " 2=%d", nms_param.top_k());
550 fprintf(pp, " 3=%d", detection_output_param.keep_top_k());
551 fprintf(pp, " 4=%e", detection_output_param.confidence_threshold());
552 }
553 else if (layer.type() == "Dropout")
554 {
555 const caffe::DropoutParameter& dropout_param = layer.dropout_param();
556 if (dropout_param.has_scale_train() && !dropout_param.scale_train())
557 {
558 float scale = 1.f - dropout_param.dropout_ratio();
559 fprintf(pp, " 0=%e", scale);
560 }
561 }
562 else if (layer.type() == "Eltwise")
563 {
564 const caffe::EltwiseParameter& eltwise_param = layer.eltwise_param();
565 int coeff_size = eltwise_param.coeff_size();
566 fprintf(pp, " 0=%d", (int)eltwise_param.operation());
567 fprintf(pp, " -23301=%d", coeff_size);
568 for (int j = 0; j < coeff_size; j++)
569 {
570 fprintf(pp, ",%e", eltwise_param.coeff(j));
571 }
572 }
573 else if (layer.type() == "ELU")
574 {
575 const caffe::ELUParameter& elu_param = layer.elu_param();
576 fprintf(pp, " 0=%e", elu_param.alpha());
577 }
578 else if (layer.type() == "Embed")
579 {
580 const caffe::LayerParameter& binlayer = net.layer(netidx);
581
582 const caffe::BlobProto& weight_blob = binlayer.blobs(0);
583 const caffe::EmbedParameter& embed_param = layer.embed_param();
584 fprintf(pp, " 0=%d", embed_param.num_output());
585 fprintf(pp, " 1=%d", embed_param.input_dim());
586 fprintf(pp, " 2=%d", embed_param.bias_term());
587 fprintf(pp, " 3=%d", weight_blob.data_size());
588
589 for (int j = 0; j < binlayer.blobs_size(); j++)
590 {
591 int quantize_tag = 0;
592 const caffe::BlobProto& blob = binlayer.blobs(j);
593
594 // write quantize tag first
595 if (j == 0)
596 fwrite(&quantize_tag, sizeof(int), 1, bp);
597
598 // write original data
599 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
600 }
601 }
602 else if (layer.type() == "InnerProduct")
603 {
604 const caffe::LayerParameter& binlayer = net.layer(netidx);
605
606 const caffe::BlobProto& weight_blob = binlayer.blobs(0);
607 const caffe::InnerProductParameter& inner_product_param = layer.inner_product_param();
608 fprintf(pp, " 0=%d", inner_product_param.num_output());
609 fprintf(pp, " 1=%d", inner_product_param.bias_term());
610 fprintf(pp, " 2=%d", weight_blob.data_size());
611
612 for (int j = 0; j < binlayer.blobs_size(); j++)
613 {
614 int quantize_tag = 0;
615 const caffe::BlobProto& blob = binlayer.blobs(j);
616
617 // we will not quantize the bias values
618 if (j == 0)
619 {
620 // write quantize tag first
621 fwrite(&quantize_tag, sizeof(int), 1, bp);
622
623 // write original data
624 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
625 }
626 else
627 {
628 // write original data
629 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
630 }
631 }
632 }
633 else if (layer.type() == "Input")
634 {
635 const caffe::InputParameter& input_param = layer.input_param();
636 const caffe::BlobShape& bs = input_param.shape(0);
637 if (bs.dim_size() == 4)
638 {
639 fprintf(pp, " 0=%zd", size_t(bs.dim(3)));
640 fprintf(pp, " 1=%zd", size_t(bs.dim(2)));
641 fprintf(pp, " 2=%zd", size_t(bs.dim(1)));
642 }
643 else if (bs.dim_size() == 3)
644 {
645 fprintf(pp, " 0=%zd", size_t(bs.dim(2)));
646 fprintf(pp, " 1=%zd", size_t(bs.dim(1)));
647 }
648 else if (bs.dim_size() == 2)
649 {
650 fprintf(pp, " 0=%zd", size_t(bs.dim(1)));
651 }
652 }
653 else if (layer.type() == "Interp")
654 {
655 const caffe::InterpParameter& interp_param = layer.interp_param();
656 fprintf(pp, " 0=%d", 2);
657 fprintf(pp, " 1=%e", (float)interp_param.zoom_factor());
658 fprintf(pp, " 2=%e", (float)interp_param.zoom_factor());
659 fprintf(pp, " 3=%d", interp_param.height());
660 fprintf(pp, " 4=%d", interp_param.width());
661 }
662 else if (layer.type() == "LRN")
663 {
664 const caffe::LRNParameter& lrn_param = layer.lrn_param();
665 fprintf(pp, " 0=%d", lrn_param.norm_region());
666 fprintf(pp, " 1=%d", lrn_param.local_size());
667 fprintf(pp, " 2=%e", lrn_param.alpha());
668 fprintf(pp, " 3=%e", lrn_param.beta());
669 }
670 else if (layer.type() == "LSTM")
671 {
672 const caffe::LayerParameter& binlayer = net.layer(netidx);
673
674 const caffe::BlobProto& weight_blob = binlayer.blobs(0);
675 const caffe::RecurrentParameter& recurrent_param = layer.recurrent_param();
676 fprintf(pp, " 0=%d", recurrent_param.num_output());
677 fprintf(pp, " 1=%d", weight_blob.data_size());
678
679 for (int j = 0; j < binlayer.blobs_size(); j++)
680 {
681 int quantize_tag = 0;
682 const caffe::BlobProto& blob = binlayer.blobs(j);
683
684 // write quantize tag first
685 fwrite(&quantize_tag, sizeof(int), 1, bp);
686
687 // write original data
688 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
689 }
690 }
691 else if (layer.type() == "MemoryData")
692 {
693 const caffe::MemoryDataParameter& memory_data_param = layer.memory_data_param();
694 fprintf(pp, " 0=%d", memory_data_param.width());
695 fprintf(pp, " 1=%d", memory_data_param.height());
696 fprintf(pp, " 2=%d", memory_data_param.channels());
697 }
698 else if (layer.type() == "MVN")
699 {
700 const caffe::MVNParameter& mvn_param = layer.mvn_param();
701 fprintf(pp, " 0=%d", mvn_param.normalize_variance());
702 fprintf(pp, " 1=%d", mvn_param.across_channels());
703 fprintf(pp, " 2=%e", mvn_param.eps());
704 }
705 else if (layer.type() == "Normalize")
706 {
707 const caffe::LayerParameter& binlayer = net.layer(netidx);
708 const caffe::BlobProto& scale_blob = binlayer.blobs(0);
709 const caffe::NormalizeParameter& norm_param = layer.norm_param();
710 fprintf(pp, " 0=%d", norm_param.across_spatial());
711 fprintf(pp, " 1=%d", norm_param.channel_shared());
712 fprintf(pp, " 2=%e", norm_param.eps());
713 fprintf(pp, " 3=%d", scale_blob.data_size());
714
715 fwrite(scale_blob.data().data(), sizeof(float), scale_blob.data_size(), bp);
716 }
717 else if (layer.type() == "Permute")
718 {
719 const caffe::PermuteParameter& permute_param = layer.permute_param();
720 int order_size = permute_param.order_size();
721 int order_type = 0;
722 if (order_size == 0)
723 order_type = 0;
724 if (order_size == 1)
725 {
726 int order0 = permute_param.order(0);
727 if (order0 == 0)
728 order_type = 0;
729 // permute with N not supported
730 }
731 if (order_size == 2)
732 {
733 int order0 = permute_param.order(0);
734 int order1 = permute_param.order(1);
735 if (order0 == 0)
736 {
737 if (order1 == 1) // 0 1 2 3
738 order_type = 0;
739 else if (order1 == 2) // 0 2 1 3
740 order_type = 2;
741 else if (order1 == 3) // 0 3 1 2
742 order_type = 4;
743 }
744 // permute with N not supported
745 }
746 if (order_size == 3 || order_size == 4)
747 {
748 int order0 = permute_param.order(0);
749 int order1 = permute_param.order(1);
750 int order2 = permute_param.order(2);
751 if (order0 == 0)
752 {
753 if (order1 == 1)
754 {
755 if (order2 == 2) // 0 1 2 3
756 order_type = 0;
757 if (order2 == 3) // 0 1 3 2
758 order_type = 1;
759 }
760 else if (order1 == 2)
761 {
762 if (order2 == 1) // 0 2 1 3
763 order_type = 2;
764 if (order2 == 3) // 0 2 3 1
765 order_type = 3;
766 }
767 else if (order1 == 3)
768 {
769 if (order2 == 1) // 0 3 1 2
770 order_type = 4;
771 if (order2 == 2) // 0 3 2 1
772 order_type = 5;
773 }
774 }
775 // permute with N not supported
776 }
777 fprintf(pp, " 0=%d", order_type);
778 }
779 else if (layer.type() == "Pooling")
780 {
781 const caffe::PoolingParameter& pooling_param = layer.pooling_param();
782 fprintf(pp, " 0=%d", pooling_param.pool());
783 if (pooling_param.has_kernel_w() && pooling_param.has_kernel_h())
784 {
785 fprintf(pp, " 1=%d", pooling_param.kernel_w());
786 fprintf(pp, " 11=%d", pooling_param.kernel_h());
787 }
788 else
789 {
790 fprintf(pp, " 1=%d", pooling_param.kernel_size());
791 }
792 if (pooling_param.has_stride_w() && pooling_param.has_stride_h())
793 {
794 fprintf(pp, " 2=%d", pooling_param.stride_w());
795 fprintf(pp, " 12=%d", pooling_param.stride_h());
796 }
797 else
798 {
799 fprintf(pp, " 2=%d", pooling_param.stride());
800 }
801 if (pooling_param.has_pad_w() && pooling_param.has_pad_h())
802 {
803 fprintf(pp, " 3=%d", pooling_param.pad_w());
804 fprintf(pp, " 13=%d", pooling_param.pad_h());
805 }
806 else
807 {
808 fprintf(pp, " 3=%d", pooling_param.pad());
809 }
810 fprintf(pp, " 4=%d", pooling_param.has_global_pooling() ? pooling_param.global_pooling() : 0);
811 }
812 else if (layer.type() == "Power")
813 {
814 const caffe::PowerParameter& power_param = layer.power_param();
815 fprintf(pp, " 0=%e", power_param.power());
816 fprintf(pp, " 1=%e", power_param.scale());
817 fprintf(pp, " 2=%e", power_param.shift());
818 }
819 else if (layer.type() == "PReLU")
820 {
821 const caffe::LayerParameter& binlayer = net.layer(netidx);
822 const caffe::BlobProto& slope_blob = binlayer.blobs(0);
823 fprintf(pp, " 0=%d", slope_blob.data_size());
824 fwrite(slope_blob.data().data(), sizeof(float), slope_blob.data_size(), bp);
825 }
826 else if (layer.type() == "PriorBox")
827 {
828 const caffe::PriorBoxParameter& prior_box_param = layer.prior_box_param();
829
830 int num_aspect_ratio = prior_box_param.aspect_ratio_size();
831 for (int j = 0; j < prior_box_param.aspect_ratio_size(); j++)
832 {
833 float ar = prior_box_param.aspect_ratio(j);
834 if (fabs(ar - 1.) < 1e-6)
835 {
836 num_aspect_ratio--;
837 }
838 }
839
840 float variances[4] = {0.1f, 0.1f, 0.1f, 0.1f};
841 if (prior_box_param.variance_size() == 4)
842 {
843 variances[0] = prior_box_param.variance(0);
844 variances[1] = prior_box_param.variance(1);
845 variances[2] = prior_box_param.variance(2);
846 variances[3] = prior_box_param.variance(3);
847 }
848 else if (prior_box_param.variance_size() == 1)
849 {
850 variances[0] = prior_box_param.variance(0);
851 variances[1] = prior_box_param.variance(0);
852 variances[2] = prior_box_param.variance(0);
853 variances[3] = prior_box_param.variance(0);
854 }
855
856 int flip = prior_box_param.has_flip() ? prior_box_param.flip() : 1;
857 int clip = prior_box_param.has_clip() ? prior_box_param.clip() : 0;
858 int image_width = -233;
859 int image_height = -233;
860 if (prior_box_param.has_img_size())
861 {
862 image_width = prior_box_param.img_size();
863 image_height = prior_box_param.img_size();
864 }
865 else if (prior_box_param.has_img_w() && prior_box_param.has_img_h())
866 {
867 image_width = prior_box_param.img_w();
868 image_height = prior_box_param.img_h();
869 }
870
871 float step_width = -233;
872 float step_height = -233;
873 if (prior_box_param.has_step())
874 {
875 step_width = prior_box_param.step();
876 step_height = prior_box_param.step();
877 }
878 else if (prior_box_param.has_step_w() && prior_box_param.has_step_h())
879 {
880 step_width = prior_box_param.step_w();
881 step_height = prior_box_param.step_h();
882 }
883
884 fprintf(pp, " -23300=%d", prior_box_param.min_size_size());
885 for (int j = 0; j < prior_box_param.min_size_size(); j++)
886 {
887 fprintf(pp, ",%e", prior_box_param.min_size(j));
888 }
889 fprintf(pp, " -23301=%d", prior_box_param.max_size_size());
890 for (int j = 0; j < prior_box_param.max_size_size(); j++)
891 {
892 fprintf(pp, ",%e", prior_box_param.max_size(j));
893 }
894 fprintf(pp, " -23302=%d", num_aspect_ratio);
895 for (int j = 0; j < prior_box_param.aspect_ratio_size(); j++)
896 {
897 float ar = prior_box_param.aspect_ratio(j);
898 if (fabs(ar - 1.) < 1e-6)
899 {
900 continue;
901 }
902 fprintf(pp, ",%e", ar);
903 }
904 fprintf(pp, " 3=%e", variances[0]);
905 fprintf(pp, " 4=%e", variances[1]);
906 fprintf(pp, " 5=%e", variances[2]);
907 fprintf(pp, " 6=%e", variances[3]);
908 fprintf(pp, " 7=%d", flip);
909 fprintf(pp, " 8=%d", clip);
910 fprintf(pp, " 9=%d", image_width);
911 fprintf(pp, " 10=%d", image_height);
912 fprintf(pp, " 11=%e", step_width);
913 fprintf(pp, " 12=%e", step_height);
914 fprintf(pp, " 13=%e", prior_box_param.offset());
915 }
916 else if (layer.type() == "PSROIPooling")
917 {
918 const caffe::PSROIPoolingParameter& psroi_pooling_param = layer.psroi_pooling_param();
919 fprintf(pp, " 0=%d", psroi_pooling_param.group_size());
920 fprintf(pp, " 1=%d", psroi_pooling_param.group_size());
921 fprintf(pp, " 2=%e", psroi_pooling_param.spatial_scale());
922 fprintf(pp, " 3=%d", psroi_pooling_param.output_dim());
923 }
924 else if (layer.type() == "Python")
925 {
926 const caffe::PythonParameter& python_param = layer.python_param();
927 std::string python_layer_name = python_param.layer();
928 if (python_layer_name == "ProposalLayer")
929 {
930 int feat_stride = 16;
931 sscanf(python_param.param_str().c_str(), "'feat_stride': %d", &feat_stride);
932
933 int base_size = 16;
934 // float ratio;
935 // float scale;
936 int pre_nms_topN = 6000;
937 int after_nms_topN = 300;
938 float nms_thresh = 0.7f;
939 int min_size = 16;
940 fprintf(pp, " 0=%d", feat_stride);
941 fprintf(pp, " 1=%d", base_size);
942 fprintf(pp, " 2=%d", pre_nms_topN);
943 fprintf(pp, " 3=%d", after_nms_topN);
944 fprintf(pp, " 4=%e", nms_thresh);
945 fprintf(pp, " 5=%d", min_size);
946 }
947 }
948 else if (layer.type() == "ReLU")
949 {
950 const caffe::ReLUParameter& relu_param = layer.relu_param();
951 if (relu_param.has_negative_slope())
952 {
953 fprintf(pp, " 0=%e", relu_param.negative_slope());
954 }
955 }
956 else if (layer.type() == "ReLU6")
957 {
958 float min = 0.f;
959 float max = 6.f;
960 fprintf(pp, " 0=%e", min);
961 fprintf(pp, " 1=%e", max);
962 }
963 else if (layer.type() == "Reorg")
964 {
965 const caffe::ReorgParameter& reorg_param = layer.reorg_param();
966 fprintf(pp, " 0=%d", reorg_param.stride());
967 }
968 else if (layer.type() == "Reshape")
969 {
970 const caffe::ReshapeParameter& reshape_param = layer.reshape_param();
971 const caffe::BlobShape& bs = reshape_param.shape();
972 if (bs.dim_size() == 1)
973 {
974 fprintf(pp, " 0=%zd 1=-233 2=-233", size_t(bs.dim(0)));
975 }
976 else if (bs.dim_size() == 2)
977 {
978 fprintf(pp, " 0=%zd 1=-233 2=-233", size_t(bs.dim(1)));
979 }
980 else if (bs.dim_size() == 3)
981 {
982 fprintf(pp, " 0=%zd 1=%zd 2=-233", size_t(bs.dim(2)), size_t(bs.dim(1)));
983 }
984 else // bs.dim_size() == 4
985 {
986 fprintf(pp, " 0=%zd 1=%zd 2=%zd", size_t(bs.dim(3)), size_t(bs.dim(2)), size_t(bs.dim(1)));
987 }
988 fprintf(pp, " 3=0"); // permute
989 }
990 else if (layer.type() == "ROIAlign")
991 {
992 const caffe::ROIAlignParameter& roi_align_param = layer.roi_align_param();
993 fprintf(pp, " 0=%d", roi_align_param.pooled_w());
994 fprintf(pp, " 1=%d", roi_align_param.pooled_h());
995 fprintf(pp, " 2=%e", roi_align_param.spatial_scale());
996 fprintf(pp, " 3=%d", 0);
997 fprintf(pp, " 4=%d", false);
998 fprintf(pp, " 5=%d", 0);
999 }
1000 else if (layer.type() == "ROIPooling")
1001 {
1002 const caffe::ROIPoolingParameter& roi_pooling_param = layer.roi_pooling_param();
1003 fprintf(pp, " 0=%d", roi_pooling_param.pooled_w());
1004 fprintf(pp, " 1=%d", roi_pooling_param.pooled_h());
1005 fprintf(pp, " 2=%e", roi_pooling_param.spatial_scale());
1006 }
1007 else if (layer.type() == "Scale")
1008 {
1009 const caffe::LayerParameter& binlayer = net.layer(netidx);
1010
1011 const caffe::ScaleParameter& scale_param = layer.scale_param();
1012 bool scale_weight = scale_param.bias_term() ? (binlayer.blobs_size() == 2) : (binlayer.blobs_size() == 1);
1013 if (scale_weight)
1014 {
1015 const caffe::BlobProto& weight_blob = binlayer.blobs(0);
1016 fprintf(pp, " 0=%d", int(weight_blob.data_size()));
1017 }
1018 else
1019 {
1020 fprintf(pp, " 0=-233");
1021 }
1022
1023 fprintf(pp, " 1=%d", scale_param.bias_term());
1024
1025 for (int j = 0; j < binlayer.blobs_size(); j++)
1026 {
1027 const caffe::BlobProto& blob = binlayer.blobs(j);
1028 fwrite(blob.data().data(), sizeof(float), blob.data_size(), bp);
1029 }
1030 }
1031 else if (layer.type() == "ShuffleChannel")
1032 {
1033 const caffe::ShuffleChannelParameter& shuffle_channel_param = layer.shuffle_channel_param();
1034 fprintf(pp, " 0=%d", shuffle_channel_param.group());
1035 }
1036 else if (layer.type() == "Slice")
1037 {
1038 const caffe::SliceParameter& slice_param = layer.slice_param();
1039 if (slice_param.slice_point_size() == 0)
1040 {
1041 int num_slice = layer.top_size();
1042 fprintf(pp, " -23300=%d", num_slice);
1043 for (int j = 0; j < num_slice; j++)
1044 {
1045 fprintf(pp, ",-233");
1046 }
1047 }
1048 else
1049 {
1050 int num_slice = slice_param.slice_point_size() + 1;
1051 fprintf(pp, " -23300=%d", num_slice);
1052 int prev_offset = 0;
1053 for (int j = 0; j < slice_param.slice_point_size(); j++)
1054 {
1055 int offset = slice_param.slice_point(j);
1056 fprintf(pp, ",%d", offset - prev_offset);
1057 prev_offset = offset;
1058 }
1059 fprintf(pp, ",-233");
1060 }
1061 int axis = 0;
1062 if (slice_param.has_axis())
1063 {
1064 axis = slice_param.axis() - 1;
1065 }
1066 else if (slice_param.has_slice_dim())
1067 {
1068 axis = slice_param.slice_dim() - 1;
1069 }
1070 fprintf(pp, " 1=%d", axis);
1071 }
1072 else if (layer.type() == "Softmax")
1073 {
1074 const caffe::SoftmaxParameter& softmax_param = layer.softmax_param();
1075 int dim = softmax_param.axis() - 1;
1076 fprintf(pp, " 0=%d", dim);
1077 fprintf(pp, " 1=1");
1078 }
1079 else if (layer.type() == "Threshold")
1080 {
1081 const caffe::ThresholdParameter& threshold_param = layer.threshold_param();
1082 fprintf(pp, " 0=%e", threshold_param.threshold());
1083 }
1084 else if (layer.type() == "YoloDetectionOutput")
1085 {
1086 const caffe::YoloDetectionOutputParameter& yolo_detection_output_param = layer.yolo_detection_output_param();
1087
1088 fprintf(pp, " 0=%d", yolo_detection_output_param.num_classes());
1089 fprintf(pp, " 1=%d", yolo_detection_output_param.num_box());
1090 fprintf(pp, " 2=%e", yolo_detection_output_param.confidence_threshold());
1091 fprintf(pp, " 3=%e", yolo_detection_output_param.nms_threshold());
1092
1093 int num_bias = yolo_detection_output_param.biases_size();
1094 fprintf(pp, " -23304=%d", num_bias);
1095 for (int j = 0; j < num_bias; j++)
1096 {
1097 fprintf(pp, ",%e", yolo_detection_output_param.biases(j));
1098 }
1099 }
1100 else if (layer.type() == "Yolov3DetectionOutput")
1101 {
1102 const caffe::Yolov3DetectionOutputParameter& yolov3_detection_output_param = layer.yolov3_detection_output_param();
1103
1104 fprintf(pp, " 0=%d", yolov3_detection_output_param.num_classes());
1105 fprintf(pp, " 1=%d", yolov3_detection_output_param.num_box());
1106 fprintf(pp, " 2=%e", yolov3_detection_output_param.confidence_threshold());
1107 fprintf(pp, " 3=%e", yolov3_detection_output_param.nms_threshold());
1108
1109 int num_bias = yolov3_detection_output_param.biases_size();
1110 fprintf(pp, " -23304=%d", num_bias);
1111 for (int j = 0; j < num_bias; j++)
1112 {
1113 fprintf(pp, ",%e", yolov3_detection_output_param.biases(j));
1114 }
1115 int num_mask = yolov3_detection_output_param.mask_size();
1116 fprintf(pp, " -23305=%d", num_mask);
1117 for (int j = 0; j < num_mask; j++)
1118 {
1119 fprintf(pp, ",%e", (float)yolov3_detection_output_param.mask(j));
1120 }
1121 int num_anchors = yolov3_detection_output_param.anchors_scale_size();
1122 fprintf(pp, " -23306=%d", num_anchors);
1123 for (int j = 0; j < num_anchors; j++)
1124 {
1125 fprintf(pp, ",%e", (float)yolov3_detection_output_param.anchors_scale(j));
1126 }
1127 fprintf(pp, " 7=%d", yolov3_detection_output_param.mask_group_num());
1128 }
1129 fprintf(pp, "\n");
1130
1131 // add split layer if top reference larger than one
1132 if (layer.bottom_size() == 1 && layer.top_size() == 1 && layer.bottom(0) == layer.top(0))
1133 {
1134 std::string blob_name = blob_name_decorated[layer.top(0)];
1135 if (bottom_reference.find(blob_name) != bottom_reference.end())
1136 {
1137 int refcount = bottom_reference[blob_name];
1138 if (refcount > 1)
1139 {
1140 char splitname[256];
1141 sprintf(splitname, "splitncnn_%d", internal_split);
1142 fprintf(pp, "%-16s %-16s %d %d", "Split", splitname, 1, refcount);
1143 fprintf(pp, " %s", blob_name.c_str());
1144
1145 for (int j = 0; j < refcount; j++)
1146 {
1147 fprintf(pp, " %s_splitncnn_%d", blob_name.c_str(), j);
1148 }
1149 fprintf(pp, "\n");
1150
1151 internal_split++;
1152 }
1153 }
1154 }
1155 else
1156 {
1157 for (int j = 0; j < layer.top_size(); j++)
1158 {
1159 std::string blob_name = layer.top(j);
1160 if (bottom_reference.find(blob_name) != bottom_reference.end())
1161 {
1162 int refcount = bottom_reference[blob_name];
1163 if (refcount > 1)
1164 {
1165 char splitname[256];
1166 sprintf(splitname, "splitncnn_%d", internal_split);
1167 fprintf(pp, "%-16s %-16s %d %d", "Split", splitname, 1, refcount);
1168 fprintf(pp, " %s", blob_name.c_str());
1169
1170 for (int j = 0; j < refcount; j++)
1171 {
1172 fprintf(pp, " %s_splitncnn_%d", blob_name.c_str(), j);
1173 }
1174 fprintf(pp, "\n");
1175
1176 internal_split++;
1177 }
1178 }
1179 }
1180 }
1181 }
1182
1183 fclose(pp);
1184 fclose(bp);
1185
1186 return 0;
1187 }
1188