灰度共生矩阵(超简单理解)

论坛 期权论坛 脚本     
匿名技术用户   2021-4-8 02:03   829   0

1.灰度共生矩阵生成原理
灰度共生矩阵(GLDM)的统计方法是20世纪70年代初由R.Haralick等人提出的,它是在假定图像中各像素间的空间分布关系包含了图像纹理信息的前提下,提出的具有广泛性的纹理分析方法。

灰度共生矩阵被定义为从灰度为i的像素点出发,离开某个固定位置(相隔距离为d,方位为)的点上灰度值为的概率,即,所有估计的值可以表示成一个矩阵的形式,以此被称为灰度共生矩阵。对于纹理变化缓慢的图像,其灰度共生矩阵对角线上的数值较大;而对于纹理变化较快的图像,其灰度共生矩阵对角线上的数值较小,对角线两侧的值较大。由于灰度共生矩阵的数据量较大,一般不直接作为区分纹理的特征,而是基于它构建的一些统计量作为纹理分类特征。Haralick曾提出了14种基于灰度共生矩阵计算出来的统计量:即:能量、熵、对比度、均匀性、相关性、方差、和平均、和方差、和熵、差方差、差平均、差熵、相关信息测度以及最大相关系数。

在网上看了很多灰度共生矩阵生成的例子感觉都没有说明白,要不就直接上结果要不就给一堆看不懂的代码和公式,后来看了matlab中的介绍就明白了,其实很简单,仔细把下面的看三遍就理解怎么来的了!(我是第三篇看明白的,当时很紧张,相信你们没问题)
下图显示了如何求解灰度共生矩阵,以(1,1)点为例,GLCM(1,1)值为1说明只有一对灰度为1的像素水平相邻。GLCM(1,2)值为2,是因为有两对灰度为1和2的像素水平相邻。(MatLab说明文档)

这里写图片描述
GLCM表其实就是所有像素可能的组合,比如,GLCM(1,1)就是I中像素值为1和1的组合,GLCM(4,5)就是I中像素4和像素5的组合,GLCM(i,j)的值呢就是I中像素为i,像素为j的有有多少和相邻的成对点。这个相邻有个规则:就是f(x,y),f(x+a,y+b)相邻,就是只有x相隔a的单位,y相隔b个单位,我们认为是相邻的。
平时我们说相邻:B点在A点右边,其实就是这里的a=1,b=0,也就是f(x,y)和f(x+1,y+0)相邻。
于是就有了:
a=1,b=0 时我们就说水平相邻:也就是0度的时候
a=1,b=1 时我们就说对角相邻,也就是45度的时候
a=-1,b=1时 即135度
其他角度类似。
在a=1,b=0时:GLCM(1,1)=1;其实就是I中有几个1和1相邻(1个)(按上面的规则)GLCM(1,2)=2,几个1和2相邻(2个)。ok!
后面好多的性质,都是在把这个矩阵计算出来之后再在这个基础上运算的,那些就不难了!

附加理解2:
共生矩阵用两个位置的像素的联合概率密度来定义,它不仅反映亮度的分布特征,也反映具有同样亮度或者接近亮度的像素之间的位置分布特性,是有关图像亮度变化的二阶统计特征。它是定义一组纹理特征的基础。

由于纹理是由灰度在空间位置上反复出现而形成的,因而在图像空间中像个某距离的两像素之间会存在一定的灰度关系,即图像中灰度的空间相关特性。灰度共生矩阵就是一种通过研究灰度的空间相关特性来描述纹理的常用方法。

灰度直方图是对图像上单个像素具有某个灰度进行统计的结果,

而灰度共生矩阵是对图像上保持某距离的两像素分别具有某灰度的状况进行统计得到的。
取图像(N×N)中任意一点 (x,y)及偏离它的另一点 (x+a,y+b),设该点对的灰度值为(g1,g2)。令点(x,y) 在整个画面上移动,则会得到各种 (g1,g2)值,设灰度值的级数为 k,则(g1,g2) 的组合共有 k^2;种。对于整个画面,统计出每一种(g1,g2)值出现的次数,然后排列成一个方阵,在用(g1,g2) 出现的总次数将它们归一化为出现的概率P(g1,g2),这样的方阵称为灰度共生矩阵。距离差分值(a,b) 取不同的数值组合,可以得到不同情况下的联合概率矩阵。(a,b)取值要根据纹理周期分布的特性来选择,对于较细的纹理,选取(1,0)、(1,1)、(2,0)等小的差分值。  当 a=1,b=0时,像素对是水平的,即0度扫描;当a=0,b=1 时,像素对是垂直的,即90度扫描;当 a=1,b=1时,像素对是右对角线的,即45度扫描;当 a=-1,b=-1时,像素对是左对角线,即135度扫描。
这样,两个象素灰度级同时发生的概率,就将 (x,y)的空间坐标转化为“灰度对” (g1,g2)的描述,形成了灰度共生矩阵。(百度百科)
一幅图象的灰度共生矩阵能反映出图象灰度关于方向、相邻间隔、变化幅度的综合信息,它是分析图象的局部模式和它们排列规则的基础。

