智能家居—— 树莓派摄像头捕捉人脸并识别

下载安装mjpg-streamer

树莓派安装libcurl库

  • 下载可以执行wget --no-check-certificate https://curl.se/download/curl-7.71.1.tar.bz2
  • 后续安装编译可以看智能家居——libcurl库简介中的步骤完成
  • 验证访问百度网站是否正常

树莓派安装openssl库

语音控制开启摄像头线程拍照

因为祥云后台所要求的图片大小在200k左右
在这里插入图片描述
拍照的关键指令:

raspistill -q 5 -t 1 -o image.jpg
  • -q 是图片质量,在0~100之间,我们调成5,压缩图片质量,
  • -t 是拍照延时,设定1s后拍照

代码及步骤

由于翔云人脸识别次数有限,无法通过每个一段时间拍一次照片,而是采用语音控制开启摄像头线程,识别到人脸就进行拍照,需要用到motion库的人脸识别和拍照指令

  1. 语音线程会调用command函数,在mian.c的command函数中添加“启动摄像头线程”
if(strcmp("OCR",CmdHandler->command) == 0){
	pthread_t cameraThread;
	pthread_create(&cameraThread,NULL,cameraThread_func,NULL);
	return;  //执行拍照就不往下走了,返回
}

  1. 在main.c文件中执行“摄像头线程调用”函数
void *cameraThread_func(void* data)//起线程的函数有格式要求
{
	struct Devices *cameraTemp;
	cameraTemp = findDeviceByNum(pDeviceHead, "c1"); //摄像头的设备编号为c1

	if(cameraTemp == NULL){  //防止段错误的必需判断
		printf("find camera error\n");
		pthread_exit(NULL); //在线程中不用return
	}

	cameraTemp->justDoOnce(); //调用设备的justDoOnce函数
}

  1. 定义摄像头对象:camera.c文件
#include "controlDevice.h"			//自定义设备类的文件
#include <stdio.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
#include<malloc.h>
#include <fcntl.h>
#include <unistd.h>


size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)
//回调函数,把从后台的数据拷贝给ocrRetBuf
{
        strncpy(ocrRetBuf,ptr,1024);
}


char* getFace()
{
	printf("人脸数据采集中...\n");
	system("raspistill -q 5 -t 1 -o image.jpg");//关键命令
	
	while(access("./image.jpg",F_OK) != 0); //判断是否拍照完毕
	
	printf("数据采集完毕\n");
	
	char* base64BufFaceRec = getBase64FromFile("./image.jpg");
	system("rm image.jpg");  //采集完成删除,防止占内存
	
	return base64BufFaceRec;   //返回刚才拍照的base64
}

char * getBase64FormFile(char * filePath){

        char *baseBuff =NULL;
        char cmd[256] = {'\0'};
        sprintf(cmd,"base64 %s >tmpFile",filePath);
        system(cmd);

        int fp = open("./tmpFile",O_RDWR);
        int fileLen = lseek(fp,0,SEEK_END);
        baseBuff = (char *)malloc(fileLen+8);
        lseek(fp,0,SEEK_SET);
        memset(baseBuff,'\0',fileLen+8);
        read(fp,baseBuff,fileLen+8);

        close(fp);
        system("rm -rf tmpFile");
        return baseBuff;

}

void postUrl()
{
        CURL *curl;
        CURLcode res;

        //分开定义,然后字符串拼接
        char* key    = "xxx";
        char* secret = "xxx";
        int   typeId = 21;
        char* format = "xml";

        char* base64BufPic1 = getFace();//摄像头拍照获取的照片
        char* base64BufPic2 = getBase64FromFile("./Your photo.jpg");//个人照片

        int len = strlen(key)+strlen(secret)+strlen(base64BufPic1)+strlen(base64BufPic2)+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",base64BufPic1,base64BufPic2,key,secret,typeId,format);//根据平台的传参格式编写

        curl = curl_easy_init();

        if(curl){
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);  //指定post内容,传入参数  
                curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");// 指定url
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,readData);  //回调函数readDate读取返回值
                res = curl_easy_perform(curl);          //类似于状态码
                printf("OK:%d\n",res);

                if(strstr(ocrRetBuf,"否") != NULL){    //判断翔云后台返回的字符串中有没有“是”
                        printf("不是同一个人\n");
                }
                else{
                        printf("是同一个人\n");//这里识别成功,去开锁!
                }
                curl_easy_cleanup(curl);
        }

}

struct Devices camera = {

	.name = "camera",
	.serialNum = "c1",
	.justDoOnce = postUrl,
	.deviceInit = cameraInit,

};

struct Devices* addCameraToDeviceLink(struct Devices *phead)		//餐厅灯(对象)加入设备链表函数
{
	if(phead == NULL){
		return &camera;
	}else{
		camera.next = phead;  //以前的头变成.next
		phead = &camera;      //更新头
		return phead;
	}
}
  1. 修改controlDevice.h文件
#include <wiringPi.h>					//wiringPi库
#include <stdio.h>
#include <stdlib.h>


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

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);	//“改变设备状态”函数指针
 	int (*justDoOnce)();				//调库人脸识别函数指针
    struct Devices *next;
};
 
