LBP study notes (3)---equivalent mode features (code implementation)

        It is recommended to read the following content after reading the LBP study notes (1) to make it easier to get started

1. A brief description of the LBP equivalent model

As an algorithm for image texture analysis, the LBP operator will generate a large number of binary patterns during processing, which seriously affects the efficiency of pattern classification and recognition. In order to solve this problem, Ojala proposed to use an "equivalent pattern" (Uniform Pattern) to reduce the dimensionality of the pattern types of the LBP operator.

Specifically, the LBP equivalence mode refers to encoding the binary mode in the LBP calculation, so that adjacent binary modes are classified into the same equivalence class when certain conditions are met. In this way, the excessive number of binary patterns can be compressed into a relatively small number of equivalent patterns, thereby improving the efficiency and reliability of the algorithm. This strategy has been widely used in practical applications and has been applied in many computer vision fields, such as face recognition, handwritten digit recognition, corneal texture recognition, etc.

2. Code implementation (key functions and complete code)

1. Obtain equivalent mode features

# 获取图像的LBP原始模式特征
def lbp_basic(self, image_array):
    basic_array = np.zeros(image_array.shape, np.uint8)
    width = image_array.shape[0]
    height = image_array.shape[1]
    for i in range(1, width - 1):
        for j in range(1, height - 1):
            sum = self.calute_basic_lbp(image_array, i, j)[1]
            basic_array[i, j] = sum
    return basic_array


# 获取图像的LBP等价模式特征
def lbp_uniform(self, image_array):
    uniform_array = np.zeros(image_array.shape, np.uint8)
    basic_array = self.lbp_basic(image_array)
    width = image_array.shape[0]
    height = image_array.shape[1]
    for i in range(1, width - 1):
        for j in range(1, height - 1):
            k = basic_array[i, j] << 1
            if k > 255:
                k = k - 255
            xor = basic_array[i, j] ^ k #^是异或运算符,用于按位比较两个二进制数字,如果对应位上的数字不同,则该位的运算结果为1,否则为0
            num = self.calc_sum(xor)
            if num <= 2:
                uniform_array[i, j] = self.uniform_map[basic_array[i, j]]
            else:
                uniform_array[i, j] = 58
    return uniform_array

 Obtaining the original mode features has been introduced in note (1), and will not be repeated here.

Before obtaining the equivalent pattern features, first obtain the original pattern features, and pass it into the basic_array in the lbp_uniform function.

Traverse each eigenvalue of basic_array, shift the eigenvalue to the left by 1 bit, (here is the feature x2), and then perform XOR operation with the original eigenvalue without shift, compare bit by bit, get a new binary sequence, and then calculate If the number of digits of 1 in the new binary sequence is greater than two, they are all classified into one type of mode, so that their pixel values ​​​​are all equal to 58. If they are less than or equal to 2, their pixel values ​​​​are based on the characteristic values ​​​​of the original data. As the key in the uniform_map dictionary, assign the value corresponding to the key.

Exclusive OR: Exclusive OR (XOR) is a logical operator, usually used to compare the corresponding bits of two binary numbers, and the result is 1 if these bits are different, otherwise the result is 0. Its symbol is usually "^".

The reason for if k > 255 here:

Assume that there is a maximum eigenvalue of 255 in the incoming original mode eigenvalue matrix, and its corresponding binary sequence is [1,1,1,1,1,1,1,1]. If you do not make a judgment at this time, let it be left Shift one bit, then it will become 510, that is, [1,1,1,1,1,1,1,1,0], so the effective length of the data (1 as the highest digit to the right) becomes 9 , our binary sequence is only 8 bits, so let 510-255 =255, so as to avoid calculation overflow when the 8 pixels around the center pixel are all larger than the center pixel.

The reason for basic_array[i,j] ^ k:

Because the main purpose of using the equivalent mode is to extract the outline stripes in the picture and choose other unimportant points, then this texture feature must be continuous in a certain area, (it can be understood as a line, then a The root line must be continuous, it cannot be one point at a time), the abstraction is [0,0,1,1,1,1,0,0], [1,1,1,1,1 ,1,1,1], [0,0,0,0,0,0,0,0], [1,1,0,0,0,0,0,1], here is 1 to be continuous or 0 must be continuous, so that 0 to 1 or 1 to 0 will jump once, and this continuous binary sequence can only jump 0 or 2 times (in a circular arrangement, connected end to end), we will put it like this The eigenvalues ​​corresponding to the binary corresponding to the number of jumps less than 2 times are used as the keys in the uniform_map dictionary (there are 58).

