VIM3 Aml_npu yolo demo相关问题

@Frank @numbqq 你们好,请教几个关于 gitlab上aml_npu_app demo(yolo)里的几个问题
(行文有点长,劳烦了 :sweat_smile:

1.demo的图像预处理是不是都转为了(-1,1)?

2.aml_npu_demo_binaries/detect_demo_picture工程是否有源码demo,比如从图片输入到预处理转换输入模型的过程?

3.demo中关于/dev/video*,对摄像头型号有要求吗?

  • 我用了三款摄像头,在ubuntu上使用guvcview,都可以使用;
    camera A: usb+1920x1080高清人脸识别摄像头;
    camera B:usb + 640x480 仅支持gray;
    camera C:usb + 640x480 rgb/gray/yuv;
  • 本地编写c++ opencv调摄像头也都可以成功
    (这种情况测试仅camera B摄像头会报 GStreamer-CRITICAL **: 08:19:47.254: gst_element_get_state: assertion 'GST_IS_ELEMENT (element)' failed ,但仍然可以采集图像),其余都正常;

  • 但当我执行./detect_demo_uvc_fb 3 /dev/video0(ctrl+alt+f1下执行) 或 ./detect_demo_x11 3 /dev/video0(gnome桌面下执行),出现了下图的问题:

    故我调用摄像头的问题可能出现在哪里?

4.下面是关于编译自己自定义卷积层的yolo模型:模型输出fmap是20x15x48和40x30x48

按照之前编译mobilenet的step1/step2/step3,可以编译出自己的***.nb和libnn_***.so,存在的问题如下:

  • 所有的分类/检测/分割都是使用 2_export_case_code.sh这个生成vnn_code***.h/.c吗?

  • 我以下的操作方式是否有误?

我使用2_export_case_code.sh,生成了下图的代码:截图录屏_选择区域_20201119165757
删除了vnn_fruitdetect.c代码里存在的preprocess和postprocess,并在目录下/detect_library/model_code/fruit_detect新建了fruit_detect工程,把红框中的代码放到相应位置,并另外两个.c文件里修改相应yolo前向解析的参数,如下
截图录屏_选择区域_20201119170525

再执行./build_vx.sh $path/to/linux_sdk_dir,生成了bin_r目录,编译出自己的***.nb和libnn_***.so

接下来是不是应该新建一个工程,读取图像文件或者摄像头并解析resultData?

@librazxc

  1. 建议你先查看一下我们Docs上的文档,你的问题上面很多都有解释

这个不是一定的,你也可以选择使用归一到[0,1],不一定是[1,1]

源码在我们的gitlab上面,docs上有写具体的地址,

你使用的是USB摄像头,请确认你的节点是不是video0.如果你是开机以后才插入USB摄像头,那么USB摄像头的节点应该是video1.如果你在开机前就插好了摄像头,那么video0就是你的USB摄像头,我认为这里是你打开错了摄像头节点.我们测试时使用的是罗技的摄像头,按道理说,一般的USB摄像头能在ubuntu下打开就是能使用的.

  1. 我不清楚你删除前处理和后处理的代码,是为了做什么.你可以参考我们的文档操作的

@Frank 你好,我是按照这个HowToTransformYolo 来学习的。理论上我想的是只要名字替换掉你们原来的*.nb和*.so,应该是可以直接执行的。

这个删除的地方说明如下:
我下载下来的SDK,是这个样子,


里面的conversion_script是一个关于mobilenettf的转换例子,我就是用这个里面的**.sh来输出我的yolo 模型vnn_fruitdetect.c和.h的文件,然后我把生成的vnn_fruitdetect.c和你们gitlab给的yolo_v3 demo里面的vnn_yolov3.c这个函数做对比,会多出image process部分,如下:

vnn_fruitdetect.c(详细内容可点开查看)
/****************************************************************************
*   Generated by ACUITY 5.11.0
*   Match ovxlib 1.1.21
*
*   Neural Network appliction network definition source file
****************************************************************************/
/*-------------------------------------------
                   Includes
 -------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>

#include "vsi_nn_pub.h"

#include "vnn_global.h"
#include "vnn_fruitdetect.h"

/*-------------------------------------------
                   Macros
 -------------------------------------------*/

#define NEW_VXNODE(_node, _type, _in, _out, _uid) do {\
        _node = vsi_nn_AddNode( graph, _type, _in, _out, NULL );\
        _node->uid = (uint32_t)_uid; \
        if( NULL == _node ) {\
            goto error;\
        }\
    } while(0)

