高级使用¶
双目红外活体检测¶
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. 去掉用不到的人脸特征版本模型¶
我们提供kFEATURE_V3, kFEATURE_V6_MASK不同版本的特征,分别对应model目录下的v3.em,v6_mask.em模型文件。例如只用到kFEATURE_V3版本, 那么model目录下的v6_mask.em模型文件可以去掉,从而缩减最终部署包的大小。
公开人脸特征比对代码¶
我们使用余弦相似算法来计算两个人脸特征字节数组的相似度。
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++版:
union FloatType {
float FloatNum;
int IntNum;
};
static void convertBytes2Feature(unsigned char* bytes, int length, std::vector<float> &feature) {
int feathreLen = length / 4;
for (int i = 0; i<feathreLen; i++) {
FloatType Number;
Number.IntNum = 0;
Number.IntNum = bytes[4 * i + 3];
Number.IntNum = (Number.IntNum << 8) | bytes[4 * i + 2];
Number.IntNum = (Number.IntNum << 8) | bytes[4 * i + 1];
Number.IntNum = (Number.IntNum << 8) | bytes[4 * i + 0];
feature.push_back(Number.FloatNum);
}
}
//vector norm
static float norm(const vector<float>& vec){
int n = vec.size();
float sum = 0.0;
for (int i = 0; i<n; ++i)
sum += vec[i] * vec[i];
return sqrt(sum);
}
/**
* Compare face feature cosine similarity.
* @param[in] feature1 First face feature
* @param[in] feature2 Second face feature
* @return face feature similarity volume
* @note cosine = (v1 dot v2)/(||v1|| * ||v2||)
*/
static float similarityByFeature(vector<unsigned char>& feature1, vector<unsigned char>& feature2){
std::vector<float> feature_arr1;
std::vector<float> 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<n; ++i){
tmp += feature_arr1[i] * feature_arr2[i];
}
float cosine = tmp / (norm(feature_arr1)*norm(feature_arr2));
float norm_cosine = 0.5 + 0.5 * cosine;
return norm_cosine;
}