基于Ubuntu22.04和OpenCV4.5.4的物联网人脸识别考勤机
前言:本人已有Ubuntu22.04的相关开发环境配置,并且默认C++和机器学习基础,这里直接从安装opencv开始,完整代码在最后。具体情况具体分析,请以实际为主。
视频参考:【大厂敲门砖】从0到1做一个物联网人脸识别考勤机项目!(附源码)_哔哩哔哩_bilibili
博客参考:做一个人脸识别考勤机项目(利用OpenCV)_基于opencv的人脸识别考勤-CSDN博客
目录
1.OpenCV配置
1.1安装OpenCV
1.2OpenCV使用的官方在线帮助文档
2.C++和OpenCV初步编程
2.1连接摄像头
2.2使用OpenCV库
2.3打开摄像头
2.4图像采集和优化处理
2.5人脸检测
2.6人脸截取及图片编码与解码
3.对接百度智能云平台
3.1注册账号
3.2创建人脸库
3.3SDK环境搭建
3.4百度云平台的接入
4.优化数据处理与分析
4.1Json数据解析
4.2记录考勤时间
4.3显示考勤信息
5.最终结果汇总
1.OpenCV配置
OpenCV是一个开源的计算机视觉和机器学习软件库其使用一系列C语言函数和少量C++类实现,内部实现了很多图像处理和计算机视觉的通用算法。
OpenCV可以运行在Linux系统上,且其轻量、高效所以在嵌入式领域得到广泛的应用。
1.1安装OpenCV
在终端输入:(注意联网)(以下使用 apt 也可)
sudo apt-get install libopencv-dev
如果出现错误无法安装(已经联网),进行更新软件包索引:
sudo apt-get update
PS:连不了网图标还不见的进行以下操作,删除NetworkManager缓存文件,重启网络服务(Ubuntu22.04的特别处理)
sudo service NetworkManager stop
sudo rm /var/lib/NetworkManager/NetworkManager.state
sudo service NetworkManager start
验证是否安装成功:
dpkg -s libopencv-dev
1.2OpenCV使用的官方在线帮助文档
网址:OpenCV - Open Computer Vision Library
找对应版本的文档:
用什么直接搜吧:
2.C++和OpenCV初步编程
2.1连接摄像头
需要让虚拟机使用本机的摄像头,选择USB3.1的兼容性:
之后可以找到摄像头设备,点击连接,之后点击确定 x2:
也可以直接在右下角连接:
PS:如果还是找不到可用的摄像头,win+R:services.msc,在服务页面找到“VMUSBArbService”服务,又发现无法启动,那么多半是因为之前卸载过VMWare且卸载不干净。解决如下:
控制面板 —> 程序与功能 —> 找到VMWare,右键点击更改 —> 点击修复 —> 重启
2.2使用OpenCV库
使用的总的头文件:
include "opencv2/opencv.hpp"
加上命名空间的声明:
using namespace cv;
因为这个头文件的是在opencv4目录下的,注意编译的时添加链接库路径,告诉编译器在指定的路径中查找头文件:(示例)
g++ main.cpp -o main -I/usr/include/opencv4
2.3打开摄像头
类:VideoCapture
头文件:videoio.hpp
作用:从视频文件、图像序列或摄像头捕获视频
VideoCapture cap(0, CAP_V4L2); //创建对象并打开默认摄像头,CAP_V4L2有自动调整参数
//cap.set(CAP_PROP_FRAME_WIDTH, 1000);//宽度
//cap.set(CAP_PROP_FRAME_HEIGHT, 960);//高度if(!cap.isOpened()){cout << "Camera opened failed." << endl;return -1;
}
cout << "Camera opened successfully." << endl;
PS:VideoCapture cap(0, CAP_V4L2)已经创建并打开了摄像头,因此检测是否打开不能用open()。另外为摄像头选择一个后端,否则会有一个不影响运行的警告。而查看启用了哪些后端可使用命令:
opencv_version -v
2.4图像采集和优化处理
类:Mat
库:core
作用:表示图像或矩阵,用于存储图像数据
函数:cvtColor()
库:imgproc
作用:用于将图像从一种颜色空间转换为另一种颜色空间
函数:imshow()
库:highgui
作用:用于在窗口中显示图像;第一个参数是窗口名称,第二个参数是图像矩阵
函数:equalizeHist()
库:imgproc
作用: 用于对灰度图像进行直方图均衡化,增强清晰度和对比度
函数:waitKey()
库:highgui
作用:用于控制图像显示窗口的关闭或循环退出
Mat ColorImage; //定义一个Mat类型的容器变量frame,用于存储视频帧
Mat GrayImage; //存储灰度图
for(;;){cap >> ColorImage; //从视频流中读取下一帧并将其存储到colorImage中cvtColor(ColorImage,GrayImage,COLOR_BGR2GRAY); //转换灰度图equalizeHist(GrayImage,GrayImage); //均衡化灰度图 (清晰度)imshow("实时视频", GrayImage); //显示帧if(waitKey(7) >= 0) break; //暂停7ms,等待用户按下按键
}
提供阶段编译测试的命令:此时编译速度已经比较慢
g++ main.cpp -o main -I/usr/include/opencv4 -lopencv_highgui -lopencv_core -lopencv_imgproc -lopencv_videoio
阶段效果:
2.5人脸检测
类:cv::CascadeClassifier(级联分类器)
库:objdetect - Object Detection(对象检测)
作用:用于加载预先训练好的级联分类器,并用于在图像中检测特定的对象;级联分类器通过一系列的弱分类器组合而成,能够在图像中快速定位和识别目标对象。
OpenCV已经自带一些训练好的模型,输入以下命令可以查看模型文件:
cd /usr/share/opencv4/haarcascades
ls
选择以下这个人脸识别模型:
pwd获取当前目录,创建对象实例:
CascadeClassifier Classifier("/usr/share/opencv4/haarcascades/haarcascade_frontalface_alt2.xml");
函数:CascadeClassifier::detectMultiScale()
库:objdetect
作用:从输入图像中检测出不同尺寸的对象,并返回一个矩形框框住识别目标
使用detectMultiScale()需要创建vector< Rect >类型的对象:
vector<Rect> AllFace;
在显示循环中加入:
Classifier.detectMultiScale(GrayImage,AllFace); //选中识别对象
函数:rectangle()
库:core
作用:在图像上绘制一个矩形框
注意一个rectangle()只能显示一个人脸的框框,并且为了防止没检测到脸时出错,加一个检测:
if(AllFace.size()){rectangle(GrayImage,AllFace[0],Scalar(255,255,255)); //只能显示一张图片,显示了第一个检测到的人脸
}
此时编译命令:
g++ main.cpp -o main -I/usr/include/opencv4 -lopencv_highgui -lopencv_core -lopencv_imgproc -lopencv_videoio -lopencv_objdetect
结果:
2.6人脸截取及图片编码与解码
函数:imencode(函数编码)
库:imgcodecs
作用:从内存中的图像数据解码为 OpenCV 的 Mat 对象;用于处理从网络传输的图像数据或从文件中读取的图像数据
使用Mat容器就可以完成截取图像,但是发送给百度只能云平台的图片还需要格式处理(转为.jpg、.png等):
Mat MatFace;
vector<uchar> JpgFace;...if(AllFace.size()){ //防止没检测到脸时出错rectangle(GrayImage, AllFace[0], Scalar(255,255,255)); //只能显示一张图像MatFace = GrayImage(AllFace[0]); //截取图像imencode(".jpg", MatFace, JpgFace); //格式转换}
...
此时编译验证:
g++ main.cpp -o main -I/usr/include/opencv4 -lopencv_highgui -lopencv_core -lopencv_imgproc -lopencv_videoio -lopencv_objdetect -lopencv_imgcodecs
3.对接百度智能云平台
3.1注册账号
百度智能云官网:百度智能云-云智一体深入产业
下拉,选择人脸搜索:
点击立即使用,登陆账号(可以使用百度其他产品账号来登陆):
再勾选协议、同意协议之后可以使用:
3.2创建人脸库
找到概览的应用数,点击数字,再点击创建应用:
给应用起名,默认已经选择人脸识别的接口,应用归属选择个人,并填写语言描述:
完成后单击立即创建:
返回应用列表之后就会看到已经创建的应用:
报表里会记录使用这个应用的情况;免费的次数:每秒提交2张识别;以下是使用说明:
可知首先需要将“公司内员工的人脸上传”,点击新建应用的【查看人脸库】,点击新建组,注意选择通用版类型,再上传人脸(尽量找正面清晰的):
完成人脸库创建:
3.3SDK环境搭建
SDK简介:SDK(Software Development Kit,软件开发工具包)是一种用于开发软件的工具集合,一般是软件工程师为特定的软件包、软件框架、硬件平台,操作系统等建立的应用于软件开发和部署的工具集合,通常包括编译器、代码库、API(应用程序编程接口)文档、示例代码等。
搭建原因:百度智能云发回来的识别结果包含各种信息,提前内容的写的代码相关繁琐,而外面可以直接使用百度写好的SDK这个库来简化处理。
在人脸识别的概况中,找到技术文档打开:
在SDK文档中的第一项里选择C++-SDK:
使用最新版本:
点击官方链接,下载C++SDK压缩包,一步步按照指示完成:
官方链接:SDK下载_文字识别SDK_语音识别SDK-百度AI开放平台
好家伙竟然已经变成C了: 点这个C HTTP SDK下载
解压下载的压缩包后,需要搬到Linux里使用(当然也可以直接在Linux的火狐浏览器下载):
依赖库libcurl(HTTP协议相关)、openssl(加密相关)、jsoncpp(提取信息)安装:
sudo apt-get install libcurl4-openssl-dev
dpkg -s libcurl4-openssl-devsudo apt-get install openssl
dpkg -s opensslsudo apt-get install libjsoncpp-dev
dpkg -s libjsoncpp-devsudo apt-get install libssl-dev //为了使用openssl/evp.h
dpkg -s libssl-dev
在写的文件中添加:
#include "face.h"
using namespace aip;
把我们写的和百度SDK的代码放在同一个文件夹里一起编译:
现在要对 base.h 和 http.h 文件中 json 头文件目录位置进行修改(根据我们安装的目录):
#include "jsoncpp/json/json.h" 或 #include <jsoncpp/json/json.h>
进入目录编译测试:
g++ main.cpp -o main -I/usr/include/opencv4 -lopencv_highgui -lopencv_core -lopencv_imgproc -lopencv_videoio -lopencv_objdetect -lopencv_imgcodecs -std=c++11 -lcurl -lcrypto -ljsoncpp
此时出现一个错误:
将 long long int 类型的变量 log_id 赋值给 Json::Value 类型的 data["log_id"] 时,编译器无法确定如何将 long long int 转换为 Json::Value。以下报错详细列出了 Json::Value 各种重载的版本:
解释原因:Json::Value 的构造函数有多种重载版本,编译器无法确定使用哪一个。long long int 是一个 64 位的整数类型,但它并不是 Json::Value 的直接支持类型。Json::Value 提供了 Int64 和 UInt64 类型,但它们与 long long int 不完全匹配,上面那些都可以转换。
在 face.h 中错误的行数修改:对 log_id 赋值这一行进行修改,明确指定类型转换,以消除歧义。
保存,重新编译即可成功。
3.4百度云平台的接入
现在需要创建一个客户端然后使用百度云平台的服务。以下是百度官方的参考代码
// 设置APPID/AK/SK
std::string app_id = "你的 App ID";
std::string api_key = "你的 Api key";
std::string secret_key = "你的 Secret Key";aip::Face client(app_id, api_key, secret_key);
在上面代码中,常量APP_ID在百度云控制台中创建,常量API_KEY与SECRET_KEY是在创建完毕应用后,系统分配给用户的,均为字符串,用于标识用户,为访问做签名验证,可在AI服务控制台中的应用列表中查看。 在之前的代码基础上添加代码或者直接用最后一行就可以了。
回到应用列表,将以下三个对应的key复制到代码中绑定:
本次项目使用的接口是人脸搜索,按需添加对应代码(以下是百度官方提供的参考):
Json::Value result;std::string image = "取决于image_type参数,传入BASE64字符串或URL字符串或FACE_TOKEN字符串";std::string image_type = "BASE64";std::string group_id_list = "3,2";// 调用人脸搜索
result = client.face_search_v3(image, image_type, group_id_list, aip::json_null);// 如果有可选参数
std::map<std::string, std::string> options;
options["match_threshold"] = "70";
options["quality_control"] = "NORMAL";
options["liveness_control"] = "LOW";
options["user_id"] = "233451";
options["max_user_num"] = "3";// 带参数调用人脸搜索
result = client.face_search_v3(image, image_type, group_id_list, options);
参数规定:
image :图片信息(总数据大小应小于10M)
image_type:图片类型 BASE64(不超过2M),图片的 URL 地址,FACE_TOKEN人脸图片的唯一标识。
group_id_list:从指定的group中进行查找用逗号分隔,上限10个
这里选择转换成 BASE64 格式。在下载的SDK中的base64.h文件中,可以查看到已经给出的转换函数:
写入自己的代码:
string Base64Face; //定义转换完成的base64格式图片
Json::Value result; //获取返回结果的容器
...
//编码成base64
Base64Face = base64_encode((char *)JpgFace.data(), JpgFace.size());
//调用人脸搜索
result = client.face_search_v3(Base64Face, "BASE64", "learning", json_null);
cout << result << endl;
编译测试:如果你出现了以下结果,表示“Open API QPS 请求限制已达到”
去查看发现:原来收到限制了,可恶
解决:去个人实名认证,领取500次调用机会,抠啊。。不过也够了
运行效果:返回Json格式的结果
返回数据说明:
{"face_token": "fid", //人脸标志"user_list": [{"group_id" : "test1", //用户所属组别"user_id": "u333333", //用户的id,名"user_info": "Test User", //说明"score": 99.3 //匹配分数}] }
4.优化数据处理与分析
4.1Json数据解析
要获取的百度所给的返回数据目标是人脸所对应的人的信息,所以需要从数据中提取特定信息:
①判断是否检测到人脸
opencv所给模型不太精确,会将疑似为人脸的物体也上传。百度会进行第二次检测是否有人脸。如果返回为空则不打印信息
②判断人脸匹配得分
匹配得分很低说明只是有人脸;百度会返回得分最高的人,即使得分很低(没有这个人脸的资料库),所以结果会不准确。所以要控制得分在一定的高分(70-90)以上才返回信息。
③只返回人脸所匹配的人的名字
增加代码:
//判断是否检测到人脸(opencv所给模型不一定准确,百度会进行第二次检测是否有人脸)
if(!result["result"].isNull())
{//判断人脸匹配得分是否大于80if(result["result"]["user_list"][0]["score"].asInt() >= 80){cout<<result["result"]["user_list"][0]["user_id"]<<endl; //输出人名}
}
运行效果:
4.2记录考勤时间
利用Linux本身提供的时间获取函数
时间容器:time_t
说明:获取 (从1970.1.1 0:0:0到现在的秒数) time(NULL);
时间转换(转换为正常时间)函数:ctime()
增加代码:
time_t sec;
...
sec = time(NULL);
cout << ctime(&sec) << endl;
运行效果:
4.3显示考勤信息
记录考勤信息:运行程序时将信息定向到文本文件中
./main >> log.txt
问题:这样执行就只会将信息存在log.txt中,不再从终端窗口显示
解决:改为从摄像头窗口的人脸识别框上直接显示人名和时间
函数:putText (图像上写字)
库:core
原型:void putText(Mat& img, const string& text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1, int lineType=8, bool bottomLeftOrigin=false )
img:要写字的图像
text:要写的字
org:字在图像上的坐标
fontFace:图像的字体(具体看官方文档)
fontScale:字的大小
color:字的颜色
添加代码:
putText(GrayImage, result["result"]["user_list"][0]["user_id"].asString(), Point(0,50), FONT_HERSHEY_SIMPLEX, 1, Scalar(255,255,255));
putText(GrayImage, ctime(&sec), Point(0,100), FONT_HERSHEY_SIMPLEX, 1, Scalar(255,255,255));
运行后的文件中:
识别效果:
5.最终结果汇总
main.cpp完整代码:
#include <iostream>
#include "opencv2/opencv.hpp"
#include "face.h"using namespace std;
using namespace cv;
using namespace aip;int main(){VideoCapture cap(0, CAP_V4L2); //创建对象并打开默认摄像头cap.set(CAP_PROP_FRAME_WIDTH, 1000); //宽度 //cap.set(CAP_PROP_FRAME_HEIGHT, 960); //高度if(!cap.isOpened()){cout << "Camera opened failed." << endl;return -1;}cout << "Camera opened successfully." << endl;Mat ColorImage; //定义一个Mat类型的容器变量frame,用于存储视频帧Mat GrayImage; //存储灰度图Mat MatFace; //保存截取的人脸图像vector<Rect> AllFace; //获取识别的人脸vector<uchar> JpgFace; //保存改格式后的人脸CascadeClassifier Classifier("/usr/share/opencv4/haarcascades/haarcascade_frontalface_alt2.xml"); //加载人脸识别模型Face client("***", "***", "***"); //客户端和人脸库绑定关系string Base64Face; //定义转换完成的base64格式图片Json::Value result; //获取返回结果的容器time_t sec; //时间容器for(;;){cap >> ColorImage; //从视频流中读取下一帧并将其存储到colorImage中(采集图像)cvtColor(ColorImage, GrayImage, COLOR_BGR2GRAY); //转换灰度图equalizeHist(GrayImage, GrayImage); //均衡化灰度图 (清晰)Classifier.detectMultiScale(GrayImage, AllFace); //选中识别对象//防止没检测到脸时出错if(AllFace.size()){rectangle(GrayImage, AllFace[0], Scalar(255,255,255)); //只能显示一张图像MatFace = GrayImage(AllFace[0]); //截取图像imencode(".jpg", MatFace, JpgFace); //格式转换Base64Face = base64_encode((char *)JpgFace.data(), JpgFace.size()); //编码成base64result = client.face_search_v3(Base64Face, "BASE64", "learning", json_null); //传输到百度,获取返回结果//cout << result << endl;//判断是否检测到人脸(opencv所给模型不一定准确,百度会进行第二次检测是否有人脸)if(!result["result"].isNull()){//判断人脸匹配得分是否大于80if(result["result"]["user_list"][0]["score"].asInt() >= 80){sec = time(NULL); //获取(从1970.1.1 0:0:0到现在的秒数)cout << ctime(&sec) << endl; //输出时间信息cout << result["result"]["user_list"][0]["user_id"] << endl; //输出人名//显示考勤信息putText(GrayImage, result["result"]["user_list"][0]["user_id"].asString(), Point(0,70), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255));putText(GrayImage,ctime(&sec), Point(0,100), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255));}}}imshow("实时视频", GrayImage); //显示帧waitKey(40); //暂停40ms}return 0;
}
编译:
g++ main.cpp -o main -I/usr/include/opencv4 -lopencv_highgui -lopencv_core -lopencv_imgproc -lopencv_videoio -lopencv_objdetect -lopencv_imgcodecs -std=c++11 -lcurl -lcrypto -ljsoncpp
运行:
./main >> log.txt
效果: