人脸识别特征提取(LBP)及其opencv实现

来源:互联网 发布:java微服务架构 编辑:程序博客网 时间:2024/05/21 08:46

        LBP是一种简单,有效的纹理分类的特征提取算法。LBP算子是由Ojala等人于1996年提出的,主要的论文是"Multiresolution gray-scale and rotation invariant texture classification with local binary patterns", pami, vol 24, no.7, July 2002。LBP就是"local binary pattern"的缩写。局部二值模式是一个简单但非常有效的纹理运算符。它将各个像素与其附近的像素进行比较,并把结果保存为二进制数。由于其辨别力强大和计算简单,局部二值模式纹理算子已经在不同的场景下得到应用。LBP最重要的属性是对诸如光照变化等造成的灰度变化的鲁棒性。它的另外一个重要特性是它的计算简单,这使得它可以对图像进行实时分析。

        局部二值模式特征向量可以通过如下方式计算:
        1.将检测窗口切分为区块(cells,例如,每个区块16x16像素)。
        2.对区块中的每个像素,与它的八个邻域像素进行比较(左上、左中、左下、右上等)。可以按照顺时针或者逆时针的顺序进行比较。
        3.对于中心像素大于某个邻域的,设置为1;否则,设置为0。这就获得了一个8位的二进制数(通常情况下会转换为十进制数字),作为该位置的特征。
        4.对每一个区块计算直方图。
        5.此时,可以选择将直方图归一化;
        6.串联所有区块的直方图,这就得到了当前检测窗口的特征向量。

        对图像中的每个像素,通过计算以其为中心的3*3邻域内各像素和中心像素的大小关系,把像素的灰度值转化为一个八位二进制序列。具体计算过程如下图所示,对于图像的任意一点Ic,其LBP特征计算为,以Ic为中心,取与Ic相邻的8各点,按照顺时针的方向记为 I0,I1,...,I7;以Ic点的像素值为阈值,如果 Ii 点的像素值小于Ic,则 Ii 被二值化为0,否则为1;将二值化得到的0、1序列看成一个8位二进制数,将该二进制数转化为十进制就可得到Ic点处的LBP算子的值。
  基本的LBP算子只局限在3*3的邻域内,对于较大图像大尺度的结构不能很好的提取需要的纹理特征,因此研究者们对LBP算子进行了扩展。新的LBP算子LBP(P,R) 可以计算不同半径邻域大小和不同像素点数的特征值,其中P表示周围像素点个数,R表示邻域半径,同时把原来的方形邻域扩展到了圆形,下图给出了四种扩展后的LBP例子,其中,R可以是小数,对于没有落到整数位置的点,根据轨道内离其最近的两个整数位置像素灰度值,利用双线性差值的方法可以计算它的灰度值。


        LBP(P,R)有2^p个值,也就是说图像共有2^p种二进制模型,然而实际研究中发现,所有模式表达信息的重要程度是不同的,统计研究表明,一幅图像中少数模式特别集中,达到总模式的百分之九十左右的比例,Ojala等人定义这种模式为Uniform模式,如果一个二进制序列看成一个圈时,0-1以及1-0的变化出现的次数总和不超过两次,那么这个序列就是Uniform模式 ,比如,00000000、00011110、00100001、11111111,在使用LBP表达图像纹理时,通常只关心Uniform模式,而将所有其他的模式归到同一类中。

        LBP算子利用了周围点与该点的关系对该点进行量化。量化后可以更有效地消除光照对图像的影响。只要光照的变化不足以改变两个点像素值之间的大小关系,那么LBP算子的值不会发生变化,所以一定程度上,基于LBP的识别算法解决了光照变化的问题,但是当图像光照变化不均匀时,各像素间的大小关系被破坏,对应的LBP模式也就发生了变化。


void elbp(Mat& src, Mat &dst, int radius, int neighbors){for (int n = 0; n < neighbors; n++){// 采样点的计算float x = static_cast<float>(-radius * sin(2.0*CV_PI*n / static_cast<float>(neighbors)));float y = static_cast<float>(radius * cos(2.0*CV_PI*n / static_cast<float>(neighbors)));// 上取整和下取整的值int fx = static_cast<int>(floor(x));int fy = static_cast<int>(floor(y));int cx = static_cast<int>(ceil(x));int cy = static_cast<int>(ceil(y));// 小数部分float ty = y - fy;float tx = x - fx;// 设置插值权重float w1 = (1 - tx) * (1 - ty);float w2 = tx  * (1 - ty);float w3 = (1 - tx) *      ty;float w4 = tx  *      ty;// 循环处理图像数据for (int i = radius; i < src.rows - radius; i++){for (int j = radius; j < src.cols - radius; j++){// 计算插值float t = static_cast<float>(w1*src.at<uchar>(i + fy, j + fx) + w2*src.at<uchar>(i + fy, j + cx) + w3*src.at<uchar>(i + cy, j + fx) + w4*src.at<uchar>(i + cy, j + cx));// 进行编码dst.at<uchar>(i - radius, j - radius) += ((t > src.at<uchar>(i, j)) || (std::abs(t - src.at<uchar>(i, j)) < std::numeric_limits<float>::epsilon())) << n;}}}}void elbp1(Mat& src, Mat &dst){// 循环处理图像数据for (int i = 1; i < src.rows - 1; i++){for (int j = 1; j < src.cols - 1; j++){uchar tt = 0;int tt1 = 0;uchar u = src.at<uchar>(i, j);if (src.at<uchar>(i - 1, j - 1)>u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i - 1, j)>u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i - 1, j + 1) > u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i, j + 1) > u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i + 1, j + 1) > u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i + 1, j) > u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i + 1, j - 1) > u) { tt += 1 << tt1; }tt1++;if (src.at<uchar>(i - 1, j) > u) { tt += 1 << tt1; }tt1++;dst.at<uchar>(i - 1, j - 1) = tt;}}}


0 0
原创粉丝点击