训练
参考:https://github.com/dbolya/yolact
转换
1.torch->onnx:
引用:https://zhuanlan.zhihu.com/p/128974102
torch version:1.10.2+cpu
(1)直接修改训练代码里的 eval.py 的 evalimage,把结果展示换成 onnx export
def evalimage(net:Yolact, path:str, save_path:str=None):
frame = torch.from_numpy(cv2.imread(path)).cuda().float()
batch = FastBaseTransform()(frame.unsqueeze(0))
preds = net(batch)
torch.onnx._export(net, batch, "yolact.onnx", export_params=True, keep_initializers_as_inputs=True, opset_version=11)
(2)根据YOLACT issue中的信息,yolact.py开头的JIT要关掉才能导出onnx
#As of March 10, 2019, Pytorch DataParallel still doesn't support JIT Script Modules
use_jit = False
(3)打开yolact.py,找到 class Yolact 的 forward 方法,把 detect 过程去掉,直接返回模型的 pred_outs 输出
# return self.detect(pred_outs, self)
return pred_outs;
(4)再一次跑一遍图片测试,不包含后处理的 yolact.onnx 出现了
python eval.py --trained_model=weights/yolact_resnet50_54_800000.pth --score_threshold=0.15 --top_k=15 --image=test.jpg
(5)简化onnx
直接导出的onnx模型有很多胶水op是ncnn不支持的,用onnx-simplifier是常规操作,得到yolact-sim.onnx
pip install -U onnx --user
pip install -U onnxruntime --user
pip install -U onnx-simplifier --user
python -m onnxsim --skip-fuse-bn yolact.onnx yolact-sim.onnx
(6)在onnx删除不支持的节点:
528 add 0
以及删除“priors”的输出
输入yolact-sim.onnx模型,得到yolact-ss.onnx模型
python onnx_simple.py
#onnx_simple.py
import onnx
model = onnx.load('yolact-sim.onnx')
#'''
# find node info
fileName='node_info.txt'
with open(fileName,'w',encoding='utf-8')as file:
for node_id,node in enumerate(model.graph.node):
file.write("######%s######rn" % node_id)
file.write(str(node))
fileName='node_out.txt'
with open(fileName,'w',encoding='utf-8')as file:
file.write(str(model.graph.output))
#'''
#删除caffe难转的add节点,加了个0,删除接上就好
testnode = model.graph.node[121]
testnode.input[0]="527"
testnode = model.graph.node[127]
testnode.input[0]="527"
add_node = model.graph.node[120]
model.graph.node.remove(add_node)
#删除不必要的constant节点 priors,原应212,但上面删除了一个节点,所以-1了
constant_node = model.graph.node[211]
model.graph.node.remove(constant_node)
out_node = model.graph.output[3]
model.graph.output.remove(out_node)
onnx.checker.check_model(model)#检查模型
onnx.save(model,"yolact-ss.onnx")
print("OK")
2.onnx->caffe:
前提环境:caffe
caffe安装参考:https://github.com/Wulingtian/yolov5_caffe.git
转换参考:
https://github.com/Wulingtian/yolov5_onnx2caffe
cd yolov5_onnx2caffe-master
_operators.py里_convert_resize 492行更改成被relu夹住的resize名称(“Resize_159”)
def _convert_resize(node,graph,err):
# factor_list = node.input_tensors.get(node.inputs[1])
node_name = node.name
input_name = str(node.inputs[0])
output_name = str(node.outputs[0])
mode = node.attrs["mode"]
#https://github.com/pytorch/pytorch/issues/6900
if str(mode,encoding="gbk") == "linear" or str(mode,encoding="gbk") == "nearest": #mode=="linear":
channels = graph.channel_dims[input_name]
scales = node.input_tensors.get(node.inputs[1])
if node_name=="Resize_159":
height_scale = 2
width_scale = 2
pad_h = int(math.ceil((height_scale - 1) / 2.))
pad_w = int(math.ceil((width_scale - 1) / 2.))
layer = myf("Deconvolution", node_name, [input_name], [output_name],
convolution_param=dict(
num_output=channels,
kernel_h=int(2 * height_scale - height_scale % 2),
kernel_w=int(2 * width_scale - width_scale % 2),
stride_h=height_scale,
stride_w=height_scale,
pad_h=pad_h,
pad_w=pad_w,
group=channels,
bias_term=False,
weight_filler=dict(type="bilinear")
),param=dict(lr_mult=0,decay_mult=0))
graph.channel_dims[output_name] = graph.channel_dims[input_name]
return layer
if len(scales) ==0:
layer = myf("Deconvolution", node_name, [input_name], [output_name],
convolution_param=dict(
num_output=channels,
kernel_h=3,#int(2 * height_scale - height_scale % 2),
kernel_w=3,#int(2 * width_scale - width_scale % 2),
stride_h=2,#height_scale,
stride_w=2,#height_scale,
pad_h=1,#pad_h,
pad_w=1,#pad_w,
group=channels,
bias_term=False,
weight_filler=dict(type="bilinear")
),param=dict(lr_mult=0,decay_mult=0))
graph.channel_dims[output_name] = graph.channel_dims[input_name]
return layer
height_scale = int(scales[2])
width_scale = int(scales[3])
pad_h = int(math.ceil((height_scale - 1) / 2.))
pad_w = int(math.ceil((width_scale - 1) / 2.))
layer = myf("Deconvolution", node_name, [input_name], [output_name],
convolution_param=dict(
num_output=channels,
# kernel_size=(int(2 * height_scale - height_scale % 2),int(2 * width_scale - width_scale % 2)),
# stride=(height_scale,width_scale),
# pad=(pad_h,pad_w),
kernel_h=int(2 * height_scale - height_scale % 2),
kernel_w=int(2 * width_scale - width_scale % 2),
stride_h=height_scale,
stride_w=height_scale,
pad_h=pad_h,
pad_w=pad_w,
group=channels,
bias_term=False,
weight_filler=dict(type="bilinear")
),param=dict(lr_mult=0,decay_mult=0))
# https://github.com/jnulzl/caffe_plus 里面的upsample 是用的nearest插值
else:
channels = graph.channel_dims[input_name]
scales = node.input_tensors.get(node.inputs[1])
height_scale = int(scales[2])
width_scale = int(scales[3])
pad_h = int(math.ceil((height_scale - 1) / 2.))
pad_w = int(math.ceil((width_scale - 1) / 2.))
layer = myf("Deconvolution", node_name, [input_name], [output_name],
convolution_param=dict(
num_output=channels,
kernel_h=int(2 * height_scale - height_scale % 2),
kernel_w=int(2 * width_scale - width_scale % 2),
stride_h=height_scale,
stride_w=height_scale,
pad_h=pad_h,
pad_w=pad_w,
group=channels,
bias_term=False,
weight_filler=dict(type="bilinear")
),param=dict(lr_mult=0,decay_mult=0))
graph.channel_dims[output_name] = graph.channel_dims[input_name]
return layer
//若模型名字有变,更改 convertCaffe.py 里onnx_path 。
//输入mymodel/yolact-ss.onnx,输出yolact.caffemodel、yolact.prototxt
python convertCaffe.py
# convertCaffe.py
if __name__ == "__main__":
onnx_path = "mymodel/yolact-ss.onnx"
prototxt_path = "mymodel/yolact.prototxt"
caffemodel_path = "mymodel/yolact.caffemodel"
graph = getGraph(onnx_path)
convertToCaffe(graph, prototxt_path, caffemodel_path)
3.ndk转换:
test_file.json
[
{
"image_path": "/home/xy/model_test/yolov3/image/",
"mean": [103.94, 116.78, 123.68],
"extension": "jpg",
"name": "input.1",
"raw_scale": 1.0,
"input_scale": 0.0171291538198013,
"grayscale": 0,
"channel_swap": [0,1,2]
}
]
aisdk部署:
main.cpp替换运行即可
//后处理参考:https://github.com/Tencent/ncnn/blob/master/examples/yolact.cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include "test_utils.h"
#include "unisocai.h"
#define INPUT_SIZE 550
#define INPUT_W 550
#define INPUT_H 550
#define LOG_TAG "UNISOC_AISDK"
#define FLOAT32 "float32"
#define Q8A "Q8A"
using namespace cv;
using namespace std;
struct Object
{
cv::Rect_ rect;
int label;
float prob;
std::vector maskdata;
cv::Mat mask;
};
static inline float intersection_area(const Object& a, const Object& b)
{
cv::Rect_ inter = a.rect & b.rect;
return inter.area();
}
static void qsort_descent_inplace(std::vector
至此大功告成
备注:
测试中转模型,onnx和caffe可以使用opencv测试,关键词,forward、cv::dnn::readNet