高级使用

双目红外活体检测

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模型文件可以去掉,从而缩减最终部署包的大小。

公开人脸特征比对代码

我们使用余弦相似算法来计算两个人脸特征字节数组的相似度。

../_images/cosine.jpg

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;
}