#define NEW_VIRTUAL_TENSOR(_id, _attr, _dtype) do {\
        memset( _attr.size, 0, VSI_NN_MAX_DIM_NUM * sizeof(uint32_t));\
        _attr.dim_num = VSI_NN_DIM_AUTO;\
        _attr.vtl = !VNN_APP_DEBUG;\
        _attr.is_const = FALSE;\
        _attr.dtype.vx_type = _dtype;\
        _id = vsi_nn_AddTensor( graph, VSI_NN_TENSOR_ID_AUTO,\
                & _attr, NULL );\
        if( VSI_NN_TENSOR_ID_NA == _id ) {\
            goto error;\
        }\
    } while(0)

// Set const tensor dims out of this macro.
#define NEW_CONST_TENSOR(_id, _attr, _dtype, _ofst, _size) do {\
        data = load_data( fp, _ofst, _size  );\
        _attr.vtl = FALSE;\
        _attr.is_const = TRUE;\
        _attr.dtype.vx_type = _dtype;\
        _id = vsi_nn_AddTensor( graph, VSI_NN_TENSOR_ID_AUTO,\
                & _attr, data );\
        free( data );\
        if( VSI_NN_TENSOR_ID_NA == _id ) {\
            goto error;\
        }\
    } while(0)

// Set generic tensor dims out of this macro.
#define NEW_NORM_TENSOR(_id, _attr, _dtype) do {\
        _attr.vtl = FALSE;\
        _attr.is_const = FALSE;\
        _attr.dtype.vx_type = _dtype;\
        _id = vsi_nn_AddTensor( graph, VSI_NN_TENSOR_ID_AUTO,\
                & _attr, NULL );\
        if( VSI_NN_TENSOR_ID_NA == _id ) {\
            goto error;\
        }\
    } while(0)

// Set generic tensor dims out of this macro.
#define NEW_NORM_TENSOR_FROM_HANDLE(_id, _attr, _dtype) do {\
        _attr.vtl = FALSE;\
        _attr.is_const = FALSE;\
        _attr.dtype.vx_type = _dtype;\
        _id = vsi_nn_AddTensorFromHandle( graph, VSI_NN_TENSOR_ID_AUTO,\
                & _attr, NULL );\
        if( VSI_NN_TENSOR_ID_NA == _id ) {\
            goto error;\
        }\
    } while(0)

#define NET_NODE_NUM            (1)
#define NET_NORM_TENSOR_NUM     (3)
#define NET_CONST_TENSOR_NUM    (0)
#define NET_VIRTUAL_TENSOR_NUM  (2)
#define NET_TOTAL_TENSOR_NUM    (NET_NORM_TENSOR_NUM + NET_CONST_TENSOR_NUM + NET_VIRTUAL_TENSOR_NUM)

/*-------------------------------------------
               Local Variables
 -------------------------------------------*/

/*-------------------------------------------
                  Functions
 -------------------------------------------*/
static uint8_t* load_data
    (
    FILE  * fp,
    size_t  ofst,
    size_t  sz
    )
{
    uint8_t* data;
    int32_t ret;
    data = NULL;
    if( NULL == fp )
    {
        return NULL;
    }

    ret = fseek(fp, ofst, SEEK_SET);
    if (ret != 0)
    {
        VSILOGE("blob seek failure.");
        return NULL;
    }

    data = (uint8_t*)malloc(sz);
    if (data == NULL)
    {
        VSILOGE("buffer malloc failure.");
        return NULL;
    }
    ret = fread(data, 1, sz, fp);
    return data;
} /* load_data() */

