【OrangePi Zero2 智能家居】阿里云人脸识别方案

2024-02-10 17:36

本文主要是介绍【OrangePi Zero2 智能家居】阿里云人脸识别方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、接入阿里云
二、C语言调用阿里云人脸识别接口
三、System V消息队列和POSIX 消息队列

一、接入阿里云

在之前树莓派的人脸识别方案采用了翔云平台的方案去1V1上传比对两张人脸比对,这种方案是可行,可
以继续采用。但为了接触更多了云平台方案,在Orange Pi Zero2里, 讲采用人脸搜索1:N方案,通过提
前在阿里云人脸数据库里存储人脸照片后,输入单张已授权人脸图像,与人脸库中人脸图片进行对比,
最终获取比对结果。

官网地址如下:https://vision.aliyun.com/

点击“人脸搜索1:N”
在这里插入图片描述
点击"立即开通":
在这里插入图片描述
使用阿里云APP/支付宝/钉钉扫码登录:
在这里插入图片描述
在这里插入图片描述
购买“人脸搜索1:N”能力,第一次购买,可以有5000次的免费使用:
在这里插入图片描述
在这里插入图片描述
开通完后, 在”工作台->开发能力->人脸人体->人脸数据库管理 " 添加人脸照片样本 :
在这里插入图片描述
在这里插入图片描述
上传数据库后,安装阿里云人脸识别SDK:

pip install alibabacloud_facebody20191230

导入ALIBABA_CLOUD_ACCESS_KEY_IDALIBABA_CLOUD_ACCESS_KEY_SECRET环境变量:

vi ~/.bashrc #最后的结尾添加, 在垃圾分类的项目里如果已经添加过就不需要添加了
export ALIBABA_CLOUD_ACCESS_KEY_ID="你的KEY_ID"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="你的KEY_SECRECT"

可以拿同一人的照片和不同人的照片用官方python代码进行对比:

# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_facebody20191230
# face.pyimport os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import SearchFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptionsconfig = Config(# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='facebody.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)search_face_request = SearchFaceAdvanceRequest()
#场景一:文件在本地
stream0 = open(r'/tmp/SearchFace.jpg', 'rb')
search_face_request.image_url_object = stream0#场景二:使用任意可访问的url
#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/facebody/SearchFace1.png'
#img = urlopen(url).read()
#search_face_request.image_url_object = io.BytesIO(img)
search_face_request.db_name = 'default'
search_face_request.limit = 5runtime_option = RuntimeOptions()try:# 初始化Clientclient = Client(config)response = client.search_face_advance(search_face_request, runtime_option)# 获取整体结果print(response.body)except Exception as error:# 获取整体报错信息print(error)# 获取单个字段print(error.code)# tips: 可通过error.__dict__查看属性名称#关闭流
#stream0.close()

一般比对成功的Python字典数据里的score会有大于0.6的值,而比对失败score普遍低于0.1。
例如下面是比对成功的数据:

