灰度共生矩阵,Gray Level Co-occurrence Matrix,简写为GLCM
由于纹理是由灰度分布在空间位置上反复出现而形成的,因而在图像空间中相隔某距离的两象素之间会存在一定的灰度关系,即图像中灰度的空间相关特性。灰度共生矩阵就是一种通过研究灰度的空间相关特性来描述纹理的常用方法。
取图像(N×N)中任意一点 (x,y)及偏离它的另一点 (x+a,y+b),设该点对的灰度值为 (g1,g2)。令点(x,y) 在整个画面上移动,则会得到各种 (g1,g2)值,设灰度值的级数为 k,则(g1,g2) 的组合共有 k 的平方种。对于整个画面,统计出每一种 (g1,g2)值出现的次数,然后排列成一个方阵,再用(g1,g2) 出现的总次数将它们归一化为出现的概率P(g1,g2) ,这样的方阵称为灰度共生矩阵。
下图显示了如何求解灰度共生矩阵,以(1,1)点为例,GLCM(1,1)值为1说明只有一对灰度为1的像素水平相邻。GLCM(1,2)值为2,是因为有两对灰度为1和2的像素水平相邻。
设f(x,y)为一幅数字图像,其大小为M×N,灰度级别为Ng,则满足一定空间关系的灰度共生矩阵为:
其中#(x)表示集合x中的元素个数,显然P为Ng×Ng的矩阵,若(x1,y1)与(x2,y2)间距离为d,两者与坐标横轴的夹角为θ,则可以得到各种间距及角度的灰度共生矩阵(i,j,d,θ)。其中元素(i,j)的值表示一个灰度为i,另一个灰度为j的两个相距为d的像素对在角的方向上出现的次数。结合上面的图会更容易理解。
在计算得到共生矩阵之后,往往不是直接应用计算的灰度共生矩阵,而是在此基础上计算纹理特征量,我们经常用对比度、能量、熵、相关性等特征量来表示纹理特征。
(1) 对比度:又称为反差,度量矩阵的值是如何分布和图像中局部变化的多少,反应了图像的清晰度和纹理的沟纹深浅。纹理的沟纹越深,反差越大,效果清晰;反之,对比值小,则沟纹浅,效果模糊。
(2) 能量:是灰度共生矩阵各元素值的平方和,是对图像纹理的灰度变化稳定程度的度量,反应了图像灰度分布均匀程度和纹理粗细度。能量值大表明当前纹理是一种规则变化较为稳定的纹理。
(3) 熵:是图像包含信息量的随机性度量。当共生矩阵中所有值均相等或者像素值表现出最大的随机性时,熵最大;因此熵值表明了图像灰度分布的复杂程度,熵值越大,图像越复杂。
(4) 相关性:也称为同质性,用来度量图像的灰度级在行或列方向上的相似程度,因此值的大小反应了局部灰度相关性,值越大,相关性也越大。
灰度共生矩阵(GLDM)的统计方法是20世纪70年代初由R.Haralick等人提出的,它是在假定图像中各像素间的空间分布关系包含了图像纹理信息的前提下,提出的具有广泛性的纹理分析方法。
度共生矩阵被定义为从灰度为i的像素点出发,离开某个固定位置(相隔距离为d,方位为)的点上灰度值为的概率,即,所有估计的值可以表示成一个矩阵的形式,以此被称为灰度共生矩阵。对于纹理变化缓慢的图像,其灰度共生矩阵对角线上的数值较大;而对于纹理变化较快的图像,其灰度共生矩阵对角线上的数值较小,对角线两侧的值较大。由于灰度共生矩阵的数据量较大,一般不直接作为区分纹理的特征,而是基于它构建的一些统计量作为纹理分类特征。Haralick曾提出了14种基于灰度共生矩阵计算出来的统计量:即:能量、熵、对比度、均匀性、相关性、方差、和平均、和方差、和熵、差方差、差平均、差熵、相关信息测度以及最大相关系数。
本文是借用一篇文章的例子讲解灰度共生矩阵,用文字说明感觉说不清,自己之前用该方法做过实验,还是会忘,所以干脆用例子的方式介绍,下一次再看也容易理解。
在图像中任意一点(x,y)及偏离它的一点(x+a,y+b)(其中a,b为整数,认为定义)构成点对。设该点对的灰度值为(f1,f2),假设图像的最大灰度级为L,则f1与f2的组合共有L*L种。对于整福图像,统计每一种(f1,f2)值出现的次数,然后排列成一个方阵,再用(f1,f2)出现的总次数将它们归一化为出现的概率P(f1,f2),由此产生的矩阵为灰度共生矩阵。
下图为一个简单的例子:
图a为原图像,最大灰度级为16。为表示方便,这里将灰度级数减小为4级,图a变为图b的形式。这样(f1,f2)取值范围便为[0,3]。取不同的间隔,将(f1,f2)各种组合出现的次数排列起来,就可得到图e~g所示的灰度共生矩阵。
图e表示图b中(x,y)与偏离它的(x+1,y+0)构成点对时,(f1,f2)取值的情况(填充黄色部分为f1取0,f2取1时的情况,由图b填充易知共10种)。同理,f,g分别表示图c,d中(x,y)分别于点(x+1,y+1),(x+2,y+0)构成的点对(f1,f2)出现的情况(图c填充黄色部分表示f1取0,f2取0时,对角线点对(0,0)出现的情况,共8种:图d填充黄色部分表示f1取0,f2取2时水平点对(0,2)出现的情况,共9种)。例如,对于a=1,b=0,点对中(0,1)的组合共出现了10次。对比可以看出,(0,1),(1,2),(2,3)和(3,0)均有较高的出现频数。图b表明,图像中存在明显的左上右下方向的纹理。
距离(a,b)的取值不同,灰度共生矩阵中的值不同。a和b的取值要根据纹理周期分布的特征来选择,对于较细的纹理,选取(1,0),(1,1),(2,0)等这样的值是有必要的。a,b取值较小对应于变化缓慢的纹理图像,其灰度共生矩阵对角线上的数值较大。纹理的变化越快,则对角线上的数值越小,而对角线两侧的值增大。
共生矩阵实际上是两个像素点的联合直方图,对于图像中细而规则的纹理,成对像素点的二维直方图倾向于均匀分布;对于粗而规则的纹理,则倾向于最对角分布。
2.灰度共生矩阵特征量
2.1对比度
度量 矩阵的值是如何分布和图像中局部变化的多少,反应了图像的清晰度和纹理的沟纹深浅。纹理的沟纹越深,反差越大,效果越清晰;反之,对比值小,则沟纹浅,效果模糊。
2.2 能量
能量变换反映了图像灰度分布均匀程度和纹理粗细度。若灰度共生矩阵的元素值相近,则能量较小,表示纹理细致;若其中一些值大,而其它值小,则能量值较大。能量值大表明一种较均一和规则变化的纹理模式。
2.3 熵
图像包含信息量的随机性度量。当共生矩阵中所有值均相等或者像素值表现出最大的随机性时,熵最大;因此熵值表明了图像灰度分布的复杂程度,熵值越大,图像越复杂。
2.4 逆方差
逆方差反映了图像纹理局部变化的大小,若图像纹理的不同区域间较均匀,变化缓慢,逆方差会较大,反之较小。
2.5相关性
用来度量图像的灰度级在行或列方向上的相似程度,因此值得大小反应了局部灰度相关性,值越大,相关性也越大。
头文件 GLCM.h
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- using namespace std;
- using namespace cv;
-
- typedef vectorint> > VecGLCM;
-
- typedef struct _GLCMFeatures
- {
- _GLCMFeatures()
- : energy(0.0)
- , entropy(0.0)
- , contrast(0.0)
- , idMoment(0.0)
- {
-
- }
-
- double energy;
- double entropy;
- double contrast;
- double idMoment;
-
- } GLCMFeatures;
-
- class GLCM
- {
- public:
- GLCM();
- ~GLCM();
-
- public:
-
- enum
- {
- GLCM_HORIZATION = 0,
- GLCM_VERTICAL = 1,
- GLCM_ANGLE45 = 2,
- GLCM_ANGLE135 = 3
- };
-
- public:
-
- void calGLCM(IplImage* inputImg, VecGLCM& vecGLCM, int angle);
-
- void getGLCMFeatures(VecGLCM& vecGLCM, GLCMFeatures& features);
- public:
-
- void initGLCM(VecGLCM& vecGLCM, int size = 16);
-
- void setGrayLevel(int grayLevel) { m_grayLevel = grayLevel; }
-
- int getGrayLevel() const { return m_grayLevel; }
- private:
-
- void getHorisonGLCM(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight);
-
- void getVertialGLCM(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight);
-
- void getGLCM45(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight);
-
- void getGLCM135(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight);
-
- private:
- int m_grayLevel;
-
- };
源文件 GLCM.cpp
- #include "GLCM.h"
-
- GLCM::GLCM() : m_grayLevel(16)
- {
-
- }
-
- GLCM::~GLCM()
- {
-
- }
-
-
-
-
-
-
-
-
- void GLCM::initGLCM(VecGLCM& vecGLCM, int size)
- {
- assert(size == m_grayLevel);
- vecGLCM.resize(size);
- for (int i = 0; i < size; ++i)
- {
- vecGLCM[i].resize(size);
- }
-
- for (int i = 0; i < size; ++i)
- {
- for (int j = 0; j < size; ++j)
- {
- vecGLCM[i][j] = 0;
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
- void GLCM::getHorisonGLCM(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight)
- {
- int height = imgHeight;
- int width = imgWidth;
-
- for (int i = 0; i < height; ++i)
- {
- for (int j = 0; j < width - 1; ++j)
- {
- int rows = src[i][j];
- int cols = src[i][j + 1];
- dst[rows][cols]++;
- }
- }
-
-
- }
-
-
-
-
-
-
-
-
-
-
- void GLCM::getVertialGLCM(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight)
- {
- int height = imgHeight;
- int width = imgWidth;
- for (int i = 0; i < height - 1; ++i)
- {
- for (int j = 0; j < width; ++j)
- {
- int rows = src[i][j];
- int cols = src[i + 1][j];
- dst[rows][cols]++;
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
- void GLCM::getGLCM45(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight)
- {
- int height = imgHeight;
- int width = imgWidth;
- for (int i = 0; i < height - 1; ++i)
- {
- for (int j = 0; j < width - 1; ++j)
- {
- int rows = src[i][j];
- int cols = src[i + 1][j + 1];
- dst[rows][cols]++;
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- void GLCM::getGLCM135(VecGLCM& src, VecGLCM& dst, int imgWidth, int imgHeight)
- {
- int height = imgHeight;
- int width = imgWidth;
- for (int i = 0; i < height - 1; ++i)
- {
- for (int j = 1; j < width; ++j)
- {
- int rows = src[i][j];
- int cols = src[i + 1][j - 1];
- dst[rows][cols]++;
- }
- }
- }
-
-
-
-
-
-
-
-
-
- void GLCM::calGLCM(IplImage* inputImg, VecGLCM& vecGLCM, int angle)
- {
- assert(inputImg->nChannels == 1);
- IplImage* src = NULL;
- src = cvCreateImage(cvGetSize(inputImg), IPL_DEPTH_32S, inputImg->nChannels);
- cvConvert(inputImg, src);
-
- int height = src->height;
- int width = src->width;
- int maxGrayLevel = 0;
-
- for (int i = 0; i < height; ++i)
- {
- for (int j = 0; j < width; ++j)
- {
- int grayVal = cvGetReal2D(src, i, j);
- if (grayVal > maxGrayLevel)
- {
- maxGrayLevel = grayVal;
- }
-
- }
- }
-
- ++maxGrayLevel;
- VecGLCM tempVec;
-
- tempVec.resize(height);
- for (int i = 0; i < height; ++i)
- {
- tempVec[i].resize(width);
- }
-
- if (maxGrayLevel > 16)
- {
- for (int i = 0; i < height; ++i)
- {
- for (int j = 0; j < width; ++j)
- {
- int tmpVal = cvGetReal2D(src, i, j);
- tmpVal /= m_grayLevel;
- tempVec[i][j] = tmpVal;
- }
- }
-
- if (angle == GLCM_HORIZATION)
- getHorisonGLCM(tempVec, vecGLCM, width, height);
- if (angle == GLCM_VERTICAL)
- getVertialGLCM(tempVec, vecGLCM, width, height);
- if (angle == GLCM_ANGLE45)
- getGLCM45(tempVec, vecGLCM, width, height);
- if (angle == GLCM_ANGLE135)
- getGLCM135(tempVec, vecGLCM, width, height);
- }
- else
- {
- for (int i = 0; i < height; ++i)
- {
- for (int j = 1; j < width; ++j)
- {
- int tmpVal = cvGetReal2D(src, i, j);
- tempVec[i][j] = tmpVal;
- }
- }
-
- if (angle == GLCM_HORIZATION)
- getHorisonGLCM(tempVec, vecGLCM, width, height);
- if (angle == GLCM_VERTICAL)
- getVertialGLCM(tempVec, vecGLCM, width, height);
- if (angle == GLCM_ANGLE45)
- getGLCM45(tempVec, vecGLCM, width, height);
- if (angle == GLCM_ANGLE135)
- getGLCM135(tempVec, vecGLCM, width, height);
- }
-
- cvReleaseImage(&src);
- }
-
-
-
-
-
-
-
-
- void GLCM::getGLCMFeatures(VecGLCM& vecGLCM, GLCMFeatures& features)
- {
- int total = 0;
-
- for (int i = 0; i < m_grayLevel; ++i)
- {
- for (int j = 0; j < m_grayLevel; ++j)
- {
- total += vecGLCM[i][j];
- }
- }
-
- vectordouble> > temp;
- temp.resize(m_grayLevel);
- for (int i = 0; i < m_grayLevel; ++i)
- {
- temp[i].resize(m_grayLevel);
- }
-
-
- for (int i = 0; i < m_grayLevel; ++i)
- {
- for (int j = 0; j < m_grayLevel; ++j)
- {
- temp[i][j] = (double)vecGLCM[i][j] / (double)total;
- }
- }
-
- for (int i = 0; i < m_grayLevel; ++i)
- {
- for (int j = 0; j < m_grayLevel; ++j)
- {
- features.energy += temp[i][j] * temp[i][j];
-
- if (temp[i][j]>0)
- features.entropy -= temp[i][j] * log(temp[i][j]);
-
- features.contrast += (double)(i - j)*(double)(i - j)*temp[i][j];
- features.idMoment += temp[i][j] / (1 + (double)(i - j)*(double)(i - j));
- }
- }
- }
main.cpp
- #include "GLCM.h"
-
- int main()
- {
-
- IplImage* img = cvLoadImage("1.jpg", 0);
- GLCM glcm;
- VecGLCM vec;
- GLCMFeatures features;
- glcm.initGLCM(vec);
-
- glcm.calGLCM(img, vec, GLCM::GLCM_HORIZATION);
- glcm.getGLCMFeatures(vec, features);
-
- glcm.calGLCM(img, vec, GLCM::GLCM_VERTICAL);
- glcm.getGLCMFeatures(vec, features);
-
- glcm.calGLCM(img, vec, GLCM::GLCM_ANGLE45);
- glcm.getGLCMFeatures(vec, features);
-
- glcm.calGLCM(img, vec, GLCM::GLCM_ANGLE135);
- glcm.getGLCMFeatures(vec, features);
-
- cout << "asm = " << features.energy << endl;
- cout << "eng = " << features.entropy << endl;
- cout << "Con = " << features.contrast << endl;
- cout << "Idm = " << features.idMoment << endl;
- system("pause");
- return 0;
- }