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

        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)及其opencv实现_第1张图片

        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(-radius * sin(2.0*CV_PI*n / static_cast(neighbors)));
		float y = static_cast(radius * cos(2.0*CV_PI*n / static_cast(neighbors)));
		// 上取整和下取整的值
		int fx = static_cast(floor(x));
		int fy = static_cast(floor(y));
		int cx = static_cast(ceil(x));
		int cy = static_cast(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(w1*src.at(i + fy, j + fx) + w2*src.at(i + fy, j + cx) + w3*src.at(i + cy, j + fx) + w4*src.at(i + cy, j + cx));
				// 进行编码
				dst.at(i - radius, j - radius) += ((t > src.at(i, j)) || (std::abs(t - src.at(i, j)) < std::numeric_limits::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(i, j);
			if (src.at(i - 1, j - 1)>u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i - 1, j)>u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i - 1, j + 1) > u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i, j + 1) > u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i + 1, j + 1) > u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i + 1, j) > u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i + 1, j - 1) > u) { tt += 1 << tt1; }
			tt1++;
			if (src.at(i - 1, j) > u) { tt += 1 << tt1; }
			tt1++;

			dst.at(i - 1, j - 1) = tt;
		}
	}
}


你可能感兴趣的:(人脸识别,算法,OpenCV,C++,图像处理)