LBP特征的简单实现以及LBP+SVM分类识别

很久没有写博客了,最近实现了一下LBP特征,写一篇相关的博客,代码写的比较挫,组织比较混乱(-_-),就将就看看吧,发现有什么错误的地方,欢迎大家批评指正。
注:本文使用VS2013+OpenCV3.0

LBP原理

LBP的原理比较简单,网上有很多很不错的文章,这里给出几篇我认为不错的文章
目标检测的图像特征提取之(二)LBP特征

LBP local binary patterns 人脸特征提取方法

LBP特征学习及实现

如果大家想深入了解LBP,可以读一读论文
[2002 PAMI] Multiresolution gray-scale and rotation
[2006 PAMI] Face Description with Local Binary Patterns Application to Face Recognition

对LBP特征向量进行提取的步骤

(1)首先将检测窗口划分为16×16的小区域(cell);
(2)计算每个像素点的旋转不变等价模式的LBP特征值,得到对应的十进制数,也就是模式。
(3)然后计算每个cell的LBP特征值直方图,然后对该直方图进行归一化处理(每个模块中,对于每个bin,h[i]/=sum,sum就是一副图像中所有等价类的个数)。
(4)最后将得到的每个cell的统计直方图进行连接成为一个特征向量,也就是整幅图的LBP纹理特征向量;
然后便可利用SVM或者其他机器学习算法进行识别了。

LBP的简单实现

下面就是最近实现的LBP

LBP.h

#ifndef __LBP_H__
#define __LBP_H__
#include "opencv2/opencv.hpp"
#include<vector>
using namespace std;
using namespace cv;

// LBP的简单实现
class LBP
{
public:
    // 灰度不变LBP(256种模式)
    void LBP_256(const Mat &srcImage,Mat &LBPImage);// 计算256维LBP特征图


    // 灰度不变+等价模式LBP(58种模式),没有考虑旋转不变
    void ComputerLBPFeatureVector(const Mat &srcImage, Size cellSize, vector<float> &featureVector);// 计算LBP特征
    void UniformLBP(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell); // 计算每个cell的特征图和特征向量
    void BuildUniformPatternTable(int *table);
    int GetHopCount(int i);// 获取i中0,1的跳变次数
    void UniformLBP_LUT(const Mat &srcImage, Mat &LBPImage);// 使用查找表,获取LBP特征图


    // 灰度不变+旋转不变+等价模式LBP(9种模式)
    void ComputerLBPFeatureVector_Rotation(const Mat &srcImage, Size cellSize, vector<float> &featureVector);// 计算LBP特征
    void RotationUniformLBP(const Mat &srcImage, Mat &LBPImage,vector<float> &LBPFeatureVector, int indexOfCell);// 计算特征图
    int ComputerValue9(int value58);// 计算9种等价模式
    uchar GetMinBinary(uchar *binary); 
    void Test();// 测试灰度不变+旋转不变+等价模式LBP
};

#endif

LBP.cpp

#include"LBP.h"

