首页 > 其他分享 >开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal

开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal

时间:2024-04-25 17:22:19浏览次数:27  
标签:multiple stream 例程 signal camera error main data loop

目录

简介

本文针对官方例程中的:02-multiple-acquisition-signal做简单的讲解。并简单介绍其中调用的g_main_loop_newg_main_loop_rung_main_loop_quitg_signal_connectarv_stream_set_emit_signals

aravis版本:0.8.31
操作系统:ubuntu-20.04
gcc版本:9.4.0

例程代码

这段代码使用Aravis的API,控制相机连续采集,并通过GLib的事件循环机制和GObject的信号系统异步地获取10个图像,主要操作步骤如下:

  • 连接相机
  • 设置采集模式为连续采集
  • 创建流对象,并向流对象的buffer池中添加buffer
  • 设置流对象信号回调函数,并使能流对象信号发射
  • 开始采集
  • 启动事件循环
  • 获取10张图像后关闭事件循环
  • 关闭流对象信号发射,释放资源

连续采集multiple-acquisition-main-thread不同的是,本例中使用GMainLoop(GLib的事件循环)来处理异步事件,图像获取过程是异步进行的。

/* SPDX-License-Identifier:Unlicense */

/* Aravis header */
#include <arv.h>
/* Standard headers */
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include "LogManager.h"

typedef struct {
	GMainLoop *main_loop;
	guint32 counter;
} AppData;

void new_buffer_cb (ArvStream *stream, void *user_data)
{
	ArvBuffer *buffer;
	AppData *app_data = static_cast<AppData*>(user_data);

	buffer = arv_stream_pop_buffer (stream);
	PAW_INFO("Acquired"<<arv_buffer_get_image_width(buffer)<<"x"<<arv_buffer_get_image_height(buffer)<< " buffer");

	arv_stream_push_buffer (stream, buffer);

	app_data->counter++;
	if (app_data->counter == 10)
		g_main_loop_quit (app_data->main_loop);
}

