C++ 近红外人脸定位与深度图人脸活体检测(.raw深度图像)

近红外人脸定位与活体检测

通过深度相机的红外图进行人脸位置定位,传给深度图进行活体检测
注:利用rgb传来的位置对应到深度图上偏差过大,直接利用深度相机产生的红外图像进行人脸位置的确定更加精准。

运行

dlib_test.cpp

#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <iostream>
#include <time.h>

#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>

using namespace dlib;
using namespace std;

/* 函数声明 */
int *face_location(char *imgFile);
bool liveness_detection(char *DeepFile, int rec_face[4]);  

const int IMG_HEIGHT =  720;
const int IMG_WIDTH =  1280;

int main()
{

	char *imgFile = "/home/zhoujie/cProject/dlib_test/0001_IR_frontface.jpg";
	int *rec_face;

    /* 调用函数得到人脸位置 */
    // Eg:rec_face = {157 ,66 ,172 ,198 }, 行取66:66+198,列取157:157+172
	rec_face = face_location(imgFile);

	//深度图与红外图是水平翻转的
	rec_face[0] = IMG_WIDTH - rec_face[0] -rec_face[2]; 
    
    cout << rec_face[0] << endl;
    cout << rec_face[1] << endl;
    cout << rec_face[2] << endl;
    cout << rec_face[3] << endl;

    char *DeepFile = "/home/zhoujie/cProject/dlib_test/raw_0001_frontface.raw";
	bool IS_FACE;

	/* 调用函数判断是否为活体 */
    IS_FACE = liveness_detection( DeepFile, rec_face);
    printf("RESULT : %d\n", IS_FACE);

    delete rec_face;
}


/* 函数 输出人脸位置 */
int *face_location(char* imgFile)
{  
    int *rec_face = new int[4];

    frontal_face_detector detector = get_frontal_face_detector();

    cout << "processing image " << imgFile << endl;

    clock_t start,finish;
    double totaltime;
    start=clock();

    array2d<unsigned char> img;
    load_image(img, imgFile);
    
    std::vector<rectangle> dets = detector(img);

    cout << "Number of faces detected: " << dets.size() << endl;

    rec_face[0] = dets[0].left();
    rec_face[1] = dets[0].top();
    rec_face[2] = dets[0].right() - dets[0].left() + 1;
    rec_face[3] = dets[0].bottom() - dets[0].top() + 1;

    finish=clock();
    totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
    cout<<"\n此程序的运行时间为"<<totaltime<<"秒!"<<endl;

    // delete rec_face;

    return  rec_face;
}