//srcImage:灰度图
//LBPImage:LBP图
void LBP::LBP_256(const Mat &srcImage, Mat &LBPImage)
{
    // 参数检查,内存分配
    int heightOfLBP = srcImage.rows;
    int widthOfLBP = srcImage.cols;
    int depth = srcImage.depth();
    int channels = srcImage.channels();
    CV_Assert(depth == CV_8U && channels==1);
    LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);

    // 计算LBP特征图
    // 扩充原图像边界,便于边界处理
    Mat extendedImage;
    copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

    // 计算LBP
    int heightOfExtendedImage = extendedImage.rows;
    int widthOfExtendedImage = extendedImage.cols;
    uchar *rowPointer_ToExtended= extendedImage.data+widthOfExtendedImage+1;
    uchar *rowPointer_ToLBP = LBPImage.data;
    int LBPValue=0;// 每个像素的LBP值
    for (int y = 1; y <= heightOfExtendedImage - 2; ++y)
    {
        // 列
        uchar *colPointer_ToExtended = rowPointer_ToExtended;
        uchar *colPointer_ToLBP = rowPointer_ToLBP;
        for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
        {
            LBPValue = 0;
            if (colPointer_ToExtended[0 - widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
                LBPValue += 128;
            if (colPointer_ToExtended[0 - widthOfExtendedImage ] >= colPointer_ToExtended[0])
                LBPValue += 64;
            if (colPointer_ToExtended[0 - widthOfExtendedImage +1] >= colPointer_ToExtended[0])
                LBPValue += 32;
            if (colPointer_ToExtended[0 +1] >= colPointer_ToExtended[0])
                LBPValue += 16;
            if (colPointer_ToExtended[0 + widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
                LBPValue += 8;
            if (colPointer_ToExtended[0 + widthOfExtendedImage] >= colPointer_ToExtended[0])
                LBPValue += 4;
            if (colPointer_ToExtended[0 + widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
                LBPValue += 2;
            if (colPointer_ToExtended[0 - 1] >= colPointer_ToExtended[0])
                LBPValue += 1;

            // LBP图
            colPointer_ToLBP[0] = LBPValue;

            // 下一个像素
            colPointer_ToExtended++;
            colPointer_ToLBP++;

        }

        // 下一行
        rowPointer_ToExtended += widthOfExtendedImage;
        rowPointer_ToLBP += widthOfLBP;

    }// end of y


}

// 使用查找表,获取LBP特征图,注意,为了方便表示特征图,58种等价模式表示为1~58,第59种混合模式表示为0
void LBP::UniformLBP_LUT(const Mat &srcImage, Mat &LBPImage)
{
    // 参数检查,内存分配
    int heightOfLBP = srcImage.rows;
    int widthOfLBP = srcImage.cols;
    int depth = srcImage.depth();
    int channels = srcImage.channels();
    CV_Assert(depth == CV_8U && channels == 1);
    LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);

    // 计算LBP图
    // 扩充原图像边界,便于边界处理
    Mat extendedImage;
    copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

    // LUT
    static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 
        0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
        , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
        , 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58};

    // 计算LBP
    int heightOfExtendedImage = extendedImage.rows;
    int widthOfExtendedImage = extendedImage.cols;
    uchar *rowPointer_ToExtended = extendedImage.data+widthOfExtendedImage+1;
    uchar *rowPointer_ToLBP = LBPImage.data;
    int LBPValue = 0;// 每个像素的LBP值
    for (int y = 1; y <= heightOfExtendedImage - 2; ++y)
    {
        // 列
        uchar *colPointer_ToExtended = rowPointer_ToExtended;
        uchar *colPointer_ToLBP = rowPointer_ToLBP;
        for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
        {
            // 计算LBP
            LBPValue = 0;
            if (colPointer_ToExtended[0 - widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
                LBPValue += 128;
            if (colPointer_ToExtended[0 - widthOfExtendedImage] >= colPointer_ToExtended[0])
                LBPValue += 64;
            if (colPointer_ToExtended[0 - widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
                LBPValue += 32;
            if (colPointer_ToExtended[0 + 1] >= colPointer_ToExtended[0])
                LBPValue += 16;
            if (colPointer_ToExtended[0 + widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
                LBPValue += 8;
            if (colPointer_ToExtended[0 + widthOfExtendedImage] >= colPointer_ToExtended[0])
                LBPValue += 4;
            if (colPointer_ToExtended[0 + widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
                LBPValue += 2;
            if (colPointer_ToExtended[0 - 1] >= colPointer_ToExtended[0])
                LBPValue += 1;

            colPointer_ToLBP[0] = table[LBPValue];

            // 下一个像素
            colPointer_ToExtended++;
            colPointer_ToLBP++;

        }

        // 下一行
        rowPointer_ToExtended += widthOfExtendedImage;
        rowPointer_ToLBP += widthOfLBP;

    }// end of y
}

//获取i中0,1的跳变次数
int LBP::GetHopCount(int i)
{
    // 转换为二进制
    int a[8] = { 0 };
    int k = 7;
    while (i)
    {
        // 除2取余
        a[k] = i % 2;
        i/=2;
        --k;
    }

    // 计算跳变次数
    int count = 0;
    for (int k = 0; k<8; ++k)
    {
        // 注意,是循环二进制
        if (a[k] != a[k + 1 == 8 ? 0 : k + 1])
        {
            ++count;
        }
    }
    return count;

}

// 建立等价模式表
// 这里为了便于建立LBP特征图,58种等价模式序号从1开始:1~58,第59类混合模式映射为0
void LBP::BuildUniformPatternTable(int *table)
{
    if (table == NULL)
    {
        return;
    }
    memset(table, 0, 256*sizeof(int));
    uchar temp = 1;
    for (int i = 0; i<256; ++i)
    {
        if (GetHopCount(i) <= 2)
        {
            table[i] = temp;
            temp++;
        }
    }

    // 输出表格
    //for (int i = 0; i < 256;++i)
        //printf("%d,",table[i]);

}

// 2015-9-25 20:31:18,by QQ
// 获取一幅图像LBP特征
// cellSize:每个cell的大小,如16*16
void LBP::ComputerLBPFeatureVector(const Mat &srcImage, Size cellSize, vector<float> &featureVector)
{
    // 设置每个窗口大小
    int widthOfCell = cellSize.width;
    int heightOfCell = cellSize.height;
    int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的个数
    int numberOfCell_Y = srcImage.rows / heightOfCell;

    // 特征向量的个数
    int numberOfDimension = 58 * numberOfCell_X*numberOfCell_Y;
    featureVector.resize(numberOfDimension,0);

    //每个cell
    Mat ROI,cell,LBPImage;
    IplImage iplImage;

    // 计算LBP特征向量
    for (int y = 0; y <= numberOfCell_Y - 1; ++y)
    {
        for (int x = 0; x <= numberOfCell_X - 1; ++x)
        {
            // 每个cell
            ROI = srcImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));

            // 拷贝每个cell
            iplImage = ROI;
            cell = cvarrToMat(&iplImage, true);

            // 计算
            UniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
        }
    }
}

// Updated:2015-9-25 20:02:18,by QQ
// 计算每个cell的特征图和特征向量
// LBPImage:LBP特征图(这里为了便于建立LBP特征图,58种等价模式序号从1开始:1~58,第59类混合模式映射为0)
// LBPFeature:每幅图的LBP特征
// indexOfCell:cell索引
// 注:你可以将第59类混合模式映射为任意数值,但是设置为0能够更好突出特征,因为要突出等价模式特征,所以非等价模式设置为0比较好
void LBP::UniformLBP(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell)
{
    // 参数检查,内存分配
    int heightOfLBP = srcImage.rows;
    int widthOfLBP = srcImage.cols;
    int depth = srcImage.depth();
    int channels = srcImage.channels();
    CV_Assert(depth == CV_8U && channels == 1);
    LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);

    // 扩充原图像边界,便于边界处理
    Mat extendedImage;
    copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

    // 构建LBP 等价模式查找表
    int table[256];
    BuildUniformPatternTable(table);

    // 计算LBP特征图
    int heightOfExtendedImage = extendedImage.rows;
    int widthOfExtendedImage = extendedImage.cols;
    uchar *rowPointer_ToExtended = extendedImage.data + widthOfExtendedImage + 1;
    uchar *rowPointer_ToLBP = LBPImage.data;
    int LBPValue = 0;// 每个像素的LBP值
    for (int y = 1; y <= heightOfExtendedImage - 2; ++y)
    {
        // 列
        uchar *colPointer_ToExtended = rowPointer_ToExtended;
        uchar *colPointer_ToLBP = rowPointer_ToLBP;
        for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
        {
            // 计算一般的256维LBP值
            LBPValue = 0;
            if (colPointer_ToExtended[0 - widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
                LBPValue += 128;
            if (colPointer_ToExtended[0 - widthOfExtendedImage] >=colPointer_ToExtended[0])
                LBPValue += 64;
            if (colPointer_ToExtended[0 - widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
                LBPValue += 32;
            if (colPointer_ToExtended[0 + 1] >= colPointer_ToExtended[0])
                LBPValue += 16;
            if (colPointer_ToExtended[0 + widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
                LBPValue += 8;
            if (colPointer_ToExtended[0 + widthOfExtendedImage] >= colPointer_ToExtended[0])
                LBPValue += 4;
            if (colPointer_ToExtended[0 + widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
                LBPValue += 2;
            if (colPointer_ToExtended[0 - 1] >= colPointer_ToExtended[0])
                LBPValue += 1;

            // 计算Uniform模式(等价模式)LBP值
            colPointer_ToLBP[0] = table[LBPValue];

            // 下一个像素
            ++colPointer_ToExtended;
            ++colPointer_ToLBP;

        }// end of x

        // 下一行
        rowPointer_ToExtended += widthOfExtendedImage;
        rowPointer_ToLBP += widthOfLBP;

    }// end of y

    // 计算cell的LBP特征
    uchar *imageDataOfLBP = LBPImage.data;
    int numberOfPixel = LBPImage.rows*LBPImage.cols;
    int index = 58 * indexOfCell;// 该cell的特征向量起始位置
    int sum = 0;
    for (int i = 0; i <= numberOfPixel - 1; ++i)
    {
        // 只统计等价模式
        if (imageDataOfLBP[i] != 0)
        {
            // 等价模式转化为0~57,所以是imageDataOfLBP[i] - 1
            ++LBPFeatureVector[index + imageDataOfLBP[i] - 1];
            ++sum;
        }
    }

    // 直方图归一化
    for (int i = 0; i <= 57; ++i)
    {
        LBPFeatureVector[index + i] /= sum;
    }
}

// 2015-10-25 18:06:30,by QQ
// 计算9种等价模式
int LBP::ComputerValue9(int value58)
{
    int value9 = 0;
    switch (value58)
    {
    case 1:
        value9 = 1;
        break;
    case 2:
        value9 = 2;
        break;
    case 4:
        value9 = 3;
        break;
    case 7:
        value9 = 4;
        break;
    case 11:
        value9 = 5;
        break;
    case 16:
        value9 = 6;
        break;
    case 22:
        value9 = 7;
        break;
    case 29:
        value9 = 8;
        break;
    case 58:
        value9 = 9;
        break;
    }

    return value9;

}
// 2015-9-24,by QQ
// 获取循环二进制的最小二进制模式
uchar LBP::GetMinBinary(uchar *binary)
{
    // 计算8个二进制
    uchar LBPValue[8] = { 0 };
    for (int i = 0; i <= 7; ++i)
    {
        LBPValue[0] += binary[i] <<(7-i);
        LBPValue[1] += binary[(i+7)%8] << (7 - i);
        LBPValue[2] += binary[(i + 6) % 8] << (7 - i);
        LBPValue[3] += binary[(i + 5) % 8] << (7 - i);
        LBPValue[4] += binary[(i + 4) % 8] << (7 - i);
        LBPValue[5] += binary[(i + 3) % 8] << (7 - i);
        LBPValue[6] += binary[(i + 2) % 8] << (7 - i);
        LBPValue[7] += binary[(i + 1) % 8] << (7 - i);
    }

    // 选择最小的
    uchar minValue = LBPValue[0];
    for (int i = 1; i <= 7; ++i)
    {
        if (LBPValue[i] < minValue)
        {
            minValue = LBPValue[i];
        }
    }

    return minValue;

}
// 2015-10-21 12:03:19,by QQ
// cellSize:每个cell的大小,如16*16
void LBP::ComputerLBPFeatureVector_Rotation(const Mat &srcImage, Size cellSize, vector<float> &featureVector)
{
    // 设置每个窗口大小
    int widthOfCell = cellSize.width;
    int heightOfCell = cellSize.height;
    int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的个数
    int numberOfCell_Y = srcImage.rows / heightOfCell;

    // 特征向量的个数
    int numberOfDimension = 9 * numberOfCell_X*numberOfCell_Y;
    featureVector.resize(numberOfDimension, 0);

    //每个cell
    Mat ROI, cell, LBPImage;
    IplImage iplImage;

    // 计算LBP特征向量
    for (int y = 0; y <= numberOfCell_Y - 1; ++y)
    {
        for (int x = 0; x <= numberOfCell_X - 1; ++x)
        {
            // 每个cell
            ROI = srcImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));

            // 拷贝每个cell
            iplImage = ROI;
            cell = cvarrToMat(&iplImage, true);

            // 计算
            RotationUniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
        }
    }
}
// 2015-9-24,by QQ
// 计算旋转不变等价模式的LBP值
void LBP::RotationUniformLBP(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell)
{
    // 参数检查,内存分配
    CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
    LBPImage.create(srcImage.size(), srcImage.depth());

    // 扩充图像,处理边界情况
    Mat extendedImage;
    copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

    // 构建LBP 等价模式查找表
    int table[256];
    BuildUniformPatternTable(table);

    uchar binary[8] = { 0 };// 记录每个像素的LBP值
    int heigthOfExtendedImage = extendedImage.rows;
    int widthOfExtendedImage = extendedImage.cols;
    int widthOfLBPImage = LBPImage.cols;
    // 行
    uchar *rowPointer_ToExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
    uchar *rowPointer_ToLBPImage = LBPImage.data;
    for (int y = 1; y <= heigthOfExtendedImage - 2; ++y)
    {
        // 列
        uchar *colPointer_ToExtendedImage = rowPointer_ToExtendedImage;
        uchar *colPointer_ToLBPImage = rowPointer_ToLBPImage;
        for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
        {
            // 计算旋转不变LBP(最小的二进制模式)
            binary[0] = colPointer_ToExtendedImage[0 - widthOfExtendedImage - 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[1] = colPointer_ToExtendedImage[0 - widthOfExtendedImage] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[2] = colPointer_ToExtendedImage[0 - widthOfExtendedImage + 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[3] = colPointer_ToExtendedImage[0 + 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[4] = colPointer_ToExtendedImage[0 + widthOfExtendedImage + 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[5] = colPointer_ToExtendedImage[0 + widthOfExtendedImage] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[6] = colPointer_ToExtendedImage[0 + widthOfExtendedImage - 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            binary[7] = colPointer_ToExtendedImage[0 - 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
            int minValue = GetMinBinary(binary);

            // 计算58种等价模式LBP
            int value58=table[minValue];

            // 计算9种等价模式
            colPointer_ToLBPImage[0] = ComputerValue9(value58);

            // 下一个像素
            ++colPointer_ToExtendedImage;
            ++colPointer_ToLBPImage;
        }

        // 下一行
        rowPointer_ToExtendedImage += widthOfExtendedImage;
        rowPointer_ToLBPImage += widthOfLBPImage;
    }

    // 计算cell的LBP特征
    uchar *imageDataOfLBP = LBPImage.data;
    int numberOfPixel = LBPImage.rows*LBPImage.cols;
    int index = 9 * indexOfCell;// 该cell的特征向量起始位置
    int sum = 0;
    for (int i = 0; i <= numberOfPixel - 1; ++i)
    {
        // 只统计等价模式
        if (imageDataOfLBP[i] != 0)
        {
            // 等价模式转化为0~8,所以是imageDataOfLBP[i] - 1
            ++LBPFeatureVector[index + imageDataOfLBP[i] - 1];
            ++sum;
        }
    }

    // 直方图归一化
    for (int i = 0; i <= 8; ++i)
    {
        LBPFeatureVector[index + i] /= sum;
    }
}

// 2015-10-21 11:58:43,by QQ
// 验证灰度不变+旋转不变+等价模式种类
void LBP::Test()
{
    uchar LBPValue[8] = { 0 };
    int k = 7, j;
    int temp;
    LBP lbp;
    int number[256] = { 0 };
    int numberOfMinBinary = 0;

    // 旋转不变
    for (int i = 0; i < 256; ++i)
    {
        k = 7;
        temp = i;
        while (k >= 0)
        {
            LBPValue[k] = temp & 1;
            temp = temp >> 1;
            --k;
        }
        int minBinary = lbp.GetMinBinary(LBPValue);

        // 查找有无重复的
        for (j = 0; j <= numberOfMinBinary - 1; ++j)
        {
            if (number[j] == minBinary)
                break;
        }
        if (j == numberOfMinBinary)
        {
            number[numberOfMinBinary++] = minBinary;
        }
    }
    cout << "旋转不变一共有:"<<numberOfMinBinary <<"种"<< endl;

    // LUT
    static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
        0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
        , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
        , 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };
    cout << "旋转不变+等价模式:" << endl;
    for (int i = 0; i <= numberOfMinBinary - 1; ++i)
    {
        cout << number[i] << " " << table[number[i]] << endl;
    }

}

LBP+SVM测试程序

LBP等价模式+SVM

#define CELLSIZE_LBP 16 // LBP的窗口大小,4,8,16
#define PATH "D:/Image/Texture/Example_01/"

void LBP_SVM()
{
    // 读入训练样本路径和类别
    vector<string> imagePaths;
    vector<int> imageClass;
    string buffer;
    int numberOfLine=0;
    ifstream file(string(PATH) + "TrainData.txt", ios::in);
    while (!file.eof())
    {
        if (getline(file, buffer))
        {
            ++numberOfLine;
            if (numberOfLine % 2 == 0)//读到样本类别
            {
                imageClass.push_back(atoi(buffer.c_str()));
            }
            else
            {
                imagePaths.push_back(buffer);

            }

        }

    }

    file.close();

    // 计算样本LBP特征向量矩阵和类别矩阵
    int lengthOfFeatureVector = (32 / CELLSIZE_LBP)*(64 / CELLSIZE_LBP) * 58; // 特征向量的维数
    Mat featureVectorOfSample(imagePaths.size(), lengthOfFeatureVector, CV_32FC1); // 样本的特征向量矩阵
    Mat classOfSample(imagePaths.size(), 1, CV_32SC1);
    vector<string>::size_type numberOfSample = imagePaths.size();
    Mat srcImage;
    LBP lbp;
    vector<float> featureVector;
    for (vector<string>::size_type i = 0; i <= numberOfSample - 1; ++i)
    {
        // 读入图片
        srcImage = imread(imagePaths[i], -1);

        // 计算样本LBP特征向量
        lbp.ComputerLBPFeatureVector(srcImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);

        for (vector<float>::size_type j = 0; j <= lengthOfFeatureVector - 1; ++j)
        {
            featureVectorOfSample.at<float>(i, j) = featureVector[j];
        }

        classOfSample.at<int>(i, 0) = imageClass[i];
    }

    // 使用SVM分类器训练
    // 参数设置
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));

    svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);
    svm->save(string(PATH)+"Classifier.xml");

    // 使用训练好的分类器进行识别
    vector<string> testImagePath;
    ifstream testFile(string(PATH)+"TestData.txt", ios::in);// 注意要去掉最后一行的换行,否则最后一幅图片读出来就是空的
    while (!testFile.eof())
    {
        getline(testFile, buffer);
        testImagePath.push_back(buffer);
    }

    // 识别
    Mat testImage;
    vector<string>::size_type numberOfTestImage = testImagePath.size();
    vector<float> featureVectorOfTestImage;
    Mat _featureVectorOfTestImage(1, lengthOfFeatureVector, CV_32FC1);
    char line[512];
    ofstream resultOfPrediction(string(PATH)+"PredictResult.txt", ios::out);

    // 注意将循环体内的耗时变量和操作提取到循环体内
    for (vector<string>::size_type i = 0; i <= numberOfTestImage-1; ++i)
    {
        testImage = imread(testImagePath[i], -1);

        // 计算LBP特征向量
        lbp.ComputerLBPFeatureVector(testImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVectorOfTestImage);
        for (vector<float>::size_type j = 0; j <= featureVectorOfTestImage.size() - 1; ++j)
        {
            _featureVectorOfTestImage.at<float>(0, j) = featureVectorOfTestImage[j];
        }

        int predict = svm->predict(_featureVectorOfTestImage);
        sprintf(line, "%s %d\n", testImagePath[i].c_str(), predict);
        resultOfPrediction << line;
    }
    resultOfPrediction.close();
}

LBP旋转不变,等价模式+SVM

void LBP_SVM_Rotation()
{
    // 读入训练样本路径和类别
    vector<string> imagePaths;
    vector<int> imageClass;
    string buffer;
    int numberOfLine = 0;
    ifstream file(string(PATH) + "TrainData.txt", ios::in);
    while (!file.eof())
    {
        if (getline(file, buffer))
        {
            ++numberOfLine;
            if (numberOfLine % 2 == 0)//读到样本类别
            {
                imageClass.push_back(atoi(buffer.c_str()));
            }
            else
            {
                imagePaths.push_back(buffer);

            }

        }

    }

    file.close();

    // 计算样本LBP特征向量矩阵和类别矩阵
    int lengthOfFeatureVector = (32 / CELLSIZE_LBP)*(64 / CELLSIZE_LBP) * 9; // 特征向量的维数
    Mat featureVectorOfSample(imagePaths.size(), lengthOfFeatureVector, CV_32FC1); // 样本的特征向量矩阵
    Mat classOfSample(imagePaths.size(), 1, CV_32SC1);
    vector<string>::size_type numberOfSample = imagePaths.size();
    Mat srcImage;
    LBP lbp;
    vector<float> featureVector;
    for (vector<string>::size_type i = 0; i <= numberOfSample - 1; ++i)
    {
        // 读入图片
        srcImage = imread(imagePaths[i], -1);

        // 计算样本LBP特征向量
        lbp.ComputerLBPFeatureVector_Rotation(srcImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);

        for (vector<float>::size_type j = 0; j <= lengthOfFeatureVector - 1; ++j)
        {
            featureVectorOfSample.at<float>(i, j) = featureVector[j];
        }

        classOfSample.at<int>(i, 0) = imageClass[i];
    }

    // 使用SVM分类器训练
    // 参数设置
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));

    svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);
    svm->save(string(PATH) + "Classifier.xml");

    // 使用训练好的分类器进行识别
    vector<string> testImagePath;
    ifstream testFile(string(PATH) + "TestData.txt", ios::in);// 注意要去掉最后一行的换行,否则最后一幅图片读出来就是空的
    while (!testFile.eof())
    {
        getline(testFile, buffer);
        testImagePath.push_back(buffer);
    }

    // 识别
    Mat testImage;
    vector<string>::size_type numberOfTestImage = testImagePath.size();
    vector<float> featureVectorOfTestImage;
    Mat _featureVectorOfTestImage(1, lengthOfFeatureVector, CV_32FC1);
    char line[512];
    ofstream resultOfPrediction(string(PATH) + "PredictResult.txt", ios::out);

    // 注意将循环体内的耗时变量和操作提取到循环体内
    for (vector<string>::size_type i = 0; i <= numberOfTestImage - 1; ++i)
    {
        testImage = imread(testImagePath[i], -1);

        // 计算LBP特征向量
        lbp.ComputerLBPFeatureVector_Rotation(testImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVectorOfTestImage);
        for (vector<float>::size_type j = 0; j <= featureVectorOfTestImage.size() - 1; ++j)
        {
            _featureVectorOfTestImage.at<float>(0, j) = featureVectorOfTestImage[j];
        }

        int predict = svm->predict(_featureVectorOfTestImage);
        sprintf(line, "%s %d\n", testImagePath[i].c_str(), predict);
        resultOfPrediction << line;
    }
    resultOfPrediction.close();
}

实验结果与分析

实验中采用了2282个32X64的正样本,2278个32X64的负样本,540个测试样本
其中,正样本从下图中截取而来
LBP特征的简单实现以及LBP+SVM分类识别_第1张图片

负样本从下图中截取而来
LBP特征的简单实现以及LBP+SVM分类识别_第2张图片

正样本是草丛,负样本是路面,分别是我中午吃完饭散步的时候,随手拍的(-_-)

实验数据可以在这里下载

下面是我实验的正确率

cell窗口大小 4 X 4 8 X 8 16 X 16
等价模式 79.6% 92.4% 96.7%
旋转不变,等价模式 88.3% 95.9% 97.2%

可以看出,旋转不变+等价模式效果更好,而且16X16的效果更好,32X32的没有测试,有兴趣的读者可以试一下,看看效果怎么样。

好了,简单的LBP实现就是这样,有什么问题的,可以留言一起讨论。

非常感谢您的阅读,如果您觉得这篇文章对您有帮助,请您支付宝扫码支持作者,多谢啦 :-)

LBP特征的简单实现以及LBP+SVM分类识别_第3张图片

你可能感兴趣的:(LBP-SVM)