感觉差不多了吧!

2.灰度共生矩阵特征量

灰度共生矩阵特征量

2.1对比度

度量 矩阵的值是如何分布和图像中局部变化的多少,反应了图像的清晰度和纹理的沟纹深浅。纹理的沟纹越深,反差越大,效果越清晰;反之,对比值小,则沟纹浅,效果模糊。

2.2 能量

能量变换反映了图像灰度分布均匀程度和纹理粗细度。若灰度共生矩阵的元素值相近,则能量较小,表示纹理细致;若其中一些值大,而其它值小,则能量值较大。能量值大表明一种较均一和规则变化的纹理模式。

2.3 熵

图像包含信息量的随机性度量。当共生矩阵中所有值均相等或者像素值表现出最大的随机性时,熵最大;因此熵值表明了图像灰度分布的复杂程度,熵值越大,图像越复杂。

2.4 逆方差

逆方差反映了图像纹理局部变化的大小,若图像纹理的不同区域间较均匀,变化缓慢,逆方差会较大,反之较小。

2.5相关性

用来度量图像的灰度级在行或列方向上的相似程度,因此值得大小反应了局部灰度相关性,值越大,相关性也越大。


————————————————
版权声明:本文为CSDN博主「sunny_develop」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guanyuqiu/article/details/53117507

3.代码

GLCM.h


#include<iostream>
#include <cassert>
#include <vector>
#include <iterator>
#include <functional>
#include <algorithm>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

typedef vector<vector<int> > 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;    // 逆差分矩, inverse difference moment

} GLCMFeatures;

class GLCM
{
public:
    GLCM();
    ~GLCM();

public:
    // 枚举灰度共生矩阵的方向
    enum 
    {
        GLCM_HORIZATION = 0,        // 水平
        GLCM_VERTICAL = 1,          // 垂直
        GLCM_ANGLE45 = 2,           // 45度角
        GLCM_ANGLE135 = 3           // 135度角
    };

public:
    // 计算灰度共生矩阵
    void calGLCM(IplImage* inputImg, VecGLCM& vecGLCM, int angle);
    // 计算特征值
    void getGLCMFeatures(VecGLCM& vecGLCM, GLCMFeatures& features);
public:
    // 初始化灰度共生矩阵
    void initGLCM(VecGLCM& vecGLCM, int size = 16);
    // 设置灰度划分等级,默认值为 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);
    // 计算 45 度灰度共生矩阵
    void getGLCM45(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight);
    // 计算 135 度灰度共生矩阵
    void getGLCM135(VecGLCM &src, VecGLCM &dst, int imgWidth, int imgHeight);

private:
    int m_grayLevel;        // 将灰度共生矩阵划分为 grayLevel 个等级

};
//GLCM.cpp
#include "GLCM.h"

GLCM::GLCM() : m_grayLevel(16)
{

}

GLCM::~GLCM()
{

}

//==============================================================================
// 函数名称: initGLCM
// 参数说明: vecGLCM,要进行初始化的共生矩阵,为二维方阵
//          size, 二维矩阵的大小,必须与图像划分的灰度等级相等
// 函数功能: 初始化二维矩阵
//==============================================================================

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;
        }
    }
}

//==============================================================================
// 函数名称: getHorisonGLCM
// 参数说明: src,要进行处理的矩阵,源数据
//          dst,输出矩阵,计算后的矩阵,即要求的灰度共生矩阵
//          imgWidth, 图像宽度
//          imgHeight, 图像高度
// 函数功能: 计算水平方向的灰度共生矩阵
//==============================================================================

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]++;
        }
    }


}

//==============================================================================
// 函数名称: getVertialGLCM
// 参数说明: src,要进行处理的矩阵,源数据
//          dst,输出矩阵,计算后的矩阵,即要求的灰度共生矩阵
//          imgWidth, 图像宽度
//          imgHeight, 图像高度
// 函数功能: 计算垂直方向的灰度共生矩阵
//==============================================================================

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]++;
        }
    }
}

//==============================================================================
// 函数名称: getGLCM45
// 参数说明: src,要进行处理的矩阵,源数据
//          dst,输出矩阵,计算后的矩阵,即要求的灰度共生矩阵
//          imgWidth, 图像宽度
//          imgHeight, 图像高度
// 函数功能: 计算45度的灰度共生矩阵
//==============================================================================

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]++;
        }
    }
}


//==============================================================================
// 函数名称: getGLCM135
// 参数说明: src,要进行处理的矩阵,源数据
//          dst,输出矩阵,计算后的矩阵,即要求的灰度共生矩阵
//          imgWidth, 图像宽度
//          imgHeight, 图像高度
// 函数功能: 计算 135 度的灰度共生矩阵
//==============================================================================

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]++;
        }
    }
}

