1.OLBP:
olbp即原始的LBP,就是比较(X,Y)周围的8个点,大于等于它就置为1,小于它就置为0,然后编码,再把这个编码后的值赋给该点。
void LBP(Mat src, Mat &dst)
{
for (int i = 1; i < src.rows-1; ++i)
{
for (int j = 1; j < src.cols - 1; ++j)
{
uchar nei[9] = {0};
nei[0] = src.at<uchar>(i, j);
nei[1] = src.at<uchar>(i-1, j-1);
nei[2] = src.at<uchar>(i-1, j);
nei[3] = src.at<uchar>(i-1, j+1);
nei[4] = src.at<uchar>(i, j+1);
nei[5] = src.at<uchar>(i+1, j+1);
nei[6] = src.at<uchar>(i+1, j);
nei[7] = src.at<uchar>(i+1, j-1);
nei[8] = src.at<uchar>(i, j-1);
uchar value = 0;
for (int n = 1; n <= 8; ++n)
{
value += (nei[n] >= nei[0]) << (8 - n);
}
dst.at<uchar>(i, j) = value;
}
}
}
2.ELBP:
为了防止溢出,在定义dst的时候,它的长宽要减2*radius。采样点(X,Y)的计算公式: 。ELBP中使用了双线性插值,就简单介绍下双线性插值。
先在X轴方向插值(双线性插值,先X或者先Y都可以,与顺序无关):
然后Y轴方向插值:
合并两个式子:
为了方便计算,把这些点放到(0,1)之间,即Q11(0,0),Q21(1,0),Q12(0,1),Q22(1,1),则上式变为:
具体的双线性插值可以看这里。
void ELBP(Mat &src, Mat &dst, int radius, int neighbors)
{
for (int n = 0; n < neighbors; ++n)
{
float x = static_cast<float>(radius*cos(2 * CV_PI*n / neighbors));
float y = static_cast<float>(-radius*sin(2 * CV_PI*n / 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 tx = x - fx;
float ty = y - fy;
float w1 = (1 - tx)*(1-ty);
float w2 = tx*(1 - ty);
float w3 = (1 - tx)*y;
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<int>(i - radius, j - radius) += ((t > src.at<uchar>(i, j)) || (std::abs(t - src.at<uchar>(i, j)) < std::numeric_limits<float>::epsilon())) << n;
}
}
}
}
最后算插值的公式是:
好像是以左下角为坐标原点来算的。最后说说显示的问题,在定义dst时好像只能是 CV_32SC1 其它类型都会报错,直接显示ELBP的结果会出现一幅黑图,看了LBP原理加源码解析下面的评论,是要将ELBP的结果convertTo为CV_8UC1才能正常显示。经过试验,感觉ELBP显示的很慢。
这些源码在OpenCV的包中可以找到,具体位置:E:\opencv\sources\modules\contrib\src\facerec.cpp
参考: