高级使用 ======== 双目红外活体检测 ------------------- ========================= 1. 摄像头要求 ========================= 为了提升双目活检的效果,算法对红外摄像头和红外补光灯有一定的要求。需要选择成像清晰的红外摄像头,控制红外补光灯的 强度,防止红外图像太亮或太暗这两种极端的状况。 ========= ============ 参数 指标 ========= ============ 宽动态比 >= 80db 信噪比 >= 50db 白平衡 建议开启 帧率 越大越好 格式 YUV/MJPG 分辨率 >= 480p 双目间距 <= 3cm ========= ============ ================================= 2. Android原生Camera API双目配置 ================================= 请与开发板厂商沟通Android固件原生Camera API是否支持同时打开两个摄像头预览,安卓5.1及5.1以前的版本默认最大支持同时打开1个摄像头预览。如果不支持双目预览,需要重新编译HAL硬件层。 在CameraHal_Module.h中将#define CAMERAS_SUPPORTED_SIMUL_MAX 1 修改为 2。 活体检测方案选择 --------------------- ========================= 1. 活体检测安全等级排名 ========================= IR活检 > RGB活检 实际应用,我们推荐使用IR活体检测,配合(彩色+红外)双目摄像头方案。 模型目录裁剪 --------------------- =========================================== 1. 去掉用不到的人脸特征版本模型 =========================================== 我们提供kFEATURE_V3, kFEATURE_V6_MASK不同版本的特征,分别对应model目录下的v3.em,v6_mask.em模型文件。例如只用到kFEATURE_V3版本, 那么model目录下的v6_mask.em模型文件可以去掉,从而缩减最终部署包的大小。 公开人脸特征比对代码 ---------------------- 我们使用余弦相似算法来计算两个人脸特征字节数组的相似度。 .. image:: /cosine.jpg :scale: 100 % :align: center JAVA版: .. code:: java public static double sumSquares(float[] data) { double ans = 0.0; for (int k = 0; k < data.length; k++) { ans += data[k] * data[k]; } return (ans); } public static double norm(float[] data) { return (Math.sqrt(sumSquares(data))); } public static double dotProduct(float[] v1, float[] v2) { double result = 0; for (int i = 0; i < v1.length; i++) { result += v1[i] * v2[i]; } return result; } /** * Comare 2 face feature cosine similarity, cosine = (v1 dot v2)/(||v1|| * ||v2||) * @param feature1 first face feature byte array * @param feature2 second face feature byte array * @note Use Little-Endian default * @return face feature cosine similarity */ public static double similarityByFeature(byte[] feature1, byte[] feature2) { float[] featureArr1 = new float[feature1.length/4]; for (int x = 0; x < feature1.length; x+=4) { int accum = 0; accum= accum|(feature1[x] & 0xff) << 0; accum= accum|(feature1[x+1] & 0xff) << 8; accum= accum|(feature1[x+2] & 0xff) << 16; accum= accum|(feature1[x+3] & 0xff) << 24; featureArr1[x/4] = Float.intBitsToFloat(accum); } float[] featureArr2 = new float[feature2.length/4]; for (int x = 0; x < feature2.length; x+=4) { int accum = 0; accum= accum|(feature2[x] & 0xff) << 0; accum= accum|(feature2[x+1] & 0xff) << 8; accum= accum|(feature2[x+2] & 0xff) << 16; accum= accum|(feature2[x+3] & 0xff) << 24; featureArr2[x/4] = Float.intBitsToFloat(accum); } // ||v1|| double feature1_norm = norm(featureArr1); // ||v2|| double feature2_norm = norm(featureArr2); // v1 dot v2 double innerProduct = dotProduct(featureArr1, featureArr2); //cosine = (v1 dot v2)/(||v1|| * ||v2||) double cosine = innerProduct / (feature1_norm*feature2_norm); // normalization to 0~1 double normCosine = 0.5 + 0.5*cosine; return normCosine; } C++版: .. code:: c++ union FloatType { float FloatNum; int IntNum; }; static void convertBytes2Feature(unsigned char* bytes, int length, std::vector &feature) { int feathreLen = length / 4; for (int i = 0; i& vec){ int n = vec.size(); float sum = 0.0; for (int i = 0; i& feature1, vector& feature2){ std::vector feature_arr1; std::vector feature_arr2; convertBytes2Feature(feature1.data(), feature1.size(), feature_arr1); convertBytes2Feature(feature2.data(), feature2.size(), feature_arr2); int n = feature_arr1.size(); float tmp = 0.0; for (int i = 0; i