{'Data': {'MatchList': [{'FaceItems': [{'Confidence': 80.54945, 'DbName':
'default', 'EntityId': 'sfms', 'FaceId': '88665949', 'Score':
0.7572572231292725}, {'Confidence': 77.51004, 'DbName': 'default', 'EntityId':
'sfms', 'FaceId': '88665951', 'Score': 0.7193253040313721}, {'Confidence':
74.420425, 'DbName': 'default', 'EntityId': 'sfms', 'FaceId': '88665946',
'Score': 0.6665557622909546}, {'Confidence': 11.461451, 'DbName': 'default',
'EntityId': 'lyf', 'FaceId': '88657431', 'Score': 0.0663260966539383},
{'Confidence': 5.28706, 'DbName': 'default', 'EntityId': 'lyf', 'FaceId':
'88657429', 'Score': 0.030595608055591583}], 'Location': {'Height': 527, 'Width':
405, 'X': 136, 'Y': 123}, 'QualitieScore': 99.3521}]}, 'RequestId': '6DE302BB-
130A-5D3C-B83D-0937D5A257FD'}

比对失败的数据则如下所示:

{'Data': {'MatchList': [{'FaceItems': [{'Confidence': 6.137868, 'DbName':
'default', 'EntityId': 'lyf', 'FaceId': '88657429', 'Score':
0.03551913797855377}, {'Confidence': 2.9869182, 'DbName': 'default', 'EntityId':
'lyf', 'FaceId': '88657433', 'Score': 0.017284952104091644}, {'Confidence':
2.0808065, 'DbName': 'default', 'EntityId': 'lyf', 'FaceId': '88657431', 'Score':
0.01204138807952404}, {'Confidence': 0.71279377, 'DbName': 'default', 'EntityId':
'lyf', 'FaceId': '88657430', 'Score': 0.004124855622649193}, {'Confidence': 0.0,
'DbName': 'default', 'EntityId': 'sfms', 'FaceId': '88665951', 'Score':
-0.09112970530986786}], 'Location': {'Height': 257, 'Width': 173, 'X': 156, 'Y':
42}, 'QualitieScore': 99.673065}]}, 'RequestId': '62C20100-CCAC-5FE2-9BA6-
AE583F0056EF'}

因此,就可以利用获取的最大score的值判断是否大于0.6来判断是否比对成功。
返回数据的说明:

Data:这是一个对象,其中包含了匹配列表的信息。
MatchList:这是一个数组,其中包含了匹配的结果。每个元素都是一个对象,代表一个匹配项。
FaceItems:这是一个数组,其中包含了匹配项中所有人脸的信息。每个元素都是一个对象,包含了一些关于
该人脸的信息,如自信度(Confidence)、数据库名(DbName)、实体ID(EntityId)、面部ID
(FaceId)和分数(Score)。
Location:这是一个对象,包含了人脸在原始图像中的位置信息,包括宽度(Width)、高度(Height)、
左上角的x坐标(X)和y坐标(Y)。
QualitieScore:这是一个浮点数,表示了整个匹配过程的质量得分。

二、C语言调用阿里云人脸识别接口

修改上一小节的face.py代码,将其中的代码封装成函数,并获取其中字典里score的最大值,以备C语言
调用:

# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_facebody20191230import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import SearchFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptionsconfig = Config(# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='facebody.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)def alibaba_face():search_face_request = SearchFaceAdvanceRequest()#场景一:文件在本地stream0 = open(r'/tmp/SearchFace.jpg', 'rb')search_face_request.image_url_object = stream0#场景二:使用任意可访问的url#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/facebody/SearchFace1.png'#img = urlopen(url).read()#search_face_request.image_url_object = io.BytesIO(img)search_face_request.db_name = 'default'search_face_request.limit = 5runtime_option = RuntimeOptions()try:# 初始化Clientclient = Client(config)response = client.search_face_advance(search_face_request, runtime_option)# 获取整体结果match_list = response.body.to_map()['Data']['MatchList']scores = [item['Score'] for item in match_list[0]['FaceItems']]highest_score = max(scores)value = round(highest_score, 2)return valueexcept Exception as error:# 获取整体报错信息print(error)# 获取单个字段print(error.code)return 0.0# tips: 可通过error.__dict__查看属性名称#关闭流stream0.close()if __name__ == "__main__":alibaba_face()
这里面对scores = [item['Score'] for item in match_list[0]['FaceItems']] 的解释:
match_list[0]['FaceItems']]输入内容为:
[{'Confidence': 12.260886, 'DbName': 'default', 'EntityId': 'sfms', 'FaceId':
'88665949', 'Score': 0.07095234096050262}, {'Confidence': 9.446312, 'DbName':
'default', 'EntityId': 'sfms', 'FaceId': '88665946', 'Score':
0.054664719849824905}, {'Confidence': 1.2030103, 'DbName': 'default', 'EntityId':
'sfms', 'FaceId': '88665951', 'Score': 0.006961682811379433}, {'Confidence': 0.0,
'DbName': 'default', 'EntityId': 'lyf', 'FaceId': '88657431', 'Score':
-0.03559441864490509}, {'Confidence': 0.0, 'DbName': 'default', 'EntityId':
'lyf', 'FaceId': '88657429', 'Score': -0.04274216294288635}]
那么[item['Score'] for item in match_list[0]['FaceItems']是一个 Python 列表推导式),
用于从嵌套的字典中提取特定的值。
具体来说,match_list 是一个包含字典的列表。每个字典里都有很多键值对,其中一个键是
'FaceItems''FaceItems' 对应的值是一个字典列表,每个字典都代表一个面部信息,并且都有一个
'Score' 键。
这个列表推导式的目的是从 data 的第一个元素(即第一个字典)中的 'FaceItems' 键对应的字典列表中
提取所有 'Score' 键的值,并将这些值存储在一个新的列表 scores 中。
分解一下这个列表推导式:
for item in data[0]['FaceItems']:这部分代码遍历 match_list 的第一个元素中的
'FaceItems' 键对应的字典列表。在每次循环中,item 被赋予列表中的下一个字典。
item['Score']:这部分代码获取当前 item(即一个包含面部信息的字典)中 'Score' 键对应的值。
[item['Score'] for item in data[0]['FaceItems']]:整体而言,这个列表推导式创建一个新的列
表 scores,该列表包含 data 中第一个元素的 'FaceItems' 键对应的所有字典的 'Score' 键的值。
最终,scores 将是一个包含所有 'Score' 值的列表,你可以对这个列表进行进一步的操作和分析,比如找
出最大值。

三、System V消息队列和POSIX 消息队列

在后面的项目中会用POSIX消息队列, 和System V消息队列(msgget、msgsnd, msgrcv)
类似,都是用以队列的形式传递消息。接口主要有以下几个:

System V消息队列POSIX 消息队列
主要函数 1. 创建或获取消息队列:
msgget(key_t key, int oflag)

2. 往消息队列中放入消息:
msgsnd(int msqid, const void *ptr, size_t length, int flag)

3. 从消息队列中读取消息:
msgrcv(int msqid, void *ptr, size_t length, long type, int flag)

4. 控制消息队列:
msgctl(int msqid, int cmd, struct msqid_ds *buf)
1. 创建或打开消息队列:
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr attr );