struct Devices* addBathroomLightToDeviceLink(struct Devices *phead);		//“浴室灯”加入设备链表函数声明
struct Devices* addBedroomLightToDeviceLink(struct Devices *phead);	        //“卧室灯”加入设备链表函数声明
struct Devices* addRestaurantLightToDeviceLink(struct Devices *phead);		//“餐厅灯”加入设备链表函数声明
struct Devices* addLivingroomLightToDeviceLink(struct Devices *phead);		//“客厅灯”加入设备链表函数声明
struct Devices* addSmokeAlarmToDeviceLink(struct Devices *phead);           //“烟雾报警器”加入设备链表函数声明
struct Devices* addBuzzerToDeviceLink(struct Devices *phead);		        //“蜂鸣器”加入设备链表函数声明
struct Devices* addCameraToDeviceLink(struct Devices *phead);				//“人脸识别”加入设备链表函数声明

参考博文:智能家居 (11) ——树莓派摄像头捕捉人脸并识别

语音控制摄像头拍照

这种方式不开启线程,而是通过语音识别执行拍照,上传到翔云服务器放回对比结果,只需要用到拍照指令

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>

typedef unsigned int bool;//数据类型别名用typedef  有分号
#define true 1            //宏定义(替换)用define         无冒号
#define false 0
 
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)
{
        strncpy(ocrRetBuf,ptr,1024);
}
 
char* getBase64FromFile(char* filePath)
{
        char* base64Buf = NULL;
        char cmd[256] = {'\0'};
 
        sprintf(cmd,"base64 %s > tmpFile",filePath);//图片的base64流导入到文件中
        system(cmd);
 
        int fd = open("./tmpFile",O_RDWR);
        int fileLen = lseek(fd,0,SEEK_END);        //计算文件大小
        lseek(fd,0,SEEK_SET);                      
 
        base64Buf = (char* )malloc(fileLen+8);
        memset(base64Buf,'\0',fileLen+8);
 
        read(fd,base64Buf,fileLen+8);              //从文件中读取base64流到字符串 
        close(fd);
        system("rm -f tmpFile");
 
        return base64Buf;
}
 
char* getFace()
{
	printf("人脸数据采集中...\n");
	system("raspistill -q 5 -t 1 -o image.jpg");
	
	while(access("./image.jpg",F_OK) != 0); //判断是否拍照完毕
	
	printf("数据采集完毕\n");
	
	char* base64BufFaceRec = getBase64FromFile("./image.jpg");
	system("rm image.jpg");  //采集完成删除,防止占内存
	return base64BufFaceRec;   //返回刚才拍照的base64
}
 
bool postUrl()       //根据文档,接口调用方法为post请求     
{
        CURL *curl;
        CURLcode res;
        
        //根据翔云平台的接口要求  分开定义,然后字符串拼接
        char* base64BufPic1 = getFace();//图片base64流
        char* base64BufPic2 = getBase64FromFile("./zms.jpg");
        char* key    = "xxxx";
        char* secret = "xxxx";
        int   typeId = 21;
        char* format = "xml"; 
        
        int len = strlen(key)+strlen(secret)+strlen(base64BufPic1)+strlen(base64BufPic2)+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",base64BufPic1,base64BufPic2,key,secret,typeId,format);
 
        curl = curl_easy_init();
        if(curl){
                //curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt");// 指定cookie缓存文件 
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);//指定post传输内容,get请求将URL和postString一次性发送
                curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");// 指定url
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,readData);  //回调函数读取返回值
                res = curl_easy_perform(curl);
                printf("OK:%d\n",res);
                curl_easy_cleanup(curl);
        }
        return true;
}
 
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 <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>					//wiringPi库
 
extern char ocrRetBuf[1024];
 
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);		//“浴室灯”加入设备链表函数声明
struct Devices* addBedroomLightToDeviceLink(struct Devices *phead);	        //“卧室灯”加入设备链表函数声明
struct Devices* addRestaurantLightToDeviceLink(struct Devices *phead);		//“餐厅灯”加入设备链表函数声明
struct Devices* addLivingroomLightToDeviceLink(struct Devices *phead);		//“客厅灯”加入设备链表函数声明
struct Devices* addSmokeAlarmToDeviceLink(struct Devices *phead);           //“烟雾报警器”加入设备链表函数声明
struct Devices* addBuzzerToDeviceLink(struct Devices *phead);		        //“蜂鸣器”加入设备链表函数声明
struct Devices* addCameraToDeviceLink(struct Devices *phead);

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

    if(strcmp("OCR",CmdHandler->command) == 0){
        tmp = findDeviceByName("camera",pdeviceHead);
		if(tmp != NULL){
            tmp->justDoOnce();
            if(strstr(ocrRetBuf,"否") != NULL){    //字符串检索 判断翔云后台返回的一大堆字符串中有没有“否”
                printf("人脸比对失败\n");
            }else{
                printf("人脸比对成功\n");
				//可以将开锁开灯等函数添加在此处
            }
        }
	}

编译:

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

参考博文:智能家居(8) —— 香橙派摄像头加入设备工厂

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值