【opencv】LBP(局部二进制模式)算法的实现

本章我们学习LBP图像的原理和使用,因为接下来教程我们要使用LBP图像的直方图来进行脸部识别。

参考资料:

http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html      (非常重要的参考文档!!!)

http://www.cnblogs.com/mikewolf2002/p/3438166.html

      LBP的基本思想是以图像中某个像素为中心,对相邻像素进行阈值比较。如果中心像素的亮度大于等于它的相邻像素,把相邻像素标记为1,否则标记为0。我们可以用二进制数字来表示LBP图中的每个像素的LBP编码,比如下图中的中心像素,它的LBP编码为:00010011,其十进制值为19。

image

用公式表示就是:

image

其中(xc,yc)是中心像素,ic是灰度值,in是相邻像素的灰度值,s是一个符号函数:

image

在OpenCV的LBP算法中,固定的领域大小不能对不同规模的细节进行编码。所以基本的LBP算法被进一步推广为使用不同大小和形状的领域,采取圆形的领域并结合双线性插值运算,我们可以获得任意半径和任意数目的领域像素点。使用圆形的LBP算子:

对于一个点image, 它的近邻点 image用以下公式计算:

image

其中R是半径,p是样本点的个数。

如果就算的结果不在像素坐标上,我们则使用双线性插值(确定他的值)进行近似处理。

image

下面的代码中,我们分别实现了通常LBP图和圆形算子LBP图。

      elbp是圆形算子LBP函数,elbp1是通常LBP图,我们分别对lena的图像进行了处理,结果如下所示,从途中可以看出来,使用圆形算子的效果锐度更强。

#include "opencv2/core/core.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <iostream>
#include <fstream>
#include <sstream>

using namespace cv;
using namespace std;

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;
                }
            }
        }

int main()
    {
    Mat img = cv::imread("../lenna.jpg", 0);
    namedWindow("image");
    imshow("image", img);

    int radius, neighbors;
    radius = 1;
    neighbors = 8;

    //创建一个LBP
    //注意为了溢出,我们行列都在原有图像上减去2个半径
    Mat dst = Mat(img.rows-2*radius, img.cols-2*radius,CV_8UC1, Scalar(0));
    elbp1(img,dst);
    namedWindow("normal");
    imshow("normal", dst);

    Mat dst1 = Mat(img.rows-2*radius, img.cols-2*radius,CV_8UC1, Scalar(0));
    elbp(img,dst1,1,8);
    namedWindow("circle");
    imshow("circle", dst1);

    while(1)
       cv::waitKey(0);
    }

imageimageimage

我们换另外一张图,该图包括不同光照下的四副照片,再来看看LBP图的效果(可以看到,LBP在光照不均匀的人脸识别中可以取得很好的应用!):

image

image

image

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值