影像特徵計算——紋理特徵

老文化沙漠發表於2020-11-24


一、什麼是紋理特徵

  • 紋理特徵是從影像中計算出來的一個值,對區域內部灰度級變化的特徵進行量化。
  • 不是基於畫素點的特徵,需要在包含多個畫素點的區域中進行統計計算。
  • 具有旋轉不變性,且對噪聲有較強的抵抗能力。
  • 當影像解析度變化的時候,計算出來的紋理可能會有較大偏差。
  • 適用於檢索具有粗細、疏密等方面較大差別的紋理影像。

一般紋理特徵有兩種表示方法:(1)共生矩陣(2)Tamura紋理特徵

二、灰度共生矩陣

1.空間灰度共生矩陣

灰度共生矩陣就是從N×N的影像f(x,y)的灰度為i的畫素出發,統計與i距離為δ=(dx2+dy2)^1/2,灰度為 j的畫素同時出現的概率P(i,j,δ,θ)。用數學表示式則為:
Alt

Alt
上述表述可能會比較抽線,接下來我們舉一個例子表述 一下:
這是原始的圖片畫素矩陣,可以看到其最大畫素為3,最小畫素為0,因此可知最後得出的共生矩陣應為4x4的矩陣(行為畫素i,列為畫素j,共生矩陣中座標(i,j)表示畫素為i的點走到距離為δ=(dx2+dy2)^1/2處灰度為 j的畫素出現的次數(歸一化形成概率))。
Alt
0度方向,只能左右走一個單位長度。此時dx=1(-1),dy=0。
Alt
根據上述的原始矩陣和距離規定我們可以求解出下面的共生矩陣:
把下面的矩陣看成是一個4x4的陣列P,P[0][0]表示畫素值為 0的點左或者右移動一格畫素值仍然為1的次數(注:不記錄重複的情況,比如原矩陣中[0][0]向右是[0][1]畫素為 0記一次,但是 [0][1]向左到[0][0]畫素為0就不計數了),總體結果如下:
Alt
常用的還有45度方向,90度方向和135度方向,就不一一結束了。

2.程式碼實現

下述程式碼只實現了0度方向的共生矩陣,其他方向的自行解決。

#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<vector>
#include<algorithm>
#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;

	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 += -(*it) * log(*it);
			//cout << *it << " ";
		}
	}
	return sum;
}

3.利用紋理特徵實現圖片分類

  • 用於測量灰度級分佈隨機性的一種特徵引數叫做熵。
  • 若影像沒有任何紋理,則灰度共生矩陣幾乎為零矩陣,則熵值接近為零;若影像有較多的細小紋理,則灰度共生矩陣中的數值近似相等,則影像的熵值最大。
  • 熵值的定義:

Alt
根據熵的大小提前共生矩陣的資訊,利用熵代表紋理特徵就可以對圖片進行分類啦。

相關文章