LBP+SVM 活体识别

    xiaoxiao2022-07-02  154

    针对上一篇“深度摄像头-活体识别”的改进版

    大致思路:

    1.RGB人脸检测

    2.同步人脸位置到深度图矩形框

    3.裁剪矩形框,提取LBP特征

    4.训练SVM模型。

    5.集成模型到demo程序

     

    详细说明:

    1.RGB人脸检测使用的是seetaface,速度和准确率比opencv自带的检测器好。返回一个faces的数组,包括人脸个数和位置信息。这里可以根据要求选择做不做最大化人脸选取,选取的过程非常简单,这里不做赘述。其实我们主要就是获取人脸位置信息(x,y表示人脸框的左上角位置,w表示框宽,h表示框高)

    2.因为摄像头出产做过对齐,所以这里可以省去对齐的操作。RGB和深度都是640*480画面,所以直接同步就行了。需要注意的是深度图的原始数据是16位,所以在转成mat格式的时候最好也使用16位,不丢失细节。cv::Mat u16depth(depthHeight,depthWidth,CV_16UC1,(void)*m_depthFrame.getData());

    3.裁剪深度人脸框位置cv::Mat depth_face = u16depth(Rect(roi.x,roi.y,roi.width,roi.height)).clone;然后主要就是对depth_face做文章了。

    (1).人脸框距离检测,因为摄像头硬件的原因,在距离较远的时候,分布到人脸上的深度信息很少。所以我人为设定在一米的距离里做活体检测。

    int font = cv::FONT_HERSHEY_COMPLEX;//字体格式 double nosemean = cv::mean(depth_face)[0]; if (nosemean > 1000) { cv::rectangle(depth,roi,cv::Scalar(0,0,255),2);//在深度图像中画出人脸 cv::putText(depth,"far",cvPoint(roi.x,roi.y),font,1.5,cv::Scalar(0,0,255),2); } else { int numOfblank = 0;//记录颜色位黑色的像素点 float rate;//黑色像素点占的比例 int mindepth = MAX_DEPTH; int maxdepth = 0; int maxrow = 0; int minrow = 0; for (int i = 0; i < depth_face.rows; i++) { for (int j = 0; j < depth_face.cols; j++) { int depth = depth_face.at<ushort>(i,j); if (depth == 0) { numOfblank++; } } } rate = (float)numOfblank / (float)(depth_face.rows * depth_face.cols); if (rate > 0.4)//黑色像素点占的比例超过40%时,不进入识别模块 { cv::rectangle(depth,roi,cv::Scalar(0,255,0),2);//在深度图像中画出人脸 cv::putText(depth,"close",cvPoint(roi.x,roi.y),font,1.5,cv::Scalar(0,255,0),2); } else { Size dsize = Size(128,128);//resize到128*128,识别模型训练的训练数据都是128*128尺寸 cv::Mat image2 = Mat(dsize,CV_16UC1); cv::resize(depth_face,image2,dsize); cv::rectangle(depth,roi,cv::Scalar(255,0,0),2);//在深度图像中画出人脸 LBP lbp; Mat feature_lbp = Mat(image2.rows,image.cols,CV_8UC1,Scalar(0)); lbp.elbp(image2,feature_lbp,1,8)//LBP提取特征,后续训练模型的时候会详细介绍 const int chanels[1] = {0}; //直方图的每一个维度的柱条的数目(就是将灰度级分组) int histSize[] = {256}; float midRanges[] = {0,255};//定义一个变量来储存单个维度数值的取值范围 const float *ranges[] = {midRanges};//确定每一个维度的取值范围,就是横坐标的总数 cv::Mat dstHist; cv::calcHist(&feature_lbp,1,channels,Mat(),dstHist,1,hisSize,ranges,true,false); cv::Mat dstHist_new; transpose(hstHist,dstHist_new); normalize(dstHist_new,dstHist_new,0,dstHist.rows,NORM_MINMAX,-1,Mat());//归一化结果存在dstHist float response = svm->predict(dstHist_new,noArray(),cv::ml::StatModel::Flags::RAW_OUTPUT);//计算出样本距离超平面的距离 float result = response * 1000000;//乘1000000是为了方便观看 if(result >= -10) { cv::rectangle(depth,roi,cv::Scalar(0,255,0),2);//在深度图像中画出人脸 cv::putText(depth,"real",cvPoint(roi.x,roi.y),font,1.5,cv::Scalar(0,255,0),2); } else { cv::rectangle(depth,roi,cv::Scalar(0,255,0),2);//在深度图像中画出人脸 cv::putText(depth,"unreal",cvPoint(roi.x,roi.y),font,1.5,cv::Scalar(0,255,0),2); } }

    这里说几点:

    试过直接用16位深度数据做识别,也试过用LBP特征图来做识别,但是训练出来的效果都不理想。最后使用的是LBP图的直方图。

    LBP提取特征的方法将在下一篇模型训练里做说明

    集成到demo程序里,活体识别准确率还可以,但是泛化效果欠佳

    后续方向:

    使用深度学习来替代SVM,现在已经基本实现,效果比svm好很多,但是速度没有svm快,采用的是MobileNet网络,直接对原始16位mat数据进行训练识别。

    rgb人脸检测依赖光线问题的解决办法是,直接训练出深度人脸检测器,目前正在进行中

     

     

     

     

     

     

     

    最新回复(0)