1. 前言
在AI人工智能發(fā)展應(yīng)用的過(guò)程中,人臉識(shí)別技術(shù)得到了重用。目前在人臉識(shí)別相關(guān)的技術(shù)已經(jīng)深入到生活方方面面。在公共安全、智能安防、手機(jī)認(rèn)證等多個(gè)領(lǐng)域得到應(yīng)用,比如: 通過(guò)人臉識(shí)別來(lái)解鎖手機(jī),通過(guò)人臉識(shí)別進(jìn)入某些政務(wù)軟件平臺(tái)或是銀行APP, 還是各大門(mén)店,超市使用的"支付寶刷臉支付"等等,這些應(yīng)用大大方便了人們生活。
目前,因?yàn)樾鹿谝咔榉揽氐男枰巳顺鲩T(mén)都需要戴口罩,查驗(yàn)健康碼和測(cè)量體溫,在辦公大樓門(mén)口、地鐵口、小區(qū)門(mén)口、商場(chǎng)門(mén)口人流量都比較大,傳統(tǒng)的人工測(cè)量體溫方式,費(fèi)時(shí)費(fèi)力、效率比較低,而且人工近距離核驗(yàn)溫度,易產(chǎn)生交叉感染風(fēng)險(xiǎn)。
當(dāng)前文章就利用 華為云提供的AI人臉識(shí)別接口+紅外測(cè)溫傳感器MLX90614ESF(DCI)+瑞芯微RK3399完成AI人臉測(cè)溫一體機(jī)設(shè)計(jì) ,方便測(cè)量來(lái)往人員的體溫,減輕防疫防控工作人員的負(fù)擔(dān)。
實(shí)現(xiàn)的具體功能: 當(dāng)檢測(cè)到人臉時(shí),就測(cè)量溫度,并檢測(cè)有沒(méi)有帶口罩;在顯示屏上實(shí)時(shí)顯示溫度信息,如果溫度超出設(shè)置值,會(huì)通過(guò)語(yǔ)音播報(bào)提示。如果人員沒(méi)有戴口罩,會(huì)語(yǔ)音提示帶口罩。
2. 軟硬件設(shè)計(jì)
(1)核心板采用瑞芯微RK3399,運(yùn)行ubuntu18.04 64位系統(tǒng),編譯器采用aarch64-linux-gcc。攝像頭采用羅技的720p攝像頭。
(2)軟件界面采用QT設(shè)計(jì),在RK3399的ubuntu18.04系統(tǒng)里可以直接安裝QT開(kāi)發(fā)環(huán)境完成開(kāi)發(fā),也可以在PC機(jī)上交叉編譯后將程序和相關(guān)庫(kù)拷貝過(guò)來(lái)。
(3)測(cè)溫傳感器采用MLX90614ESF(DCI),這個(gè)是紅外非接觸式測(cè)溫傳感器,測(cè)量距離可達(dá)到1米左右,這個(gè)模塊是IIC協(xié)議接口。
3. 華為云人臉識(shí)別服務(wù)
3.1 開(kāi)通人臉識(shí)別服務(wù)
官網(wǎng)地址: https://www.huaweicloud.com/product/face.html
3.2 人臉識(shí)別接口功能
華為云提供的人臉檢測(cè)可以對(duì)輸入圖片進(jìn)行人臉檢測(cè)和分析,輸出人臉在圖像中的位置、人臉關(guān)鍵點(diǎn)位置、人臉關(guān)鍵屬性等信息。支持識(shí)別JPG、PNG、JPEG、BMP等格式的圖片,上傳圖片時(shí)選擇將圖片轉(zhuǎn)為Base64編碼上傳。
(1)接口請(qǐng)求方式: POST
(2)接口請(qǐng)求地址
格式:
https://face.{endpoint}.myhuaweicloud.com/v2/{project_id}/face-detect
?
示例:
https://face.cn-north-4.myhuaweicloud.com/v2/0e5957be8a00f53c2fa7c0045e4d8fbf/face-detect
(3)請(qǐng)求頭的參數(shù)
{
"X-Auth-Token": "******"
}
X-Auth-Token字段是訪問(wèn)華為云的任何API接口都需要填,獲取方法看這里: https://bbs.huaweicloud.com/blogs/317759 翻到2.3小節(jié)。
(4)請(qǐng)求Body參數(shù)
image_base64字段 : 存放Base64編碼后的圖片數(shù)據(jù),大小不超過(guò)8MB,建議小于1MB。
?
attributes字段 :這個(gè)參數(shù)可以選擇不填,不填就只是返回人臉的在圖片里的尺寸位置。如果希望獲取更多的屬性列表,可以填下面之這些屬性:
2:年齡
4:裝束(帽子、眼鏡)
6:口罩
7:發(fā)型
8:胡須
11:圖片類型
12:質(zhì)量
13:表情
21:人臉圖片旋轉(zhuǎn)角(順時(shí)針偏轉(zhuǎn)角度),支持0°、90°、180°和270°圖片旋轉(zhuǎn)。
?
多個(gè)屬性間使用逗號(hào)(,)隔開(kāi)。
(5)響應(yīng)參數(shù)
如果圖像里沒(méi)有人臉,返回的數(shù)據(jù)是這樣的:
{"faces":[]}
?
如果沒(méi)有填額外的屬性,返回的數(shù)據(jù)是這樣的:
{
"faces": [
{
"bounding_box": {
"top_left_x": 61,
"top_left_y": 54,
"width": 114,
"height": 151
}
}
]
}
?
填了額外的屬性,返回的數(shù)據(jù)是這樣的:
{
"faces": [
{
"bounding_box": {
"top_left_x": 61,
"top_left_y": 54,
"width": 114,
"height": 151
},
"attributes": {
"age": 30,
"dress": {
"glass": "none",
"hat": "none"
},
"mask": "none",
"hair": "short",
"beard": "none",
"phototype": "internet photo",
"quality": {
"total_score": 0.62109375,
"blur": 0.3359375,
"pose": 0.266357421875,
"occlusion": 0.330810546875,
"illumination": 0.378662109375
},
"expression": {
"type": "neutral",
"probability": 0.9991200566291809
}
}
}
]
}
3.3 調(diào)試接口
地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=FRS&api=DetectFaceByFile
如果最開(kāi)始想體驗(yàn)一下接口,了解參數(shù)的含義,可以先使用在線調(diào)試接口測(cè)試一下效果。
現(xiàn)在人臉檢測(cè)的調(diào)試接口可以直接在網(wǎng)頁(yè)上選擇本地圖片,不用再傳bash64數(shù)據(jù),測(cè)試更加方便。
4. 設(shè)計(jì)設(shè)備端程序
4.1 調(diào)用人臉檢測(cè)接口
//人臉檢測(cè)
void Widget::FaceCheck(QImage image)
{
QString requestUrl;
QNetworkRequest request;
?
//存放圖片BASE64編碼
QString imgData;
?
//設(shè)置請(qǐng)求地址
QUrl url;
?
//人臉檢測(cè)請(qǐng)求地址
requestUrl = QString("https://face.%1.myhuaweicloud.com/v2/%2/face-detect")
.arg(SERVER_ID)
.arg(PROJECT_ID);
?
//設(shè)置數(shù)據(jù)提交格式
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
?
//將圖片進(jìn)行Base64編碼
imgData = QString(toBase64(image)); //編碼后的圖片大小不超過(guò)2M
//設(shè)置token
request.setRawHeader("X-Auth-Token",Token);
?
//構(gòu)造請(qǐng)求
url.setUrl(requestUrl);
request.setUrl(url);
?
QString post_param=QString
("{"
""image_base64": "%1","
""attributes":%2"
"}").arg(imgData).arg("6");
?
//發(fā)送請(qǐng)求
manager- >post(request, post_param.toUtf8());
}
4.2 接口數(shù)據(jù)解析
if(function_select==6)
{
//解析數(shù)據(jù)
QJsonParseError json_error;
QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
if(json_error.error == QJsonParseError::NoError)
{
//判斷是否是對(duì)象,然后開(kāi)始解析數(shù)據(jù)
if(document.isObject())
{
QJsonObject obj = document.object();
//解析錯(cuò)誤代碼
if(obj.contains("faces"))
{
QJsonArray face_arr=obj.take("faces").toArray();
?
for(int i=0;i< face_arr.size();i++)
{
QJsonObject object=face_arr.at(i).toObject();
if(object.contains("bounding_box"))
{
QJsonObject obj1=object.take("bounding_box").toObject();
?
int top_left_x=0;
int top_left_y=0;
int width=0;
int height=0;
?
if(obj1.contains("top_left_x"))
{
top_left_x=obj1.take("top_left_x").toInt();
}
if(obj1.contains("top_left_y"))
{
top_left_y=obj1.take("top_left_y").toInt();
}
if(obj1.contains("width"))
{
width=obj1.take("width").toInt();
}
if(obj1.contains("height"))
{
height=obj1.take("height").toInt();
}
?
qDebug()< < "top_left_x:"<
4.3 token獲取
void Widget::GetToken()
{
//表示獲取token
function_select=3;
?
QString requestUrl;
QNetworkRequest request;
?
//設(shè)置請(qǐng)求地址
QUrl url;
?
//獲取token請(qǐng)求地址
requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")
.arg(SERVER_ID);
?
//自己創(chuàng)建的TCP服務(wù)器,測(cè)試用
//requestUrl="http://10.0.0.6:8080";
?
//設(shè)置數(shù)據(jù)提交格式
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
?
//構(gòu)造請(qǐng)求
url.setUrl(requestUrl);
?
request.setUrl(url);
?
QString text =QString("{"auth":{"identity":{"methods":["password"],"password":"
"{"user":{"domain": {"
""name":"%1"},"name": "%2","password": "%3"}}},"
""scope":{"project":{"name":"%4"}}}}")
.arg(MAIN_USER)
.arg(IAM_USER)
.arg(IAM_PASSWORD)
.arg(SERVER_ID);
?
//發(fā)送請(qǐng)求
manager- >post(request, text.toUtf8());
}
4.4 攝像頭初始化
//查找系統(tǒng)可用攝像頭
void Widget::Find_CameraNumber()
{
//清空列表
ui- >comboBox_camera_number- >clear();
?
/*查找電腦當(dāng)前可用攝像頭*/
cameras = QCameraInfo::availableCameras();
if(cameras.count())
{
for(int i=0;i< cameras.count();i++)
{
ui- >comboBox_camera_number- >addItem(tr("%1").arg(i));
}
ui- >pushButton_start_camera- >setEnabled(true);
}
else
{
QMessageBox::warning(this,tr("提示"),"本機(jī)沒(méi)有可用的攝像頭!
"
"軟件作者:DS小龍哥
"
"BUG反饋:1126626497@qq.com");
ui- >pushButton_start_camera- >setEnabled(false);
}
?
/*攝像頭沒(méi)有啟動(dòng)時(shí),按鈕不可用*/
ui- >pushButton_find- >setEnabled(false);
ui- >pushButton_delete- >setEnabled(false);
ui- >pushButton_update- >setEnabled(false);
ui- >pushButton_register- >setEnabled(false);
}
?
?
//啟動(dòng)攝像頭
void Widget::on_pushButton_start_camera_clicked()
{
//攝像頭啟動(dòng)標(biāo)志
if(camera_flag) //如果攝像頭已經(jīng)啟動(dòng)一次,再次啟動(dòng)需要將之前的空間釋放掉
{
camera- >stop();
delete camera;
ui- >horizontalLayout_2- >removeWidget(videoWidget);
delete videoWidget;
}
?
camera_flag=1; //標(biāo)志攝像頭已經(jīng)啟動(dòng)一次
?
//攝像頭啟動(dòng)之后,就無(wú)法在重復(fù)啟動(dòng)
ui- >pushButton_start_camera- >setEnabled(false);
?
/*創(chuàng)建攝像頭對(duì)象,根據(jù)選擇的攝像頭打開(kāi)*/
camera = new QCamera(cameras.at(ui- >comboBox_camera_number- >currentIndex()));
?
/*構(gòu)造捕獲的對(duì)象*/
camera_image_capture = new QCameraImageCapture(camera);
?
/*設(shè)置捕獲的目的地*/
camera_image_capture- >setCaptureDestination(QCameraImageCapture::CaptureToFile);
?
//設(shè)置截圖輸出、緩沖區(qū)格式、分辨
camera_image_capture- >setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
camera_image_capture- >setBufferFormat(QVideoFrame::PixelFormat::Format_Jpeg);
?
//設(shè)置截圖的圖片尺寸
iamge_setting.setResolution(320,240);
camera_image_capture- >setEncodingSettings(iamge_setting);
?
//關(guān)聯(lián)捕獲的信號(hào),發(fā)出捕獲截圖信號(hào)時(shí),發(fā)出信號(hào)
connect(camera_image_capture,&QCameraImageCapture::imageCaptured,this,&Widget::processCapturedImage);
?
/*配置攝像頭捕獲模式為幀捕獲模式*/
camera- >setCaptureMode(QCamera::CaptureViewfinder);
?
videoWidget = new QVideoWidget();
videoWidget- >setMinimumSize(320,240);
?
//將攝像頭顯示窗口加入到布局中
ui- >horizontalLayout_2- >insertWidget(0,videoWidget);
?
/*設(shè)置取景器顯示*/
camera- >setViewfinder(videoWidget);
?
/*啟動(dòng)攝像頭*/
camera- >start();
?
/*攝像頭啟動(dòng)時(shí),按鈕可用*/
ui- >pushButton_find- >setEnabled(true);
ui- >pushButton_delete- >setEnabled(true);
ui- >pushButton_update- >setEnabled(true);
ui- >pushButton_register- >setEnabled(true);
}