目录
- 实现思路
- 主要功能代码
- 功能验证
实现思路
学完前面在Ubuntu系统下的人脸识别内容,本节我们将实现在树莓派上通过一个指令启动摄像头进行拍照并且完成人脸识别,为后面我们的智能家居系统加入人脸识别开锁功能做准备。
首先,去扩容一下SD卡
其次,使用指令拍一张照片
上文介绍的拍照指令,运行即可拍照:
raspistill -o image3.jpg
如果直接使用上面这个指令,那拍出来的图片大概有1.3M左右,不符合祥云后台所要求的图片大小在200k以下
那么我们可以对图片进行压缩,可以改变摄像头的参数,降低摄像头输出的图片质量。通过查阅树莓派摄像头参数设置,做如下配置:
raspistill -q 5 -t 1 -o image.jpg
-q
是图片质量,在0~100之间,我们调成5,压缩图片质量-t
是拍照延时,设定1s后拍照
经过测试,拍出来的图片由原来的1.3M变成了90多K,而且图片还很清晰。
做好上述准备工作,我们就可以发送指令open-c1
启动摄像头线程了,在这个线程中,需要完成下列操作:
①摄像头拍照返回base64流,
②然后用翔云平台进行对比识别,
③返回识别的结果
线程结束完成一次拍照识别
主要功能代码
(1)通过拍照指令提取当前人脸数据的base64流
上面介绍了树莓派的拍照指令,问题是拍照的时机怎么确定?
- 原始手动的话,采用按钮方式人脸靠近手动拍照;
- 自动的话一种方式是通过每隔一秒拍照一次,不断对比,很快就会将OCR识别次数用完;
- 另一种方式我们可以识别人物动作,发现有人来再进行拍照识别,具体可参照这篇博文:树莓派摄像头使用Motion监测人物动作
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
}
(2)通过命令起摄像头线程
if(0 == strcmp(devName,"c1")){ //open-c1起摄像头线程
pthread_t cameraThread;
pthread_create(&cameraThread,NULL,cameraThread_func,NULL);
return; //执行拍照就不往下走了,返回
}
(3)线程函数
void *cameraThread_func(void* data)//起线程的函数有格式要求
{
struct Devices *cameraTemp;
cameraTemp = findDeviceByNum(pDeviceHead, "c1"); //摄像头的设备编号为c1
if(cameraTemp == NULL){ //防止段错误的必需判断,当给指针赋值是,一定要考虑NULL的情况,否则后续操作都是空谈
printf("find camera error\n");
pthread_exit(NULL); //在线程中不用return
}
cameraTemp->justDoOnce(); //设备都要从工厂里面取出来.可不能camera.justDoOnce,谁认识你这个camera!
}
(4)摄像头的结构体
struct Devices camera = {
.name = "camera",
.serialNum = "c1",
.justDoOnce = postUrl,
.deviceInit = cameraInit,
};
(5)调库人脸识别函数
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 phtot.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);
}
}
(6)回调函数
char ocrRetBuf[1024] = {'\0'};//全局变量,用来接收从OCR后台返回的数据
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)
//回调函数,把从后台的数据拷贝给ocrRetBuf
{
strncpy(ocrRetBuf,ptr,1024);
}
功能验证
提前拍好自己的一张帅气照片放到树莓派里面,靠近树莓派摄像头识别成功,相应的继电器打开,锁开启。