Then, if you want to obtain this kind of binary sequence that can judge whether the line texture of the picture is continuous, the easiest way is to perform an XOR operation on the original mode eigenvalue and the value obtained by shifting it to the left by one bit.

continuous:

 Discontinuous:

 2. Get the decimal number of the binary sequence

# 图像的LBP原始特征计算算法:将图像指定位置的像素与周围8个像素比较
# 比中心像素大的点赋值为1,比中心像素小的赋值为0,返回得到的二进制序列
def calute_basic_lbp(self, image_array, i, j):
    sum = []
    num = 0
    if image_array[i - 1, j - 1] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 0
    else:
        sum.append(0)
    if image_array[i - 1, j] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 1
    else:
        sum.append(0)
    if image_array[i - 1, j + 1] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 2
    else:
        sum.append(0)
    if image_array[i, j - 1] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 3
    else:
        sum.append(0)
    if image_array[i, j + 1] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 4
    else:
        sum.append(0)
    if image_array[i + 1, j - 1] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 5
    else:
        sum.append(0)
    if image_array[i + 1, j] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 6
    else:
        sum.append(0)
    if image_array[i + 1, j + 1] > image_array[i, j]:
        sum.append(1)
        num += 2 ** 7
    else:
        sum.append(0)
    return sum, num

3. Get the digit of 1 after XOR

# 获取值r的二进制中1的位数
def calc_sum(self, r):
    num = 0
    while (r):
        r &= (r - 1)
        num += 1
    return num

4. Create an equivalent pattern feature dictionary

def __init__(self):
    # uniform_map为等价模式的58种特征值从小到大进行序列化编号得到的字典  58种
    self.uniform_map = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 6: 5, 7: 6, 8: 7, 12: 8, 14: 9, 15: 10, 16: 11, 24: 12, 28: 13,
                        30: 14, 31: 15, 32: 16, 48: 17, 56: 18, 60: 19, 62: 20, 63: 21, 64: 22, 96: 23, 112: 24,
                        120: 25, 124: 26, 126: 27, 127: 28, 128: 29, 129: 30, 131: 31, 135: 32, 143: 33, 159: 34,
                        191: 35, 192: 36, 193: 37, 195: 38, 199: 39, 207: 40, 223: 41, 224: 42, 225: 43, 227: 44,
                        231: 45, 239: 46, 240: 47, 241: 48, 243: 49, 247: 50, 248: 51, 249: 52, 251: 53, 252: 54,
                        253: 55, 254: 56, 255: 57}

 The keys in the dictionary are the original mode eigenvalues ​​whose number of jumps does not exceed 2, there are 58, and there are a total of 59 modes that are equivalent and unchanged, and those with a number of jumps exceeding 2 are unified into one mode, and the values ​​​​corresponding to the keys are these The pixel value in mode.

5. Complete code

import numpy as np
import cv2
from PIL import Image
from pylab import *


