diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e9c27c..c292300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,9 @@ target_link_libraries(test_shelfnet tkDNN) add_executable(test_shelfnet_berkeley tests/shelfnet/shelfnet_berkeley.cpp) target_link_libraries(test_shelfnet_berkeley tkDNN) +add_executable(test_shelfnet_mapillary tests/shelfnet/shelfnet_mapillary.cpp) +target_link_libraries(test_shelfnet_mapillary tkDNN) + # DEMOS add_executable(test_rtinference tests/test_rtinference/rtinference.cpp) target_link_libraries(test_rtinference tkDNN) diff --git a/README_seg.md b/README_seg.md new file mode 100644 index 0000000..a9afcd0 --- /dev/null +++ b/README_seg.md @@ -0,0 +1,56 @@ +# Semantic Segmentation with tkDNN + +Currently tkDNN supports only ShelfNet as semantic segmentation network. + +## Export weights from Shelfnet +To get the weights needed to run Mobilenet tests use [this](https://git.hipert.unimore.it/mverucchi/shelfnet) fork of a Pytorch implementation of Shelfnet network. + +``` +git clone https://git.hipert.unimore.it/mverucchi/shelfnet +cd shelfnet +cd ShelfNet18_realtime +conda env create --file shelfnet_env.yml +conda activate shelfnet +mkdir layer debug +python export.py +``` + + +## Run the demo + +To run the semantic segmentation demo follow these steps (example with shelfnet_mapillary): +``` +rm shelfnet_mapillary_fp32.rt # be sure to delete(or move) old tensorRT files +export TKDNN_BATCHSIZE=4 # be sure you have batch size > than 1 if you want to run inference on images bigger than 1024 +./test_shelfnet_mapillary # run the yolo test (is slow) +./demo shelfnet_mapillary_fp32.rt ../demo/yolo_test.mp4 1 15 +``` +In general the demo program takes the following parameters: +``` +./seg_demo +``` +where +* `````` is the rt file generated by a test +* ```<``` is the path to a video file or a camera input +* `````` number of batches to use in inference (N.B. you should first export TKDNN_BATCHSIZE to the required n_batches and create again the rt file for the network). +* ``````is the number of classes the network is trained on +* `````` if set to 0 the demo will not resize the input frames, but use it as it is, otherwise it will resize it. +* `````` is `````` is set to 1, then the input frames will be proportionally resized using `````` as width baseline. +* `````` if set to 0 the demo will not show the visualization but save the video into result.mp4 (if n-batches ==1) +* `````` if set to 0 (deafult) the demo will run, otherwise the evaluation of a dataset will run and the output of the segmentation will be saved. Attention: this is under development and paths are embedded, so change them in the code in advance. + +N.b. By default it is used FP32 inference + + + +## Existing tests and supported networks + +| Test Name | Network | Dataset | N Classes | Input size | Weights | +| :---------------- | :-------------------------------------------- | :-----------------------------------------------------------: | :-------: | :-----------: | :------------------------------------------------------------------------ | +| shelfnet | ShelfNet18_realtime1 | [Cityscapes](https://www.cityscapes-dataset.com/) | 19 | 1024x1024 | [weights](https://cloud.hipert.unimore.it/s/mEDZMRJaGCFWSJF/download) | +| shelfnet_berkeley | ShelfNet18_realtime1 | [DeepDrive](https://bdd-data.berkeley.edu/) | 20 | 1024x1024 | [weights](https://cloud.hipert.unimore.it/s/m92e7QdD9gYMF7f/download) | +| shelfnet_mapillary | ShelfNet18_realtime1 | [Mapillary Vistas](https://www.mapillary.com/dataset/vistas?pKey=aFWuj_m4nGoq3-tDz5KAqQ)* | 15 | 1024x1024 | [weights](https://cloud.hipert.unimore.it/s/6WnZCKLjik7xrny/download) | + +1. Zhuang, Juntang, et al. "ShelfNet for fast semantic segmentation." Proceedings of the IEEE International Conference on Computer Vision Workshops. 2019. + +*. Mapillary Vistas has originally 66 classes, but we reduced them to 15 to improve the results on the categories of our interest. \ No newline at end of file diff --git a/demo/demo/seg_demo.cpp b/demo/demo/seg_demo.cpp index 35b4fa8..bd31a1a 100644 --- a/demo/demo/seg_demo.cpp +++ b/demo/demo/seg_demo.cpp @@ -48,22 +48,37 @@ int main(int argc, char *argv[]) { int n_classes = 19; if(argc > 4) n_classes = atoi(argv[4]); - bool show = true; + bool resize = false; if(argc > 5) - show = atoi(argv[5]); - bool write_pred = false; + resize = atoi(argv[5]); + int baseline_resize = 1024; if(argc > 6) - write_pred = atoi(argv[6]); - + baseline_resize = atoi(argv[6]); + bool show = true; + if(argc > 7) + show = atoi(argv[7]); + bool write_pred = false; + if(argc > 8) + write_pred = atoi(argv[8]); + if(resize && (baseline_resize < 0 || baseline_resize > 5000)) + FatalError("Problem with baseline resize") if(n_batch < 1 || n_batch > 64) FatalError("Batch dim not supported"); + std::string net_name; + removePathAndExtension(net, net_name); + bool mapillary_15 = false; //TODO change me pls + if(n_classes == 15 && net_name == "shelfnet_mapillary_fp32") + mapillary_15 = true; + + //net initialization tk::dnn::SegmentationNN segNN; segNN.init(net, n_classes, n_batch); int height = 0, width = 0; - + int basewidth=baseline_resize, hsize; + if(write_pred){ std::string gt_folder = "../demo/CityScapes_val/images/"; std::string images_names = "../demo/CityScapes_val/all_images.txt"; @@ -85,9 +100,16 @@ int main(int argc, char *argv[]) { cv::VideoWriter resultVideo; if(SAVE_RESULT) { - int w = cap.get(cv::CAP_PROP_FRAME_WIDTH); - int h = cap.get(cv::CAP_PROP_FRAME_HEIGHT); - resultVideo.open("result.mp4", cv::VideoWriter::fourcc('M','P','4','V'), 30, cv::Size(1024, 1024)); + int w,h; + if(resize){ + w = basewidth; + h = int((float(cap.get(cv::CAP_PROP_FRAME_HEIGHT))*float(basewidth/float(cap.get(cv::CAP_PROP_FRAME_WIDTH))))); + } + else{ + w = cap.get(cv::CAP_PROP_FRAME_WIDTH); + h = cap.get(cv::CAP_PROP_FRAME_HEIGHT); + } + resultVideo.open("result.mp4", cv::VideoWriter::fourcc('M','P','4','V'), 30, cv::Size(w, h)); } cv::Mat frame; @@ -95,11 +117,17 @@ int main(int argc, char *argv[]) { cap >> frame; if(!frame.data) break; + + if(resize){ + hsize = int((float(frame.rows)*float(basewidth/float(frame.cols)))); + cv::resize(frame, frame, cv::Size(basewidth, hsize)); + } + height = frame.rows; width = frame.cols; //inference - segNN.updateOriginal(frame); + segNN.updateOriginal(frame, true, mapillary_15); if(show) segNN.draw(); diff --git a/include/tkDNN/NetworkViz.h b/include/tkDNN/NetworkViz.h index 94468fc..2e2c3f4 100644 --- a/include/tkDNN/NetworkViz.h +++ b/include/tkDNN/NetworkViz.h @@ -5,8 +5,8 @@ namespace tk { namespace dnn { -cv::Mat vizFloat2colorMap(cv::Mat map, double min=0, double max=0); -cv::Mat vizData2Mat(dnnType *dataInput, tk::dnn::dataDim_t dim, int imgdim, double min=0, double max=0); +cv::Mat vizFloat2colorMap(cv::Mat map, double min=0, double max=0, bool mapillary_15=false); +cv::Mat vizData2Mat(dnnType *dataInput, tk::dnn::dataDim_t dim, int imgdim, double min=0, double max=0, bool mapillary_15=false); cv::Mat vizLayer2Mat(tk::dnn::Network *net, int layer, int imgdim = 1000); }} diff --git a/include/tkDNN/SegmentationNN.h b/include/tkDNN/SegmentationNN.h index edede43..ec6ac79 100644 --- a/include/tkDNN/SegmentationNN.h +++ b/include/tkDNN/SegmentationNN.h @@ -97,7 +97,7 @@ class SegmentationNN { * * @param bi batch index */ - void postprocess(const int bi=0, bool appy_colormap = true) { + void postprocess(const int bi=0, bool appy_colormap = true, bool mapillary_15=false) { dnnType *rt_out = (dnnType *)netRT->buffersRT[1]+ netRT->buffersDIM[1].tot()*bi; dataDim_t odim = netRT->output_dim; @@ -112,7 +112,7 @@ class SegmentationNN { cv::Mat colored; if(appy_colormap) - colored = vizData2Mat(tmpOutData_h, vdim, 1024, 0, 18); + colored = vizData2Mat(tmpOutData_h, vdim, 1024, 0, classes, mapillary_15); else{ cv::Mat colored_fp32 (cv::Size(odim.w, odim.h),CV_32FC1, tmpOutData_h); colored_fp32.convertTo(colored, CV_8UC1); @@ -234,7 +234,7 @@ class SegmentationNN { } } - void updateOriginal(cv::Mat frame, bool apply_colormap=true){ + void updateOriginal(cv::Mat frame, bool apply_colormap=true, bool mapillary_15=false){ std::vector splitted_frames; int H, W, net_H, net_W; @@ -347,7 +347,7 @@ class SegmentationNN { cv::Mat colored; if(apply_colormap) - colored = vizData2Mat(tmpOutData_h, vdim, 1024, 0, 18); + colored = vizData2Mat(tmpOutData_h, vdim, 1024, 0, classes, mapillary_15); else{ cv::Mat colored_fp32 (cv::Size(odim.w, odim.h),CV_32FC1, tmpOutData_h); colored_fp32.convertTo(colored, CV_8UC1); diff --git a/src/NetworkViz.cpp b/src/NetworkViz.cpp index 842a26e..4a3f113 100644 --- a/src/NetworkViz.cpp +++ b/src/NetworkViz.cpp @@ -6,22 +6,114 @@ namespace tk { namespace dnn { -cv::Mat vizFloat2colorMap(cv::Mat map,double min, double max) { +cv::Mat mapillary_15_map(cv::Mat adjMap){ + + // cv::imshow("test", adjMap); + // cv::waitKey(0); + cv::Mat M1(1, 256, CV_8UC1), M2(1, 256, CV_8UC1), M3(1, 256, CV_8UC1); + + M3.at(0)=165; + M2.at(0)=42; + M1.at(0)=45; + + M3.at(1)=196; + M2.at(1)=196; + M1.at(1)=196; + + M3.at(2)=90; + M2.at(2)=120; + M1.at(2)=150; + + M3.at(3)=128; + M2.at(3)=64; + M1.at(3)=128; + + M3.at(4)=70; + M2.at(4)=70; + M1.at(4)=70; + + M3.at(5)=220; + M2.at(5)=20; + M1.at(5)=60; + + M3.at(6)=255; + M2.at(6)=255; + M1.at(6)=255; + + M3.at(7)=107; + M2.at(7)=142; + M1.at(7)=35; + + M3.at(8)=70; + M2.at(8)=130; + M1.at(8)=180; + + M3.at(9)=220; + M2.at(9)=220; + M1.at(9)=220; + + M3.at(10)=153; + M2.at(10)=153; + M1.at(10)=153; + + M3.at(11)=128; + M2.at(11)=128; + M1.at(11)=128; + + M3.at(12)=119; + M2.at(12)=11; + M1.at(12)=32; + + M3.at(13)=0; + M2.at(13)=0; + M1.at(13)=142; + + for(int i=14;i<256;i++) + { + M1.at(i)=0; + M2.at(i)=0; + M3.at(i)=0; + } + + cv::Mat r1,r2,r3; + + cv::LUT(adjMap,M1,r1); + cv::LUT(adjMap,M2,r2); + cv::LUT(adjMap,M3,r3); + + std::vector planes; + planes.push_back(r1); + planes.push_back(r2); + planes.push_back(r3); + + cv::Mat dst; + cv::merge(planes,dst); + return dst; + + +} + +cv::Mat vizFloat2colorMap(cv::Mat map,double min, double max, bool mapillary_15) { if(min == 0 && max == 0) cv::minMaxIdx(map, &min, &max); cv::Mat adjMap; - // expand your range to 0..255. Similar to histEq(); - map.convertTo(adjMap,CV_8UC1, 255 / (max-min), -min); - //return adjMap; - cv::Mat falseColorsMap; - applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_VIRIDIS); + + if(mapillary_15){ + map.convertTo(adjMap,CV_8UC1); + falseColorsMap = mapillary_15_map(adjMap); + } + else{ + // expand your range to 0..255. Similar to histEq(); + map.convertTo(adjMap,CV_8UC1, 255 / (max-min), -min); + applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_JET); + } return falseColorsMap; } -cv::Mat vizData2Mat(dnnType *dataInput, tk::dnn::dataDim_t dim, int imgdim, double min, double max) { +cv::Mat vizData2Mat(dnnType *dataInput, tk::dnn::dataDim_t dim, int imgdim, double min, double max, bool mapillary_15) { dnnType *data = nullptr; // copy to CPU @@ -37,7 +129,7 @@ cv::Mat vizData2Mat(dnnType *dataInput, tk::dnn::dataDim_t dim, int imgdim, doub cv::Mat grid = cv::Mat(gridSize, CV_8UC3, cv::Scalar(0)); for(int i=0; i +#include +#include + +#include "tkdnn.h" +#include "NetworkViz.h" + + +const char *input_bin = "shelfnet_mapillary/debug/input.bin"; + +const char *backbone[] = { + "shelfnet_mapillary/layers/backbone-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer1-0-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer1-0-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer1-1-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer1-1-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer2-0-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer2-0-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer2-0-downsample-0.bin", + "shelfnet_mapillary/layers/backbone-layer2-1-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer2-1-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer3-0-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer3-0-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer3-0-downsample-0.bin", + "shelfnet_mapillary/layers/backbone-layer3-1-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer3-1-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer4-0-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer4-0-conv2.bin", + "shelfnet_mapillary/layers/backbone-layer4-0-downsample-0.bin", + "shelfnet_mapillary/layers/backbone-layer4-1-conv1.bin", + "shelfnet_mapillary/layers/backbone-layer4-1-conv2.bin"}; + +const char *conv_out[] = { + "shelfnet_mapillary/layers/conv_out-conv-conv.bin", + "shelfnet_mapillary/layers/conv_out-conv_out.bin", + "shelfnet_mapillary/layers/conv_out16-conv-conv.bin", + "shelfnet_mapillary/layers/conv_out16-conv_out.bin", + "shelfnet_mapillary/layers/conv_out32-conv-conv.bin", + "shelfnet_mapillary/layers/conv_out32-conv_out.bin" + }; + +const char *decoder[] = { + "shelfnet_mapillary/layers/decoder-bottom-conv1.bin", + "shelfnet_mapillary/layers/decoder-bottom-conv12.bin", + "shelfnet_mapillary/layers/decoder-up_conv_list-0-conv-conv.bin", + "shelfnet_mapillary/layers/decoder-up_conv_list-0-conv_atten.bin", + "shelfnet_mapillary/layers/decoder-up_dense_list-0-conv.bin", + "shelfnet_mapillary/layers/decoder-up_conv_list-1-conv-conv.bin", + "shelfnet_mapillary/layers/decoder-up_conv_list-1-conv_atten.bin", + "shelfnet_mapillary/layers/decoder-up_dense_list-1-conv.bin" + }; + + +const char *ladder[] = { + "shelfnet_mapillary/layers/ladder-inconv-conv1.bin", + "shelfnet_mapillary/layers/ladder-inconv-conv12.bin", + "shelfnet_mapillary/layers/ladder-down_module_list-0-conv1.bin", + "shelfnet_mapillary/layers/ladder-down_module_list-0-conv12.bin", + "shelfnet_mapillary/layers/ladder-down_conv_list-0.bin", + + "shelfnet_mapillary/layers/ladder-down_module_list-1-conv1.bin", + "shelfnet_mapillary/layers/ladder-down_module_list-1-conv12.bin", + "shelfnet_mapillary/layers/ladder-down_conv_list-1.bin", + + "shelfnet_mapillary/layers/ladder-bottom-conv1.bin", + "shelfnet_mapillary/layers/ladder-bottom-conv12.bin", + + + + "shelfnet_mapillary/layers/ladder-up_conv_list-0-conv-conv.bin", + "shelfnet_mapillary/layers/ladder-up_conv_list-0-conv_atten.bin", + "shelfnet_mapillary/layers/ladder-up_dense_list-0-conv.bin", + + + "shelfnet_mapillary/layers/ladder-up_conv_list-1-conv-conv.bin", + "shelfnet_mapillary/layers/ladder-up_conv_list-1-conv_atten.bin", + "shelfnet_mapillary/layers/ladder-up_dense_list-1-conv.bin"}; + +const char *trans[] = { + "shelfnet_mapillary/layers/trans1-conv.bin", + "shelfnet_mapillary/layers/trans2-conv.bin", + "shelfnet_mapillary/layers/trans3-conv.bin"}; +int main() +{ + + downloadWeightsifDoNotExist(input_bin, "shelfnet_mapillary", "https://cloud.hipert.unimore.it/s/6WnZCKLjik7xrny/download"); + + int classes = 15; + + // Network layout + tk::dnn::dataDim_t dim(1, 3, 1024, 1024, 1); + tk::dnn::Network net(dim); + + int bi = 0, di = 0, li = 0, ci = 0; + new tk::dnn::Conv2d(&net, 64, 7, 7, 2, 2, 3, 3, backbone[bi++], true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + tk::dnn::Layer* last = new tk::dnn::Pooling (&net, 3, 3, 2, 2, 1, 1, tk::dnn::POOLING_MAX); + + + + for(int i=0; i<2; ++i){ + new tk::dnn::Conv2d (&net, 64, 3, 3, 1, 1, 1, 1, backbone[bi++], true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + new tk::dnn::Conv2d (&net, 64, 3, 3, 1, 1, 1, 1, backbone[bi++], true); + new tk::dnn::Shortcut(&net, last); + last = new tk::dnn::Activation (&net, CUDNN_ACTIVATION_RELU); + } + + std::vector features; + for(int i=0;i<3;++i){ + int out_channel = pow(2,7+i); + std::cout< up_out; + //bottom + new tk::dnn::Conv2d (&net, 256, 3, 3, 1, 1, 1, 1, decoder[di++], true, false, 1, true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + new tk::dnn::Conv2d (&net, 256, 3, 3, 1, 1, 1, 1, decoder[di++], true, false, 1, true); + new tk::dnn::Shortcut(&net, last); + last = new tk::dnn::Activation (&net, CUDNN_ACTIVATION_RELU); + up_out.push_back(last); + + for(int i=0; i<2; ++i){ + int out_channel = pow(2,7-i); + //up-conv + std::cout<output_dim.w, last->output_dim.h, last->output_dim.w, last->output_dim.h, 0, 0, tk::dnn::POOLING_AVERAGE); + new tk::dnn::Conv2d (&net, out_channel, 1, 1, 1, 1, 0, 0, decoder[di++], true); + + tk::dnn::Layer* act = new tk::dnn::Activation (&net, CUDNN_ACTIVATION_SIGMOID); + new tk::dnn::Route(&net, &last, 1); + new tk::dnn::Shortcut(&net, act, true); + + //interpolate + new tk::dnn::Resize(&net, 1,2,2); + new tk::dnn::Shortcut(&net, features[1-i]); + + //up-dense + new tk::dnn::Conv2d (&net, out_channel, 3, 3, 1, 1, 1, 1, decoder[di++], true); + last = new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + up_out.push_back(last); + } + + //LADDER + + std::vector down_out; + new tk::dnn::Conv2d (&net, 64, 3, 3, 1, 1, 1, 1, ladder[li++], true, false, 1, true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + new tk::dnn::Conv2d (&net, 64, 3, 3, 1, 1, 1, 1, ladder[li++], true, false, 1, true); + new tk::dnn::Shortcut(&net, last); + new tk::dnn::Activation (&net, CUDNN_ACTIVATION_RELU); + + for(int i=0; i<2;++i){ + int out_channel = pow(2,6+i); + tk::dnn::Layer* l_last = new tk::dnn::Shortcut(&net, up_out[2-i]); + + new tk::dnn::Conv2d (&net, out_channel, 3, 3, 1, 1, 1, 1, ladder[li++], true, false, 1, true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + new tk::dnn::Conv2d (&net, out_channel, 3, 3, 1, 1, 1, 1, ladder[li++], true, false, 1, true); + new tk::dnn::Shortcut(&net, l_last); + l_last = new tk::dnn::Activation (&net, CUDNN_ACTIVATION_RELU); + down_out.push_back(l_last); + + new tk::dnn::Conv2d (&net, out_channel*2, 3, 3, 2, 2, 1, 1, ladder[li++], false); + last = new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.0f); //should be ReLU + } + + new tk::dnn::Conv2d (&net, 256, 3, 3, 1, 1, 1, 1, ladder[li++], true, false, 1, true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + new tk::dnn::Conv2d (&net, 256, 3, 3, 1, 1, 1, 1, ladder[li++], true, false, 1, true); + new tk::dnn::Shortcut(&net, last); + last = new tk::dnn::Activation (&net, CUDNN_ACTIVATION_RELU); + up_out.clear(); + up_out.push_back(last); + + for(int i=0; i<2; ++i){ + int out_channel = pow(2,7-i); + //up-conv + new tk::dnn::Conv2d (&net, out_channel, 3, 3, 1, 1, 1, 1, ladder[li++], true); + last = new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + + new tk::dnn::Pooling(&net, last->output_dim.w, last->output_dim.h, last->output_dim.w, last->output_dim.h, 0, 0, tk::dnn::POOLING_AVERAGE); + new tk::dnn::Conv2d (&net, out_channel, 1, 1, 1, 1, 0, 0, ladder[li++], true); + + tk::dnn::Layer* act = new tk::dnn::Activation (&net, CUDNN_ACTIVATION_SIGMOID); + new tk::dnn::Route(&net, &last, 1); + new tk::dnn::Shortcut(&net, act, true); + + //interpolate + new tk::dnn::Resize(&net, 1,2,2); + new tk::dnn::Shortcut(&net, down_out[1-i]); + + // //up-dense + new tk::dnn::Conv2d (&net, out_channel, 3, 3, 1, 1, 1, 1, ladder[li++], true); + last = new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + up_out.push_back(last); + } + + + // for(int i=2;i>=0;--i){ + // new tk::dnn::Route(&net, &up_out[i], 1); + new tk::dnn::Conv2d (&net, 64, 3, 3, 1, 1, 1, 1, conv_out[ci++], true); + new tk::dnn::Activation (&net, tk::dnn::ACTIVATION_LEAKY, 0.0f, 0.01); + new tk::dnn::Conv2d (&net, classes, 3, 3, 1, 1, 1, 1, conv_out[ci++], false); + /*up_out[i] =*/ new tk::dnn::Resize(&net, classes, net.input_dim.h, net.input_dim.w, true, tk::dnn::ResizeMode_t::LINEAR); + // } + + new tk::dnn::Softmax(&net); + + const char *output_bin = "shelfnet_mapillary/debug/softmax.bin"; + + // Load input + dnnType *data; + dnnType *input_h; + readBinaryFile(input_bin, dim.tot(), &input_h, &data); + std::cout<<"Input:"<