灰度共生矩阵的原理及实现(特征提取)-OpenCV


最近在研究机器学习相关内容,后面会尽量花时间整理成一个系列的博客,然后朋友让我帮他实现一种基于SVR支持向量回归的图像质量评价方法,然而在文章的开头竟然发现
灰度共生矩阵这个陌生的家伙,于是便有此文。

灰度共生矩阵的原理及实现(特征提取)-OpenCV_第1张图片

主要参考博客1:http://blog.csdn.net/jialeheyeshu/article/details/51337225
主要参考博客2:http://blog.csdn.net/guanyuqiu/article/details/53117507
主要参考博客3:http://www.cnblogs.com/rong86/p/3695621.html
主要参考博客4:http://blog.csdn.net/lingtianyulong/article/details/53032034

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说明文档)
灰度共生矩阵的原理及实现(特征提取)-OpenCV_第2张图片

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.灰度共生矩阵特征量(字写的不好,请见谅)

(上述公式的来源要仔细推敲一番,在这里我的目的主要不是这个,所以咩有很详细的写出来,欢迎交流留言)

3.Code

GLCM.h


#include
#include 
#include 
#include 
#include 
#include 
#include 

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 

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<<"  " <"  "<< contrast_average<<"  " << idMoment_average << endl;
        finout1 << energy_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 <
            i++;
            outstr.str("");

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

最近事情较多,博客没有及时更新,但是留言私信我都会看到,欢迎交流!
青雲-吾道乐途

你可能感兴趣的:(opencv,灰度共生矩阵,GLCM,图像处理,纹理,OpenCV)