文章目录

  • 一、什么是纹理特征
  • 二、灰度共生矩阵
  • 1.空间灰度共生矩阵
  • 2.代码实现
  • 3.利用纹理特征实现图片分类
  • 熵(上述代码已经实现)
  • 能量
  • 对比度
  • 均匀度



一、什么是纹理特征

  • 纹理特征是从图像中计算出来的一个值,对区域内部灰度级变化的特征进行量化。
  • 不是基于像素点的特征,需要在包含多个像素点的区域中进行统计计算。
  • 具有旋转不变性,且对噪声有较强的抵抗能力。
  • 当图像分辨率变化的时候,计算出来的纹理可能会有较大偏差。
  • 适用于检索具有粗细、疏密等方面较大差别的纹理图像。

一般纹理特征有两种表示方法:(1)共生矩阵(2)Tamura纹理特征

二、灰度共生矩阵

1.空间灰度共生矩阵

灰度共生矩阵就是从N×N的图像f(x,y)的灰度为i的像素出发,统计与i距离为δ=(dx2+dy2)^1/2,灰度为 j的像素同时出现的概率P(i,j,δ,θ)。用数学表达式则为:

Tamura纹理特征匹配 python 纹理特征参数_c++

Tamura纹理特征匹配 python 纹理特征参数_c++_02


上述表述可能会比较抽线,接下来我们举一个例子表述 一下:

这是原始的图片像素矩阵,可以看到其最大像素为3,最小像素为0,因此可知最后得出的共生矩阵应为4x4的矩阵(行为像素i,列为像素j,共生矩阵中坐标(i,j)表示像素为i的点走到距离为δ=(dx2+dy2)^1/2处灰度为 j的像素出现的次数(归一化形成概率))。

Tamura纹理特征匹配 python 纹理特征参数_图像处理_03


0度方向,只能左右走一个单位长度。此时dx=1(-1),dy=0。

Tamura纹理特征匹配 python 纹理特征参数_共生矩阵_04


根据上述的原始矩阵和距离规定我们可以求解出下面的共生矩阵:

把下面的矩阵看成是一个4x4的数组P,P[0][0]表示像素值为 0的点左或者右移动一格像素值仍然为1的次数(注:不记录重复的情况,比如原矩阵中[0][0]向右是[0][1]像素为 0记一次,但是 [0][1]向左到[0][0]像素为0就不计数了),总体结果如下:

Tamura纹理特征匹配 python 纹理特征参数_共生矩阵_05


常用的还有45度方向,90度方向和135度方向,就不一一结束了。

2.代码实现

下述代码只实现了0度方向的共生矩阵,其他方向的自行解决。

#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<vector>
#include<algorithm>
#include <numeric>
#include<iterator>
//迭代器
#include<cmath>
using namespace std;
using namespace cv;
typedef vector<vector<uchar>> VecGLCM;
void VecGLCMCount0(VecGLCM& GM_VecGLCM,cv::Mat PriImage, int nCols, int nRows);  
double ComputeEntropy(VecGLCM& GM_VecGLCM, int size);
int main()
{
	double Entropy;  //熵值
	cv::Mat image1 = imread("C4F00001.jpg",IMREAD_GRAYSCALE);
	if (image1.empty())
	{
		cout << "图片读取失败 " << endl;
	}
	int nRows = image1.rows;
	int nCols = image1.cols;
	cout << "行数:"<<image1.rows << endl;
	cout << "列数"<<image1.cols << endl;
	//namedWindow("image1", 0);
	//imshow("image1", image1);
	//waitKey(0);

	VecGLCM VecGlcm(256);
	for (int i = 0; i < 256; i++)
	{
		VecGlcm[i].resize(256);
	}
	for (int i = 0; i < 256; i++)
	{
		for (int j = 0; j < 256; j++)
		{
			VecGlcm[i][j] = 0;
		}
	}

	VecGLCMCount0(VecGlcm, image1, nCols, nRows);
	Entropy= ComputeEntropy(VecGlcm, 256);
	cout << "熵值:"<<Entropy << endl;
	return 0;
}


//==============================================================================
// 函数名称: VecGLCMCount0
// 参数说明: PriImage为初始的图片,nCols为列,nRows为行数
// 函数功能: 进行0度方向的共生矩阵求解 
//==============================================================================
void VecGLCMCount0(VecGLCM& GM_VecGLCM,cv::Mat PriImage, int nCols, int nRows)
{
	int VecGLCM_Col;
	int VecGLCM_Row;
	uchar* p;
	for (int i = 0; i < nRows; i++)
	{
		p = PriImage.ptr<uchar>(i);//获取每行首地址
		for (int j = 0; j < nCols - 1; ++j)
		{
			VecGLCM_Col = p[j];
			VecGLCM_Row = p[j + 1];
			GM_VecGLCM[VecGLCM_Col][VecGLCM_Row]++;
		}
	}
}

//==============================================================================
// 函数名称: ComputeEntropy
// 参数说明: GM_VecGLCM为共生矩阵,size为矩阵的大小(size X size)
// 函数功能: 求共生矩阵的熵
//==============================================================================
double ComputeEntropy(VecGLCM& GM_VecGLCM, int size)
{
	double sum = 0;
	vector<vector<uchar>>::iterator IE;
	
	long long tatol = 0;
	for(int i=0;i<GM_VecGLCM.size();i++)
	{
		tatol += accumulate(GM_VecGLCM[i].begin(),GM_VecGLCM[i].end(),0)	;
	}

	vector<uchar>::iterator it;
	for (IE = GM_VecGLCM.begin(); IE < GM_VecGLCM.end(); IE++)
	{
		for (it = (*IE).begin(); it < (*IE).end(); it++)
		{
			if ((*it) != 0)  sum += -(double(*it)/tatol) * log(double(*it)/tatol);
			//cout << *it << " ";
		}
	}

	return sum;
}

3.利用纹理特征实现图片分类

熵(上述代码已经实现)

  • 用于测量灰度级分布随机性的一种特征参数叫做熵。
  • 若图像没有任何纹理,则灰度共生矩阵几乎为零矩阵,则熵值接近为零;若图像有较多的细小纹理,则灰度共生矩阵中的数值近似相等,则图像的熵值最大。
  • 熵值的定义:

Tamura纹理特征匹配 python 纹理特征参数_c++_06


根据熵的大小提前共生矩阵的信息,利用熵代表纹理特征就可以对图片进行分类啦。当然能量、对比度、均匀度 也有类似功能,具体可根据相关项目进行对比选择。

能量

Tamura纹理特征匹配 python 纹理特征参数_c++_07


反应均匀性和平滑性。

对比度

Tamura纹理特征匹配 python 纹理特征参数_Tamura纹理特征匹配 python_08


反映图像点对中前后点间灰度差的度量。

均匀度

Tamura纹理特征匹配 python 纹理特征参数_图像处理_09


反映图像的均匀程度。