首先感谢阅读,如果您也对TDA4相关的开发感兴趣,我们这边有个学习交流微信群,可以入群和大家一起交流学习。
资历较浅,水平有限,如遇错误,请大家多指正!
保持开源精神,共同分享、进步!
博主WX : AIR_12 我会拉你入群。
链接:TDA4 相关专栏 链接:TDA4 Demo Gitee开源库
欢迎大家加入,一起维护这个开源库,给更多的朋友提供帮助。
更新时间:2022年06月30日
一、Graph和Pipeline的区别和对比:
1、Pipeline的本质:通过分时复用的方式,提高了硬件的整体利用率,但在本质上,运行单条流水线和运行一次graph的时间是相同的。
2、原有的Graph,如果执行vxProcessGraph,那么graph一旦运行起来,就不允许被打断;且同一个时刻,只有一个核参与整体的graph的处理过程。
3、但是Pipeline,可以实现在graph运行过程中,对关心的数据即通过(graph_parameters_queue_params_list)进行调整,比如图片替换、复制等等。
4、同时,pipeline还允许不同的核在同一个时间,参与到不同流水线的处理过程中。整体上提升了硬件的利用效率。
Graph | Pipeline | |
是否可打断 | 不可以 | 可以(通过graph_parameters_queue_params_list) |
硬件同时运行 | 不可以 | 可以(同一个时刻,允许多个核同时运行不同的pipeline的处理) |
二、例程说明
本例程以SDK 0800版本为基准,使用 ./apps/dl_demos/app_tidl_od_cam 作为示例。
在app_create_graph 函数内,创建了一下一系列的 node 顺序如下:
节点名称 | 节点主要功能 |
CaptureNode | 从摄像头获取传感器图像数据 |
VpacVissNode | 将RAW 数据转换成YUV格式的数据 |
AewbNode | 实现图像自动白平衡等相关的处理 |
VpacLdcNode | 对图像进行畸变矫正等 |
VpacMscScaleNode | 图像缩放处理节点(缩放至训练网络接收的size) |
ImgPreProcNode | 图像预处理节点(将图像数据预处理转换成Tidl模块可以处理的RGB格式等) |
TIDLNode | 深度网络,实现目标检测、推理等功能 |
DrawBoxDetectionsNode | 绘制检测到物体框的节点 |
ImgMosaicNode | 图像镶嵌节点,将多个图像嵌入到同一个图像内 |
DisplayNode | 显示节点,将经过镶嵌的图像显示到显示器 |
其中 VpacVissNode AewbNode VpacLdcNode VpacMscScaleNode ImgPreProcNode
在创建的时候,会使用vxReplicateNode节点进行相应节点的复制。这里我当时的主要疑问是复制了几个这样的节点。
在创建这些node的时候,会传入一个vx_object_array,具体说明参考这里
[TI TDA4 J721E] TIOVX 常用函数详解 之——vxReplicateNode_AIRKernel的博客-CSDN博客
创建的总数量应该是和这个参数包含的成员数量相当。(成员数量=4 ,创建的个数 = 4-1 =3),因为已经创建了一个Node了。
三、graph_parameters_queue_params_list
graph_parameters_queue_params_list 必须被设置,这个列表表示在graph运行过程中可以被访问对象的索引。
graph_parameter_index = 0;
add_graph_parameter_by_node_index(obj->graph, obj->captureObj.node, 1); //将captureNode 的序号为1的参数,加入到 graph_parameters_queue_params_list中
obj->captureObj.graph_parameter_index = graph_parameter_index; //设置capture节点的 索引
graph_parameters_queue_params_list[graph_parameter_index].graph_parameter_index = graph_parameter_index; //设置graph_parameters_queue_params_list中对应成员的索引
graph_parameters_queue_params_list[graph_parameter_index].refs_list_size = APP_BUFFER_Q_DEPTH; //注意:设置该成员包含的子成员的个数
graph_parameters_queue_params_list[graph_parameter_index].refs_list = (vx_reference *)&obj->captureObj.raw_image_arr[0]; //成员指针指向被操作的对象地址
graph_parameter_index++; //索引数量增加
vxSetGraphScheduleConfig(obj->graph,
VX_GRAPH_SCHEDULE_MODE_QUEUE_AUTO,
graph_parameter_index,
graph_parameters_queue_params_list);
四、Enqueue 和 Dequeue 相关顺序说明
更新时间:2022年06月30日
工作流程(请结合第五点代码):
1、根据需要,可以将需要对enqueue对象进行的修改完成后,等待enqueue。
2、根据流水线深度,Enqueue相关的对象 。
3、执行Dequeue。
4、执行Dequeue后,会自动将流水线向前执行一条流水线,完成一个完整graph的运行(注意此处说的是整个pipline的graph_parameters_queue_params_list只有一个成员。如果有两个成员,需要继续再次Dequeue,才能完成整条流水线的工作)。
五、Enqueue 和 Dequeue
请结合代码注释阅读!!!
if(obj->pipeline <= 0)
{
/* Enqueue outpus */
/* Enqueue inputs during pipeup dont execute */
//将capture获取到的图像,逐个入队到graph中
vxGraphParameterEnqueueReadyRef(obj->graph, captureObj->graph_parameter_index, (vx_reference*)&captureObj->raw_image_arr[obj->enqueueCnt], 1);
obj->enqueueCnt++;
obj->enqueueCnt = (obj->enqueueCnt >= APP_BUFFER_Q_DEPTH)? 0 : obj->enqueueCnt;
obj->pipeline++;
}
if(obj->pipeline > 0)
{
vx_image capture_input_image;
uint32_t num_refs;
/* Dequeue input */
//将经过运行captureNode节点,得到的输出图像出队。
vxGraphParameterDequeueDoneRef(obj->graph, captureObj->graph_parameter_index, (vx_reference*)&capture_input_image, 1, &num_refs);
// Dequeue实际上是将TIOVX空间的数据,映射到用户空间内,用户可以根据这个指针对数据进行修改。
//经过出对的图像数据,可以在这里进行操作。
// 执行出队后,capture_input_image图像可能位于captureObj->raw_image_arr[array]中的任意位置,如果需要确定,需要将
// capture_input_image和captureObj->raw_image_arr[array]中的对象进行对比,参考0703的decode部分。
//
/* Enqueue input - start execution */
//将映射到用户空间的对象重新入队。原来的位置会被自动至为NULL,直到下次dequeue。
vxGraphParameterEnqueueReadyRef(obj->graph, captureObj->graph_parameter_index, (vx_reference*)&capture_input_image, 1);
obj->enqueueCnt++;
obj->dequeueCnt++;
obj->enqueueCnt = (obj->enqueueCnt >= APP_BUFFER_Q_DEPTH)? 0 : obj->enqueueCnt;
obj->dequeueCnt = (obj->dequeueCnt >= APP_BUFFER_Q_DEPTH)? 0 : obj->dequeueCnt;
}
appPerfPointEnd(&obj->total_perf);
以下是0703版本Decode版本的相关代码:
其中这两个函数,就是为了查找到被dequeue出来的对象在array中的索引。
app_find_image_array_index
app_find_user_object_array_index
vx_status app_run_decodeGraph(vx_graph graph, DecodeObj *decodeObj, vx_user_data_object *bitStream)
{
vx_status status = VX_SUCCESS;
if (decodeObj->pipeline < 0)
{
/* Enqueue output */
vxGraphParameterEnqueueReadyRef(graph, decodeObj->output_image_graph_parameter_index, (vx_reference *)&decodeObj->output_image[decodeObj->enqueueCnt], 1);
app_copy_encodeBitStream_to_decodeBitStream(&decodeObj->bitstream_obj[decodeObj->enqueueCnt], bitStream);
/* Enqueue input - start execution */
vxGraphParameterEnqueueReadyRef(graph, decodeObj->input_bitstream_graph_parameter_index, (vx_reference *)&decodeObj->bitstream_obj[decodeObj->enqueueCnt], 1);
decodeObj->enqueueCnt++;
decodeObj->enqueueCnt = (decodeObj->enqueueCnt >= decodeObj->num_buf) ? 0 : decodeObj->enqueueCnt;
decodeObj->pipeline++;
}
else if (decodeObj->pipeline >= 0)
{
vx_int32 array_idx = -1, img_array_idx = -1;
vx_image out_image;
vx_user_data_object in_bitstream;
uint32_t num_refs;
/* Dequeue & Save output */
//参数说明:要出队的参数索引、被出队的对象填充该区域、最大的出队个数、实际的出队个数
vxGraphParameterDequeueDoneRef(graph, decodeObj->output_image_graph_parameter_index, (vx_reference *)&out_image, 1, &num_refs);
app_find_image_array_index(decodeObj->output_image, (vx_reference)out_image, decodeObj->num_buf, &img_array_idx);
if (img_array_idx != -1)
{
app_decode_saveImageToFile("/home/root/mydecode.yuv", &decodeObj->output_image[img_array_idx]);
}
/* Dequeue input */
vxGraphParameterDequeueDoneRef(graph, decodeObj->input_bitstream_graph_parameter_index, (vx_reference *)&in_bitstream, 1, &num_refs);
/* Enqueue output */
vxGraphParameterEnqueueReadyRef(graph, decodeObj->output_image_graph_parameter_index, (vx_reference *)&out_image, 1);
app_find_user_object_array_index(decodeObj->bitstream_obj, (vx_reference)in_bitstream, decodeObj->num_buf, &array_idx);
if (array_idx != -1)
{
app_copy_encodeBitStream_to_decodeBitStream(&decodeObj->bitstream_obj[array_idx], bitStream);
}
/* Enqueue input - start execution */
vxGraphParameterEnqueueReadyRef(graph, decodeObj->input_bitstream_graph_parameter_index, (vx_reference *)&in_bitstream, 1);
}
return status;
}
void app_find_user_object_array_index(vx_user_data_object object_array[], vx_reference ref, vx_int32 array_size, vx_int32 *array_idx)
{
vx_int32 i;
*array_idx = -1;
for (i = 0; i < array_size; i++)
{
if (ref == (vx_reference)object_array[i])
{
*array_idx = i;
break;
}
}
}
void app_find_image_array_index(vx_image image_array[], vx_reference ref, vx_int32 array_size, vx_int32 *array_idx)
{
vx_int32 i;
*array_idx = -1;
for (i = 0; i < array_size; i++)
{
if (ref == (vx_reference)image_array[i])
{
*array_idx = i;
break;
}
}
}
【声明】
【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】
【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】
【如有嵌入式相关项目需求,欢迎私信】