特征提取之灰度游程(行程)矩阵-GLRLM

  最近在项目中要进行图像的特征提取工作,为了便于以后查阅和使用,遂写博客以记录。
  说到图像的纹理特征,大家能想到的就是灰度共生矩阵(Gray-Level Co-Occurrence Matrix, GLCM)、灰度游程矩阵(Gray-Level Run-Length Matrix, GLRLM)、局部二值模式(Local Binary Pattern, LBP)和方向梯度直方图(Histogram of Oriented Gradient, HOG)。


  这篇博文主要总结一下灰度游程矩阵,顾名思义,灰度游程矩阵就是灰度值游行的长度所组成的矩阵。我们直接上定义,记灰度共生矩阵为D[i, j, theta],其中i表示原始图像中的像素值,i的所有取值为原始图像的灰度级数,j表示像素值所游走的长度,也就是在图像中有j个连续的i出现,theta表示计算的方向,一般有0度45度90度135度


  下面以一个例子说明:

Img = [[1, 1, 0, 0],
       [1, 1, 0, 0],
       [0, 0, 2, 2],
       [0, 0, 2, 2]]

  我们以theta=0计算GLRLMtheta=0也就是按照水平方向计算,按照上述可以将灰度游程矩阵表示为D[i, j, theta]D.shape=(3, 4, 1),其中3表示灰度阶数[0, 1, 2]4表示最长的数据值,即对角线长为41表示theta的个数,即只有theta=0。按照水平方向计算,统计像素00连续出现4次为00连续出现3次为00连续出现2次为40连续出现1次为0这里需要注意:因为在计算0连续出现2次时已经计算了所有的0,所以再计算0出现一次时就不将刚才计算过的0列入其中),所以像素0结果为[0, 4, 0, 0]。按照以上方法计算像素1结果为[0, 2, 0, 0],所以有:

按照theta=0度计算结果:
P_0 = [[0, 4, 0, 0],
       [0, 2, 0, 0],
       [0, 2, 0, 0]]

按照theta=45度计算结果:
P_45 = [[4, 0, 0, 1],
        [2, 1, 0, 0],
        [2, 1, 0, 0]]

按照theta=90度计算结果:
P_90 = [[0, 4, 0, 0],
        [0, 2, 0, 0],
        [0, 2, 0, 0]]

按照theta=135度计算结果:
P_135 = [[4, 2, 0, 0],
         [2, 1, 0, 0],
         [2, 1, 0, 0]]

此处要注意两点:

  1. 每次计算时从最长的像素串开始统计;
  2. 对于已经统计过的像素串,在计算比其长度小的子串时应该舍弃。

说清楚了计算方法,我们接下来上代码:

def getGrayLevelRumatrix(self, array, theta):
        '''
        计算给定图像的灰度游程矩阵
        参数:
        array: 输入,需要计算的图像
        theta: 输入,计算灰度游程矩阵时采用的角度,list类型,可包含字段:['deg0', 'deg45', 'deg90', 'deg135']
        glrlm: 输出,灰度游程矩阵的计算结果
        '''
        P = array
        x, y = P.shape
        min_pixels = np.min(P)   # 图像中最小的像素值
        run_length = max(x, y)   # 像素的最大游行长度
        num_level = np.max(P) - np.min(P) + 1   # 图像的灰度级数

        deg0 = [val.tolist() for sublist in np.vsplit(P, x) for val in sublist]   # 0度矩阵统计
        deg90 = [val.tolist() for sublist in np.split(np.transpose(P), y) for val in sublist]   # 90度矩阵统计
        diags = [P[::-1, :].diagonal(i) for i in range(-P.shape[0]+1, P.shape[1])]   #45度矩阵统计
        deg45 = [n.tolist() for n in diags]
        Pt = np.rot90(P, 3)   # 135度矩阵统计
        diags = [Pt[::-1, :].diagonal(i) for i in range(-Pt.shape[0]+1, Pt.shape[1])]
        deg135 = [n.tolist() for n in diags]

        def length(l):
            if hasattr(l, '__len__'):
                return np.size(l)
            else:
                i = 0
                for _ in l:
                    i += 1
                return i

        glrlm = np.zeros((num_level, run_length, len(theta)))   # 按照统计矩阵记录所有的数据, 第三维度表示计算角度
        for angle in theta:
            for splitvec in range(0, len(eval(angle))):
                flattened = eval(angle)[splitvec]
                answer = []
                for key, iter in groupby(flattened):   # 计算单个矩阵的像素统计信息
                    answer.append((key, length(iter)))   
                for ansIndex in range(0, len(answer)):
                    glrlm[int(answer[ansIndex][0]-min_pixels), int(answer[ansIndex][1]-1), theta.index(angle)] += 1   # 每次将统计像素值减去最小值就可以填入GLRLM矩阵中
        return glrlm

  做简单说明,每次计算时按照需要计算的角度将矩阵进行整理然后统计,填入初始化的GLRLM矩阵中。计算0度,直接对原始图像按行整理,然后使用groupby()函数对每行的数进行统计,例如[0, 0, 2, 3, 3, 4, 6]groupby的结果为[(0, 2), (2, 1), (3, 2), (4, 1), (6, 1)],圆括号内的第一个数代表真实的像素值,第二个值代表像素值出现的次数,计算90度时只需将原始矩阵转置,然后采用同样方法统计。计算135度,对于一个矩阵,采用diagonal()函数取对角线,采用加位移参数的方式取遍所有135度值,使用groupby()完成统计,计算45度时只需将原始矩阵顺时针旋转270度,同样采用取对角线方式计算即可。


  灰度游程矩阵只是对图像像素信息的度量和统计,在实际使用的过程中还需要针对生成的灰度游程矩阵进行计算,得到基于灰度共生矩阵的图像特征信息。下面代码实现了对11个灰度游程矩阵特征的提取:
