特征提取——纹理特征
图像处理之特征提取(二)之LBP特征简单梳理 https://blog.csdn.net/coming_is_winter/article/details/72859957 https://blog.csdn.net/zouxy09/article/details/7929531 LBP特征理解。 http://blog.csdn.net/hqh45/article/details/24501097 LBP(Local Binary Pattern,局部二值模式)是一种用来描述图像局部纹理特征的算子;它具有旋转不变性和灰度不变性等显著的优点。它是首先由T. Ojala, M.Pietikäinen, 和 D. Harwood 在1994年提出,用于纹理特征提取。而且,提取的特征是图像的局部的纹理特征;
lbp理论:
原始的LBP算子定义为在3*3的窗口内,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3*3邻域内的8个点经比较可产生8位二进制数(通常转换为十进制数即LBP码,共256种),即得到该窗口中心像素点的LBP值,并用这个值来反映该区域的纹理信息。
圆形LBP算子: 半径为R
基本的 LBP算子的最大缺陷在于它只覆盖了一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的需要。为了适应不同尺度的纹理特征,并达到灰度和旋转不变性的要求,Ojala等对 LBP 算子进行了改进,将 3×3邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域,改进后的 LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点。从而得到了诸如半径为R的圆形区域内含有P个采样点的LBP算子;
即不断旋转圆形邻域得到一系列初始定义的 LBP值,取其最小值作为该邻域的 LBP 值。
概念:
灰度共生矩阵是涉及像素距离和角度的矩阵函数,它通过计算图像中一定距离和一定方向的两点灰度之间的相关性,来反映图像在<font color=red>方向、间隔、变化幅度及快慢</font>上的综合信息。 灰度直方图是对图像上单个像素具有某个灰度进行统计的结果,而<font color=red>灰度共生矩阵是对图像上保持某距离的两像素分别具有某灰度的状况进行统计得到的。</font>
含义: 以(1,1)点为例,GLCM(1,1)值为1说明左侧原图只有一对灰度为1的像素水平相邻。GLCM(1,2)值为2,是因为原图有两对灰度为1和2的像素水平相邻。
矩阵
|1 2 1|
|2 2 2|
|1 2 2|
对应GLMC矩阵(最大是2,所以是2*2矩阵)
GLCM矩阵:
1 2
1|0 2|
2|1 6|
0(1,1)相邻, 2(1,2)水平相邻, 2(2,1)垂直相邻, 6(2,2)相邻
灰度共生矩阵的特征
1) 角二阶矩(Angular Second Moment, ASM)公式:ASM = sum(p(i,j)^2),其中 p(i,j) 表示归一后的灰度共生矩阵意义:角二阶矩是图像灰度分布均匀程度和纹理粗细的一个度量,当图像纹理绞细致、灰度分布均匀时,能量值较大,反之,较小。<font color=red>结论:值大,灰度分布均匀</font>
2) 熵(Entropy, ENT)公式:ENT=sum[-p(i,j)log(p(i,j))] 意义:描述图像具有的信息量的度量,表明图像的复杂程度,当复杂程度高时,熵值较大,反之则较小。<font color=red>结论:值大,复杂程度高</font>
3) 反差分矩阵(Inverse Differential Moment, IDM)公式:IDM=sum[p(i,j)/(1+(i-j)^2)]意义:反映了纹理的清晰程度和规则程度,纹理清晰、规律性较强、易于描述的,值较大;杂乱无章的,难于描述的,值较小。<font color=red>结论:值大,纹理清晰、规律性较强</font>
Gabor函数是一个用于边缘提取的线性滤波器
作用: Gabor小波可以方便的提取图像在<font color=red>各个尺度和方向上的纹理信息</font>,同时在一定程度上降低了图像中光照变化和噪声的影响。 提取目标的<font color=red>局部空间和频率域信息</font>方面具有良好的特性。 特点: 1、Gabor小波对于图像的边缘敏感 2、对光照不敏感 3、对图像旋转有一定适应性
// 0°灰度共生矩阵
void getGLCM0(Mat& src, Mat& dst, int gray_level)// 0度灰度共生矩阵
{
CV_Assert(1 == src.channels());
int height = src.rows;
int width = src.cols;
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width - 1; j++)
{
// 同样的像素对,水平相邻
int rows = srcdata[j];
int cols = srcdata[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
// 90°灰度共生矩阵
void getGLCM90(Mat& src, Mat& dst, int gray_level)
{
CV_Assert(1 == src.channels());
int height = src.rows;
int width = src.cols;
dst = Mat(gray_level, gray_level, CV_32SC1, Scalar(0));
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width; j++)
{
// 同样的像素对,垂直相邻
int rows = srcdata[j];
int cols = srcdata1[j];
dst.ptr<int>(rows)[cols]++;
}
}
}
// 45°灰度共生矩阵
void getGLCM45(Mat& src, Mat& dst, int gray_level)
{
CV_Assert(1 == src.channels());
int height = src.rows;
int width = src.cols;
dst = Mat(gray_level, gray_level, CV_32SC1, Scalar(0));
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width - 1; j++)
{
// 同样的像素对,45°相邻
int rows = srcdata[j];
int cols = srcdata1[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
// 135°灰度共生矩阵
void getGLCM135(Mat& src, Mat& dst, int gray_level)
{
CV_Assert(1 == src.channels());
int height = src.rows;
int width = src.cols;
dst = Mat(gray_level, gray_level, CV_32SC1, Scalar(0));
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 1; j < width; j++)
{
// 同样的像素对,135°相邻
int rows = srcdata[j];
int cols = srcdata1[j - 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
// 计算特征值
void featureGLCM(Mat&src, double& Asm, double& Ent, double& Con, double& Idm)
{
CV_Assert(src.channels() == 1);
int height = src.rows;
int width = src.cols;
int total = 0;
//求图像所有像素的灰度值的和
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
total += srcdata[j];
}
}
//图像每一个像素的的值除以像素总和
Mat mean;
mean.create(height, width, CV_64FC1);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
double*copydata = mean.ptr<double>(i);
for (int j = 0; j < width; j++)
{
copydata[j] = (double)srcdata[j] / (double)total;
}
}
for (int i = 0; i < height; i++)
{
double*srcdata = mean.ptr<double>(i);
for (int j = 0; j < width; j++)
{
// 能量
Asm += srcdata[j] * srcdata[j];
// 熵(Entropy)
if (srcdata[j]>0)
Ent -= srcdata[j] * log(srcdata[j]);
// 对比度
Con += (double)(i - j)*(double)(i - j)*srcdata[j];
// 逆差矩
Idm += srcdata[j] / (1 + (double)(i - j)*(double)(i - j));
}
}
}