vsi_nn_graph_t * vnn_CreateFruitdetect
    (
    const char * data_file_name,
    vsi_nn_context_t in_ctx,
//删除了下面的图像处理部分
    const vsi_nn_preprocess_map_element_t * pre_process_map,
    uint32_t pre_process_map_count,
    const vsi_nn_postprocess_map_element_t * post_process_map,
    uint32_t post_process_map_count
    )
{
    vsi_status              status;
    vsi_bool                release_ctx;
    vsi_nn_context_t        ctx;
    vsi_nn_graph_t *        graph;
    vsi_nn_node_t *         node[NET_NODE_NUM];
    vsi_nn_tensor_id_t      norm_tensor[NET_NORM_TENSOR_NUM];
    
    vsi_nn_tensor_attr_t    attr;
    FILE *                  fp;
    uint8_t *               data;
//删除了下面的图像处理部分
    uint32_t                i = 0;
    char *                  use_img_process_s;
    int32_t                 enable_pre_post_process = 0;





    ctx = NULL;
    graph = NULL;
    status = VSI_FAILURE;
    memset( &attr, 0, sizeof( attr ) );

    fp = fopen( data_file_name, "rb" );
    if( NULL == fp )
    {
        VSILOGE( "Open file %s failed.", data_file_name );
        goto error;
    }

    if( NULL == in_ctx )
    {
        ctx = vsi_nn_CreateContext();
    }
    else
    {
        ctx = in_ctx;
    }

    graph = vsi_nn_CreateGraph( ctx, NET_TOTAL_TENSOR_NUM, NET_NODE_NUM );
    if( NULL == graph )
    {
        VSILOGE( "Create graph fail." );
        goto error;
    }
    vsi_nn_SetGraphVersion( graph, VNN_VERSION_MAJOR, VNN_VERSION_MINOR, VNN_VERSION_PATCH );
    vsi_nn_SetGraphInputs( graph, NULL, 1 );
    vsi_nn_SetGraphOutputs( graph, NULL, 2 );

/*-----------------------------------------
  Register client ops
 -----------------------------------------*/


/*-----------------------------------------
  Node definitions
 -----------------------------------------*/

    /*-----------------------------------------
      lid       - nbg_0
      var       - node[0]
      name      - nbg
      operation - nbg
      input     - [320, 240, 3, 1]
      output    - [20, 15, 48, 1]
                  [40, 30, 48, 1]
    -----------------------------------------*/
    NEW_VXNODE(node[0], VSI_NN_OP_NBG, 1, 2, 0);
    node[0]->nn_param.nbg.type = VSI_NN_NBG_FILE;
    node[0]->nn_param.nbg.url = data_file_name;


/*-----------------------------------------
  Tensor initialize
 -----------------------------------------*/
    attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW;
    /* @input_0:out0 */
    attr.size[0] = 320;
    attr.size[1] = 240;
    attr.size[2] = 3;
    attr.size[3] = 1;
    attr.dim_num = 4;
    attr.dtype.fl = 7;
    attr.dtype.qnt_type = VSI_NN_QNT_TYPE_DFP;
    NEW_NORM_TENSOR(norm_tensor[0], attr, VSI_NN_TYPE_INT8);

    /* @output_19:out0 */
    attr.size[0] = 20;
    attr.size[1] = 15;
    attr.size[2] = 48;
    attr.size[3] = 1;
    attr.dim_num = 4;
    attr.dtype.fl = 1;
    attr.dtype.qnt_type = VSI_NN_QNT_TYPE_DFP;
    NEW_NORM_TENSOR(norm_tensor[1], attr, VSI_NN_TYPE_INT8);

    /* @output_26:out0 */
    attr.size[0] = 40;
    attr.size[1] = 30;
    attr.size[2] = 48;
    attr.size[3] = 1;
    attr.dim_num = 4;
    attr.dtype.fl = 1;
    attr.dtype.qnt_type = VSI_NN_QNT_TYPE_DFP;
    NEW_NORM_TENSOR(norm_tensor[2], attr, VSI_NN_TYPE_INT8);







/*-----------------------------------------
  Connection initialize
 -----------------------------------------*/
    node[0]->input.tensors[0] = norm_tensor[0];
    node[0]->output.tensors[0] = norm_tensor[1];
    node[0]->output.tensors[1] = norm_tensor[2];

    /* nbg_0 */



    graph->input.tensors[0] = norm_tensor[0];
    graph->output.tensors[0] = norm_tensor[1];
    graph->output.tensors[1] = norm_tensor[2];

//删除了下面的图像处理部分
    use_img_process_s = getenv( "VSI_USE_IMAGE_PROCESS" );
    if( use_img_process_s )
    {
        enable_pre_post_process = atoi(use_img_process_s);
    }
    if( enable_pre_post_process )
    {
        if( pre_process_map_count > 0 )
        {
            for( i = 0; i < pre_process_map_count; i++ )
            {
                status = vsi_nn_AddGraphPreProcess(graph, pre_process_map[i].graph_input_idx,
                                                   pre_process_map[i].preprocesses,
                                                   pre_process_map[i].preprocess_count);
                TEST_CHECK_STATUS( status, error );
            }
        }

        if( post_process_map_count > 0 )
        {
            for( i = 0; i < post_process_map_count; i++ )
            {
                 status = vsi_nn_AddGraphPostProcess(graph, post_process_map[i].graph_output_idx,
                                                     post_process_map[i].postprocesses,
                                                     post_process_map[i].postprocess_count);
                 TEST_CHECK_STATUS( status, error );
            }
        }

        status = vsi_nn_SetupGraph( graph, TRUE );
        TEST_CHECK_STATUS( status, error );
    }
    else
    {
        status = vsi_nn_SetupGraph( graph, FALSE );
        TEST_CHECK_STATUS( status, error );
    }

    if( VSI_FAILURE == status )
    {
        goto error;
    }

    fclose( fp );

    return graph;

error:
    if( NULL != fp )
    {
        fclose( fp );
    }

    release_ctx = ( NULL == in_ctx );
    vsi_nn_DumpGraphToJson( graph );
    vnn_ReleaseFruitdetect( graph, release_ctx );

    return NULL;
} /* vsi_nn_CreateFruitdetect() */

