代码说明
代码说明分为两个部分,第一部分是Ruyi Studio平台上的代码,第二部分是Visual Studio平台上的代码。
一、Ruyi Studio平台
这一部分的功能的是实现模型的调用,其中MTCNN模型使用参考样例,然后在参考样例的基础上加入了ENET模型。这一部分根据代码中函数调用的先后顺序依次进行分析,重点介绍ENET模型的调用。
1、 main
int main(int argc, char* argv[])
{
/*set stderr &stdout buffer to NULL to flush print info immediately*/
setbuf(stderr, NULL);
setbuf(stdout, NULL);
/*Detection Mtcnn*/
SvpSampleCnnDetMtcnn(); //MTCNN网络入口
return 0;
}
路径:sample_simulator\src\main.cpp
作用:开始执行程序,调用SvpSampleCnnDetMtcnn()函数
说明:无
更改:无
2、SvpSampleCnnDetMtcnn
void SvpSampleCnnDetMtcnn()
{
printf("%s start ...\n", __FUNCTION__);
SvpSampleMtcnn();
printf("%s end ...\n\n", __FUNCTION__);
fflush(stdout);
}
路径:sample_simulator\src\SvpSampleMtcnn.cpp
作用:调用SvpSampleMtcnn()函数
说明:无
更改:无
3、SvpSampleMtcnn
void SvpSampleMtcnn()
{
const HI_U32 pNetModelNum = 1;
std::vector<HI_CHAR*> models;
HI_U32 i = 0;
while (g_mtcnnModelName[i] != nullptr) {
models.push_back(const_cast<HI_CHAR*>(g_mtcnnModelName[i]));
i++;
}
CHECK_EXP_VOID_RET(sizeof(s_scales) / sizeof(HI_DOUBLE) != pNetModelNum + 2, "P-NET model number is not expect value[%d]", pNetModelNum);
std::vector<HI_FLOAT> scales;
i = 0;
while (i < pNetModelNum + 2) {
scales.push_back((HI_FLOAT)s_scales[i]);
i++;
}
(void)SvpSampleCnnDetectionMtcnn(models, pNetModelNum, g_image, scales);
}
路径:sample_simulator\src\SvpSampleMtcnn.cpp
作用:调用SvpSampleCnnDetectionMtcnn()函数,并给出模型路径、输入图像数量、图像路径、图像尺寸四个参数
说明:注意图像尺寸这个参数,该参数是double数组,名称为s_scales[],其中包含图像宽、高、缩放尺寸,其中缩放尺寸对应图像中人脸的大小(请参考源代码中的注释)
Notes:
- MTCNN preprocessing needs to resize the image to different scales, and then get all the bounding boxes through P-NET. Since P-NET is a convolutional network, it can support input images of any scale. But wk compilation requires a fixed input scale, so this sample takes only one valid scale and uses a fixed image size and scale in s_scales.
- MTCNN converts the input image to different scales in order to match the face in the image to the frame size of 1212, or most of the face can fall into the 1212 box. Training related. For example, the size of the face in the picture in this example is about 1/5 of the total size, 450 * 1/5 * 0.14 is approximately equal to 12, so 0.14 is an effective scale.
更改:修改参数图像尺寸s_scales[];添加ENET模型的路径;修改输入图像的路径
原代码:static HI_DOUBLE s_scales[] = { 450, 344, 0.14189182274208526};//人脸占图像高度1/5
修改为:static HI_DOUBLE s_scales[] = { 450, 344, 0.135418};//人脸占图像高度1/3
const HI_CHAR *g_mtcnnModelName[] = {
"../../data/detection/mtcnn/inst/12net_mtcnn_inst.wk",
"../../data/detection/mtcnn/inst/24net_mtcnn_inst.wk",
"../../data/detection/mtcnn/inst/48net_mtcnn_inst.wk",
"../../data/detection/enet/inst/net_enet_inst.wk",//添加ENET模型
nullptr
};
#else /* func wk */
const HI_CHAR *g_mtcnnModelName[] = {
"../../data/detection/mtcnn/inst/12net_mtcnn_func.wk",
"../../data/detection/mtcnn/inst/24net_mtcnn_func.wk",
"../../data/detection/mtcnn/inst/48net_mtcnn_func.wk",
"../../data/detection/enet/inst/net_enet_func.wk",//添加ENET模型
nullptr
};
#endif
const HI_CHAR *g_image = "./../../data/detection/images/ref_mtcnn/two_persons_face.jpg";//输入图像路径
const HI_CHAR* g_mtcnnResultPath = "result_SVP_SAMPLE_MTCNN/";
4、SvpSampleCnnDetectionMtcnn
HI_S32 SvpSampleCnnDetectionMtcnn(const std::vector<HI_CHAR*>& modelNames, HI_U32 u32PNetModelNum,
const HI_CHAR* image, const std::vector<HI_FLOAT> scales)
{
#ifdef USE_OPENCV
HI_S32 ret = HI_SUCCESS;
HI_U32 width = (HI_U32)scales.at(0);
HI_U32 height = (HI_U32)scales.at(1);
// P-Net
vector<SVP_SAMPLE_BOX_S> resultBox;
for (HI_U32 i = 0; i < u32PNetModelNum; i++)
{
vector<SVP_SAMPLE_BOX_S> tempResultBoxs;
ret = SvpSampleMtcnnOneModel(modelNames.at(i), image, scales.at(i + 2), width, height, MTCNN_NET_P, NULL, tempResultBoxs);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn P-Net process fail.");
for (SVP_SAMPLE_BOX_S box : tempResultBoxs) {
if (box.u32Mask == 0) {
resultBox.push_back(box);
}
}
}
CHECK_EXP_RET(resultBox.size() == 0, HI_SUCCESS, "P-Net 1 get bounding box 0.");
vector<SVP_SAMPLE_BOX_S> pNetResultBoxs;
ret = MtcnnNms(resultBox, 0.7f, NMS_IOU);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn nms fail.");
for (SVP_SAMPLE_BOX_S box : resultBox) {
if (box.u32Mask == 0) {
pNetResultBoxs.push_back(box);
}
}
CHECK_EXP_RET(pNetResultBoxs.size() == 0, HI_SUCCESS, "P-Net 2 get bounding box 0.");
// draw
mtcnn_draw_result(image, pNetResultBoxs, NULL, (string(g_mtcnnResultPath) + outPImage).c_str());
// R-Net
resultBox.clear();
for (SVP_SAMPLE_BOX_S box : pNetResultBoxs) {
vector<SVP_SAMPLE_BOX_S> tempResultBoxs;
ret = SvpSampleMtcnnOneModel(modelNames.at(u32PNetModelNum), image, 1, width, height, MTCNN_NET_R, &box, tempResultBoxs);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn R-Net process fail.");
if (tempResultBoxs.size() == 1)
{
resultBox.push_back(tempResultBoxs.at(0));
}
}
CHECK_EXP_RET(resultBox.size() == 0, HI_SUCCESS, "R-Net 1 get bounding box 0.");
vector<SVP_SAMPLE_BOX_S> rNetResultBoxs;
ret = MtcnnNms(resultBox, 0.6f, NMS_IOU);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn nms fail.");
for (SVP_SAMPLE_BOX_S box : resultBox) {
if (box.u32Mask == 0) {
rNetResultBoxs.push_back(box);
}
}
CHECK_EXP_RET(rNetResultBoxs.size() == 0, HI_SUCCESS, "R-Net 2 get bounding box 0.");
// draw
mtcnn_draw_result(image, rNetResultBoxs, NULL, (string(g_mtcnnResultPath) + outRImage).c_str());
// O-Net
resultBox.clear();
vector<MTCNN_KEYPOINT> resultKeypoints;
for (SVP_SAMPLE_BOX_S box : rNetResultBoxs) {
vector<SVP_SAMPLE_BOX_S> tempResultBoxs;
vector<MTCNN_KEYPOINT> tempResultKeypoints;
ret = SvpSampleMtcnnOneModel(modelNames.at(u32PNetModelNum + 1), image, 1, width, height, MTCNN_NET_O, &box, tempResultBoxs, &tempResultKeypoints);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn O-Net process fail.");
if (tempResultBoxs.size() == 1)
{
resultBox.push_back(tempResultBoxs.at(0));
resultKeypoints.push_back(tempResultKeypoints.at(0));
}
}
CHECK_EXP_RET(resultBox.size() == 0, HI_SUCCESS, "O-Net 1 get bounding box 0.");
vector<SVP_SAMPLE_BOX_S> oNetResultBoxs;
vector<MTCNN_KEYPOINT> oNetResultKeypoints;
ret = MtcnnNms(resultBox, 0.7f, NMS_IOM);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn nms fail.");
for (HI_U32 i = 0; i < resultBox.size(); i++) {
if (resultBox.at(i).u32Mask == 0) {
oNetResultBoxs.push_back(resultBox.at(i));
oNetResultKeypoints.push_back(resultKeypoints.at(i));
}
}
CHECK_EXP_RET(oNetResultBoxs.size() == 0, HI_SUCCESS, "O-Net 2 get bounding box 0.");
// draw
mtcnn_draw_result(image, oNetResultBoxs, &oNetResultKeypoints, (string(g_mtcnnResultPath) + outOImage).c_str());
//eye-net
//从o_net获得得到的眼睛位置中得到眼睛周围19.25×19.25的图片
vector<SVP_SAMPLE_BOX_S> Eye_Boxs;
SVP_SAMPLE_BOX_S Temporary_Boxs1,Temporary_Boxs2; //temporary box
for (HI_U32 i = 0; i < resultBox.size(); i++) {
if (oNetResultKeypoints.size() != 0) {
//center point
Temporary_Boxs1.f32Xmax = SVP_MIN(width, oNetResultKeypoints.at(i).px1+9.625);
Temporary_Boxs1.f32Ymax = SVP_MIN(height, oNetResultKeypoints.at(i).py1+9.625);
Temporary_Boxs1.f32Xmin = SVP_MAX(0.0f,oNetResultKeypoints.at(i).px1-9.625);;
Temporary_Boxs1.f32Ymin = SVP_MAX(0.0f,oNetResultKeypoints.at(i).py1-9.625);
Temporary_Boxs1.f32ClsScore = 1;
Temporary_Boxs1.u32Mask = 0;
Temporary_Boxs2.f32Xmax = SVP_MIN(width, oNetResultKeypoints.at(i).px2+9.625);
Temporary_Boxs2.f32Ymax = SVP_MIN(height, oNetResultKeypoints.at(i).py2+9.625);
Temporary_Boxs2.f32Xmin = SVP_MAX(0.0f, oNetResultKeypoints.at(i).px2-9.625);;
Temporary_Boxs2.f32Ymin = SVP_MAX(0.0f, oNetResultKeypoints.at(i).py2-9.625);
Temporary_Boxs2.f32ClsScore = 1;
Temporary_Boxs2.u32Mask = 0;
//Eye_Boxs
if ((Temporary_Boxs1.f32Xmax > Temporary_Boxs1.f32Xmin) && (Temporary_Boxs1.f32Ymax > Temporary_Boxs1.f32Ymin)) {
Eye_Boxs.push_back(Temporary_Boxs1);
}
if ((Temporary_Boxs2.f32Xmax > Temporary_Boxs2.f32Xmin) && (Temporary_Boxs2.f32Ymax > Temporary_Boxs2.f32Ymin)) {
Eye_Boxs.push_back(Temporary_Boxs2);
}
}
}
//做出眼睛图
vector<SVP_SAMPLE_BOX_S> EyeResultBoxs;
ret = MtcnnNms(Eye_Boxs, 0.6f, NMS_IOU);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn nms fail.");
for (SVP_SAMPLE_BOX_S box : Eye_Boxs) {
if (box.u32Mask == 0) {
EyeResultBoxs.push_back(box);
}
}
CHECK_EXP_RET(EyeResultBoxs.size() == 0, HI_SUCCESS, "R-Net 2 get bounding box 0.");
// draw
mtcnn_draw_result(image, EyeResultBoxs, NULL, (string(g_mtcnnResultPath) + outEImage).c_str());
//通过Eye_box调用enet.wk文件
for (SVP_SAMPLE_BOX_S box : Eye_Boxs) {
ret = EyeOneModel(modelNames.at(u32PNetModelNum + 2), image, 1, width, height, &box);
CHECK_EXP_RET(ret != HI_SUCCESS, ret, "mtcnn O-Net process fail.");
}
return ret;
#else
fprintf(stderr, "%s Need opencv enable!\n", __FUNCTION__);
return HI_FAILURE;
#endif
}
路径:sample_simulator\detection\mtcnn\src\mtcnn_interface.cpp
作用:P-Net、R-Net和O-Net的结构相同,只是输入输出的参数不同,因此先介绍P-Net模块。首先调用SvpSampleMtcnnOneModel()函数,返回得到tempResultBoxs(P-Net识别得到的预测框),然后再通过MtcnnNms()函数实现NMS算法(非极大值抑制),得到筛选后的预测框resultBox,最后通过mtcnn_draw_result()函数在原始输入图像上绘制预测框,输出得到P-Net图像。R-Net相对于P-Net来说,还需要将P-Net输出的预测框resultBox作为参数放入SvpSampleMtcnnOneModel()函数中。O-Net相对于R-Net来说,还需要添加一个resultKeypoints参数来得到面部关键点的坐标。
说明:该函数下实现了MTCNN模型的调用,该函数中包含P-Net、R-Net和O-Net的输入和输出,而ENET模型的输入需要用到O-Net的输出,因此在该函数中加入了ENET模型的调用。对于ENET首先根据O-Net输出oNetResultKeypoints(=resultKeypoints)得到双眼位置坐标(px1,py1)和(px2,py2),然后根据双眼坐标设置双眼所在区域Eye_Boxs,然后调用mtcnn_draw_result()函数绘制出显示人眼框的图像,然后调用自定义的EyeOneModel()函数,EyeOneModel()参考SvpSampleMtcnnOneModel()函数实现。
更改:添加eye-net模块(参考以上代码)
注:Eye_Boxs决定了ENET模型输入图像包含的区域,Eye_Boxs的大小会影响ENET识别的准确率。在s_scales[]中的缩放因子是0.135418,人脸长度占图像高度的1/3,此时Eye_Boxs应设置为19.25×19.25。这里提个小建议:如果能动态选择Eye_Boxs的大小,系统性能就会有较大的提高。
5、 EyeOneModel
HI_S32 EyeOneModel(const HI_CHAR *pcModelName, const HI_CHAR *pcImageName,
const HI_FLOAT scale, const HI_U32 width, const HI_U32 height,
SVP_SAMPLE_BOX_S* targetBox)
{
/**************************************************************************/
/* 1. check input para */
CHECK_EXP_RET(NULL == pcModelName, HI_ERR_SVP_NNIE_NULL_PTR, "Error(%#x): %s input pcModelName nullptr error!", HI_ERR_SVP_NNIE_NULL_PTR, __FUNCTION__);
CHECK_EXP_RET(NULL == pcImageName, HI_ERR_SVP_NNIE_NULL_PTR, "Error(%#x): %s input pcImageName nullptr error!", HI_ERR_SVP_NNIE_NULL_PTR, __FUNCTION__);
/**************************************************************************/
/* 2. declare definitions */
HI_S32 s32Ret = HI_SUCCESS;
SVP_NNIE_ONE_SEG_DET_S stDetParam = { 0 };
SVP_NNIE_CFG_S stDetCfg = { 0 };
/**************************************************************************/
/* 3. init resources */
/* mkdir to save result, name folder by model type */
string strResultFolderDir = g_mtcnnResultPath;
s32Ret = SvpSampleMkdir(strResultFolderDir.c_str());
CHECK_EXP_RET(HI_SUCCESS != s32Ret, s32Ret, "SvpSampleMkdir(%s) failed", strResultFolderDir.c_str());
stDetCfg.pszModelName = pcModelName;
stDetCfg.u32MaxInputNum = 1;
stDetParam.u32TotalImgNum = 1;
HI_BOOL bLoadImgList = HI_FALSE; // mtcnn will do SvpSampleReadBoxSrcImg after SvpSampleOneSegDetCnnInit
s32Ret = SvpSampleOneSegDetCnnInit(&stDetCfg, &stDetParam, bLoadImgList);
CHECK_EXP_RET(HI_SUCCESS != s32Ret, s32Ret, "EyeOneModelInit failed");
CHECK_EXP_RET(1 != stDetParam.stModel.astSeg[0].u16SrcNum, HI_FAILURE, "WK src number not 1.");
/**************************************************************************/
/* 5. read mtcnn src images for next stage */
s32Ret = SvpSampleReadBoxSrcImg(stDetParam.astSrc, 0, (HI_CHAR*)pcImageName, targetBox, HI_TRUE);
CHECK_EXP_GOTO(HI_SUCCESS != s32Ret, Fail, "Error(%#x):EyeOneModelImg failed!", s32Ret);
/**************************************************************************/
/* 6. run forward */
s32Ret = SvpSampleCnnDetectionForword(&stDetParam, &stDetCfg);
CHECK_EXP_GOTO(HI_SUCCESS != s32Ret, Fail, "EyeOneModelForword failed");
/**************************************************************************/
/* 7. run get detection result */
s32Ret = EyeOneModelResult(&(stDetParam.astDst[0]), scale, width, height, targetBox);
CHECK_EXP_GOTO(HI_SUCCESS != s32Ret, Fail, "EyeOneModelResult failed");
/**************************************************************************/
/* 8. deinit resources */
Fail:
//
return s32Ret;
}
路径:sample_simulator\detection\mtcnn\src\mtcnn_software.cpp
作用:调用函数SvpSampleOneSegDetCnnInit()获取模型以及模型参数(输入图像数量、输入图像尺寸),调用函数SvpSampleReadBoxSrcImg()获取输入图像,调用函数SvpSampleCnnDetectionForword()将图像导入模型,最后执行函数EyeOneModelResult(),用于获取识别结果,保存进入txt文件中。
说明:该函数参考SvpSampleMtcnnOneModel()函数实现,只修改了步骤7. run get detection result,采用自定义了函数EyeOneModelResult(),用于对ENET模型识别得到的结果进行分析。
更改:添加EyeOneModel()函数到mtcnn_software.cpp文件中。
6、 EyeOneModelResult
static HI_S32 EyeOneModelResult(const SVP_DST_BLOB_S *pstDstBlob,
const HI_FLOAT scale,
const HI_U32 width,
const HI_U32 height,
SVP_SAMPLE_BOX_S* targetBox)
{
HI_FLOAT denominator = 4096.0;
/* 2. declare definitions */
HI_FLOAT eye_close = *(HI_S32*)(pstDstBlob->u64VirAddr+2)/denominator;//首地址+2对应闭眼状态
HI_FLOAT eye_open = *(HI_S32*)(pstDstBlob->u64VirAddr+1)/denominator;//首地址+1对应睁眼状态
//读取之前数据
ifstream in("d:\\eye_out.txt");
char last;
if(in.is_open())
{
while(!in.eof())
{
in.get(last);
in.close();
}
}
//保存输出
ofstream out;
out.open("d:\\eye_out.txt");
if(out.is_open())
{
if(last == 'a')
{
out<<'b';//标记
data=eye_open-eye_close;
// ofstream out_a;//验证数据
// out_a.open("d:\\eye_out_a.txt");
// if(out_a.is_open())
// {
// out_a<<data<<endl;
// }
// out_a.close();
}
else
{
// ofstream out_a;//验证数据
// out_a.open("d:\\eye_out_b.txt");
// if(out_a.is_open())
// {
// out_a<<eye_open-eye_close<<endl;
// }
// out_a.close();
data=data + eye_open-eye_close;
if(data > 0)
{
out<<'1'; //正常
}
if(data < 0)
{
out<<'0'; //疲劳驾驶
}
if(data == 0)
{
out<<'2'; //警告
}
}
}
out.close();
return HI_SUCCESS;
}
路径:sample_simulator\detection\mtcnn\src\mtcnn_software.cpp
作用:首先从内存中获取ENET识别的输出
HI_FLOAT eye_close = *(HI_S32*)(pstDstBlob->u64VirAddr+2)/denominator;//首地址+2对应闭眼状态
HI_FLOAT eye_open = *(HI_S32*)(pstDstBlob->u64VirAddr+1)/denominator;//首地址+1对应睁眼状态
然后读取txt文件确认当前识别人眼的数目,如果是’a’说明目前只识别了一只眼睛,此时令data=eye_open-eye_close;
,如果是’b’说明目前正在识别第二只眼睛,此时令data=data + eye_open-eye_close;
,然后判断data与0的大小识别的结果存入eye_out.txt文件中。
注:通过
data=data + eye_open-eye_close
的方式对双眼状态进行识别只是一个简单的方式,这里提个小建议:如果能设计一个新的算法,应该能够提高识别的准确率。
说明:其中data为全局变量double data=0;//输出结果
,在文件mtcnn_software.cpp中声明,用于存储ENET识别得到的[a,b]的两个数的差值。
更改:添加EyeOneModelResult()函数到mtcnn_software.cpp文件中;加入全局变量data。
#include "SvpSampleCom.h"
using namespace std;
extern const HI_CHAR* g_mtcnnResultPath;
double data=0;//输出结果
7、 遇到的问题及解决方法
①ENET输入图像需要进行放大
在5 EyeOneModel中提到,通过函数SvpSampleReadBoxSrcImg()获取输入图像,在函数SvpSampleReadBoxSrcImg()中,通过调用函数SVPUtils_ReadBoxImage()进行图像的提取,其中函数SVPUtils_ReadBoxImage()使用了cv::resize()
将提取得到图像变换到模型要求的输入图像的大小,因此可以直接使用参考样例提供的函数对ENET模型的输入图像进行尺寸变换。
函数SVPUtils_ReadBoxImage()的路径是sample_simulator\utils\cv\src\cv_read_image.cpp,为了提高图像缩放的效果,修改了函数SVPUtils_ReadBoxImage()中resize()的参数,使得resize的方式是三次样条插值。
原代码:cv::resize(dstMat, dstMat, cv::Size(u32DstWidth, u32DstHeight);
修改为:cv::resize(dstMat, dstMat, cv::Size(u32DstWidth, u32DstHeight),cv::INTER_CUBIC);
②没有找到合适的调试方式
在使用Ruyi Studio时没有找到合适的观察中间变量的方法,推荐将想要观察的数据存入txt文件中。
③Ruyi Studio不能使用opencv的部分函数
在使用opencv函数进行拍照时,发现Ruyi Studio无法调用相关的函数(如cvCapture的成员函数),即使对Ruyi Studio依赖的opencv库进行替换后,依旧不能使用opencv库中的部分函数,个人认为Ruyi Studio是针对摄像头开发的软件平台,可能并不支持在电脑上进行拍照,因此拍照的程序用Visual Studio实现。
④pytorch转caffe
pythorch转caffe后,对应的.prototxt文件格式存在一个问题,即input_shape {dim: 1 dim: 3 dim: 48 dim: 48}没有大括号,需要添加大括号才能确保.prototxt文件有效。
⑤cfg文件的配置
添加ENET模型时需要设置cfg文件,可以参考mtcnn中的cfg文件进行设置,主要修改.prototxt和.caffemodel的路径即可,同时要给出输入图像的路径,输入图像的数量,输出文件的路径。
⑥总结
在Ruyi Studio平台主要实现模型的调用和双眼识别结果的分析,由于无法在Ruyi Studio平台实现拍照的功能,因此直接生成sample_simulator.exe(注意是release版本),在Visual Studio平台调用即可。使用时需要给出输入图像的路径,识别结束后在指定路径下的eye_out.txt文件中获取识别结果。
二、Visual Studio平台
Visual Studio平台实现拍照、调用sample_simulator.exe和显示输出结果的功能。其中包含3个模式:
模式1:拍摄1张照片,并将识别得到的结果显示在DOS界面上,用于系统的调试(只需要检测一张图片,花费时间较短);
模式2:每隔一秒拍摄一张照片,总共拍摄10张,拍摄结束后调用模型进行识别,根据识别得到的结果判断驾驶员是否疲劳驾驶;
模式3:连续对多张照片进行识别,并将每张照片的检测结果保存进入eye_out_test.txt文件中,其中“1”对应睁眼状态,“0”对应闭眼状态,“a”对应无法检测。
本篇文章根据main函数中函数出现的顺序依次进行介绍。
1、main
开始运行程序,根据用于选择执行对应的模式。
2、take_photo
模式1下的拍照程序,拍摄一张照片,并保存到指定路径下(即模型输入图像对应的路径)。
3、read_date
模式1下的读取程序,读取sample_simulator.exe对应的txt文件,获得图像识别结果。
4、take_photo2
模式2下的拍照程序,拍摄十张照片,并按照顺序保存到指定路径下(该路径自定义,用于存储十张图片)。
5、image_test2
模式2下的读取程序,首先获得take_photo2拍摄得到的图片1.jpg,将图片复制到指定路径下(即模型输入图像对应的路径),然后通过system("eye_test.bat");
调用模型进行识别,识别结束后读取txt文件中的结果,然后循环此过程十次,统计得到的结果,根据10次识别中睁眼状态的次数判断驾驶员是否疲劳驾驶。
6、photo_process
由于提供的人脸图像数据集不是450×344的尺寸,因此需要进行尺寸变换,photo_process()函数可以对人脸图像数据集中所有图片进行尺寸变换,该函数可以选择使用。
7、image_test
模式3下的读取程序,首先获得人脸图像数据集中的图片1.jpg,将图片复制到指定路径下(即模型输入图像对应的路径),然后通过system("eye_test.bat");
调用模型进行识别,识别结束后读取eye_out.txt文件中的结果,并将结果保存进入eye_out_test.txt文件中,然后循环此过程N次(N为数据集中图像总数),最终得到该数据集中所有图像的识别结果,其中“1”对应睁眼状态,“0”对应闭眼状态,“a”对应无法检测。
注:使用image_test()函数时需要给出人脸图像数据集中图片的总数,当数据集中图片数量变化时需要对image_test()函数进行修改。
8、common
工具函数,用于更改take_photo2()保存图片的路径,在进行对照实验时需要拍摄很多组不同条件的图片,因此需要分文件夹保存对应图片,需要频繁修改拍摄图像的路径,因此设置了common函数,在修改图像路径时只需要在common()中修改即可。
9、eye_test.bat
windows系统中的脚本文件,用于执行sample_simulator.exe。
cmd /k "cd /d E:\soft\RuyiStudio-2.0.41\workspace2\sample_simulator\Release && sample_simulator.exe &&exit"
10、总结
因为实现拍照、复制图像、图像变换时用到opencv库,因此需要在Visual Studio中添加opencv的依赖;模式1中需要输入字符’a’进行拍照,此时需要先选中拍摄的窗口(不是DOS窗口),才能输入指令;在使用模式3时,系统偶尔会弹出超出内存的提示,这是需要等待解决方案,然后选择确认,再选择cancle即可继续识别其他图像(这里不清楚为什么)。