//==============================================================================
// 函数名称: calGLCM
// 参数说明: inputImg,要进行纹理特征计算的图像,为灰度图像
//          vecGLCM, 输出矩阵,根据灰度图像计算出的灰度共生阵
//          angle,灰度共生矩阵的方向,有水平、垂直、45度、135度四个方向
// 函数功能: 计算灰度共生矩阵
//==============================================================================

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;
            }

        }
    }// end for i

    ++maxGrayLevel;
    VecGLCM tempVec;
    // 初始化动态数组
    tempVec.resize(height);
    for (int i = 0; i < height; ++i)
    {
        tempVec[i].resize(width);
    }

    if (maxGrayLevel > 16)//若灰度级数大于16,则将图像的灰度级缩小至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)     // 45 度灰度共生阵
            getGLCM45(tempVec, vecGLCM, width, height);
        if (angle == GLCM_ANGLE135)    // 135 度灰度共生阵
            getGLCM135(tempVec, vecGLCM, width, height);
    }
    else//若灰度级数小于16,则生成相应的灰度共生矩阵
    {
        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)     // 45 度灰度共生阵
            getGLCM45(tempVec, vecGLCM, width, height);
        if (angle == GLCM_ANGLE135)    // 135 度灰度共生阵
            getGLCM135(tempVec, vecGLCM, width, height);
    }

    cvReleaseImage(&src);
}

//==============================================================================
// 函数名称: getGLCMFeatures
// 参数说明: vecGLCM, 输入矩阵,灰度共生阵
//          features,灰度共生矩阵计算的特征值,主要包含了能量、熵、对比度、逆差分矩
// 函数功能: 根据灰度共生矩阵计算的特征值
//==============================================================================

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];     // 求所有图像的灰度值的和
        }
    }

    vector<vector<double> > 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"
#include <string>

void getFileName();
string names[2000];
char data[20];
const string filename = "C:\\Users\\ltc\\Desktop\\data5\\Sobel\\数值处理\\背景\\";

int main()
{
    fstream finout1("data.txt", ios::in | ios::out|ios::trunc);
    getFileName();
    int i = 0;
    char data1[20];
    while (names[i].length() > 5){
        strcpy_s(data1, names[i].c_str());
        string imagename = data1;
        //灰度共生矩阵
        IplImage* img = cvLoadImage(data1, 0);
        cvSetImageROI(img, cvRect(1453, 1149,557, 557));
        /*cvNamedWindow("ShowSRC"); 
        cvShowImage("ShowSRC",img); 
        cvWaitKey(0);  */

        GLCM glcm;
        VecGLCM vec;
        GLCMFeatures features;
        glcm.initGLCM(vec);
        // 水平
        glcm.calGLCM(img, vec, GLCM::GLCM_HORIZATION);
        glcm.getGLCMFeatures(vec, features);
        double energy_hor = features.energy;
        double entropy_hor = features.entropy;
        double contrast_hor = features.contrast;
        double idMoment_hor = features.idMoment;

        // 垂直
        glcm.calGLCM(img, vec, GLCM::GLCM_VERTICAL);
        glcm.getGLCMFeatures(vec, features);
        double energy_vetical = features.energy;
        double entropy_vetical = features.entropy;
        double contrast_vetical = features.contrast;
        double idMoment_vetical = features.idMoment;


        // 45 度
        glcm.calGLCM(img, vec, GLCM::GLCM_ANGLE45);
        glcm.getGLCMFeatures(vec, features);
        double energy_45 = features.energy;
        double entropy_45 = features.entropy;
        double contrast_45 = features.contrast;
        double idMoment_45 = features.idMoment;


        // 135 度
        glcm.calGLCM(img, vec, GLCM::GLCM_ANGLE135);
        glcm.getGLCMFeatures(vec, features);
        double energy_135 = features.energy;
        double entropy_135 = features.entropy;
        double contrast_135 = features.contrast;
        double idMoment_135 = features.idMoment;

        double energy_average = (energy_135 + energy_45 + energy_hor + energy_vetical) / 4;
        double entropy_average = (entropy_135 + entropy_45 + entropy_hor + entropy_vetical) / 4;
        double contrast_average = (contrast_135 + contrast_45 + contrast_hor + contrast_vetical) / 4;
        double idMoment_average = (idMoment_135 + idMoment_45 + idMoment_hor + idMoment_vetical) / 4;


        cout<< energy_average<<"  " <<entropy_average <<"  "<< contrast_average<<"  " << idMoment_average << endl;
        finout1 << energy_average<<"  " <<entropy_average <<"  "<< contrast_average<<"  " << idMoment_average << endl;
        i++;
    }
    system("pause");
    return 0;
}

void getFileName(){
    fstream finout("C:\\Users\\ltc\\Desktop\\data5\\FILENAME.TXT", ios::in | ios::out);
    ostringstream outstr;
    if (finout.is_open()){
        finout.seekg(0);
        int i = 0;
        while (finout.getline(data, 19)){
            outstr << data;

            names[i] = outstr.str();
            //cout <<setw(30)<<i<<setw(30)<<names[i]<<'\n';
            i++;
            outstr.str("");

        }
    }
    else
        cout << "failed" << endl;
}


————————————————
版权声明:本文为CSDN博主「青雲-吾道乐途」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37059483/article/details/78292869

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP