灰度共生矩陣(超簡單理解)

 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章