[TI TDA4 J721E]基于USB摄像头YUV数据的目标检测和Label提取(基于app_od demo)

          首先感谢阅读,如果您也对TDA4相关的开发感兴趣,我们这边有个学习交流微信群,可以入群和大家一起交流学习。

资历较浅,水平有限,如遇错误,请大家多指正!

保持开源精神,共同分享、进步!

博主WX : AIR_12  我会拉你入群。

链接:TDA4 相关专栏        链接:TDA4  Demo  Gitee开源库

欢迎大家加入,一起维护这个开源库,给更多的朋友提供帮助。


很久没有更新博客了,最近以来,博主一直在恶补深度学习、神经网络、机器训练等相关的知识。有了这些基础知识的加持,总算能够弄出一点东西来了,现在和大家分享一下。


一、基于0800版本的app_tidl_od demo的USB摄像头数据的移植

        这里比较简单,原本这个demo是从文件内获取图像数据,我走了一个捷径,把从图片获取图像的函数,替换成从USB获取YUV 图像。

 原图像

替换以后的图像 (可以动态识别)


二、移植过程

        1、增加了usb相关的两个文件、color_converte相关的两个文件

        2、将这两个模块封装成模块化的结构。

        3、具体内容大家可以在源码中走读。这里不做赘述。


三、提取识别物体的过程几分析

经过tidl node的推理以后,输出的tensor应该是一个经过全连接层(或分类器)得到的相关数据。在图像分类任务内,可能是每个类别对应的得分;在目标识别的任务里,经过变换得到的是每一个识别物体的属性信息。如下:

1、图像分类任务内(见app_tidl demo):

        将输出结果映射出来:

status = tivxMapTensorPatch(output_tensors[id], 3, start, output_sizes, &map_id_output, output_strides, &output_buffer, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);

紧接着,注意pout是一个指针!!我在这里看错了,一直以为是一个数值,所以卡壳了很久。这个指针代表这分类器每个位置的地址。

地址里面存储的就是对应的1000分类每个类别的得分。(具体大家还是要看代码app_tidl 的main.c里面)

特别值得注意:这里有outPadT  outPadL,这里可以认为是经过tidl_node推理后,如果要做padding的话,这里要计算进去。

outPadT[id] * output_sizes[0] 这里可以看做第一行有多少个0;

outPadL[id] 可以看做左边设置的填充了几个0;

经过这两个的偏移,就得到了实际想要的对象的第一个数值。

if(data_type == VX_TYPE_FLOAT32)
{
    float *pOut;
    float score[5];

    pOut = (float *)output_buffer + (ioBufDesc->outPadT[id] * output_sizes[0]) + ioBufDesc->outPadL[id];


    for(i = 0; i < 5; i++)
    {
        score[i] = FLT_MIN;
        classid[i] = (vx_uint32)-1;

        for(j = 0; j < ioBufDesc->outWidth[id]; j++)
        {
            if(pOut[j] > score[i])
            {
                score[i] = pOut[j];
                classid[i] = j;
            }
        }
        if(classid[i] < ioBufDesc->outWidth[id] )
        {
            pOut[classid[i]] = FLT_MIN;
        }
        else
        {
            classid[i] = 0; /* invalid class ID, ideally it should not reach here */
        }
    }
    #ifdef APP_DEBUG
    APP_PRINTF("app_tidl: Image classification Top-5 results: \n");
    for(i = 0; i < 5; i++)
    {
        APP_PRINTF("app_tidl:  %s, class-id: %d, score: %f \n", (char *)&imgnet_labels[classid[i] + label_offset], classid[i], score[i]);
    }
    #endif
}

2、在目标识别任务内(见github app_tidl_usb_cam demo)

这段代码是自己写的一个代码。主要功能是去获取已经检测到的物体的标签。

tensor的解析内容和上面的内容大同小异。多出了步骤是对tensor实际数据的起始做了指针类型的转换,使得能适配目标物属性的指针类型。比如TIDL_ODLayerObjInfo * 。 所以这个tensor包含的就不仅仅是每个类别的得分了。这里需要仔细看一下,目前能用就行,可暂时不深究。

