本章我们学习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。

用公式表示就是:

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

在OpenCV的LBP算法中,使用圆形的LBP算子:

 

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

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

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

下面的代码中,我们分别实现了通常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);
}

我们换另外一张图,该图包括不同光照下的四副照片,再来看看LBP图的效果:

 

 

程序代码:

FirstOpenCV36

OpenCV学习(39) OpenCV中的LBP图像的更多相关文章

  1. OpenCV学习(22) opencv中使用kmeans算法

    kmeans算法的原理参考:http://www.cnblogs.com/mikewolf2002/p/3368118.html 下面学习一下opencv中kmeans函数的使用.      首先我们 ...

  2. OpenCV学习笔记:resize函数改变图像的大小

    OpenCV提供了resize函数来改变图像的大小,函数原型如下: , , int interpolation=INTER_LINEAR ); 参数解释: src:输入,原图像,即待改变大小的图像: ...

  3. OpenCV学习笔记——OpenCV安装

    关于OpenCV安装 1.下载和安装OpenCV SDK 在官网:http://opencv.org/上找到OpenCV windows版下载 . 后得到一个 opencv-2.X.X.exe的文件, ...

  4. OpenCV学习2-----使用inpaint函数进行图像修复

    安装opencv时,在opencv的安装路径下, sources\samples\cpp\  路径里面提供了好多经典的例子,很值得学习. 这次的例子是利用inpaint函数进行图像修复. CV_EXP ...

  5. OpenCV学习(35) OpenCV中的PCA算法

    PCA算法的基本原理可以参考:http://www.cnblogs.com/mikewolf2002/p/3429711.html     对一副宽p.高q的二维灰度图,要完整表示该图像,需要m = ...

  6. 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起

    话说,平凡之处显真格,这一点也没错!  比如,对旋转图像进行双线性插值,很简单吧?  可,对我,折腾了大半天,也没有达到预期效果!  尤其是三个误区让我抓瞎好久: 1,坐标旋转公式.   这东西,要用 ...

  7. opencv学习笔记(03)——遍历图像(迭代器法)

    #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include & ...

  8. opencv学习笔记(01)——操作图像的像素

    #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <ope ...

  9. opencv学习笔记(02)——遍历图像(指针法)

    #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <ope ...

随机推荐

  1. HTTP.Socket.TCP详解

    这会没事,整理了一下HTTP,socket,TCP之间的关系与区别,我们在面试的时候应该会经常问到这方面的东西,那么什么是HTTP呢? HTTP属于老话题了,在项目中我们经常需要往服务端发POST或者 ...

  2. A - Superset CodeForces - 97B(人生第一个分治法,感觉,像二分啊。。)

    /* 分治法,第一次做不是很懂,借鉴了神犇代码,但实操之后感觉像二分,,可能做得少了或者就是.... */ 题目大意: 一个集合里有若干点,要求你添加某些点后保证这个集合里的任意两点满足以下三个条件中 ...

  3. linux/mac下命令行rm回收站--rmtrash

    Linux.mac的命令行下没有回收站功能,很多时候手一抖就把重要文件给 rm -fr * 了,虽然linux下有可能通过lost +found/debugfs找回,但难度也比较大,不能保证一定能够找 ...

  4. 【知了堂学习笔记】java 方法重载与重写的归纳

    方法重载:Java的方法重载,就是在类中可以创建多个方法,它们可以有相同的名字,但必须具有不同的参数,即或者是参数的个数不同,或者是参数的类型不同.调用方法时通过传递给它们的不同个数和类型的参数来决定 ...

  5. 深度学习基础系列(四)| 理解softmax函数

    深度学习最终目的表现为解决分类或回归问题.在现实应用中,输出层我们大多采用softmax或sigmoid函数来输出分类概率值,其中二元分类可以应用sigmoid函数. 而在多元分类的问题中,我们默认采 ...

  6. ARM Linux 驱动Input子系统之按键驱动测试

    上一篇已经谈过,在现内核的中引入设备树之后对于内核驱动的编写,主要集中在硬件接口的配置上了即xxxx.dts文件的编写. 在自己的开发板上移植按键驱动: 1.根据开发板的原理图 确定按键的硬件接口为: ...

  7. shell 变量中间有空格 如何传入整个变量

    parmfile='abc  123' RunProgram programname "${parmfile}"  -->传入abc 123 RunProgram progr ...

  8. Ninject

    一.为什么要使用依赖注入框架 依赖注入框架也叫IoC容器.它的作用使类与类之间解耦 我们看看为什么要用依赖注入框架,举个几个梨子: 1,高度耦合的类 有一个Order类,Order类是用于订单操作的, ...

  9. SDOI 2017 Round2 滚粗了

    没进省队qwq 技不如人,甘拜下风

  10. NOIP差不多可以退役的退役记录

    好吧,现在既然苟回来了,就来回忆一下我NOIP2017的黑历史吧. Day-1: 原本以为是很平静的一天,上午学考机房强行断网,原因是腾出网速给对面学考的同学们查资料用.好吧没法刷题我只能选择颓废…… ...