2. 发送消息到消息队列:
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio)

3. 从消息队列接收消息:
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio)

4. 关闭一个消息队列:
int mq_close(mqd_t mqdes)

5. 删除一个消息队列:
int mq_unlink(const char *name)

6. 注册消息队列的异步通知:
int mq_notify(mqd_t mqdes,const struct sigevent *notification);

7. 获取消息队列的属性:
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

8. 设置消息队列的属性:
int mq_setattr(mqd_t mqdes, struct mq_attr *attr, struct mq_attr *oattr);
基本用法
1.创建或获取消息队列:
使用msgget()函数来创建或获取一个消息队列。该函数接受一个键(key)和一个标志(flag)作为参数。如果键的值为IPC_PRIVATE或当前没有消息队列与给定键相关联,将会创建一个新的消息队列。标志位可以用来指定权限组合。

2. 往消息队列中放入消息:
使用msgsnd()函数来往一个消息队列中放入一个消息。该函数接受四个参数,分别为消息队列标识符、指向消息的指针、消息的大小以及标志位。成功放入消息后,该函数返回0,否则返回-1并设置errno来表示错误原因。

3. 从消息队列中读取消息:
使用msgrcv()函数来从一个消息队列中读取一个消息。该函数接受五个参数,分别为消息队列标识符、指向消息的指针、消息的最大大小、消息的类型以及标志位。成功读取消息后,该函数返回读取到的消息的大小,否则返回-1并设置errno来表示错误原因。

4. 控制消息队列:
使用msgctl()函数来对一个消息队列进行控制操作,如删除、设置权限等。该函数接受三个参数,分别为消息队列标识符、操作命令以及一个可选的参数。
1.创建或打开消息队列:
使用mqd_t mq_open(const char *name,int oflag,mode_t mode,struct mq_attr *attr);函数来创建或打开一个消息队列。该函数接受队列名称、打开标志以及可选的权限和属性作为参数。如果队列不存在且指定了创建标志,将会创建一个新的消息队列。成功创建或打开后,函数返回一个消息队列描述符(mqd_t)。

2. 发送消息:
使用int mq_send(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned int msg_prio);函数来发送一个消息到指定的消息队列。该函数接受消息队列描述符、指向消息的指针以及消息的大小作为参数。发送消息时,可以指定消息的优先级,较高的优先级数值表示较高的优先级。成功发送后,函数返回0,否则返回-1并设置errno来表示错误原因。

3. 接收消息:
使用ssize_t mq_receive(mqd_t mqdes, char *mdg_ptr,size_t msg_len,unsigned int *msg_prio);函数来从指定的消息队列中接收一个消息。该函数接受消息队列描述符、指向接收缓冲区的指针以及缓冲区的最大大小作为参数。接收消息时,可以选择按优先级接收,也可以选择非阻塞接收。成功接收后,函数返回接收到的消息的大小,否则返回-1并设置errno来表示错误原因。

