#include<iostream>
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include <bitset>
//#include<opencv2\contrib\contrib.hpp>
using namespace std;
using namespace cv;
//原始LBP特征计算
template <typename _tp>
void getOriginLBPFeature(InputArray _src, OutputArray _dst)
{
Mat src = _src.getMat();
_dst.create(src.rows - 2, src.cols - 2, CV_8UC1);
Mat dst = _dst.getMat();
dst.setTo(0);
for (int i = 1; i<src.rows - 1; i++)
{
for (int j = 1; j<src.cols - 1; j++)
{
_tp center = src.at<_tp>(i, j);
unsigned char lbpCode = 0;
lbpCode |= (src.at<_tp>(i - 1, j - 1) > center) << 7;
lbpCode |= (src.at<_tp>(i - 1, j) > center) << 6;
lbpCode |= (src.at<_tp>(i - 1, j + 1) > center) << 5;
lbpCode |= (src.at<_tp>(i, j + 1) > center) << 4;
lbpCode |= (src.at<_tp>(i + 1, j + 1) > center) << 3;
lbpCode |= (src.at<_tp>(i + 1, j) > center) << 2;
lbpCode |= (src.at<_tp>(i + 1, j - 1) > center) << 1;
lbpCode |= (src.at<_tp>(i, j - 1) > center) << 0;
dst.at<uchar>(i - 1, j - 1) = lbpCode;
}
}
}
/*
//圆形LBP特征计算,这种方法适于理解,但在效率上存在问题,声明时默认neighbors=8
template <typename _tp>
void getCircularLBPFeature(InputArray _src, OutputArray _dst, int radius, int neighbors)
{
Mat src = _src.getMat();
//LBP特征图像的行数和列数的计算要准确
_dst.create(src.rows - 2 * radius, src.cols - 2 * radius, CV_8UC1);
Mat dst = _dst.getMat();
dst.setTo(0);
//循环处理每个像素
for (int i = radius; i<src.rows - radius; i++)
{
for (int j = radius; j<src.cols - radius; j++)
{
//获得中心像素点的灰度值
_tp center = src.at<_tp>(i, j);
unsigned char lbpCode = 0;
for (int k = 0; k<neighbors; k++)
{
//根据公式计算第k个采样点的坐标,这个地方可以优化,不必每次都进行计算radius*cos,radius*sin
float x = i + static_cast<float>(radius * \
cos(2.0 * CV_PI * k / neighbors));
float y = j - static_cast<float>(radius * \
sin(2.0 * CV_PI * k / neighbors));
//根据取整结果进行双线性插值,得到第k个采样点的灰度值
//1.分别对x,y进行上下取整
int x1 = static_cast<int>(floor(x));
int x2 = static_cast<int>(ceil(x));
int y1 = static_cast<int>(floor(y));
int y2 = static_cast<int>(ceil(y));
//2.计算四个点(x1,y1),(x1,y2),(x2,y1),(x2,y2)的权重
//下面的权重计算方式有个问题,如果四个点都相等,则权重全为0,计算出来的插值为0
//float w1 = (x2-x)*(y2-y); //(x1,y1)
//float w2 = (x2-x)*(y-y1); //(x1,y2)
//float w3 = (x-x1)*(y2-y); //(x2,y1)
//float w4 = (x-x1)*(y-y1); //(x2,y2)
//将坐标映射到0-1之间
float tx = x - x1;
float ty = y - y1;
//根据0-1之间的x,y的权重计算公式计算权重
float w1 = (1 - tx) * (1 - ty);
float w2 = tx * (1 - ty);
float w3 = (1 - tx) * ty;
float w4 = tx * ty;
//3.根据双线性插值公式计算第k个采样点的灰度值
float neighbor = src.at<_tp>(x1, y1) * w1 + src.at<_tp>(x1, y2) *w2 \
+ src.at<_tp>(x2, y1) * w3 + src.at<_tp>(x2, y2) *w4;
//通过比较获得LBP值,并按顺序排列起来
lbpCode |= (neighbor>center) << (neighbors - k - 1);
}
dst.at<uchar>(i - radius, j - radius) = lbpCode;
}
}
}
*/
//圆形LBP特征计算,效率优化版本,声明时默认neighbors=8
template <typename _tp>
void getCircularLBPFeatureOptimization(InputArray _src, OutputArray _dst, int radius, int neighbors)
{
Mat src = _src.getMat();
//LBP特征图像的行数和列数的计算要准确
_dst.create(src.rows - 2 * radius, src.cols - 2 * radius, CV_8UC1);
Mat dst = _dst.getMat();
dst.setTo(0);
for (int k = 0; k<neighbors; k++)
{
//计算采样点对于中心点坐标的偏移量rx,ry
float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));
float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));
//为双线性插值做准备
//对采样点偏移量分别进行上下取整
int x1 = static_cast<int>(floor(rx));
int x2 = static_cast<int>(ceil(rx));
int y1 = static_cast<int>(floor(ry));
int y2 = static_cast<int>(ceil(ry));
//将坐标偏移量映射到0-1之间
float tx = rx - x1;
float ty = ry - y1;
//根据0-1之间的x,y的权重计算公式计算权重,权重与坐标具体位置无关,与坐标间的差值有关
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++)
{
//获得中心像素点的灰度值
_tp center = src.at<_tp>(i, j);
//根据双线性插值公式计算第k个采样点的灰度值
float neighbor = src.at<_tp>(i + x1, j + y1) * w1 + src.at<_tp>(i + x1, j + y2) *w2 \
+ src.at<_tp>(i + x2, j + y1) * w3 + src.at<_tp>(i + x2, j + y2) *w4;
//LBP特征图像的每个邻居的LBP值累加,累加通过与操作完成,对应的LBP值通过移位取得
dst.at<uchar>(i - radius, j - radius) |= (neighbor>center) << (neighbors - k - 1);
}
}
}
}
//旋转不变圆形LBP特征计算,声明时默认neighbors=8
template <typename _tp>
void getRotationInvariantLBPFeature(InputArray _src, OutputArray _dst, int radius, int neighbors)
{
Mat src = _src.getMat();
//LBP特征图像的行数和列数的计算要准确
_dst.create(src.rows - 2 * radius, src.cols - 2 * radius, CV_8UC1);
Mat dst = _dst.getMat();
dst.setTo(0);
for (int k = 0; k<neighbors; k++)
{
//计算采样点对于中心点坐标的偏移量rx,ry
float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));
float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));
//为双线性插值做准备
//对采样点偏移量分别进行上下取整
int x1 = static_cast<int>(floor(rx));
int x2 = static_cast<int>(ceil(rx));
int y1 = static_cast<int>(floor(ry));
int y2 = static_cast<int>(ceil(ry));
//将坐标偏移量映射到0-1之间
float tx = rx - x1;
float ty = ry - y1;
//根据0-1之间的x,y的权重计算公式计算权重,权重与坐标具体位置无关,与坐标间的差值有关
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++)
{
//获得中心像素点的灰度值
_tp center = src.at<_tp>(i, j);
//根据双线性插值公式计算第k个采样点的灰度值
float neighbor = src.at<_tp>(i + x1, j + y1) * w1 + src.at<_tp>(i + x1, j + y2) *w2 \
+ src.at<_tp>(i + x2, j + y1) * w3 + src.at<_tp>(i + x2, j + y2) *w4;
//LBP特征图像的每个邻居的LBP值累加,累加通过与操作完成,对应的LBP值通过移位取得
dst.at<uchar>(i - radius, j - radius) |= (neighbor>center) << (neighbors - k - 1);
}
}
}
//进行旋转不变处理
for (int i = 0; i<dst.rows; i++)
{
for (int j = 0; j<dst.cols; j++)
{
unsigned char currentValue = dst.at<uchar>(i, j);
unsigned char minValue = currentValue;
for (int k = 1; k<neighbors; k++)
{
//循环左移
unsigned char temp = (currentValue >> (neighbors - k)) | (currentValue << k);
if (temp < minValue)
{
minValue = temp;
}
}
dst.at<uchar>(i, j) = minValue;
}
}
}
//等价模式LBP特征计算
template <typename _tp>
void getUniformPatternLBPFeature(InputArray _src, OutputArray _dst, int radius, int neighbors)
{
Mat src = _src.getMat();
//LBP特征图像的行数和列数的计算要准确
_dst.create(src.rows - 2 * radius, src.cols - 2 * radius, CV_8UC1);
Mat dst = _dst.getMat();
dst.setTo(0);
//LBP特征值对应图像灰度编码表,直接默认采样点为8位
uchar temp = 1;
uchar table[256] = { 0 };
for (int i = 0; i<256; i++)
{
if (getHopTimes(i)<3)
{
table[i] = temp;
temp++;
}
}
//是否进行UniformPattern编码的标志
bool flag = false;
//计算LBP特征图
for (int k = 0; k<neighbors; k++)
{
if (k == neighbors - 1)
{
flag = true;
}
//计算采样点对于中心点坐标的偏移量rx,ry
float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));
float ry = -static_cast<float>(rad