class LBP:
    def __init__(self):
        # uniform_map为等价模式的58种特征值从小到大进行序列化编号得到的字典  58种
        self.uniform_map = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 6: 5, 7: 6, 8: 7, 12: 8,14: 9, 15: 10, 16: 11, 24: 12, 28: 13, 30: 14, 31: 15, 32: 16, 48: 17, 56: 18, 60: 19, 62: 20, 63: 21, 64: 22, 96: 23, 112: 24,120: 25, 124: 26, 126: 27, 127: 28, 128: 29, 129: 30, 131: 31, 135: 32,143: 33, 159: 34, 191: 35, 192: 36, 193: 37, 195: 38, 199: 39, 207: 40,223: 41, 224: 42, 225: 43, 227: 44, 231: 45, 239: 46, 240: 47, 241: 48,243: 49, 247: 50, 248: 51, 249: 52, 251: 53, 252: 54, 253: 55, 254: 56,255: 57}

    # 图像的LBP原始特征计算算法:将图像指定位置的像素与周围8个像素比较
    # 比中心像素大的点赋值为1,比中心像素小的赋值为0,返回得到的二进制序列
    def calute_basic_lbp(self, image_array, i, j):
        sum = []
        num = 0
        if image_array[i - 1, j - 1] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 0
        else:
            sum.append(0)
        if image_array[i - 1, j] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 1
        else:
            sum.append(0)
        if image_array[i - 1, j + 1] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 2
        else:
            sum.append(0)
        if image_array[i, j - 1] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 3
        else:
            sum.append(0)
        if image_array[i, j + 1] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 4
        else:
            sum.append(0)
        if image_array[i + 1, j - 1] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 5
        else:
            sum.append(0)
        if image_array[i + 1, j] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 6
        else:
            sum.append(0)
        if image_array[i + 1, j + 1] > image_array[i, j]:
            sum.append(1)
            num += 2 ** 7
        else:
            sum.append(0)
        return sum, num


    # 获取值r的二进制中1的位数
    def calc_sum(self, r):
        num = 0
        while (r):
            r &= (r - 1)
            num += 1
        return num

    # 获取图像的LBP原始模式特征
    def lbp_basic(self, image_array):
        basic_array = np.zeros(image_array.shape, np.uint8)
        width = image_array.shape[0]
        height = image_array.shape[1]
        for i in range(1, width - 1):
            for j in range(1, height - 1):
                sum = self.calute_basic_lbp(image_array, i, j)[1]
                basic_array[i, j] = sum
        return basic_array


    # 获取图像的LBP等价模式特征
    def lbp_uniform(self, image_array):
        uniform_array = np.zeros(image_array.shape, np.uint8)
        basic_array = self.lbp_basic(image_array)
        width = image_array.shape[0]
        height = image_array.shape[1]
        for i in range(1, width - 1):
            for j in range(1, height - 1):
                k = basic_array[i, j] << 1
                if k > 255:
                    k = k - 255
                xor = basic_array[i, j] ^ k #^是异或运算符,用于按位比较两个二进制数字,如果对应位上的数字不同,则该位的运算结果为1,否则为0
                num = self.calc_sum(xor)
                if num <= 2:
                    uniform_array[i, j] = self.uniform_map[basic_array[i, j]]
                else:
                    uniform_array[i, j] = 58
        return uniform_array


    # 绘制指定维数和范围的图像灰度归一化统计直方图
    def show_hist(self, img_array, im_bins, im_range):
        #直方图的x轴是灰度值,y轴是图片中具有同一个灰度值的点的数目, [img_array]原图像图像格式为uint8或float32,[0]0表示灰度,None无掩膜
        hist = cv2.calcHist([img_array], [0], None, im_bins, im_range)
        hist = cv2.normalize(hist, hist).flatten() #对计算出来的直方图数据进行归一化处理,并将结果扁平化为1D数组
        # print(hist)
        plt.plot(hist, color='r')
        plt.xlim(im_range) #设置X轴的取值范围
        plt.show()

    # 绘制图像原始LBP特征的归一化统计直方图
    def show_basic_hist(self, img_array):
        self.show_hist(img_array, [256], [0, 256])

    # 绘制图像等价模式LBP特征的归一化统计直方图
    def show_uniform_hist(self, img_array):
        self.show_hist(img_array, [60], [0, 60]) #[60]把像素值区间分成60部分,[0, 60]像素值区间

    # 显示图像
    def show_image(self, image_array):
        plt.imshow(image_array, cmap='Greys_r')
        plt.show()

if __name__ == '__main__':
    image = cv2.imread('F:/in_picture/001.png')
    image_array = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #BGR格式图像转灰度图像
    lbp = LBP()
    plt.imshow(image_array, cmap='Greys_r')  # 去掉参数就是热量图了
    plt.title('Original')
    plt.show()

    # 获取图像原始LBP特征,并显示其统计直方图与特征图像
    basic_array = lbp.lbp_basic(image_array)
    lbp.show_basic_hist(basic_array)
    lbp.show_image(basic_array)

    #获取图像等价模式LBP特征,并显示其统计直方图与特征图像
    uniform_array=lbp.lbp_uniform(image_array)
    lbp.show_uniform_hist(uniform_array)
    lbp.show_image(uniform_array)

3. Summary

The equivalent invariant mode mainly uses the XOR logic operation to filter out the continuous lines. Before performing the XOR operation, the data must be processed to avoid calculation overflow.

If there is any mistake, please correct me!

Guess you like

Origin blog.csdn.net/qq_66036911/article/details/130335915