4. 关闭消息队列:
使用int mq_close(mqd_t mqdes);函数来关闭一个已打开的消息队列。该函数接受消息队列描述符作为参数。关闭消息队列后,相关的资源将被释放。

5. 删除消息队列:
使用int mq_unlink(const char *name);函数来删除一个已存在的消息队列。该函数接受队列名称作为参数。删除一个消息队列将会移除与之关联的所有消息和状态。



2、3步可以改成下面的6、7、8步,支持异步通知:



6. 设置异步通知:
使用int mq_notify(mqd_t mqdes,const struct sigevent *notification);函数来注册一个进程以接收异步通知。该函数接受消息队列描述符、一个指向sigevent结构的指针以及一个通知标志作为参数。在sigevent结构中,可以设置当消息到达时要发送的信号或者要调用的回调函数。通过设置用int mq_notify(mqd_t mqdes,const struct sigevent *notification);,当消息队列从空变为非空时,已注册的进程将收到一个信号或触发一个回调函数,以异步地通知该进程。

7. 发送消息:
使用int mq_send(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned int msg_prio);函数来发送一个消息到指定的消息队列。该函数接受消息队列描述符、指向消息的指针以及消息的大小作为参数。发送消息时,可以指定消息的优先级,较高的优先级数值表示较高的优先级。成功发送后,函数返回0,否则返回-1并设置errno来表示错误原因。

8.处理异步通知:
当有新消息到达时,已注册的进程将收到一个信号或触发一个回调函数。在信号处理函数或回调函数中,可以执行相关的操作来处理新到达的消息。例如,可以调用ssize_t mq_receive(mqd_t mqdes, char *mdg_ptr,size_t msg_len,unsigned int *msg_prio);来接收并处理消息。

其他说明

  1. mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr) 中oflag和
    mode 参数说明
    参数oflag:同int open(const char *pathname, int flags, mode_t mode);函数的的oflag类似有
    O_RDONLY、O_RDWR, O_WRONLY,除此之外还有 O_CREAT、O_EXCL(如果 O_CREAT 指定,但
    name 不存在,就返回错误),O_NONBLOCK(以非阻塞方式打开消息队列,在正常情况下
    mq_receive和mq_send 函数会阻塞的地方,使用该标志打开的消息队列会返回 EAGAIN 错误)。
    参数mode:同int open(const char *pathname, int flags, mode_t mode);函数的mode参数,用于指定
    权限位, 比如0644权限
  2. 关于 struct mq_attr属性结构提:
struct mq_attr
{long mq_flags;//阻塞标志, 0(阻塞)或O_NONBLOCKlong mq_maxmsg;//最大消息数long mq_msgsize;//每个消息最大大小long mq_curmsgs;//当前消息数
};
  1. mq_notiy函数的使用注意事项:
    a. 注册撤销:当通知被发送给它的注册进程时,其注册会被撤销。这意味着,如果希望继续接收通知,
    进程必须再次调用 mq_notify 以重新注册。
    b. 空队列与数据到来:消息机制触发条件是,在消息队列为空的情况下有数据到来才会触发。当消息队
    列不为空时,即使有新的数据到来也不会触发通知。
    c. 阻塞与通知:只有当没有任何线程阻塞在该队列的 mq_receive 调用的前提下,通知才会发出。这意
    味着,如果有线程正在等待接收消息,通知可能不会被发送。
  2. struct sigevent和sigval_t sigev_val 的定义如下:
union sigval { /* Data passed with notification */int sival_int; /* Integer value */void *sival_ptr; /* Pointer value */
};struct sigevent {int sigev_notify; /* Notification method */int sigev_signo; /* Notification signal */union sigval sigev_value;/* Data passed with notification */void (*sigev_notify_function) (union sigval);/* Function used for threadnotification (SIGEV_THREAD) */void *sigev_notify_attributes;/* Attributes for notification thread(SIGEV_THREAD) */pid_t sigev_notify_thread_id;/* ID of thread to signal(SIGEV_THREAD_ID); Linux-specific */
};

a. sigev_notify取值:
  SIGEV_NONE:这个值表示不需要任何通知。当sigev_notify被设置为这个值时,即使事件发生了,也不会有任何通知发送到进程。
  SIGEV_SIGNAL:事件发生时,将sigev_signo指定的信号发送给指定的进程;
  SIGEV_THREAD:事件发生时,内核会(在此进程内)以sigev_notify_attributes为线程属性创建一个线程,并让其执行sigev_notify_function,并以sigev_value为其参数
