LBP 연구 노트(3)---등가 모드 기능(코드 구현)

LBP 연구 노트(1)를         읽은 후 다음 내용을 읽으면 보다 쉽게 ​​시작할 수 있습니다.

1. LBP 등가 모델에 대한 간략한 설명

이미지 질감 분석을 위한 알고리즘으로서 LBP 연산자는 처리 중에 많은 이진 패턴을 생성하여 패턴 분류 및 인식의 효율성에 심각한 영향을 미칩니다. 이 문제를 해결하기 위해 Ojala는 LBP 연산자의 패턴 유형의 차원을 줄이기 위해 "등가 패턴"(Uniform Pattern)을 사용할 것을 제안했습니다.

구체적으로 LBP 등가 모드는 LBP 계산에서 이진 모드를 인코딩하여 특정 조건이 충족되면 인접한 이진 모드가 동일한 등가 클래스로 분류되도록 하는 것을 말합니다. 이러한 방식으로 과도한 수의 이진 패턴을 상대적으로 적은 수의 등가 패턴으로 압축할 수 있으므로 알고리즘의 효율성과 신뢰성을 향상시킬 수 있습니다. 이 전략은 실제 응용 분야에서 널리 사용되었으며 얼굴 인식, 필기 숫자 인식, 각막 질감 인식 등과 같은 많은 컴퓨터 비전 분야에 적용되었습니다.

2. 코드 구현(주요 기능 및 전체 코드)

1. 동등한 모드 기능 얻기

# 获取图像的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

 원래 모드 기능을 얻는 방법은 참고(1)에서 소개했으며 여기서는 반복하지 않습니다.

동등한 패턴 특징을 얻기 전에 먼저 원래 패턴 특징을 얻고 lbp_uniform 함수의 basic_array에 전달합니다.

basic_array의 각 고유값을 탐색하고 고유값을 왼쪽으로 1비트 이동(여기서는 x2 특성)한 다음 이동하지 않고 원래 고유값과 XOR 연산을 수행하고 비트별로 비교하여 새로운 이진 시퀀스를 얻은 다음 계산합니다. 새로운 이진수열에서 1의 자릿수가 2보다 크면 모두 한 종류의 모드로 분류되므로 해당 픽셀 값은 모두 58이 됩니다. 2, 그들의 픽셀 값은 원본 데이터의 특성 값을 기준으로 하여 uniform_map 사전의 키로 해당 키에 해당하는 값을 할당합니다.

배타적 OR: 배타적 OR(XOR)은 일반적으로 두 이진수의 해당 비트를 비교하는 데 사용되는 논리 연산자이며, 이 비트가 다르면 결과가 1이고 그렇지 않으면 결과가 0입니다. 기호는 일반적으로 "^"입니다.

k > 255인 이유는 다음과 같습니다.

들어오는 원본 모드 고유값 행렬에서 최대 고유값이 255이고 이에 대응하는 이진수열이 [1,1,1,1,1,1,1,1]이라고 가정합니다. 1비트 이동하면 510, 즉 [1,1,1,1,1,1,1,1,0]이 되므로 데이터의 유효 길이(1은 오른쪽의 가장 높은 숫자)는 9 가 되고 이진 시퀀스는 8비트이므로 510-255 =255로 하여 중앙 픽셀 주변의 8개 픽셀이 모두 중앙 픽셀보다 클 때 계산 오버플로를 방지합니다.

basic_array[i,j] ^ k의 이유:

등가 모드를 사용하는 주요 목적은 그림에서 윤곽선 줄무늬를 추출하고 중요하지 않은 다른 점을 선택하는 것이기 때문에 이 텍스처 기능은 특정 영역에서 연속적이어야 합니다. 연속적이어야 하며 한 번에 한 지점일 수 없음) 추상화는 [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], 여기서 1은 연속적이거나 0은 연속적이어야 합니다. , 그래서 0에서 1 또는 1에서 0은 한 번 점프하고 이 연속 이진 시퀀스는 0 또는 2번만 점프할 수 있습니다(원형 배열에서 끝에서 끝까지 연결됨). 2회 미만의 점프 횟수에 해당하는 바이너리가 uniform_map 사전(58개 있음)의 키로 사용됩니다.

그런 다음 그림의 선 질감이 연속적인지 여부를 판단할 수 있는 이러한 이진 시퀀스를 얻으려면 원래 모드 고유값과 왼쪽으로 이동하여 얻은 값을 XOR 연산하는 것이 가장 쉬운 방법입니다. 한 비트.

마디 없는:

 끊어진:

 2. 이진 시퀀스의 십진수 얻기

# 图像的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. XOR 후 1의 자리수 구하기

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

4. 동등한 패턴 기능 사전 생성

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}

 사전에 있는 키는 점프 횟수가 2를 초과하지 않는 원래의 모드 고유값으로 58개이며, 동일하고 변경되지 않은 모드는 총 59개이며, 점프 횟수가 2를 초과하는 것은 통일한다. 하나의 모드로, 키에 해당하는 값은 다음과 같습니다. 모드의 픽셀 값.

5. 완전한 코드

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. 요약

등가 불변 모드는 주로 XOR 논리 연산을 사용하여 연속 라인을 필터링합니다.XOR 연산을 수행하기 전에 계산 오버플로를 방지하기 위해 데이터를 처리해야 합니다.

틀린 부분이 있으면 수정 부탁드립니다!

추천

출처blog.csdn.net/qq_66036911/article/details/130335915