在实际代码之前先写几个公用的函数,完成下标ij的计算(calcuteIJ())、按照指定维度进行乘除操作(apply_over_degree())和计算所有像素和(calcuteS()),如下:

def apply_over_degree(function, x1, x2):
        rows, cols, nums = x1.shape
        result = np.ndarray((rows, cols, nums))
        for i in range(nums):
            print(x1[:, :, i])
            result[:, :, i] = function(x1[:, :, i], x2)
            print(result[:, :, i])
        result[result == np.inf] = 0
        result[np.isnan(result)] = 0
        return result

    def calcuteIJ(rlmatrix):
        gray_level, run_length, _ = rlmatrix.shape
        I, J = np.ogrid[0:gray_level, 0:run_length]
        return I, J+1

    def calcuteS(rlmatrix):
        return np.apply_over_axes(np.sum, rlmatrix, axes=(0, 1))[0, 0]
  1. Short Run Emphasis(SRE)
    image.png
def getShortRunEmphasis(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.divide, rlmatrix, (J*J)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S
  1. Long Run Emphasis(LRE)
    image.png
def getLongRunEmphasis(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.multiply, rlmatrix, (J*J)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S
  1. Gray Level Non-Uniformity(GLN)
    image.png
def getGrayLevelNonUniformity(rlmatrix):
        G = np.apply_over_axes(np.sum, rlmatrix, axes=1)
        numerator = np.apply_over_axes(np.sum, (G*G), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

以下同上述一样,就不具体列出来了


image.png
# 4. RLN
    def getRunLengthNonUniformity(rlmatrix):
        R = np.apply_over_axes(np.sum, rlmatrix, axes=0)
        numerator = np.apply_over_axes(np.sum, (R*R), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

    # 5. RP
    def getRunPercentage(rlmatrix):
        gray_level, run_length, _ = rlmatrix.shape
        num_voxels = gray_level * run_length
        return self.calcuteS(rlmatrix) / num_voxels

    # 6. LGLRE
    def getLowGrayLevelRunEmphasis(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.divide, rlmatrix, (I*I)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

    # 7. HGLRE
    def getHighGrayLevelRunEmphais(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.multiply, rlmatrix, (I*I)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

    # 8. SRLGLE
    def getShortRunLowGrayLevelEmphasis(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.divide, rlmatrix, (I*I*J*J)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

    # 9. SRHGLE
    def getShortRunHighGrayLevelEmphasis(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        temp = self.apply_over_degree(np.multiply, rlmatrix, (I*I))
        print('-----------------------')
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.divide, temp, (J*J)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

    # 10. LRLGLE
    def getLongRunLow(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        temp = self.apply_over_degree(np.multiply, rlmatrix, (J*J), axes=(0, 1))
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.divide, temp, (J*J)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

    # 11. LRHGLE
    def getLongRunHighGrayLevelEmphais(rlmatrix):
        I, J = self.calcuteIJ(rlmatrix)
        numerator = np.apply_over_axes(np.sum, self.apply_over_degree(np.multiply, rlmatrix, (I*I*J*J)), axes=(0, 1))[0, 0]
        S = self.calcuteS(rlmatrix)
        return numerator / S

以上代码参考github:https://github.com/nkma1989/Image-segmentation-and-feature-extraction-and-calling-Azure-ML-Model/blob/master/functions.py
如果有什么不对之处,希望大家指正,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容