int main (int argc, char **argv)
{
	ArvCamera *camera;
	AppData app_data;
	GError *error = NULL;

	app_data.main_loop = g_main_loop_new (NULL, FALSE);
	app_data.counter = 0;

	//连接相机
	camera = arv_camera_new (NULL, &error);

	if (ARV_IS_CAMERA (camera)) {
		ArvStream *stream = NULL;

		printf ("Found camera '%s'\n", arv_camera_get_model_name (camera, NULL));
		//设置采集模式
		arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error);
		//创建流对象
		if (error == NULL)
			stream = arv_camera_create_stream (camera, NULL, NULL, &error);

		if (ARV_IS_STREAM (stream)) {
			int i;
			size_t payload;

			//获取有效负载大小
			payload = arv_camera_get_payload (camera, &error);
			if (error == NULL) {
				//设置流对象的缓冲区数量
				for (i = 0; i < 5; i++)
					arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL));
			}

			//设置流对象信号回调函数
			g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &app_data);
			//设置流对象发射信号
			//当流对象接收到新的缓冲区时,发射new-buffer信号
			arv_stream_set_emit_signals (stream, TRUE);

			//开始采集
			if (error == NULL)
				arv_camera_start_acquisition (camera, &error);
			
			//启动主循环
			PAW_INFO("start main loop");
			if (error == NULL)
				g_main_loop_run (app_data.main_loop);
			PAW_INFO("start main loop end");

			if (error == NULL)
				//停止采集
				arv_camera_stop_acquisition (camera, &error);

			arv_stream_set_emit_signals (stream, FALSE);
			g_clear_object (&stream);
		}
		
		g_clear_object (&camera);
	}
	
	g_main_loop_unref (app_data.main_loop);

	if (error != NULL) {
		/* En error happened, display the correspdonding message */
		printf ("Error: %s\n", error->message);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

注:PAW_INFO是我自定义的用于打印日志的宏

运行结果:

其中<>之间的是线程号。

函数说明

g_main_loop_new

简介:GLib的API,构造GMainLoop对象

GMainLoop* g_main_loop_new(GMainContext* context, gboolean is_running)

其中:
[in]context:一个GMainContext,如果为NULL,将使用全局默认的main上下文
[in]is_running:设置为TRUE表示循环正在运行。这不是很重要,因为只要后面调用g_main_loop_run()就会将其设置为TRUE。

g_main_loop_run

简介:GLib的API,运行一个主循环,直到在循环中调用g_main_loop_quit()

void g_main_loop_run(GMainLoop* loop)

g_main_loop_quit

简介:GLib的API,停止GMainLoop的运行。任何使用g_main_loop_run()开启的循环都将返回。

void g_main_loop_quit(GMainLoop* loop)

g_signal_connect

简介:GObject的宏,用于将信号处理器连接到特定对象的某个信号上。当一个信号被发出时,处理器将被同步调用。

#define g_signal_connect(instance, detailed_signal, c_handler, data)

arv_stream_set_emit_signals

简介:控制流对象信号发射。默认情况下流对象发射信号是禁用的,因为信号发射在性能上有一定开销而且在某些应用场景下是不需要的。

void arv_stream_set_emit_signals(ArvStream* stream, gboolean emit_signals)

Available since: 0.2.0

Q&A

回调函数的同步调用与异步调用

观察程序运行时的日志,可以发现new_buffer_cb的运行并不是在主线程中。

但是按照g_signal_connect的描述,回调函数应该是被同步调用,也就是说new_buffer_cb理论上应该在主线程被调用。
后来查看文档发现,在GObject的信号系统中,处理器的调用是同步的。当信号发射时,其关联的所有处理器会都会在发射信号的线程中按照它们被连接的顺序依次执行。

所以正确的应该是:处理器是在信号发射的线程被调用,而不是在处理器被注册时的线程。

在本例中,预定义的信号new-buffer的处理器new_buffer_cb被绑定在流对象上,这意味着每当流对象有一个新的buffer可用时,这个信号就会被发射,随后new_buffer_cb就被调用。而官方文档钟提到,流对象内部是使用一个单独的线程来监听数据的到达,因此信号是在这个单独的线程被发射的,也就是说回调函数也是在这个单独的线程被调用的,而不是在主线程中。

帧丢失问题

官方给出的例程中,先启动的相机采集,然后才开始事件循环。我认为这样的话会存在丢帧的问题,因为在事件循环启动并准备好处理接收到的图像之前,相机可能已经开始发送数据,如果数据流的缓冲不足或处理不及时,新的图像数据可能会覆盖还未处理的旧数据,或者直接被丢弃。

所以我对代码做了一些改动,改变调用顺序为先开启事件循环,然后再启动相机的采集,代码如下:

/* SPDX-License-Identifier:Unlicense */
/* Aravis header */
#include <arv.h>
/* Standard headers */
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include "LogManager.h"

typedef struct {
	GMainLoop *main_loop;
	guint32 counter;
	ArvCamera *camera;
} AppData;

gboolean start_acquisition_cb(gpointer user_data)
{
	AppData *app_data = static_cast<AppData*>(user_data);
    GError *error = NULL;

    arv_camera_start_acquisition(app_data->camera, &error);

    if (error != NULL) {
        printf("Error: %s\n", error->message);
        g_main_loop_quit(app_data->main_loop);
    }
    //只调用一次
    return FALSE; 
}

...

int main (int argc, char **argv)
{
	AppData app_data;
	GError *error = NULL;

	app_data.main_loop = g_main_loop_new (NULL, FALSE);
	app_data.counter = 0;

	app_data.camera = arv_camera_new (NULL, &error);

	if (ARV_IS_CAMERA (app_data.camera)) {
		ArvStream *stream = NULL;

		printf ("Found camera '%s'\n", arv_camera_get_model_name (app_data.camera, NULL));

		arv_camera_set_acquisition_mode (app_data.camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error);

		if (error == NULL)
			stream = arv_camera_create_stream (app_data.camera, NULL, NULL, &error);

		if (ARV_IS_STREAM (stream)) {
			int i;
			size_t payload;

			payload = arv_camera_get_payload (app_data.camera, &error);
			if (error == NULL) {
				for (i = 0; i < 5; i++)
					arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL));
			}

			g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &app_data);

            PAW_INFO("emit signals");
            arv_stream_set_emit_signals (stream, TRUE);
            PAW_INFO("emit signals end");
            
            /* if (error == NULL)
				arv_camera_start_acquisition (camera, &error); */
			
			//在主循环开始后尽快执行一次start_acquisition_cb
			g_idle_add(start_acquisition_cb, &app_data);
			
			PAW_INFO("start main loop");
			if (error == NULL)
				g_main_loop_run (app_data.main_loop);
			PAW_INFO("start main loop end");
			if (error == NULL)
				arv_camera_stop_acquisition (app_data.camera, &error);

			arv_stream_set_emit_signals (stream, FALSE);

			g_clear_object (&stream);
		}

		g_clear_object (&app_data.camera);
	}

	g_main_loop_unref (app_data.main_loop);

	if (error != NULL) {
		printf ("Error: %s\n", error->message);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

标签:multiple,stream,例程,signal,camera,error,main,data,loop
From: https://www.cnblogs.com/paw5zx/p/18158190

相关文章

  • 【专题STM32F03】FreeRTOS 队列queue传递结构体,野火例程代码简单修改。
    /************************************************************************@filemain.c*@authorfire*@versionV1.0*@date2018-xx-xx*@briefFreeRTOSV9.0.0+STM32消息队列******************************************************......
  • [985] Filter by Column Value & Multiple Conditions in Pandas dataframe
    ref:WaystofilterPandasDataFramebycolumnvaluesFilterbyColumnValue:Toselectrowsbasedonaspecificcolumnvalue,usetheindexchainmethod.Forexample,tofilterrowswheresalesareover300:Pythongreater_than=df[df['Sales&#......
  • 开源相机管理库Aravis例程学习(二)——连续采集multiple-acquisition-main-thread
    目录简介例程代码函数说明arv_camera_set_acquisition_modearv_camera_create_streamarv_camera_get_payloadarv_buffer_newarv_stream_push_bufferarv_camera_start_acquisitionarv_stream_pop_bufferarv_camera_stop_acquisition简介本文针对官方例程中的:02-multiple-acquisit......
  • CH582/CH592_EVT中RF_Device(主机)例程详解_底层自动跳频管理_支持一对七通讯
    目标程序路径: 与RF_Device程序相比,RF_Host主要讲解三个地方,其他接口与RF_Device一致,查看这篇博客:CH582/CH592_EVT中RF_Device(从机)例程详解_底层自动跳频管理_支持一对七通讯1、Host配对绑定逻辑:程序中默认逻辑为上电后前三秒钟允许配对绑定新设备,超过三秒钟则从flash中取出......
  • CH582/CH592_EVT中RF_Device(从机)例程详解
    依旧以CH582例程做讲解,CH592与CH582接口部分一致,其他地方大同小异。RF_Device例程路径: 1、main函数初始化配置 2、RF参数初始化 3、上电后启动绑定回连任务  4、RF_DMA初始化  5、RF绑定回调任务  6、数据发送接口 7、定时器测试发送数据到对......
  • CH573 CH582 CH592外设IAP例程讲解
    一.根据所选芯片型号下载官网最新例程,tips:若使用的是ch571这类codeflash是192k的芯片,需要将iap程序中的宏定义进行修改:源程序是0x0007000(448k),修改为0x0003000(192k)#defineAPP_CODE_END_ADDR0x00030000二.根据所选芯片型号下载对应的程序,三.串口1接usb转......
  • 开源相机管理库Aravis例程学习(一)——单帧采集single-acquisition
    目录简介源码函数说明arv_camera_newarv_camera_acquisitionarv_camera_get_model_namearv_buffer_get_image_widtharv_buffer_get_image_height简介本文针对官方例程中的第一个例程:single-acquisition做简单的讲解,并简单分析其中调用的arv_camera_new,arv_camera_acquisition,ar......
  • 第五十节:Core8.0中的新变化(SignalR写法、本机AOT发布)
    一.新变化 详见:https://learn.microsoft.com/zh-cn/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0如下图: 二. SignalR改变JS中在ASP.NETCore7.0或更早版本中默认值的两倍值的分配varconnection=newsignalR.HubConnectionBuilder().withU......
  • Android Graphics 多屏同显/异显 - C++示例程序(标准版)
    ”为了理解Android多屏同显/异显的基本原理,我们将从NativeLevel入手,基于GraphicsAPIs写作一个简单的C++版本的多屏显示互动的演示程序。通过这个程序我们将了解常用的多屏显示相关的接口的使用方法。“  01多屏显示C++示例概况 源码下载请查看文章末尾源码下载方......
  • RGB到Lab的转换原理及例程
    RGB到Lab的转换是将RGB颜色空间转换为Lab颜色空间,其中Lab颜色空间是一种在人眼感知上更均匀的颜色模型。转换过程包括以下步骤:将RGB值中的每个通道值除以255,将其转换为范围在0-1之间的小数。对每个通道值应用逆伽马校正,以纠正显示设备的非线性响应。可以使用以下公式:R'=......