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

news/2024/5/18 22:18:41

目录
  • 简介
  • 例程代码
  • 函数说明
    • g_main_loop_new
    • g_main_loop_run
    • g_main_loop_quit
    • g_signal_connect
    • arv_stream_set_emit_signals
  • Q&A
    • 回调函数的同步调用与异步调用
    • 帧丢失问题

简介

本文针对官方例程中的: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_cbg_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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/24780.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

openCV 图像清晰度检测

图像清晰度评价算法有很多种,在空域中,主要思路是考察图像的领域对比度,即相邻像素间的灰度特征的梯度差;在频域中,主要思路是考察图像的频率分量,对焦清晰的图像高频分量较多,对焦模糊的图像低频分量较多。 这里实现3种清晰度评价方法,分别是Tenengrad梯度方法、Lapla…

Linux:VMware切换仅主机模式并配置静态IP

配置网络编辑器 点击“编辑”->“虚拟网络编辑器”没有仅主机模式的话,可以通过“添加网络”进行新增网络配置。更改虚拟机网路模式 右键“创建的虚拟就”->“设置”登录虚拟机配置静态IP 切换目录到“/etc/sysconfig/network-scripts/”修改“if-ens33”文件TYPE=Ether…

日志服务 HarmonyOS NEXT 日志采集最佳实践

背景信息 随着数字化新时代的全面展开以及 5G 与物联网(IoT)技术的迅速普及,操作系统正面临前所未有的变革需求。在这个背景下,华为公司自主研发的鸿蒙操作系统(HarmonyOS)应运而生,旨在满足万物互联时代的多元化设备接入、高效协同和安全可靠运行的需求。 HarmonyOS 不…

[IOI2019] 景点划分

连通块划分令人忍俊不禁的是,11 月的模拟赛出现了 “摩拉克斯” 一题,被取之。2 月 JOISC 出现这个模型,被取之。2 月模拟赛出现这个模型,被取之。本题再次出现这个模型,被取之。 呃呃呃呃呃呃呃呃呃啊。 首先进行一些简单的分析:令 \(A\le B\le C\),构造 \(A,B\) 合法的…

网络拓扑—WEB-IIS服务搭建

均使用Windows Server 2003进行搭建目录WEB-IIS服务搭建网络拓扑配置网络IISPC安装IIS服务配置IIS服务(默认站点)PC机访问网页配置IIS服务(新建站点)PC机访问网页 WEB-IIS服务搭建 网络拓扑//交换机忽略不计 IIS服务IP:192.168.1.1 PC机IP:192.168.1.2配置网络 IISPC安装…

RocketMQ定时/延时消息

什么是延时消息 当消息写入到Broker后,在指定的时长后才可被消费处理的消息,称为延时消息。 采用RocketMQ的延时消息可以实现定时任务的功能,而无需使用定时器。典型的应用场景是,电商交 易中超时未支付关闭订单的场景,12306平台订票超时未支付取消订票的场景。在电商平台…

Android开发中Button背景颜色不能修改问题及解决方法

问题: 使用Android Studio进行android开发时,不管是拖出来的Button,还是自己设置的Button,Button的背景色一直无法修改,呈现系统默认的紫色。 例如我的代码,预览按钮的时候应该是彩色,但还是默认的颜色:紫色 问题原因: 出现该问题的原因主要是因为使用Android Studio …

用“芯”服务,安安“芯芯” 经纬恒润功能安全软件库SAFETY BASE V1.0正式发布

基于目前的市场需求,经纬恒润产品安全团队潜心研发,现正式推出满足ISO26262的安全软件库SAFETY BASE V1.0(满足EGAS L3)。 当前,半导体厂商在进行满足功能安全芯片开发时,除由其自身实现的安全机制以外,还会在安全手册(safety manual)中提出大量需要被自主研发的…