【智能家居】八、监控摄像采集、人脸识别比对进行开门功能点

news2024/11/29 4:47:42

一、使用 fswebcam 测试 USB 摄像头
二、根据demo来实现功能点
三、功能点编写编译运行实现

一、使用 fswebcam 测试 USB 摄像头

a. 安装 fswebcam

orangepi@orangepi:~$ sudo apt update
orangepi@orangepi:~$ sudo apt-get install -y fswebcam

b. 安装完 fswebcam 后可以使用下面的命令来拍照

a) -d 选项用于指定 USB 摄像头的设备节点
b) --no-banner 用于去除照片的水印
c) -r 选项用于指定照片的分辨率
d) -S 选项用设置于跳过前面的帧数
e) ./image.jpg 用于设置生成的照片的名字和路径

orangepi@orangepi:~$ sudo fswebcam -d /dev/video0 \ --no-banner -r 1280x720 -S 5 ./image.jpg

c. 在服务器版的 linux 系统中,拍完照后可以使用 scp 命令将拍好的图片传到
Ubuntu PC 上镜像观看

orangepi@orangepi:~$ scp image.jpg test@192.168.1.55:/home/test(根据实际情况修改 IP 地址和路径)

d. 在桌面版的 linux 系统中,可以通过 HDMI 显示器直接查看拍摄的图片

这里使用fswebcam进行拍照。参考用户手册

首先在/smart_home拍照,命名为imageComp.jpg

sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./imageComp.jpg

二、根据demo来实现功能点

demo.c

主要的功能是通过摄像头采集人脸数据,然后通过 cURL 发送 POST 请求到指定的 API 接口,接收 OCR 后台返回的数据。在这个过程中,你使用了一些全局变量、文件 I/O、cURL 库等。

以下是一些建议和注意事项:

  1. 错误处理: 在系统调用和库函数调用后,最好检查其返回值,以确保操作成功。例如,你可以在文件打开、内存分配等操作后添加错误检查,并在失败时输出错误信息。

  2. 函数封装: 考虑将一些相关的操作封装成函数,以提高代码的模块性和可读性。例如,可以将 cURL 相关的初始化和清理操作封装成函数。

  3. 全局变量的使用: 全局变量在函数间传递数据,但过度使用全局变量可能导致代码难以理解和维护。尽量将数据传递作为函数参数,以提高函数的可复用性。

  4. 内存释放: 在使用 malloc 分配内存后,确保在不再需要使用该内存时调用 free 进行释放,以避免内存泄漏。

  5. 字符串操作: 在使用字符串拼接函数(如 sprintf)时,确保目标缓冲区足够大以防止缓冲区溢出。

  6. 资源释放顺序: 在释放资源时,注意释放的顺序,以避免悬挂指针或资源泄漏。

  7. 安全性: 尽量避免使用 system 函数来执行外部命令,这可能带来安全风险。如果可能的话,考虑使用更安全的库函数或 API。

下面是一些可能的改进:

// 错误处理函数
void handleError(const char *message)
{
    perror(message);
    exit(EXIT_FAILURE);
}

// 初始化 cURL
CURL *initCurl()
{
    CURL *curl = curl_easy_init();
    if (!curl)
    {
        handleError("curl_easy_init failed");
    }
    return curl;
}

// 释放 cURL 资源
void cleanupCurl(CURL *curl)
{
    curl_easy_cleanup(curl);
}

// 发送 cURL POST 请求
bool sendPostRequest(const char *url, const char *postString, size_t (*writeCallback)(void *, size_t, size_t, void *))
{
    CURL *curl = initCurl();
    if (!curl)
    {
        return false;
    }

    CURLcode res;

    // 设置 cURL 选项
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);

    // 执行请求
    res = curl_easy_perform(curl);

    // 检查执行结果
    if (res != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        cleanupCurl(curl);
        return false;
    }

    cleanupCurl(curl);
    return true;
}

// 获取人脸数据
char *getFace()
{
    printf("人脸数据采集中...\n");
    system("sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./image.jpg");

    while (access("./image.jpg", F_OK) != 0)
    {
        // 等待文件存在
    }

    printf("数据采集完毕\n");

    // 获取 base64 数据
    char *base64BufFaceRec = getBase64FromFile("./image.jpg");
    remove("./image.jpg"); // 采集完成删除,防止占内存

    return base64BufFaceRec;
}

