图像的浅层特征与深度特征_图像LBP特征
文中主要介绍了LBP特徵的基本概念及其发展历程。文中提到该方法最初於1996年由Ojala等提出,在随后几年中对该算法进行了改进工作。具体而言,在2002年提出了两种改进型LBP编码方案:均衡模式LBP编码和旋转不变性LBP编码。总体而言该方法较为简洁明了。然而尽管通过LBP编码处理后对图像进行分类的效果仍然较为理想。这种技术不仅可以用于人脸分类问题中,还可以扩展至其他基于纹理特徵的对象分类场景。通常采用以下步骤进行处理:首先对编码后的图像进行分块;其次计算每一块的直方图;最后将所有块对应的直方图连接成一个统一的一维向量表示。这种统一的一维向量即可用作图像的关键表徵工具,在实际应用中可结合SVM或KNN等监督学习算法实现分类任务
LBP编码方案相对简洁明了,在图像处理领域具有一定的代表性。

图1 LBP算法编码示意
原始的LBP编码缺乏旋转不变性,并且容易受到噪声干扰。因此研究者对其进行了多项改进工作。在2002年,作者提出了改进方案:将传统的3×3正方形区域转换为圆形区域,并通过比较圆形边界上各点与其中心点的亮度值来进行特征提取。此外,作者进一步分析了二进制模式中从1变为0以及从0变为1的变化次数,并发现图像上的特征通常不会出现两次变化。为此,作者提出了均衡模式LBP编码方法,在这种编码方式下只有58种不同的编码结果。其他出现两次变化的情况则被归类为第59种码字类型。为了进一步增强编码的安全性,在此基础上还设计了一种旋转不变性编码方式:通过对提取到的二进制模式进行循环移位处理,并取最大值作为最终特征码字。图2展示了基于圆形LBP的示意图,图3则展示了该旋转不变性编码的结果展示图,在这种情况下只有36种不同的码字类型被采用

图2 圆形LBP邻域编码

