0x0 EAIDK310板子初上手
前两周收到EAIDK310板子,板子做工不错,设计紧凑。主控芯片使用Rockchip的RK3228H,四核Cortext-A53,Mali 450MP2,还支持USB3.0、2.4g/5g双频Wifi等强大的连接能力。

板子结构尺寸和树莓派3是完全兼容的,使用之前树莓派3的盒子装上去灰常的合适,哈哈哈~
经过两周的使用感觉很不错,运行稳定。用Tengine框架尝试跑了几个模型,根据官方提供文档和代码示例上手还是比较容易的,这次先发OpenPose项目分享给大家。
代码已经上传到github上,获取地址在这里。
0x1 环境准备
Tengine版本
本项目直接使用Tengine 1.7.1内测版本,想要试用的同学可以向官方申请。
有试了一下直接使用从官网下载的1.0版本,也是可以转换成功的,openpose模型的算子Tengine1.0应该就能支持。
模型下载
OpenPose原始模型可以从这里下载:
prototxt
caffemodel
其中,本项目将prototxt的输入层修改为160x160。
模型转换
使用以下命令将OpenPose原始的Caffe模型转换为Tengine模型:
./convert_model_to_tm -f caffe -p pose_deploy_linevec.prototxt -m pose_iter_440000.caffemodel -o openpose.tm0x2 代码编写
初始化和加载模型
Tengine初始化和加载模型的代码基本和classification示例程序一样,代码片段如下所示:
    // init tengine
    init_tengine();
    if(request_tengine_version("0.9") < 0)
        return false;
    // load model
    graph_t graph = nullptr;
    graph = create_graph(nullptr, model_format, model_file);
    if(!graph)
    {
        std::cerr << "Create graph0 failed.\n";
        std::cout << "errno:" << get_tengine_errno() << "\n";
        return false;
    }
    // input
    int img_size = img_h * img_w * 3;
    int dims[] = {1, 3, img_h, img_w};
    float* input_data = ( float* )malloc(sizeof(float) * img_size);
    tensor_t input_tensor = get_graph_input_tensor(graph, 0, 0);
    set_tensor_shape(input_tensor, dims, 4);
    // prerun
    int ret = prerun_graph(graph);
    if(-1 == ret)
    {
        std::cout << " prerun graph failed \n";
        return false;
    }其中create_graph传入的参数model_format为tengine,model_file为openpose.tm模型文件路径。
输入数据前处理
数据前处理包括图像缩放、bgr->rgb转换以及mean和scale的处理。处理函数和classification示例一样。
void get_input_data(const char* image_file, float* input_data, const int img_h, const int img_w, const float* mean,
                    const float scale)
{
    image img_o = imread(image_file);
    if(img_o.data == NULL)
    {
        std::cerr << "Failed to read image file " << image_file << ".\n";
        return;
    }
    image img = resize_image(img_o, img_w, img_h);    
    img = rgb2bgr_premute(img);    
    int hw = img_h * img_w;
    for(int h = 0; h < img_h; h++)
    {
        for(int w = 0; w < img_w; w++)
        {
            for(int c = 0; c < 3; c++)
            {
                input_data[c * hw + h * img_w + w] = 
                                    (img.data[c * hw + h * img_w + w] - mean[c]) * scale;
            }
        }
    }
}其中传入的目标缩放宽高设为160,mean和scale需要分别设置成0 0 0和1.0/255。
模型推理
接下来就是设置模型输入Tensor、进行模型推理和获取输出Tensor数据了,代码如下所示:
       get_input_data(image_file, input_data, img_h, img_w, mean, scale);
       set_tensor_buffer(input_tensor, input_data, img_size * 4);
       run_graph(graph, 1);
       tensor_t output_tensor = get_graph_output_tensor(graph, 0, 0);
       float* data = ( float* )get_tensor_buffer(output_tensor);输出后处理
OpenPose模型推理的输出Tensor数据只是一个热图(heatmap),还需要进行缩放、滤波、关键点检测和获取有效连接等处理才能最后得到输出的点。
后处理代码根据网上开源的opencv版本(地址)进行修改,网上有和该代码相应的资料,感兴趣的同学可以去学习(地址),这里就不详细介绍后处理的流程。
0x3 运行效果
下面是对一些图片的运行效果,目前在EAIDK310上用float32跑速度会有点慢,下一步打算对模型进行量化来提升速度。

