首先感谢阅读,如果您也对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 !
【声明】
【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】
【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】
【如有嵌入式相关项目需求,欢迎私信】