b. sigev_signo: 在sigev_notify=SIGEV_SIGNAL时使用,指定信号类别, 例如SIGUSR1、SIGUSR2 等
c.sigev_value: sigev_notify=SIGEV_SIGEV_THREAD时使用,作为sigev_notify_function的参数, 当发送信号时,这个值会传递给信号处理函数。

示例1:使用阻塞方式读写

// 包含所需的头文件
#include <pthread.h> // POSIX线程库
#include <stdio.h> // 标准输入输出库
#include <stdlib.h> // 标准库
#include <unistd.h> // UNIX标准库
#include <mqueue.h> // POSIX消息队列库
#include <string.h> // 字符串处理库// 定义消息队列的名称和要发送的消息
#define QUEUE_NAME "/test_queue" // 消息队列的名称
#define MESSAGE "Hello, World!" // 要发送的消息// 全局的消息队列描述符
mqd_t mq;// sender_thread函数:发送线程的主体
void *sender_thread(void *arg) {char message[] = MESSAGE; // 创建要发送的消息的副本printf("Sender thread started.\n"); // 打印发送线程开始的消息mq_send(mq, message, strlen(message) + 1, 0); // 发送消息到消息队列printf("Message sent.\n"); // 打印消息已发送的消息return NULL; // 返回NULL,表示线程正常结束
}// receiver_thread函数:接收线程的主体
void *receiver_thread(void *arg) {char buffer[256]; // 创建用于接收消息的缓冲区printf("Receiver thread started.\n"); // 打印接收线程开始的消息mq_receive(mq, buffer, sizeof(buffer), NULL); // 从消息队列接收消息printf("Received message: %s\n", buffer); // 打印已接收的消息return NULL; // 返回NULL,表示线程正常结束
}// main函数:程序的入口点
int main() {pthread_t sender, receiver; // 创建发送和接收线程的标识符struct mq_attr attr; // 创建消息队列属性结构体变量// 设置消息队列的属性值attr.mq_flags = 0; // 消息队列的标志位设置为0attr.mq_maxmsg = 10; // 消息队列的最大消息数设置为10attr.mq_msgsize = 256; // 消息队列的每个消息的最大大小设置为256字节attr.mq_curmsgs = 0; // 消息队列的当前消息数设置为0// 打开或创建名为QUEUE_NAME的消息队列,并设置其属性为attr指定的值mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0666, &attr);if (mq == (mqd_t)-1) { // 如果打开或创建失败,则打印错误信息并返回1perror("mq_open");return 1;}// 创建发送线程,如果创建失败,则打印错误信息并返回1if (pthread_create(&sender, NULL, sender_thread, NULL) != 0) {perror("pthread_create (sender)");return 1;}// 创建接收线程,如果创建失败,则打印错误信息并返回1if (pthread_create(&receiver, NULL, receiver_thread, NULL) != 0) {perror("pthread_create (receiver)");return 1;}// 等待发送线程结束,如果发送线程已经结束,则立即返回,否则阻塞等待其结束pthread_join(sender, NULL);// 等待接收线程结束,如果接收线程已经结束,则立即返回,否则阻塞等待其结束pthread_join(receiver, NULL);// 关闭已打开的消息队列描述符mq所引用的消息队列,并释放由该描述符占用的所有资源mq_close(mq);// 删除名为QUEUE_NAME的消息队列,并将其从系统中删除,如果成功则返回0,否则返回-1并设置errno以指示错误。mq_unlink(QUEUE_NAME); // 删除消息队列return 0; // 程序正常退出,返回0
}

示例2: 使用mq_notify sigev_notify = SIGEV_THREAD异步通知的方式实现