/* 函数判断是否为活体 */
bool liveness_detection(char *DeepFile, int rec_face[4])
{
    const int ITER = 10000; // 随机取点次数
    const float PLANE_OR_NOT = 0.1; // 判断是否为平面的分界线
	const int SIGMA = 1;
    typedef unsigned short UNIT16;
	
	// 从.raw读取二进制16位数据到MatDATA
	UNIT16 MatDATA[IMG_HEIGHT*IMG_WIDTH];
	FILE *fp = NULL;
	fp = fopen( DeepFile, "rb" );
    fread(MatDATA,sizeof(UNIT16),IMG_HEIGHT*IMG_WIDTH,fp);
	fclose(fp);
	
	int n = 0;
	int i,j;
	int COL = rec_face[0],ROW = rec_face[1],FACE_WIDTH = rec_face[2],FACE_HEIGHT = rec_face[3]; //位置信息
	// txt :157 66 172 198 , 取行66:66+198,列取157:157+172
	int faceno0_num = FACE_HEIGHT*FACE_WIDTH -1; 
	int FaceDATA[3][160000];
	n = 0;
	for(i = 1;i< FACE_HEIGHT+1;i++)
		{
			for(j= 1;j< FACE_WIDTH+1;j++) 
			{ 
				if (MatDATA[IMG_WIDTH*(ROW+i-2)+COL+j-2] == 0)
				{
					faceno0_num -= 1; // 非零深度点个数为 faceno0_num+1
					continue;
				}
				FaceDATA[1][n] = i;
				FaceDATA[0][n] = j; 
				FaceDATA[2][n] = MatDATA[IMG_WIDTH*(ROW+i-2)+COL+j-2];
				n += 1;
			} 
		} 
	
	int pretotal = 0;  // 符合拟合模型的数据的个数
	int x[3],y[3],z[3];  // 随机取三个点 
	srand((unsigned)time(NULL));
	float a,b,c;  // 拟合平面方程 z=ax+by+c
	// float besta,bestb,bestc;  // 最佳参数
	int rand_num[3];
	float check,distance;
	int total = 0;
	for(i = 0; i < ITER; i++)
	{
		do{
			rand_num[0] = std::rand()%faceno0_num; 
			rand_num[1] = std::rand()%faceno0_num; 
			rand_num[2] = std::rand()%faceno0_num; 
		}while(rand_num[0] == rand_num[1] || rand_num[0] == rand_num[2] || rand_num[1] == rand_num[2]);
		for(n = 0; n < 3; n++ )
		{
			x[n] = FaceDATA[0][rand_num[n]];
			y[n] = FaceDATA[1][rand_num[n]];
			z[n] = FaceDATA[2][rand_num[n]];
			// printf("%d,%d,%d,%d\n", x[n],y[n],z[n],n);
		}
		check = (x[0]-x[1])*(y[0]-y[2]) - (x[0]-x[2])*(y[0]-y[1]);
		if ( check == 0)  // 防止提示浮点数例外 (核心已转储)
		{
			i -= 1;
			continue;
		}
		a = ( (z[0]-z[1])*(y[0]-y[2]) - (z[0]-z[2])*(y[0]-y[1]) )/( (x[0]-x[1])*(y[0]-y[2]) - (x[0]-x[2])*(y[0]-y[1]) );
        if (y[0] == y[2])  // 防止提示浮点数例外 (核心已转储)
		{
			i -= 1;
			continue;
		}
		b = ((z[0] - z[2]) - a * (x[0] - x[2]))/(y[0]-y[2]);
        c = z[0]- a * x[0] - b * y[0];
		// printf("%f,%f,%f\n",a,b,c);
		total = 0;
		for(n = 0; n < faceno0_num +1 ; n++ )
		{
			distance = fabs(a*FaceDATA[0][n] + b*FaceDATA[1][n] - 1*FaceDATA[2][n] + c*1);
			if (distance < SIGMA)
			{
				total +=1;
			}
		}
		// printf("%d,%f,%d\n",i,distance,total);
		if (total > pretotal)  // 找到符合拟合平面数据最多的拟合平面
        {
			pretotal=total;
		}
	}
	float pretotal_ary = pretotal *1.0/ faceno0_num ;
	printf("%d,%f\n", pretotal,pretotal_ary);
	bool IS_FACE;

    if (pretotal_ary < PLANE_OR_NOT)
	{
		IS_FACE =  true;
	}
	else
	{
		IS_FACE = false;
	}
	return  IS_FACE;
} 

Ubuntu下编译Dlib库参考这里 https://blog.csdn.net/ffcjjhv/article/details/84660869
修改CMakeLists.txt相应路径
cmake .
make
./dlib_test

运行结果

在这里插入图片描述
项目地址:https://github.com/zj19941113/Face-Liveness_detection

活检部分原理

Func_liveness_detection.c

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>

const int IMG_HEIGHT =  345;
const int IMG_WIDTH =  400;
const int ITER = 10000; // 随机取点次数
const float PLANE_OR_NOT = 0.1; // 判断是否为平面的分界线
typedef unsigned short UNIT16;