void vnn_ReleaseFruitdetect
    (
    vsi_nn_graph_t * graph,
    vsi_bool release_ctx
    )
{
    vsi_nn_context_t ctx;
    if( NULL != graph )
    {
        ctx = graph->ctx;
        vsi_nn_ReleaseGraph( &graph );

        /*-----------------------------------------
        Unregister client ops
        -----------------------------------------*/
        

        if( release_ctx )
        {
            vsi_nn_ReleaseContext( &ctx );
        }
    }
} /* vsi_nn_ReleaseFruitdetect() */



所以为了保持和你们vnn_yolov3.c的一致性(你们demo的代码我看图像处理部分,是在yolo_v3_process.c里面的),我就删了vnn_fruitdetect.c里面关于图像处理部分的代码。是不是我用的2_export_cae_code.sh哪里有问题

这里我的意思是仅是说你们gitlab的yolo系列代码,是不是用的都是归一化到[-1,1]的

我的设备号video0和video1和video2,本地opencv直接调都是没问题的

@librazxc

不同模型转换出来的输出代码是不一样的,这个vnn的文件不能也不需要做修改.如果你的模型不是yolo的模型,是不能替换进我们的demo运行的,我们的demo只执行替换对应的模型,比如你用yolov3的模型,就使用yolo_v3的源码,如果不是,就不能进行替换.

是的,我们的demo的yolo系列都是归一到正负一的.

摄像头的话,我们demo里面也是opencv接口去调用的,你可以从源码看到

@librazxc 这边建议你等待新的SDK release,目前是存在一些问题的,我已经在新的SDK里面修复了这些问题.并且到时候Docs也会同步更新

@Frank 那请问新的sdk大概啥时候会更新呀?
我的模型是yolo模型,也就是说我现在export case code输出的vnn代码存在这个预处理,而你们的gitlab yolo demo的vnn_yolo_**.c代码里没有预处理,造成这个原因的是模型转换工具SDK不同?

还有docs里的transfer yolo这个目录conversions_scripts里的东西是否可以发送一下?

@librazxc
预计是下周.
vnn的转换文件不需要修改.
所有的东西都在SDK里面了.

@Frank 你好,我现在重新走了一遍流程:

git clone https://gitlab.com/khadas/aml_npu_demo_binaries.git
cd aml_npu_demo_binaries/detect_demo
sudo ./INSTALL 
ls /dev/video* 此处只有video0了
./detect_demo_uvc_fb 0 /dev/video0

然后我就将系统切换到桌面下,再执行./detect_demo_x11 0 /dev/video0

这是为何呀?我啥也没有修改。

本地opencv测试如下:

@librazxc 你这个固件修改过么,感觉摄像头的节点不对,如果video0是你的USB摄像头的节点,会有一个video1是mipi的节点,但是现在没有看到

@Frank 我装的是ubuntu202005的桌面版系统,然后执行sudo apt-get update 和 sudo apt-get upgrade,其余就没操作了。我应该尝试怎么样的操作,才有可能拯救它 :joy:

@Frank 不接任何usb camera设备,ls /dev/video*输出如下

@librazxc 我晚点按照你的这个步骤测试看看

@Frank 非常感谢,静候佳音ing!

@librazxc 你好,我这边按照你的步骤测试了,刷机0530的gnome桌面的ubuntu系统.开机之后sudo apt update && sudo apt upgrade.随后重启

khadas@Khadas:~$ ls /dev/video*
/dev/video0   /dev/video11  /dev/video13
/dev/video10  /dev/video12  /dev/videosync

正常应该是存在这个mipi的节点的.这边建议你重新刷机,按照步骤走一遍

@Frank 好的。你的意思就是说,不接任何设备情况下,咱们这个板子默认分配了一个/dev/video设备号给mipi,是吧?

@librazxc 是的,默认的这个节点是给mipi摄像头用的.如果你接了其他的USB摄像头,这个节点依然存在的,但是mipi摄像头的节点可能会改变

@Frank 您好,我重新烧写了一遍系统,执行update和upgrade之后,又出现/dev/video0的mipi节点了。刚想尝试跑一下yolo 动态识别的demo,发现gitlab更新了。6.4.3的NPU SDK下载链接貌似还没出来,我刚申请的时候还是6.4.2.1的链接。我系统已经升级到最新的了,原有的aml_app_demo和binary demo还能在板子上跑起来吗?如何可以查看我系统的npu driver版本?

@librazxc 最新的SDK今天就会放出下载.你可以晚点下载新的SDK包.

原来的跑不起来的了,需要用新的跑,demo仓库我也更新了

$ sudo dmesg | grep "Gal"

就能看到驱动版本号了

@Frank 收到,谢谢,期待中…… :smiley:

@librazxc 我跟同事确认下,确认放出去以后会在这里告诉你的