网上关于灰度共生矩阵及其统计量的介绍有很多,这里就不再赘述了。这篇博客https://blog.csdn.net/guanyuqiu/article/details/53117507,介绍的还算清楚。本篇博客主要讲如何使用灰度共生矩阵统计量。Haralick等人定义了14个纹理分析的灰度共生矩阵特征参数。Ulaby等人研究发现:在基GLCM的14个纹理特征中,仅有4个特征是不相关的,这4个特征即便于计算又能给出较高的分类精度,一般采用这四种特征Contrast、Correlation、Energy、Homogemeity。
取步长为1,对0,45,90,135方向求统计量。对上诉指标进行归一化(特征),可以送到神经网络或支持向量机进行相关学习(分类,识别等任务)。经常看到分类纺织品、木材纹路等。提取纹理特征时主要存在两方面的缺陷:1.缺乏移动不变性,输入信号很小的位移会造成能量分布的很大变化。2. 较差的方向选择性,图像的微小改变会造成识别效果的降低。
其主要思想:用5*5或7*7滑动窗口遍历图像,计算窗口内的对比度、相关性、能量、同质性值,然后替换该像素的像素值。生成的纹理特征影像图可以认识是边缘图。下图是5*5滑动窗口,步长为1,0方向生成的结果图。论文“基于灰度共生矩阵提取纹理特征图像的研究”冯建辉,说纹理特征影像可以提高分类效果,但是每个小窗口都要计算灰度共生矩阵,然后计算统计量。计算过于复杂,耗费时间。目前有很多种方法提高分类效果,所以这种方法目前并不常用了。
原图 对比度
相关性 能量
我学习灰度共生矩阵,主要想计算图像复杂度,应用到其他图像处理其他方面。但是对比度、相关、能量、同质之间的权重不同很合理的定义。映射到的灰度级不同,会导致对比度有较大的变化,所以在和其他统计量加权求和时,最好归一化。 只要复杂度,与目测结果一致就可。比如a图看着比b图复杂,a图的复杂度比b图的复杂度高即可。
附件:
#include
#include
#include
using namespace std;
using namespace cv;
// 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++)
{
uchar *srcdata = src.ptr(i);
for (int j = 0; j < width - 1; j++)
{
// 同样的像素对,水平相邻
int rows = srcdata[j] * gray_level/256;
int cols = srcdata[j + 1] * gray_level / 256;
dst.at(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++)
{
uchar*srcdata = src.ptr(i) ;
uchar*srcdata1 = src.ptr(i + 1);
for (int j = 0; j < width; j++)
{
// 同样的像素对,垂直相邻
int rows = srcdata[j] / 256;
int cols = srcdata1[j] / 256;
dst.ptr(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++)
{
uchar*srcdata = src.ptr(i);
uchar*srcdata1 = src.ptr(i + 1);
for (int j = 0; j < width - 1; j++)
{
// 同样的像素对,45°相邻
int rows = srcdata[j] / 256;
int cols = srcdata1[j + 1] / 256;
dst.ptr(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++)
{
uchar*srcdata = src.ptr(i);
uchar*srcdata1 = src.ptr(i + 1);
for (int j = 1; j < width; j++)
{
// 同样的像素对,135°相邻
int rows = srcdata[j] / 256;
int cols = srcdata1[j - 1] / 256;
dst.ptr(rows)[cols]++;
}
}
}
// 计算特征值
void featureGLCM(Mat&src, double& Asm, double& Ent, double& Con, double& Idm,double &Res)
{
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(i);
for (int j = 0; j < width; j++)
{
total += srcdata[j];
}
}
//图像每一个像素的的值除以像素总和
Mat mean;
mean.create(height, width, CV_64FC1);
float u1=0., u2=0.;
float imean = 0, jmean = 0;
float ivar = 0, jvar = 0;
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr(i);
double*copydata = mean.ptr(i);
for (int j = 0; j < width; j++)
{
copydata[j] = (double)srcdata[j] / (double)total;
imean += copydata[j] * i;
jmean += copydata[j] * j;
}
}
for (int i = 0; i < height; i++)
{
double*srcdata = mean.ptr(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));
//相关性
ivar += srcdata[j] * (i - imean)*(i - imean);
jvar += srcdata[j] * (j - jmean)*(j - jmean);
}
}
for (int i = 0; i < height; i++)
{
double*srcdata = mean.ptr(i);
for (int j = 0; j < width; j++)
{
Res += (srcdata[j] *(i - imean)*(j - jmean)) / sqrt(ivar*jvar);
}
}
}
void TexturalMat(Mat & src, Mat &dst, int size,int angle,int level)
{
dst=Mat(src.size(), CV_32SC1, Scalar(0));
Mat srcROI = Mat(size, size, CV_32SC1, Scalar(0));
Mat ROI, ROIdst;
int basis = size / 2;
for (int i = 0; i < src.size().height-size-1; i++)
{
for (int j = 0; j < src.size().width-size-1; j++)
{
double Asm = 0.0, Ent = 0.0, Con = 0.0, Idm = 0.0, Res = 0.0;
if (i + size <= src.size().width&& j + size <= src.size().height)
{
Mat ROI = src(Rect(j, i, size, size));
switch (angle)
{
case 0: getGLCM0(ROI, ROIdst, level);
break;
case 45:getGLCM45(ROI, ROIdst, level);
break;
case 90:getGLCM90(ROI, ROIdst, level);
break;
case 135:getGLCM135(ROI, ROIdst, level);
break;
}
featureGLCM(ROIdst, Asm, Ent, Con, Idm, Res);
dst.ptr(i + basis)[j + basis] = Asm*255 ;
}
}
}
}
#include
void main()
{
Mat src = imread("lena.jpg",1);
Mat gray;
cvtColor(src,gray,CV_RGB2GRAY);
Mat dst;//纹理特征影像图
//5*5 size
// 0-255 映射到0-8
TexturalMat(gray, dst, 5, 0, 8);
imshow("dst",dst*255);
getGLCM0(gray, dst, 8);
double Asm=0.0, Ent=0.0, Con=0.0, Idm=0.0,Res=0.0;
featureGLCM(dst, Asm, Ent, Con, Idm,Res);
cout << "Asm:" << Asm << "\n" << "Ent:" << Ent << "\n" << "Con: " << Con << "\n" << "Idm: " << Idm << "\n" << "Res: " << Res << endl;
waitKey(0);
}
/* matlab 灰度共生矩阵计算及其统计量
I=imread('lena.jpg');
gray=rgb2gray(I)
GLCM2 = graycomatrix(gray,'NumLevels',16,'Offset',[0,2;-2,2;-2,0;-2,-2]);
stats = graycoprops(GLCM2,{'all'})
I=imread('lena.jpg');
gray=rgb2gray(I);
GS=graycomatrix(gray,'NumLevels',256);%水平位置算子计算共生矩阵
GSn=GS/sum(GS(:));%归一化矩阵
stats=graycoprops(GS,'all');%共生矩阵的描述子
maxProbability=max(GSn(:));%最大概率
corr=stats.Correlation;%相关
contrast=stats.Contrast;%对比度
energy=stats.Energy;%能量
hom=stats.Homogeneity;%同质
for J=1:size(GSn,1)
sumcols(J)=sum(-GSn(J,1:end).*log2(GSn(J,1:end)+eps));
end
entropy=sum(sumcols);%熵
Y=[maxProbability,corr,contrast,energy,hom,entropy];
*/