// 发送人脸数据到 OCR 后台
bool sendFaceToOcr()
{
    char *img1 = getFace(); // 图片 base64 流
    char *img2 = getBase64FromFile("/home/orangepi/smart_home/test/imageComp.jpg");
    char *key = "JFD5c1iBh9LVqPkkZMqM8Q";
    char *secret =

 "76f444813fc945bd9543e4d7e086ad87";
    int typeId = 21;
    char *format = "xml";

    // 分配足够的空间
    int len = strlen(key) + strlen(secret) + strlen(img1) + strlen(img2) + 128;
    char *postString = (char *)malloc(len);
    if (!postString)
    {
        handleError("Memory allocation failed");
    }

    // 字符串拼接
    snprintf(postString, len, "img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", img1, img2, key, secret, typeId, format);

    // 发送 POST 请求
    bool result = sendPostRequest("https://netocr.com/api/faceliu.do", postString, readData);

    // 释放动态分配的内存
    free(img1);
    free(img2);
    free(postString);

    return result;
}

// 入口函数
int main(void)
{
    // 发送人脸数据到 OCR 后台
    if (sendFaceToOcr())
    {
        // 处理 OCR 后台返回的数据
        // ...
    }

    return 0;
}

这个版本进行了一些重构,提高了可读性和模块性,同时加入了错误处理。你可以根据需要进一步调整。

三、功能点编写编译运行实现

camera.c

#include "controlDevice.h"
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

char ocrReturnBuf[1024] = {'\0'}; // 全局变量,用来接收从OCR后台返回的数据

// 回调函数,读取从OCR后台返回的数据(把从后台的数据拷贝给ocrReturnBuf)
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)
{
        size_t totalSize = size * nmemb;

        // 为了避免溢出,计算实际拷贝的长度
        size_t copySize = (totalSize < (sizeof(ocrReturnBuf) - 1)) ? totalSize : (sizeof(ocrReturnBuf) - 1);

        // 拷贝数据到 ocrRetBuf 中
        memcpy(ocrReturnBuf, ptr, copySize);

        // 手动添加字符串终结符
        ocrReturnBuf[copySize] = '\0';

        return totalSize;
}

char *getBase64FromFile(const char *filePath)
{
        char cmd[256] = {'\0'};
        char *base64Buf = NULL;

        // 使用安全的方式构建命令
        snprintf(cmd, sizeof(cmd), "base64 %s | tr -d '\n' > tmpFile", filePath);

        if (system(cmd) == -1) {
                perror("Error executing system command");
                return NULL;
        }

        int fd = open("./tmpFile", O_RDWR);
        if (fd == -1) {
                perror("Error opening file");
                return NULL;
        }

        // 计算文件大小
        int fileLen = lseek(fd, 0, SEEK_END);
        lseek(fd, 0, SEEK_SET);

        // 动态分配内存
        base64Buf = (char *)malloc(fileLen + 1);
        if (base64Buf == NULL) {
                perror("Error allocating memory");
                close(fd);
                return NULL;
        }

        memset(base64Buf, '\0', fileLen + 1);

        // 读取文件内容到字符串
        if (read(fd, base64Buf, fileLen) == -1) {
                perror("Error reading file");
                free(base64Buf);
                close(fd);
                return NULL;
        }

        close(fd);

        // 删除临时文件
        if (remove("tmpFile") == -1) {
                perror("Error deleting temporary file");
        }

        return base64Buf;
}

// 获取人脸数据
char *getFace()
{
        printf("人脸数据采集中...\n");
        system("sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 /home/orangepi/smart_home/test/image.jpg");

        while (access("./image.jpg", F_OK) != 0); // 判断是否拍照完毕

        printf("数据采集完毕\n");

        // 获取 base64 数据
        char *base64BufFaceRec = getBase64FromFile("./image.jpg");
        remove("./image.jpg");   // 采集完成删除,防止占内存

        return base64BufFaceRec; // 返回刚才拍照的base64
}

