diff --git a/psenet/CMakeLists.txt b/psenet/CMakeLists.txt
new file mode 100644
index 0000000..b9c6ef2
--- /dev/null
+++ b/psenet/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 2.6)
+
+project(PSENet)
+
+add_definitions(-std=c++11)
+
+option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_BUILD_TYPE Debug)
+
+find_package(CUDA REQUIRED)
+
+set(CUDA_NVCC_PLAGS ${CUDA_NVCC_PLAGS};-std=c++11;-g;-G;-gencode;arch=compute_30;code=sm_30)
+
+include_directories(${PROJECT_SOURCE_DIR}/include)
+# include and link dirs of cuda and tensorrt, you need adapt them if yours are different
+# cuda
+include_directories(/usr/local/cuda/include)
+link_directories(/usr/local/cuda/lib64)
+# tensorrt
+include_directories(/usr/include/x86_64-linux-gnu/)
+link_directories(/usr/lib/x86_64-linux-gnu/)
+
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Ofast -Wfatal-errors -D_MWAITXINTRIN_H_INCLUDED")
+
+
+
+find_package(OpenCV)
+include_directories(OpenCV_INCLUDE_DIRS)
+
+file(GLOB SOURCE_FILES "*.h" "*.cpp")
+
+add_executable(psenet ${SOURCE_FILES})
+target_link_libraries(psenet nvinfer)
+target_link_libraries(psenet cudart)
+target_link_libraries(psenet ${OpenCV_LIBS})
+
+add_definitions(-O2 -pthread)
+
diff --git a/psenet/README.md b/psenet/README.md
new file mode 100644
index 0000000..e019609
--- /dev/null
+++ b/psenet/README.md
@@ -0,0 +1,53 @@
+# PSENet
+
+**preprocessing + inference + postprocessing = 30ms** with fp32 on Tesla P40.
+The Tensorflow implementation is [tensorflow_PSENet](https://github.com/liuheng92/tensorflow_PSENet).
+
+## Key Features
+- Generating `.wts` from `Tensorflow`.
+- Dynamic batch and dynamic shape input.
+- Object-Oriented Programming.
+- Practice with C++ 11.
+
+
+
+
+## How to Run
+
+* 1. generate .wts
+
+ Download pretrained model from https://github.com/liuheng92/tensorflow_PSENet
+ and put `model.ckpt.*` to `model` dir. Add a file `model/checkpoint` with content
+ ```
+ model_checkpoint_path: "model.ckpt"
+ all_model_checkpoint_paths: "model.ckpt"
+ ```
+ Then run
+
+ ```
+ python gen_tf_wts.py
+ ```
+ which will gengerate a `psenet.wts`.
+* 2. cmake and make
+
+ ```
+ mkdir build
+ cd build
+ cmake ..
+ make
+ ```
+* 3. build engine and run detection
+ ```
+ cp ../psenet.wts ./
+ cp ../test.jpg ./
+ ./psenet -s // serialize model to plan file
+ ./psenet -d // deserialize plan file and run inference"
+ ```
+
+## Known Issues
+1. The output of network is not completely the same as the tf's due to the difference between tensorrt's `addResize` and `tf.image.resize`, I will figure it out.
+
+## Todo
+
+* use `ExponentialMovingAverage` weight.
+* faster preporcess and postprocess.
\ No newline at end of file
diff --git a/psenet/gen_tf_wts.py b/psenet/gen_tf_wts.py
new file mode 100644
index 0000000..0501b75
--- /dev/null
+++ b/psenet/gen_tf_wts.py
@@ -0,0 +1,31 @@
+from sys import prefix
+import tensorflow as tf
+from tensorflow.python import pywrap_tensorflow
+import numpy as np
+import struct
+
+model_dir = "model"
+
+ckpt = tf.train.get_checkpoint_state(model_dir)
+ckpt_path = ckpt.model_checkpoint_path
+
+reader = pywrap_tensorflow.NewCheckpointReader(ckpt_path)
+param_dict = reader.get_variable_to_shape_map()
+
+
+f = open(r"psenet.wts", "w")
+keys = param_dict.keys()
+f.write("{}\n".format(len(keys)))
+
+for key in keys:
+ weight = reader.get_tensor(key)
+ print(key, weight.shape)
+ if len(weight.shape) == 4:
+ weight = np.transpose(weight, (3, 2, 0, 1))
+ print(weight.shape)
+ weight = np.reshape(weight, -1)
+ f.write("{} {} ".format(key, len(weight)))
+ for w in weight:
+ f.write(" ")
+ f.write(struct.pack(">f", float(w)).hex())
+ f.write("\n")
\ No newline at end of file
diff --git a/psenet/layers.cpp b/psenet/layers.cpp
new file mode 100644
index 0000000..acbaba0
--- /dev/null
+++ b/psenet/layers.cpp
@@ -0,0 +1,136 @@
+#include "layers.h"
+
+IScaleLayer *addBatchNorm2d(INetworkDefinition *network, std::map &weightMap, ITensor &input, std::string lname, float eps)
+{
+ float *gamma = (float *)weightMap[lname + "gamma"].values; // scale
+ float *beta = (float *)weightMap[lname + "beta"].values; // offset
+ float *mean = (float *)weightMap[lname + "moving_mean"].values;
+ float *var = (float *)weightMap[lname + "moving_variance"].values;
+ int len = weightMap[lname + "moving_variance"].count;
+
+ float *scval = reinterpret_cast(malloc(sizeof(float) * len));
+ for (auto i = 0; i < len; i++)
+ {
+ scval[i] = gamma[i] / sqrt(var[i] + eps);
+ }
+ Weights scale{DataType::kFLOAT, scval, len};
+
+ float *shval = reinterpret_cast(malloc(sizeof(float) * len));
+ for (auto i = 0; i < len; i++)
+ {
+ shval[i] = beta[i] - mean[i] * gamma[i] / sqrt(var[i] + eps);
+ }
+ Weights shift{DataType::kFLOAT, shval, len};
+
+ float *pval = reinterpret_cast(malloc(sizeof(float) * len));
+ for (auto i = 0; i < len; i++)
+ {
+ pval[i] = 1.0;
+ }
+ Weights power{DataType::kFLOAT, pval, len};
+
+ IScaleLayer *scale_1 = network->addScale(input, ScaleMode::kCHANNEL, shift, scale, power);
+ assert(scale_1);
+ return scale_1;
+}
+
+IActivationLayer *bottleneck(INetworkDefinition *network, std::map &weightMap, ITensor &input, int ch, int stride, std::string lname, int branch_type)
+{
+
+ Weights emptywts{DataType::kFLOAT, nullptr, 0};
+
+ IConvolutionLayer *conv1 = network->addConvolution(input, ch, DimsHW{1, 1}, weightMap[lname + "conv1/weights"], emptywts);
+ assert(conv1);
+
+ Dims conv1_shape = conv1->getOutput(0)->getDimensions();
+
+ IScaleLayer *bn1 = addBatchNorm2d(network, weightMap, *conv1->getOutput(0), lname + "conv1/BatchNorm/", 1e-5);
+ assert(bn1);
+
+ Dims bn1_shape = bn1->getOutput(0)->getDimensions();
+
+ IActivationLayer *relu1 = network->addActivation(*bn1->getOutput(0), ActivationType::kRELU);
+ assert(relu1);
+
+ Dims relu1_shape = relu1->getOutput(0)->getDimensions();
+
+ IConvolutionLayer *conv2 = network->addConvolution(*relu1->getOutput(0), ch, DimsHW{3, 3}, weightMap[lname + "conv2/weights"], emptywts);
+ assert(conv2);
+ conv2->setStride(DimsHW{stride, stride});
+ conv2->setPadding(DimsHW{1, 1});
+
+ Dims conv2_shape = conv2->getOutput(0)->getDimensions();
+
+ IScaleLayer *bn2 = addBatchNorm2d(network, weightMap, *conv2->getOutput(0), lname + "conv2/BatchNorm/", 1e-5);
+ assert(bn2);
+
+ Dims bn2_shape = bn2->getOutput(0)->getDimensions();
+
+ IActivationLayer *relu2 = network->addActivation(*bn2->getOutput(0), ActivationType::kRELU);
+ assert(relu2);
+
+ Dims relu2_shape = relu2->getOutput(0)->getDimensions();
+
+ IConvolutionLayer *conv3 = network->addConvolution(*relu2->getOutput(0), ch * 4, DimsHW{1, 1}, weightMap[lname + "conv3/weights"], emptywts);
+ assert(conv3);
+
+ Dims conv3_shape = conv3->getOutput(0)->getDimensions();
+
+ IScaleLayer *bn3 = addBatchNorm2d(network, weightMap, *conv3->getOutput(0), lname + "conv3/BatchNorm/", 1e-5);
+ assert(bn3);
+ IElementWiseLayer *ew1;
+ Dims ew1_shape;
+
+ // branch_type 0:shortcut,1:conv+bn+shortcut,2:maxpool+shortcut
+ if (branch_type == 0)
+ {
+ ew1 = network->addElementWise(input, *bn3->getOutput(0), ElementWiseOperation::kSUM);
+ assert(ew1);
+ ew1_shape = ew1->getOutput(0)->getDimensions();
+ assert(ew1);
+ }
+ else if (branch_type == 1)
+ {
+ IConvolutionLayer *conv4 = network->addConvolution(input, ch * 4, DimsHW{1, 1}, weightMap[lname + "shortcut/weights"], emptywts);
+ assert(conv4);
+ conv4->setStride(DimsHW{stride, stride});
+ IScaleLayer *bn4 = addBatchNorm2d(network, weightMap, *conv4->getOutput(0), lname + "shortcut/BatchNorm/", 1e-5);
+ assert(bn4);
+ ew1 = network->addElementWise(*bn4->getOutput(0), *bn3->getOutput(0), ElementWiseOperation::kSUM);
+ assert(ew1);
+ ew1_shape = ew1->getOutput(0)->getDimensions();
+ assert(ew1);
+ }
+ else
+ {
+ IPoolingLayer *pool = network->addPoolingNd(input, PoolingType::kMAX, DimsHW{1, 1});
+ assert(pool);
+ pool->setStrideNd(DimsHW{2, 2});
+ ew1 = network->addElementWise(*pool->getOutput(0), *bn3->getOutput(0), ElementWiseOperation::kSUM);
+ assert(ew1);
+ ew1_shape = ew1->getOutput(0)->getDimensions();
+ assert(ew1);
+ }
+
+ IActivationLayer *relu3 = network->addActivation(*ew1->getOutput(0), ActivationType::kRELU);
+
+ Dims relu3_shape = relu3->getOutput(0)->getDimensions();
+
+ assert(relu3);
+ return relu3;
+}
+
+IActivationLayer *ConvRelu(INetworkDefinition *network, std::map &weightMap, ITensor &input, int outch, int kernel, int stride, std::string lname)
+{
+ IConvolutionLayer *conv = network->addConvolution(input, 256, DimsHW{kernel, kernel}, weightMap[lname + "weights"], weightMap[lname + "biases"]);
+ assert(conv);
+ conv->setStride(DimsHW{stride, stride});
+ if (kernel == 3 || stride == 2)
+ {
+ conv->setPadding(DimsHW{1, 1});
+ }
+
+ IActivationLayer *ac = network->addActivation(*conv->getOutput(0), ActivationType::kRELU);
+ assert(ac);
+ return ac;
+}
\ No newline at end of file
diff --git a/psenet/layers.h b/psenet/layers.h
new file mode 100644
index 0000000..bd85a18
--- /dev/null
+++ b/psenet/layers.h
@@ -0,0 +1,18 @@
+#ifndef TENSORRTX_LAYERS_H
+#define TENSORRTX_LAYERS_H
+
+#include