int readFileList(char *basePath)
{
    DIR *dir;
    struct dirent *ptr; 
    char base[8]; 
    char title[4];
    char *p=".raw"; //需要的子串;
    char *p2=".txt";
    char *padd="/";
    int len;
    char Deepfile_raw[100];
    char Deepfile_txt[100];

    UNIT16 MatDATA[IMG_HEIGHT*IMG_WIDTH];
    FILE *fp = NULL;
    int DeepDATA[3][IMG_HEIGHT*IMG_WIDTH];
    int length;
    int n;
    int i,j;
    FILE *fp2 = NULL;
    char buf[20];
    char *ptr2;
    int rec_face[4];
    int COL ,ROW ,FACE_WIDTH ,FACE_HEIGHT ; 
    int FaceDATA[3][40000];
    int faceno0_num ;
    int sigma = 1;
    int pretotal;  // 符合拟合模型的数据的个数
    int x[3],y[3],z[3];  // 随机取三个点 
    float a,b,c;  // 拟合平面方程 z=ax+by+c
    // float besta,bestb,bestc;  // 最佳参数
    int rand_num[3];
    float check,distance;
    int total;
 

    if ((dir=opendir(basePath)) == NULL)
    {
        perror("Open dir error...");
        exit(1);
    }
    while ((ptr=readdir(dir)) != NULL)
    {
        strcpy(base, ptr->d_name);
        if(strstr(base,p)) 
        {
            len = strlen(base);
            memset(title, '\0', sizeof(title));
            strncpy(title, base, len -4);
            strcpy(Deepfile_raw, basePath);
            strcat(Deepfile_raw, padd);
            strcat(Deepfile_raw, title);
            strcpy(Deepfile_txt, Deepfile_raw);
            strcat(Deepfile_raw, p);
            strcat(Deepfile_txt, p2);
            // printf("%s\n",Deepfile_raw); 
            // printf("%s\n",Deepfile_txt); 

            // 从.raw读取二进制16位数据到MatDATA 
            fp = fopen( Deepfile_raw, "rb" );
            fread(MatDATA,sizeof(UNIT16),IMG_HEIGHT*IMG_WIDTH,fp);
            fclose(fp);
            // length = sizeof(MatDATA) / sizeof(UNIT16); 
            // printf("数组的长度为: %d\n",length); //length 应为IMG_HEIGHT*IMG_WIDTH
            n = 0;
            // DeepDATA三行分别为深度图行数,列数,深度信息
            for(i=1;i< IMG_HEIGHT+1 ;i++)
                {
                    for(j=1;j< IMG_WIDTH+1 ;j++) 
                    { 
                        DeepDATA[0][n] = i;
                        DeepDATA[1][n] = j;
                        DeepDATA[2][n] = MatDATA[n];
                        n += 1;
                    } 
                } 
            // int test1 = 110194 ;
            // printf("%d,%d,%d\n",DeepDATA[0][test1],DeepDATA[1][test1],DeepDATA[2][test1]);	
            
            // FaceDATA为深度图DeepDATA裁剪后且去除零深度信息后的人脸部分
            n = 0;
            fp2 = fopen(Deepfile_txt, "r");
            fgets(buf, 20, fp2);
            // printf("%s\n", buf );
            ptr2 = strtok(buf, " ");
            for(n = 0; n < 4; n++)
            {
                rec_face[n] = atoi(ptr2);
                ptr2 = strtok(NULL, " ");

            }
            fclose(fp2);
            COL = rec_face[0],ROW = rec_face[1],FACE_WIDTH = rec_face[2],FACE_HEIGHT = rec_face[3]; //位置信息
            // txt :157 66 172 198 , 取行66:66+198,列取157:157+172
            faceno0_num = FACE_HEIGHT*FACE_WIDTH -1; 
            n = 0;
            for(i = 1;i< FACE_HEIGHT+1;i++)
                {
                    for(j= 1;j< FACE_WIDTH+1;j++) 
                    { 
                        if (MatDATA[IMG_WIDTH*(ROW+i-2)+COL+j-2] == 0)
                        {
                            faceno0_num -= 1; // 非零深度点个数为 faceno0_num+1
                            continue;
                        }
                        FaceDATA[1][n] = i;
                        FaceDATA[0][n] = j; 
                        FaceDATA[2][n] = MatDATA[IMG_WIDTH*(ROW+i-2)+COL+j-2];
                        n += 1;
                    } 
                } 
            // int test = 6804;  
            // printf("%d,%d,%d,%d\n",test,FaceDATA[0][test],FaceDATA[1][test],FaceDATA[2][test]);	
            
            srand((unsigned)time(NULL));
            pretotal = 0;
            total = 0;
            for(i = 0; i < ITER; i++)
            {
                do{
                    rand_num[0] = rand()%faceno0_num; 
                    rand_num[1] = rand()%faceno0_num; 
                    rand_num[2] = rand()%faceno0_num; 
                }while(rand_num[0] == rand_num[1] || rand_num[0] == rand_num[2] || rand_num[1] == rand_num[2]);
                for(n = 0; n < 3; n++ )
                {
                    x[n] = FaceDATA[0][rand_num[n]];
                    y[n] = FaceDATA[1][rand_num[n]];
                    z[n] = FaceDATA[2][rand_num[n]];
                    // printf("%d,%d,%d,%d\n", x[n],y[n],z[n],n);
                }
                check = (x[0]-x[1])*(y[0]-y[2]) - (x[0]-x[2])*(y[0]-y[1]);
                if ( check == 0)  // 防止提示浮点数例外 (核心已转储)
                {
                    i -= 1;
                    continue;
                }
                a = ( (z[0]-z[1])*(y[0]-y[2]) - (z[0]-z[2])*(y[0]-y[1]) )*1.0/( (x[0]-x[1])*(y[0]-y[2]) - (x[0]-x[2])*(y[0]-y[1]) );
                if (y[0] == y[2])  // 防止提示浮点数例外 (核心已转储)
                {
                    i -= 1;
                    continue;
                }
                b = ((z[0] - z[2]) - a * (x[0] - x[2]))*1.0/(y[0]-y[2]);
                c = z[0]- a * x[0] - b * y[0];
                // printf("%f,%f,%f\n",a,b,c);
                total = 0;
                for(n = 0; n < faceno0_num +1 ; n++ )
                {
                    distance = fabs(a*FaceDATA[0][n] + b*FaceDATA[1][n] - 1*FaceDATA[2][n] + c*1);
                    if (distance < sigma)
                    {
                        total +=1;
                    }
                }
                // printf("%d,%f,%d\n",i,distance,total);
                if (total > pretotal)  // 找到符合拟合平面数据最多的拟合平面
                {
                    pretotal=total;
                    // besta = a;
                    // bestb = b;
                    // bestc = c;
                }
            }
            float pretotal_ary = pretotal *1.0/ faceno0_num ;
            printf("pretotal = %d,_ary = %f,",pretotal,pretotal_ary);
            printf("%s",base);
            bool IS_FACE;

            if (pretotal_ary>PLANE_OR_NOT)
            {
                IS_FACE =  false;
                printf("不是人脸\n");
            }
            else
            {
                IS_FACE = true;
                printf("是人脸\n");
            }
        }  
    }
    closedir(dir);
}

int main(void)
{
    DIR *dir;
    char *basePath = "/home/zhoujie/liveness detection/raw文件/non-face";
    readFileList(basePath);
    return 0;
}

github地址:https://github.com/zj19941113/Face-Liveness_detection
上面这个项目使用的数据集标定的不太准,PLANE_OR_NOT参数选为 0.1

这个数据集是自己标定的,数量较少但是比较准确: https://pan.baidu.com/s/161xSbayGW7tKg0tKfTW1mw ,PLANE_OR_NOT参数选为 0.2

快速鼠标标定深度图人脸位置: https://blog.csdn.net/ffcjjhv/article/details/83270002

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值