// 根据文档,接口调用方法为post请求
void postUrl()
{
        CURL *curl;
        CURLcode res;

        // 根据翔云平台的接口要求  分开定义,然后字符串拼接
        char *img1 = getFace(); // 图片base64流
        char *img2 = getBase64FromFile("/home/orangepi/smart_home/test/imageComp.jpg");
        char *key = "JFD5c1iBh9LVqPkkZMqM8Q";
        char *secret = "76f444813fc945bd9543e4d7e086ad87";
        int typeId = 21;
        char *format = "xml";

        int len = strlen(key) + strlen(secret) + strlen(img1) + strlen(img2) + 128; // 分配空间不够会>导致栈溢出
        char* postString = (char*)malloc(len);
        memset(postString, '\0', len);//因为postString是一个指针,不能用sizeof来计算其指向的大小

        // 字符串拼接
        sprintf(postString, "img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", img1, img2, key, secret, typeId, format);
        
        // 初始化 cURL
        curl = curl_easy_init();
        if (curl)
        {
                // 指定cookie缓存文件
                // if (curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt") != CURLE_OK)
                // {
                //         fprintf(stderr, "Failed to set cookie file\n");
                //         return false; // 在设置失败时,直接返回
                // }
                // 指定post传输内容,get请求将URL和postString一次性发送
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);
                // 指定url
                curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");
                // 回调函数读取返回值
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData);

                // 执行请求
                res = curl_easy_perform(curl);
                if (res != CURLE_OK) {
                        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
                        // 处理错误
                }

                // 释放 cURL 资源
                curl_easy_cleanup(curl);
        }
        // 释放动态分配的内存
        free(img1);
        free(img2);
        free(postString);
}

struct Devices camera = {
    .deviceName = "camera",
    .justDoOnce = postUrl
};

struct Devices *addCameraToDeviceLink(struct Devices *phead)
{
        if (phead == NULL) {
                return &camera;
        }
        else {
                camera.next = phead; // 以前的头变成.next
                phead = &camera;     // 更新头
                return phead;
        }
}

controlDevice.h

#include <wiringPi.h>					//wiringPi库
#include <stdio.h>
#include <stdlib.h>

// 设备结构体
struct Devices                          //设备类
{
    char deviceName[128];               //设备名
    int status;                         //状态
    int pinNum;							//引脚号

    // 函数指针,用于设备控制
    int (*Init)(int pinNum);			//“初始化设备”函数指针
	int (*open)(int pinNum);			//“打开设备”函数指针
	int (*close)(int pinNum);			//“关闭设备”函数指针
    int (*readStatus)(int pinNum);		//“读取设备状态”函数指针  为火灾报警器准备
	int (*changeStatus)(int status);	//“改变设备状态”函数指针
    void (*justDoOnce)();               // 仅执行一次的操作

    struct Devices *next;
};
 
struct Devices* addBathroomLightToDeviceLink(struct Devices *phead);		//“浴室灯”加入设备链表函数声明      2
struct Devices* addBedroomLightToDeviceLink(struct Devices *phead);	        //“卧室灯”加入设备链表函数声明      8
struct Devices* addRestaurantLightToDeviceLink(struct Devices *phead);		//“餐厅灯”加入设备链表函数声明      13
struct Devices* addLivingroomLightToDeviceLink(struct Devices *phead);		//“客厅灯”加入设备链表函数声明      16
struct Devices* addSmokeAlarmToDeviceLink(struct Devices *phead);           //“烟雾报警器”加入设备链表函数声明  6
struct Devices* addBuzzerToDeviceLink(struct Devices *phead);		        //“蜂鸣器”加入设备链表函数声明      9
struct Devices *addCameraToDeviceLink(struct Devices *phead);               // “摄像头”加入设备链表
struct Devices *addLockToDeviceLink(struct Devices *phead);                 // “门锁”加入设备链表

main.c

