1.研究背景
图像的特征提取是图像的识别和分类、基于内容的图像检索、图像数据挖掘等研究内容的基础性工作,其中图像的纹理特征对描述图像内容具有重要意义,纹理特征提取己成为目前图像领域研究的热点。
图像的纹理特征描述图像景物的表面性质,是从图像中计算出的一个值,反应图像对应物品的质地,如粗糙度、颗粒度、随机性和规范性等。图像纹理常被应用于卫星遥感地表图像分析,图像分类、模式识别等。
GLCM 纹理提取方法具有较强的适应能力和稳健性,近年来已越来越多地用于图像的检测和分类。
2.方法原理
GLCM名为灰度共生矩阵,指的是一种通过研究灰度的空间相关特性来描述纹理的常用方法。假设原图的灰度级为L,那么灰度共生矩阵是L*L大小的计数矩阵。GLCM表其实就是所有像素可能的组合。
图示例子
灰度共生矩阵元素所表示的含义,以(1,1)点为例,GLCM(1,1)值为1说明左侧原图只有一对灰度为1的像素水平相邻。GLCM(1,2)值为2,是因为原图有两对灰度为1和2的像素水平相邻。
注:f(x,y),f(x+a,y+b)相邻,就是只有x相隔a的单位,y相隔b个单位,我们认为是相邻的。
a=1,b=0 时我们就说水平相邻:也就是0度的时候
a=1,b=1 时我们就说对角相邻,也就是45度的时候
a=-1,b=1 时 即135度……
因此对于灰度共生矩阵,需要确定以下几个参数:
- 灰度共生矩阵灰度级 :与灰度共生矩阵的阶数相同,即当灰度图像灰度级为N时,灰度共生矩阵为N × N的矩阵。一般一幅图的灰度级有256级,0-255,但由于计算量大,一般选取4、8、16作为灰度级。
- 滑动窗口尺寸:即每次计算特征值所选用的窗口矩阵大小,一般选取5或7。
- 方向选择:计算灰度共生矩阵的方向一般为0°,45°,90°,135°四个方向;求出四个方向矩阵的特征值后,可以通过计算四个特征值的平均值作为最终特征值共生矩阵。
- 步距d:一般选择d = 1,即中心像素直接与其相邻像素点做比较运算共生矩阵在精细纹理中随距离而快速变化,而在粗糙纹理中随距离则变化缓慢。一般而言,对于平滑纹理用较大的距离,对于粗糙纹理用较小的距离会取得较好的效果。
常用的glcm特征
对于纹理变换缓慢的图像,灰度共生矩阵对角线上的数值越大;图像纹理在局部变换较大,则偏离矩阵对角线的元素值较大。但是由于灰度共生矩阵的数据量较大,一般不直接作为区分纹理特征的依据。常用基于灰度共生矩阵计算出来的统计标量。信息熵、对比度、同质性(逆差距)、相关性、能量五个较常用的特征信息进行图像描述。
设f(x,y)为一幅二维数字图象,其大小为M×N,灰度级别为Ng,则满足一定空间关系的灰度共生矩阵为:
P(i,j)= #{(x1,y1),(x2,y2)∈M×N|f(x1,y1)=i,f(x2,y2)=j}
1.能量(ASM):ASM=∑i∑j(P(i,j))2
反映了图像灰度分布均匀程度和纹理粗细度。ASM值大表明一种较均一和规则变化的纹理模式。
2.对比度(Contrast):CON=∑i∑j(i−j)2P(i,j)
表现纹理的沟纹深,反差大,效果清晰。
3.同质性(Homogeneity):IDM=∑i∑j(P(i,j)/(1+(i-j)2)
测量图像的局部均匀性,非均匀图像的值较低,均匀图像的值较高。与对比度或相异性相反,同质性的权重随着元素值与对角线的距离而减小,其减小方式是指数形式的。
4.相关性(Corrln):CORRLN=[∑i∑j((ij)P(i,j))−μxμy]/σxσy
它度量空间灰度共生矩阵元素在行或列方向上的相似程度,因此,相关值大小反映了图像中局部灰度相关性。当矩阵元素值均匀相等时,相关值就大;相反,如果矩阵像元值相差很大则相关值小。如果图像中有水平方向纹理。
5.熵(Entropy):ENT=−∑i∑jP(i,j)logP(i,j)
表现图像灰度分布的复杂程度,熵值越大,图像越复杂。
其中信息熵、对比度、能量越大表示该超像素块的纹理越复杂,颜色跳跃越大;同质性和相关性数值越大,其对应的超像素块越纹理平滑,颜色均匀。
3.程序流程
1.将原图灰度量化
原图的灰度级为256时,当分成N个灰度级时,直接将像素点的灰度值除以(256/N)取整。比如,选择4灰度级,就将像素点灰度值除以64即可。
2.计算一个矩阵窗口中,按照某个方向统计的灰度共生矩阵
d=1,求0°,45°,90°,135°方向矩阵的共生矩阵
3.矩阵的归一化
将矩阵所有元素与矩阵中所有元素之和作除运算,得到概率矩阵。
4.计算单窗口灰度共生矩阵特征值
一般采用五个最常用的特征来提取图像的纹理特征:能量、对比度、同质性,相关度、熵。
5.计算单窗口所有统计方向对应的特征值后平均化
一个滑动窗口计算结束后,该窗口就可以移动一个像素点,形成另一个小窗口图像,重复进行上一步的计算,生成新窗口图像的共生矩阵和纹理特征值;以此类推,滑动窗口遍历完所有的图像像素点后,整个图像就形成了一个由纹理特征值构成的一个纹理特征值矩阵。
图示步骤
4.结果结论
附
1.fast_glcm.py
#!--*--coding: utf-8--*--
import numpy as np
import cv2
def fast_glcm(img, vmin=0, vmax=255, nbit=8, kernel_size=5):
mi, ma = vmin, vmax
ks = kernel_size
h,w = img.shape
# digitize
bins = np.linspace(mi, ma+1, nbit+1)
gl1 = np.digitize(img, bins) - 1
gl2 = np.append(gl1[:,1:], gl1[:,-1:], axis=1)
# make glcm
glcm = np.zeros((nbit, nbit, h, w), dtype=np.uint8)
for i in range(nbit):
for j in range(nbit):
mask = ((gl1==i) & (gl2==j))
glcm[i,j, mask] = 1
kernel = np.ones((ks, ks), dtype=np.uint8)
for i in range(nbit):
for j in range(nbit):
glcm[i,j] = cv2.filter2D(glcm[i,j], -1, kernel)
glcm = glcm.astype(np.float32)
return glcm
def fast_glcm_mean(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm mean
'''
h,w = img.shape
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
mean = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
mean += glcm[i,j] * i / (nbit)**2
return mean
def fast_glcm_std(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm std
'''
h,w = img.shape
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
mean = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
mean += glcm[i,j] * i / (nbit)**2
std2 = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
std2 += (glcm[i,j] * i - mean)**2
std = np.sqrt(std2)
return std
def fast_glcm_contrast(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm contrast
'''
h,w = img.shape
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
cont = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
cont += glcm[i,j] * (i-j)**2
return cont
def fast_glcm_dissimilarity(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm dissimilarity
'''
h,w = img.shape
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
diss = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
diss += glcm[i,j] * np.abs(i-j)
return diss
def fast_glcm_homogeneity(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm homogeneity
'''
h,w = img.shape
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
homo = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
homo += glcm[i,j] / (1.+(i-j)**2)
return homo
def fast_glcm_ASM(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm asm, energy
'''
h,w = img.shape
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
asm = np.zeros((h,w), dtype=np.float32)
for i in range(nbit):
for j in range(nbit):
asm += glcm[i,j]**2
ene = np.sqrt(asm)
return asm, ene
def fast_glcm_max(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm max
'''
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
max_ = np.max(glcm, axis=(0,1))
return max_
def fast_glcm_entropy(img, vmin=0, vmax=255, nbit=8, ks=5):
'''
calc glcm entropy
'''
glcm = fast_glcm(img, vmin, vmax, nbit, ks)
pnorm = glcm / np.sum(glcm, axis=(0,1)) + 1./ks**2
ent = np.sum(-pnorm * np.log(pnorm), axis=(0,1))
return ent
if __name__ == '__main__':
from skimage import data
img = data.camera()
h,w = img.shape
img[:,:w//2] = img[:,:w//2]//2+127
nbit = 8
ks = 5
mi, ma = 0, 255
glcm_mean = fast_glcm_mean(img, mi, ma, nbit, ks)
2.glcm example
#!--*--coding: utf-8--*--
import numpy as np
from skimage import data
from matplotlib import pyplot as plt
import fast_glcm
if __name__ == '__main__':
img = data.camera()
h,w = img.shape
glcm_mean = fast_glcm.fast_glcm_mean(img)
plt.imshow(glcm_mean)
plt.tight_layout()
plt.show()
3.plot example
#!--*--coding: utf-8--*--
import numpy as np
from skimage import data
from matplotlib import pyplot as plt
import fast_glcm
from PIL import Image
def main():
pass
if __name__ == '__main__':
img_file = "test.jpg"
img=np.array(Image.open(img_file).convert('L'))
h,w = img.shape
mean = fast_glcm.fast_glcm_mean(img)
std = fast_glcm.fast_glcm_std(img)
cont = fast_glcm.fast_glcm_contrast(img)
diss = fast_glcm.fast_glcm_dissimilarity(img)
homo = fast_glcm.fast_glcm_homogeneity(img)
asm, ene = fast_glcm.fast_glcm_ASM(img)
ma = fast_glcm.fast_glcm_max(img)
ent = fast_glcm.fast_glcm_entropy(img)
plt.figure(figsize=(10,4.5))
fs = 15
plt.subplot(2,5,1)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(img)
plt.title('original', fontsize=fs)
plt.subplot(2,5,2)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(mean)
plt.title('mean', fontsize=fs)
plt.subplot(2,5,3)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(std)
plt.title('std', fontsize=fs)
plt.subplot(2,5,4)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(cont)
plt.title('contrast', fontsize=fs)
plt.subplot(2,5,5)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(diss)
plt.title('dissimilarity', fontsize=fs)
plt.subplot(2,5,6)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(homo)
plt.title('homogeneity', fontsize=fs)
plt.subplot(2,5,7)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(asm)
plt.title('ASM', fontsize=fs)
plt.subplot(2,5,8)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(ene)
plt.title('energy', fontsize=fs)
plt.subplot(2,5,9)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(ma)
plt.title('max', fontsize=fs)
plt.subplot(2,5,10)
plt.tick_params(labelbottom=False, labelleft=False)
plt.imshow(ent)
plt.title('entropy', fontsize=fs)
plt.tight_layout(pad=0.5)
plt.savefig('output.jpg')
plt.show()