图3 旋转不变性36种编码结果
LBP作为一种图像处理技术,在反映局部纹理特征方面具有显著作用。自其提出以来,在Google Scholar上的引用次数已经超过了一万次。这表明这种改进方法确实在一定程度上提高了准确性。改进这一类的算法也较为普遍。Spring出版了多本详尽阐述Local Binary Patterns领域的书籍《Computer Vision Using Local Binary Patterns》和《Local Binary Patterns: New Variants and Applications》,这些著作进一步凸显了其在相关领域的重要地位
为何LBP算法表现出良好的效果?从其编码机制的角度分析可以看出该算法同样依赖于图 像的空间变化特性。就一般情况而言这反映了图像是如何在不同区域展现细节特征以及这两者之 间的关系。进一步地这一特性使得它对光照条件的变化较为不敏感同时这种特性使得它对光照条 件的变化较为不敏感不同类型的物体或场景之间在这一特性上的差异相对较小这使得基于这些 特征信息的方法具备了良好的分类能力实际上在许多与数字视觉相关的领域中图像是一个关键 的研究对象其处理过程中通常会涉及到对其Gradient属性的研究和应用这种方法本质 上是通过分析局部区域内的Gradient分布情况来提取特征信息从而达到识别目标的目的因此基 于Gradient描述算子判别性比较高利用 Gradient关系进行分类效果普遍较好
该算法相对简单。然而,在LBP的基础上进行改进仍存在较大的改进潜力。对于希望在此领域发表论文的学者而言,探索改进方向仍然具有可行性。该算法的基本原理较为基础,在此将通过具体的代码实现来阐述其工作原理,并提供参考代码以便读者借鉴。此外,在OpenCV中提供了相应的实现代码。
#include#include #include#include#include#includeusing namespace std;using namespace cv;///原始LBP编码void originLBP(Mat src,Mat dst){ for(int i=1;i-1;i++) { for(int j=1;j-1;j++) { unsigned char center = src.at(i,j); unsigned char lbpCode = 0; lbpCode |= (src.at(i-1,j-1) > center) << 7; lbpCode |= (src.at(i-1,j ) > center) << 6; lbpCode |= (src.at(i-1,j+1) > center) << 5; lbpCode |= (src.at(i ,j+1) > center) << 4; lbpCode |= (src.at(i+1,j+1) > center) << 3; lbpCode |= (src.at(i+1,j ) > center) << 2; lbpCode |= (src.at(i+1,j-1) > center) << 1; lbpCode |= (src.at(i ,j-1) > center) << 0; dst.at(i-1,j-1) = lbpCode; } }}template <typename _Tp> staticinline void elbp_(InputArray _src, OutputArray _dst, int radius, int neighbors) { //get matrices Mat src = _src.getMat(); // allocate memory for result _dst.create(src.rows-2*radius, src.cols-2*radius, CV_32SC1); Mat dst = _dst.getMat(); // zero dst.setTo(0); for(int n=0; n { // sample points float x = static_cast<float>(radius * cos(2.0*CV_PI*n/static_cast<float>(neighbors))); float y = static_cast<float>(-radius * sin(2.0*CV_PI*n/static_cast<float>(neighbors))); // relative indices 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)); // fractional part float ty = y - fy; float tx = x - fx; // set interpolation weights float w1 = (1 - tx) * (1 - ty); float w2 = tx * (1 - ty); float w3 = (1 - tx) * ty; float w4 = tx * ty; // iterate through your data for(int i=radius; i < src.rows-radius;i++) { for(int j=radius;j < src.cols-radius;j++) { // calculate interpolated value float t = static_cast<float>(w1*src.at<_tp>(i+fy,j+fx) + w2*src.at<_tp>(i+fy,j+cx) + w3*src.at<_tp>(i+cy,j+fx) + w4*src.at<_tp>(i+cy,j+cx)); // floating point precision, so check some machine-dependent epsilon dst.at<int>(i-radius,j-radius) += ((t > src.at<_tp>(i,j)) || (std::abs(t-src.at<_tp>(i,j)) < std::numeric_limits<float>::epsilon())) << n; } } }}static void elbp(InputArray src, OutputArray dst, int radius, int neighbors){ int type = src.type(); switch (type) { case CV_8SC1: elbp_<char>(src,dst, radius, neighbors); break; case CV_8UC1: elbp_<unsigned char>(src, dst, radius, neighbors); break; case CV_16SC1: elbp_<short>(src,dst, radius, neighbors); break; case CV_16UC1: elbp_<unsigned short>(src,dst, radius, neighbors); break; case CV_32SC1: elbp_<int>(src,dst, radius, neighbors); break; case CV_32FC1: elbp_<float>(src,dst, radius, neighbors); break; case CV_64FC1: elbp_<double>(src,dst, radius, neighbors); break; default: string error_msg = format("Using Original Local Binary Patterns for feature extraction only works on single-channel images (given %d). Please pass the image data as a grayscale image!", type); CV_Error(Error::StsNotImplemented, error_msg); break; }}//圆形LBPMat circularLBP(InputArray src, int radius, int neighbors) { Mat dst; elbp(src, dst, radius, neighbors); return dst;}//计算跳变次数int getHopTimes(int n){ int count = 0; bitset<8> binaryCode = n; for(int i=0;i<8;i++) { if(binaryCode[i] != binaryCode[(i+1)%8]) { count++; } } return count;}///均衡模式LBP编码Mat uniformPatternLBP(Mat src,int radius,int neighbors){ Mat dst; //建立映射表 uchar temp = 1; uchar table[256] = {0}; for(int i=0;i<256;i++) { if(getHopTimes(i)<3) { table[i] = temp; temp++; } } //调用圆形LBP编码 Mat CircularDst = circularLBP(src, radius, neighbors); //根据映射表重新确定dst的值 Mat dst_abs; convertScaleAbs(CircularDst,dst_abs);//使用线性变换转换输入数组元素成8位无符号整型 //根据映射表重新确定dst的值 int rows = dst_abs.rows; int cols = dst_abs.cols; for(int i = 0;i { uchar* data = dst_abs.ptr(i); for(int j = 0;j < cols;j++) { data[j] = table[data[j]]; } } convertScaleAbs(dst_abs,dst);//使用线性变换转换输入数组元素成8位无符号整型 return dst;}int* rotation_invariant_mapping(int range,int num_sp)//range=256,num_sp=8{ int newMax,rm,r; int tmpMap[256]; int Mapping[256]; newMax = 0; for (int i = 0 ; i < range ; i++) { rm = i; r = i; for (int j = 0 ; j < num_sp -1 ;j++) { //将r向左循环移动一位,当r超过num_sp位时,舍弃 r = r << 1; if (r > range -1) { r = r - (range -1); } //printf("%d,%d\n",r,rm); if (r < rm) { rm = r; } } if (tmpMap[rm] < 0) { tmpMap[rm] = newMax; newMax++; } Mapping[i] = tmpMap[rm]; } return Mapping;}Mat rotationInvariantOfMapLBP(Mat img,int radius,int neighbors){ uchar RITable[256]; int* test; test = rotation_invariant_mapping(256,8); for(int i = 0;i < 256;i++) { RITable[i] = test[i]; } Mat result; result.create(img.rows - 2*radius, img.cols -2*radius , img.type()); result.setTo(0); for(int i = radius; i { for(int j = radius;j { uchar center = img.at(i, j); uchar code = 0; code |= (img.at(i-radius, j-radius) >= center)<<7; code |= (img.at(i-radius, j) >= center)<<6; code |= (img.at(i-radius, j+radius) >= center)<<5; code |= (img.at(i, j+radius) >= center)<<4; code |= (img.at(i+radius, j+radius) >= center)<<3; code |= (img.at(i+radius, j) >= center)<<2; code |= (img.at(i+radius, j-radius) >= center)<<1; code |= (img.at(i, j-radius) >= center)<<0; result.at(i -radius, j -radius) = RITable
读取源图像到变量src。
初始化窗口名为"原图"并显示原图。
初始化目标图像矩阵oriDst并赋值与源图像相同尺寸。
调用originLBP函数计算原始LBP特征。
初始化目标图像矩阵circularDst并赋值与源图像相同尺寸。
调用circularLBP函数计算环状结构LBP特征。
将circularDst转换为绝对值并存储于origin_dst_abs中。
初始化目标图像矩阵UniformDst并赋值与源图像相同尺寸。
调用uniformPatternLBP函数计算均匀模式LBP特征。
将UniformDst转换为绝对值并存储于uniform_dst_abs中。
初始化目标图像矩阵rotationInvariantDst并赋值与源图像相同尺寸。
调用rotationInvariantOfMapLBP函数计算旋转不变性LBP特征。
将rotationInvariantDst转换为绝对值并存储于rotationInvariant_dst_abs中。
分别初始化窗口名为并显示各计算结果:original destination, origin_dst_abs, uniform_dst_abs, rotationInvariant_dst_abs.
AI写代码
下面是运行结果图。

图4 原图

图5 原始LBP编码

图6 圆形LBP编码

图7 均衡模式LBP编码

图8 旋转不变LBP编码