在main.c文件里的Command(struct InputCommand* CmdHandler)函数中添加

    // OCR 指令:执行人脸识别功能进行开门
    if (strcmp("OCR", CmdHandler->command) == 0)
    {
        tmp = findDeviceByName("camera", pdeviceHead);
        if (tmp != NULL)
        {
            tmp->justDoOnce();
            // 字符串检索 判断翔云后台返回的一大堆字符串中有没有“否”
            if (strstr(ocrReturnBuf, "否") != NULL)
            {
                printf("人脸比对失败\n");
            }
            else
            {
                printf("人脸比对成功\n");
                tmp = findDeviceByName("lock", pdeviceHead);
                if (tmp != NULL)
                {
                    tmp->open(tmp->pinNum);
                    printf("已开门\n");
                    delay(3000);
                    tmp->close(tmp->pinNum);
                }
            }
        }
    }

这里的摄像头只是当作一个设备去用,目前实现通过串口指令然后system()进行拍照。然后翔云平台进行人脸对比,未实现自动人脸检测。

所以摄像头没有另创线程。但是做视频监控可以另创线程。

这样当串口发送OCR时,实现人脸对比并开锁,所以没有用线程去做

当然,要把camera、lock设备加入设备工厂

注意:ocrReturnBuf这个因为要再不同文件调用的全局变量所以要extern

编译运行

gcc *.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -I /home/orangepi/curl-7.71.1/_install/include/ -L /home/orangepi/curl-7.71.1/_install/lib/ -lcurl

sudo ./a.out

在这里插入图片描述

运行结果

在这里插入图片描述

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

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

相关文章

036.Python面向对象_self_cls_super

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

AMC8美国数学竞赛历年真题集在线练习操作指南和2024年备考建议

今天是2023年12月10日&#xff0c;距离2024年的AMC8美国数学竞赛的举办还有40天时间。据六分成长了解&#xff0c;有一些孩子报名参加了AMC8的机构培训班系统学习&#xff0c;也有一些孩子选择了自己自学备考。 有家长问AMC8的培训是否一定要参加机构的培训班学习&#xff1f;…

Aho Corasick Algorithm

文章目录 前言介绍实现参考 前言 Aho Corasick Algorithm又叫AC自动机&#xff0c;该算法是一个匹配算法&#xff0c;用来匹配文本Text中多个patterns分别出现的次数&#xff1b; 我们定义n为patterns的总长度&#xff1b;m为Text的长度&#xff1b; 问题&#xff1a;在ahis…

C语言算法与数据结构,旅游景区地图求最短路径

背景&#xff1a; 本次作业要求完成一个编程项目。请虚构一张旅游景区地图&#xff0c;景区地图包括景点&#xff08;结点&#xff09;和道路&#xff08;边&#xff09;&#xff1a;地图上用字母标注出一些点&#xff0c;表示景点&#xff08;比如&#xff0c;以点 A、B、C、…

【C语言】字符串函数strcpystrcatstrcmpstrstr的使⽤和模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

聚类算法的性能度量

聚类算法的性能度量 聚类算法就是根据数据中样本与样本之间的距离或相似度&#xff0c;将样本划分为若干组&#xff0f;类&#xff0f;簇&#xff0c;其划分的原则&#xff1a;簇内样本相似、簇间样本不相似&#xff0c;聚类的结果是产生一个簇的集合。 其划分方式主要分为两…

前后端联调神器《OpenAPI-Codegen》

在后端开发完接口之后&#xff0c;前端如果再去写一遍接口来联调的话&#xff0c;会很浪费时间&#xff0c;这个时候使用OpenAPI接口文档来生成Axios接口代码的话&#xff0c;会大大提高我们的开发效率。 Axios引入 Axios是一个基于Promise的HTTP客户端&#xff0c;用于浏览器…

leetcode每日一题39

122.买卖股票的最佳时机II 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j] j0 不持有股票 j1持有股票 i&#xff1a;第i天 dp[i][j]&#xff1a;第i天&#xff0c;持有状态为j时的最大现金确定递推公式 dp[i][0]max(dp[i-1][0], dp[i-1][1]prices[i]); 第…

Linux权限(用户角色+文件权限属性)

Linux权限 文章目录 Linux权限一.文件权限1.快速掌握修改权限的方法&#xff08;修改文件权限属性&#xff09;2.对比权限的有无&#xff0c;以及具体的体现3.修改权限的第二套方法&#xff08;修改用户角色&#xff09;4.文件类型&#xff08;Linux下一切皆文件&#xff09; 二…

