├── README.md ├── libfacedet.cpp ├── libfacedet.h ├── main.cpp ├── utils.cpp └── utils.h /README.md: -------------------------------------------------------------------------------- 1 | # 一种适合工业级应用的基于深度学习的实时人脸检测与识别算法的C++实现 2 | # 本套程序仅仅只依赖opencv库,就可以正常运行 3 | 模型文件在百度网盘里,下载链接是 4 | https://pan.baidu.com/s/1Cvw9925YhwsJosNB9fd5MQ 提取码: ev9g 5 | 6 | 下载完成后,把models文件夹与代码放在同一目录下,配置好opencv环境后就可以运行程序了。 7 | 我这边用的是opencv4.4.0,建议安装这个最新版的opencv库,不然就可能在程序运行时出现异常中断的。 8 | 在主函数里提供了4个功能,分别是: 9 | 10 | (1).输入一幅图片,做人脸检测。 11 | 12 | (2).输入一个文件夹,提取批量图片的人脸特征向量,然后把人脸特征向量保存为bin文件。 13 | 14 | (3).读取人脸特征向量的bin文件,输入一幅图片,检测人脸并提取特征向量,然后计算特征向量的距离,做人脸识别。 15 | 16 | (4).输入一幅图片,检测人脸,然后使用pfld网络检测人脸98个关键点 17 | 18 | 我的程序是在win10系统里运行的,如果切换到linux系统下运行,那么在执行第3个功能时,里面有遍历文件夹里的所有文件和目录的函数getAllFiles。 19 | 而这个函数在windows和linux里的代码实现是有所不同的。因此,如果你想在linux系统里运行人脸识别程序,那么需要按照代码里的注释说明去屏蔽一些代码打开另一些代码。 20 | 在windows系统里,路径分隔符可以是"/"或者"\\",在Linux系统里,路径分隔符是"/",为了保证系统的兼容性,在本程序里, 21 | 路径分隔符统一使用"/" 22 | 23 | 这套程序里的人脸检测和人脸特征向量提取模块已经久远了,在2021年11月6日,我在github发布了使用OpenCV部署libface人脸检测和SFace人脸识别, 24 | 包含C++和Python两种版本的程序,仅仅只依赖OpenCV库就能运行。源码地址是: 25 | https://github.com/hpc203/libface-sface_detect-recognition-opencv 26 | -------------------------------------------------------------------------------- /libfacedet.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/libface-detect-recognition/955355588c125026ebe2fea3049477bdedb9f515/libfacedet.cpp -------------------------------------------------------------------------------- /libfacedet.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/libface-detect-recognition/955355588c125026ebe2fea3049477bdedb9f515/libfacedet.h -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/libface-detect-recognition/955355588c125026ebe2fea3049477bdedb9f515/main.cpp -------------------------------------------------------------------------------- /utils.cpp: -------------------------------------------------------------------------------- 1 | #include"utils.h" 2 | 3 | void getAllFiles(string path, vector& files) ///windows系统里遍历文件夹里的全部文件和目录, 如果在ubuntu系统里,需要屏蔽这段代码 4 | { 5 | intptr_t hFile = 0;//文件句柄 64位下long 改为 intptr_t 6 | struct _finddata_t fileinfo;//文件信息 7 | string p; 8 | if ((hFile = _findfirst(p.assign(path).append("/*").c_str(), &fileinfo)) != -1) //文件存在 9 | { 10 | do 11 | { 12 | if ((fileinfo.attrib & _A_SUBDIR))//判断是否为文件夹 13 | { 14 | if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)//文件夹名中不含"."和".." 15 | { 16 | //files.push_back(p.assign(path).append("/").append(fileinfo.name)); //保存文件夹名 17 | getAllFiles(p.assign(path).append("/").append(fileinfo.name), files); //递归遍历文件夹里的文件 18 | } 19 | } 20 | else 21 | { 22 | files.push_back(p.assign(path).append("/").append(fileinfo.name));//如果不是文件夹,储存文件名 23 | } 24 | } while (_findnext(hFile, &fileinfo) == 0); 25 | _findclose(hFile); 26 | } 27 | } 28 | 29 | ///ubuntu系统里遍历文件夹里的全部文件和目录, 如果在windows系统里,需要屏蔽这段代码 30 | /* 31 | void getAllFiles(string path, vector& files) 32 | { 33 | const char * dir_name = path.c_str(); 34 | // check the parameter ! 35 | if( NULL == dir_name ) 36 | { 37 | cout<<" dir_name is null ! "<d_name, &s); // 获取下一级成员属性 65 | if(S_ISDIR(s.st_mode)) // 判断下一级成员是否是目录 66 | { 67 | // get rid of "." and ".." 68 | if( strcmp( filename->d_name , "." ) != 0 && strcmp( filename->d_name , "..") != 0) 69 | { 70 | char* abspath = new char[strlen(dir_name) + strlen(filename->d_name)+1]; 71 | sprintf(abspath, "%s/%s", dir_name, filename->d_name); 72 | getAllFiles(abspath, files); 73 | delete [] abspath; 74 | } 75 | } 76 | else 77 | { 78 | string filepath = dir_name; 79 | filepath.append("/").append(filename->d_name); 80 | files.push_back(filepath); 81 | } 82 | } 83 | chdir( ".. "); 84 | closedir(dir); 85 | } 86 | */ 87 | 88 | int write_face_feature_name2bin(int num_face, int len_feature, const float* output, const vector names, const char* bin_name) 89 | { 90 | FILE* fp = fopen(bin_name, "wb"); 91 | fwrite(&num_face, sizeof(int), 1, fp); 92 | fwrite(&len_feature, sizeof(int), 1, fp); 93 | fwrite(output, sizeof(float), num_face * len_feature, fp); 94 | for (int i = 0; i < names.size(); i++) //// num_face == names.size(); 95 | { 96 | int len_s = names[i].length(); 97 | fwrite(&len_s, sizeof(int), 1, fp); 98 | fwrite(names[i].c_str(), sizeof(char), len_s + 1, fp); ///字符串末尾'\0'也算一个字符的 99 | } 100 | fclose(fp); 101 | return 0; 102 | } 103 | 104 | float* read_face_feature_name2bin(int* num_face, int* len_feature, vector& names, const char* bin_name) 105 | { 106 | FILE* fp = fopen(bin_name, "rb"); 107 | fread(num_face, sizeof(int), 1, fp); 108 | fread(len_feature, sizeof(int), 1, fp); 109 | int len = (*num_face) * (*len_feature); 110 | float* output = new float[len]; 111 | fread(output, sizeof(float), len, fp);//导入数据 112 | for (int i = 0; i < *num_face; i++) 113 | { 114 | int len_s = 0; 115 | fread(&len_s, sizeof(int), 1, fp); 116 | char* name = new char[len_s + 1]; ///字符串末尾'\0'也算一个字符的 117 | fread(name, sizeof(char), len_s + 1, fp);//导入数据 118 | //cout << name << endl; 119 | names.push_back(name); 120 | delete[] name; 121 | } 122 | 123 | fclose(fp);//关闭文件。 124 | return output; 125 | } 126 | 127 | int Get_Min_Euclid_Dist(float* face_features, vector out_feature, int num_face, int len_feature, float* dist_feature) ////欧几里得距离值越小,两个向量越相似 128 | { 129 | int i = 0, j = 0, min_ind = 0; 130 | float euclid_dist = 0, square = 0, min_dist = 10000; 131 | for (i = 0; i < num_face; i++) 132 | { 133 | euclid_dist = 0; 134 | for (j = 0; j < len_feature; j++) 135 | { 136 | square = (out_feature[j] - face_features[i * len_feature + j]) * (out_feature[j] - face_features[i * len_feature + j]); 137 | euclid_dist += square; 138 | } 139 | euclid_dist = sqrt(euclid_dist); 140 | dist_feature[i] = euclid_dist; 141 | if (euclid_dist < min_dist) 142 | { 143 | min_dist = euclid_dist; 144 | min_ind = i; 145 | } 146 | } 147 | return min_ind; 148 | } 149 | 150 | /*余弦距离值越大,两个向量越相似 151 | * 定义向量a和向量b 152 | 余弦值cos(theta) = (a * b) / (|a| * |b|) 153 | |a|和|b|表示向量a和向量b的模 154 | 在计算余弦值之前,已经对向量做了单位归一化,因此|a| = |b| = 1 155 | 那么cos(theta) = (a * b) 156 | */ 157 | int Get_Max_Cos_Dist(float* face_features, vector out_feature, int num_face, int len_feature, float* dist_feature) ////余弦距离值越大,两个向量越相似 158 | { 159 | int i = 0, j = 0, max_ind = 0; 160 | float cos_dist = 0, max_dist = -10000; 161 | for (i = 0; i < num_face; i++) 162 | { 163 | cos_dist = 0; 164 | for (j = 0; j < len_feature; j++) 165 | { 166 | cos_dist = cos_dist + (out_feature[j] * face_features[i * len_feature + j]); 167 | } 168 | dist_feature[i] = cos_dist; 169 | if (cos_dist > max_dist) 170 | { 171 | max_dist = cos_dist; 172 | max_ind = i; 173 | } 174 | } 175 | return max_ind; 176 | } 177 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | #include ///windows系统里遍历文件夹函数需要用到的头文件,如果在ubuntu系统运行程序,需要屏蔽这行代码 4 | #include 5 | #include 6 | #include 7 | 8 | //ubuntu系统里遍历文件夹函数需要用到的头文件,如果在ubuntu系统运行程序,需要打开这段代码 9 | /* 10 | #include 11 | #include 12 | #include 13 | */ 14 | 15 | using namespace std; 16 | /*这个文件夹里包含多个子文件夹, 每个子文件夹的名字是人名. 17 | 子文件夹里包含这个人的人脸图片, 18 | 图片是肖像照, 里面只有一个人脸*/ 19 | void getAllFiles(string path, vector& files); ///windows系统里遍历文件夹里的全部文件和目录 20 | //void getAllFiles(string path, vector& files); ///ubuntu系统里遍历文件夹里的全部文件和目录 21 | 22 | inline int MinInt(int a, int b) //返回整数a和b中较小的一个 23 | { 24 | return (a < b) * a + (1 - (a < b)) * b; 25 | } 26 | 27 | inline int MaxInt(int a, int b) //返回整数a和b中较大的一个 28 | { 29 | return (a > b) * a + (1 - (a > b)) * b; 30 | } 31 | 32 | inline string fromPath_Getname(string filepath) 33 | { 34 | size_t pos_end = filepath.rfind("/"); ////倒数第1个路径间隔符, 路径分隔符是/ 35 | size_t pos_start = filepath.substr(0, pos_end).rfind("/"); ////倒数第2个路径间隔符 36 | return filepath.substr(pos_start + 1, pos_end - pos_start - 1); 37 | } 38 | 39 | inline string fromPath_Get_imgname(string filepath) 40 | { 41 | size_t pos_end = filepath.rfind("/"); ////倒数第1个路径间隔符 42 | return filepath.substr(pos_end + 1); 43 | } 44 | 45 | int write_face_feature_name2bin(int num_face, int len_feature, const float* output, const vector names, const char* bin_name); 46 | 47 | float* read_face_feature_name2bin(int* num_face, int* len_feature, vector& names, const char* bin_name); 48 | 49 | int Get_Min_Euclid_Dist(float* face_features, vector out_feature, int num_face, int len_feature, float* dist_feature); 50 | 51 | int Get_Max_Cos_Dist(float* face_features, vector out_feature, int num_face, int len_feature, float* dist_feature); 52 | 53 | typedef struct Rec_thresh 54 | { 55 | float thresh; 56 | string type; 57 | } Rec_thresh; 58 | --------------------------------------------------------------------------------