vx_status usbCamGetLable(char *file_name, PreProcObj *preProcObj, AppObj *obj)
{
    vx_status status = VX_SUCCESS;

    vx_tensor output;
    vx_size numCh;
    vx_int32 ch;

    vxQueryObjectArray((vx_object_array)obj->tidlObj.output_tensor_arr[0], VX_OBJECT_ARRAY_NUMITEMS, &numCh, sizeof(vx_size));

    for (ch = 0; ch < numCh; ch++)
    {
        vx_size num_dims;
        void *data_ptr;
        vx_map_id map_id;

        vx_size start[APP_MAX_TENSOR_DIMS];
        vx_size tensor_strides[APP_MAX_TENSOR_DIMS];
        vx_size tensor_sizes[APP_MAX_TENSOR_DIMS];

        /* Write Y plane */
        output = (vx_tensor)vxGetObjectArrayItem((vx_object_array)obj->tidlObj.output_tensor_arr[0], ch);

        vxQueryTensor(output, VX_TENSOR_NUMBER_OF_DIMS, &num_dims, sizeof(vx_size));

        if (num_dims != 3)
        {
            printf("Number of dims are != 3! exiting.. \n");
            break;
        }

        vxQueryTensor(output, VX_TENSOR_DIMS, tensor_sizes, 3 * sizeof(vx_size));

        start[0] = start[1] = start[2] = 0;

        tensor_strides[0] = 1;
        tensor_strides[1] = tensor_strides[0];
        tensor_strides[2] = tensor_strides[1] * tensor_strides[1];

        status = tivxMapTensorPatch(output, num_dims, start, tensor_sizes, &map_id, tensor_strides, &data_ptr, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);

        sTIDL_IOBufDesc_t *ioBufDesc;

        TIDL_ODLayerHeaderInfo *pHeader;
        TIDL_ODLayerObjInfo *pObjInfo;
        vx_float32 *pOut;
        vx_uint32 numObjs;
        vx_float32 *output_buffer;
        vx_size output_sizes[4];

        vx_int32 i;

        ioBufDesc = (sTIDL_IOBufDesc_t *)&obj->drawDetectionsObj.params.ioBufDesc;

        output_buffer = (vx_float32 *)data_ptr;

        output_sizes[0] = ioBufDesc->outWidth[0] + ioBufDesc->outPadL[0] + ioBufDesc->outPadR[0];
        output_sizes[1] = ioBufDesc->outHeight[0] + ioBufDesc->outPadT[0] + ioBufDesc->outPadB[0];
        output_sizes[2] = ioBufDesc->outNumChannels[0];

        pOut = (vx_float32 *)output_buffer + (ioBufDesc->outPadT[0] * output_sizes[0]) + ioBufDesc->outPadL[0];
        pHeader = (TIDL_ODLayerHeaderInfo *)pOut;
        pObjInfo = (TIDL_ODLayerObjInfo *)((uint8_t *)pOut + (vx_uint32)pHeader->objInfoOffset);
        numObjs = (vx_uint32)pHeader->numDetObjects;


        for (i = 0; i < numObjs; i++)
        {
            //   printf("\n in loop !!!!\n");
            pObjInfo = pObjInfo;
             TIDL_ODLayerObjInfo *pDet = (TIDL_ODLayerObjInfo *)((uint8_t *)pObjInfo + (i * ((vx_uint32)pHeader->objInfoSize)));

             // /*Drawing of object for display purpose should be done only when score is high */
             if ((pDet->score >= 0.95)) // mAP值大约用户设定值
             {
                 vx_int32 label = (vx_int32)pDet->label;
                 printf("\nLabel = %i  It's a %s !!!  Score = %f\n", label, (char *)&od_labels[label], pDet->score);
            }
        }            
        
        if (VX_SUCCESS == status)
        {
            tivxUnmapTensorPatch(output, map_id);
        }
        vxReleaseTensor(&output);
    }
    return (status);
}

3、获取到物体的label以后,如何对应物体的名称

走读代码以后,会发现,这个物体的label值,实际上就是一个整型数值。这个整型数值代表的是提前训练的网络生成的类别的ID。必须找到这个网络的类别,才能知道对应的物体名称。(在图像分类任务里面,是存在一个imagenet_class_labels.c 文件,标示了具体ID对应的名字。)

这里我找了很久,也没找到,几乎要放弃的时候,回头看了一下官方的demo说明手册,vision_apps/docs/user_guide/group_apps_dl_demos_app_tidl_od.html

在官方的SDK 文档里面,我看到这个目标识别的网络是从这里下载的

 https://github.com/Robert-JunWang/Pelee

于是在这个库里面,看到了当时训练的分类。现在得到Label值,那就对应上了,这个名称了。

 4、将名称按照顺序添加到程序内

在app_tidl_od demo里面添加了一个c文件。


const char od_labels[21][16] = {
    "background",  // label =0
    "aeroplane",   // label =1
    "bicycle",     // label =2
    "bird",        // label =3
    "boat",        // label =4
    "bottle",      // label =5
    "bus",         // label =6
    "car",         // label =7
    "cat",         // label =8
    "chair",       // label =9
    "cow",         // label =10
    "diningtable", // label =11
    "dog",         // label =12
    "horse",       // label =13
    "motorbike",   // label =14
    "person",      // label =15
    "pottedplant", // label =16
    "sheep",       // label =17
    "sofa",        // label =18
    "train",       // label =19
    "tvmonitor"    // label =20
};
if ((pDet->score >= 0.95)) // mAP值大约用户设定值
{
     vx_int32 label = (vx_int32)pDet->label;
     printf("\nLabel = %i  It's a %s !!!  Score = %f\n", label, (char *)&od_labels[label], pDet->score);
}

这里我在程序里面已经移植好了,大家知道我是如何获取到这些信息的步骤就行了。


四、效果如下:

标签如下,没来得及将名称打印到屏幕上。大家自行处理吧。

Good Luck !


【声明】
【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】
【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】
【如有嵌入式相关项目需求,欢迎私信】


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值