#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>#if 0
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr attr );
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); /tiemou
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio);
int mq_close(mqd_t mqdes);
int mq_unlink(const char *name)struct mq_attr
{long mq_flags;//阻塞标志, 0(阻塞)或O_NONBLOCKlong mq_maxmsg;//最大消息数long mq_msgsize;//每个消息最大大小long mq_curmsgs;//当前消息数
};union sigval { /* Data passed with notification */int sival_int; /* Integer value */void *sival_ptr; /* Pointer value */
};struct sigevent {int sigev_notify; /* Notification method */int sigev_signo; /* Notification signal */union sigval sigev_value;/* Data passed with notification */void (*sigev_notify_function) (union sigval);/* Function used for threadnotification (SIGEV_THREAD) */void *sigev_notify_attributes;/* Attributes for notification thread(SIGEV_THREAD) */pid_t sigev_notify_thread_id;/* ID of thread to signal(SIGEV_THREAD_ID); Linux-specific */
};#endif#define QUEQUE_NAME "/test_queue"
#define MESSAGE "mqueque,test!"void *sender_thread(void *arg)
{// 发送消息mqd_t mqd = *(mqd_t *)arg;int send_size = -1;char message[] = MESSAGE;printf("sender thread message=%s, mqd=%d\n", message, mqd);send_size = mq_send(mqd, message, strlen(message) + 1, 0);if (-1 == send_size){if (errno == EAGAIN){printf("message queque is full\n");}else{perror("mq_send");}}return NULL;
}void notify_thread (union sigval sval)
{// 获取消息队列描述符mqd_t mqd = -1;mqd = *((mqd_t *)sval.sival_ptr);// 定义一个缓冲区,用于存储接收到的消息char buffer[256];// 定义一个变量,用于存储接收到的消息的大小ssize_t bytes_read;// 定义一个结构体,用于重新注册消息队列的通知struct sigevent sev;// 打印提示信息printf("notify_thread started, mqd=%d\n", mqd);// 循环接收消息,直到队列为空while (1){// 从消息队列中接收消息bytes_read = mq_receive(mqd, buffer, 256, NULL);// 如果接收失败,检查错误码if (bytes_read == -1){// 如果错误码是EAGAIN,说明队列为空,跳出循环if (errno == EAGAIN){printf("queue is empty\n");break;}// 否则,打印错误信息,退出程序else{perror("mq_receive");exit(1);}}// 如果接收成功,打印接收到的消息的大小和内容printf("read %ld bytes: %s\n", (long)bytes_read, buffer);}// 重新注册消息队列的通知,使用同样的回调函数和参数sev.sigev_notify = SIGEV_THREAD;sev.sigev_notify_function = notify_thread;sev.sigev_notify_attributes = NULL;sev.sigev_value.sival_ptr = &mqd;if (mq_notify(mqd, &sev) == -1){perror("mq_notify");exit(1);}
}#if 0
void *receiver_thread(void *arg)
{mqd_t mqd = *(mqd_t *)arg;ssize_t receiver_size = -1;//接收消息char buffer[256];printf("Receive trehad start\n");receiver_size = mq_receive(mqd, buffer, sizeof(buffer), NULL);printf("receiver thread message=%s, mqd=%d, receiver_size=%ld\n", buffer, mqd, receiver_size);return NULL;
}
#endifint main(int argc, char *argv[])
{pthread_t sender, receiver;//创建消息队列mqd_t mqd = -1;struct mq_attr attr;attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = 256;attr.mq_curmsgs = 0;mqd = mq_open(QUEQUE_NAME, O_CREAT | O_RDWR, 0666, &attr);if (mqd == (mqd_t)-1 ){perror("mq_open");return -1;}struct sigevent sev;// 注册消息队列的通知,使用线程模式,指定回调函数和参数sev.sigev_notify = SIGEV_THREAD;sev.sigev_notify_function = notify_thread;sev.sigev_notify_attributes = NULL;sev.sigev_value.sival_ptr = &mqd;if (mq_notify(mqd, &sev) == -1){perror("mq_notify");exit(1);}if (pthread_create(&sender, NULL, sender_thread, (void *)&mqd) != 0){perror("pthread_create sender");return -1;}#if 0if (pthread_create(&receiver, NULL, receiver_thread, (void *)&mqd) != 0){perror("pthread_create receiver");return -1;}
#endifpthread_join(sender, NULL);sleep(5);//等待触发并把消息读走//pthread_join(receiver, NULL);mq_close(mqd);mq_unlink(QUEQUE_NAME);//sleep(5);return 0;
}

这篇关于【OrangePi Zero2 智能家居】阿里云人脸识别方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697692

相关文章

乐鑫 Matter 技术体验日|快速落地 Matter 产品,引领智能家居生态新发展