编辑拒稿理由是重复率高

大家好&#xff0c;今天来聊聊编辑拒稿理由是重复率高&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 编辑拒稿理由是重复率高 当作者提交论文到学术期刊后&#xff0c;编辑会对论文进行审核小发猫写作…

Vue3: 给表格多个字段添加排序功能

问题 在Vue3项目中&#xff0c;使用element-plus的表格组件绘制表格后&#xff0c;需要令表格的多个字段可以进行选择排序&#xff08;选择升序或者降序&#xff09;但是排序功能好像有时候会出错&#xff0c;需要排序的字段多了之后&#xff0c;排序功能有时候会不起作用 解…

算法专题一:双指针

算法专题一&#xff1a;双指针 一&#xff1a;移动零1.GIF题目解析&#xff1a; 二&#xff1a;复写零2.GIF题目解析&#xff1a; 三&#xff1a;快乐数3.GIF题目解析&#xff1a; 四&#xff1a;装水最多容器&#xff1a;4.GIF题目解析&#xff1a; 五&#xff1a;有效三角形的…

知识产权服务企业网站建设效果如何

知识产权服务也有较高的市场需求度&#xff0c;尤其如今互联网深入到各个行业&#xff0c;无论个人还是企业都会以不同的方式经营&#xff0c;相应的为保障自身权益&#xff0c;注册商标、专利等自然不可少&#xff0c;而对普通小白来说&#xff0c;想要完成这些流程也是有些难…

NSSCTF-Crypto靶场练习--第11-20题wp

文章目录 [SWPUCTF 2021 新生赛]traditional[LitCTF 2023]梦想是红色的 (初级)[SWPUCTF 2021 新生赛]crypto2[羊城杯 2021]Bigrsa[LitCTF 2023]Hex&#xff1f;Hex&#xff01;(初级)[SWPU 2020]happy[AFCTF 2018]BASE[安洵杯 2019]JustBase[鹤城杯 2021]Crazy_Rsa_Tech[SWPUCT…

mybatisPlus框架

1、特性 无侵入 &#xff1a;只做增强不做改变&#xff0c;引入它不会对现有工程产生影响&#xff0c;如丝般顺滑 损耗小 &#xff1a;启动即会自动注入基本 CURD &#xff0c;性能基本无损耗&#xff0c;直接面向对象操作 强大的 CRUD 操作 &#xff1a;内置通用 Mapper 、…

Oracle(2-12)User-Managed Complete Recovery

文章目录 一、基础知识1、Media Recovery 介质恢复2、Recovery Steps 恢复步骤3、恢复4、Recovery in ARCHIVELOG 在ARCHIVELOG中恢复5、Complete Recovery完全恢复6、CR in ARCHIVELOG Mode 归档日志模式下的完全恢复7、Determine Files Need Recovery确定需要恢复的文件8、Ab…

JDK8新特性:Lambda表达式规则及用法,方法引用

目录 Lambda表达式是JDK8新增的一种语法格式 1.作用 2.用法规则&#xff1a; 3.方法引用 Lambda表达式是JDK8新增的一种语法格式 1.作用 简化匿名内部类的代码写法 Lambad用法前提&#xff1a;只能简化函数式接口&#xff08;一般加有Funcationallnterface&#xff09;&a…

2023年11月10日 Go生态洞察:十四年Go的成长之路

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

STL(八)(总结篇)

###以四道题来总结 题号:lanqiao OJ 3226 1.宝藏排序II ### 这道题主要考察sort,非常简单输出就是升序不需要自定义比较函数 #include<bits/stdc.h> using namespace std; const int N1e55; //这里用int就足够了不需要开long long int a[N]; int main(){ios::sync_with…

TCP的滑动窗口机制

网络的错误检测和补偿机制非常复杂。 一、等待超时时间&#xff08;返回ACK号的等待时间&#xff09; 当网络繁忙时会发生拥塞&#xff0c;ACK号的返回变慢&#xff0c;较短的等待时间会导致频繁的数据重传&#xff0c;导致本就拥塞的网络雪上加霜。如果等待时间过长&#xf…