随着 Matter 协议的推广和普及,智能家居行业正迎来新的发展机遇,众多厂商纷纷投身于 Matter 产品的研发与验证。然而,开发者普遍面临技术门槛高、认证流程繁琐、生产管理复杂等诸多挑战。  乐鑫信息科技 (688018.SH) 凭借深厚的研发实力与行业洞察力,推出了全面的 Matter 解决方案,包含基于乐鑫 SoC 的 Matter 硬件平台、基于开源 ESP-Matter SDK 的一

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来,无需把原生代码转换为uniapp,可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录,原生入口组件的路径 4、manifest.json中配置分包,使用原生组件 5、需要把原生代码包里的页面修改成组件的方

【新闻】AI程序员要来了吗?阿里云官宣

内容提要 6 月 21 日,在阿里云上海 AI 峰会上,阿里云宣布推出首个AI 程序员。 据介绍,这个AI程序员具备架构师、开发工程师、测试工程师等多种岗位的技能,能一站式自主完成任务分解、代码编写、测试、问题修复、代码提交整个过程,最快分钟级即可完成应用开发,大幅提升研发效率。 近段时间以来,有关AI的实践应用突破不断,全球开发者加速研发步伐。有业内人士坦言,随着大模型性能逐渐提升,AI应

【建设方案】基于gis地理信息的智慧巡检解决方案(源文件word)

传统的巡检采取人工记录的方式,该工作模式在生产中存在很大弊端,可能造成巡检不到位、操作失误、观察不仔细、历史问题难以追溯等现象,使得巡检数据不准确,设备故障隐患得不到及时发现和处理。因此建立一套完善的巡检管理系统是企业实现精细化管理的一项重要工作。 基于GIS地理信息系统绘制常规巡检线路,设置线路巡检频率,当线路处于激活状态时,可根据已设置的频率自动生成巡检线路任务,并以消息的形式推送给执行人,

分布式锁实现方案-基于Redis实现的分布式锁

目录 一、基于Lua+看门狗实现 1.1 缓存实体 1.2 延迟队列存储实体 1.3 分布式锁RedisDistributedLockWithDog 1.4 看门狗线程续期 1.5 测试类 1.6 测试结果 1.7 总结 二、RedLock分布式锁 2.1 Redlock分布式锁简介 2.2 RedLock测试例子 2.3 RedLock 加锁核心源码分析 2.4

Android Apk瘦身方案1——R.java文件常量内联

R.java 文件结构 R.java 是自动生成的,它包含了应用内所有资源的名称到数值的映射关系。先创建一个最简单的工程,看看 R.java 文件的内容: R文件生成的目录为app/build/generated/not_namespaced_r_class_sources/xxxxxDebug/processXXXXDebugResources/r/com/xxx/xxx/R.java

CloudStack管理员文档 - 服务方案

用户创建一个实例可以又很多个选项来设定该实例的特性和性能。CloudStack提供以下几种方式: 服务方案,由管理员定义,提供了CPU速度,CPU数量,内存大小,根磁盘的标签,以及其他选项磁盘方案,由管理员定义,为主存储提供了磁盘大小和IOPS的选项网络方案,由管理员定义, 计算和磁盘方案 服务方案是CPU,内存,磁盘等虚拟硬件特性的集合。管理员可以创建各种服务方案,终端用户在创建虚拟机的时

华为欧拉 openEuler24.03 更新 阿里 yum源

华为欧拉 openEuler24.03 更新 阿里 yum源 备份 yum 源编写 阿里云 yum源 配置文件更新 yum 缓存 备份 yum 源 mv /etc/yum.repos.d/openEuler.repo /etc/yum.repos.d/openEuler.repo.bak 编写 阿里云 yum源 配置文件 vim /etc/yum.repos.d/openEuler.r

ESP32使用MQTT_TCP连接阿里云

ESP32-IDF中MQTT函数的介绍 esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config) 函数功能:mqtt 客户端初始化 函数形参: mqtt 客户端的配置 函数返回值:正确则返回根据配置创建的 mqtt 客户端句柄;异常则返回 NULL。 其中函数形参 esp_mqtt

硬件上STM32F4xx兼容STM32F1xx的方案

前言 2020年开始,因为疫情,全球晶圆缺货,加上不少供应商屯芯片,导致ST的芯片价格一路飙涨,特别是STM32F1系列的单片机,价格涨的特别离谱,还缺货。。。。问了以下ST代理商,说STM32F1系列的属于168nm产品线的,正在被ST淘汰,让尽快用先进一点工艺的代替,手里有个项目用的STMF103VET6,代理商推荐先用STM32F401VE代替,国内现在右不少厂家可以pin2pin替代ST