LBP算法以及Python实现

本文介绍图像处理中特征提取的常用算子 - LBP 算法。

LBP,全称Local Binary Pattern,局部二值模式,是一种能够描述图像纹理的算法,并且具有旋转不变性和灰度不变性等优点。本文会介绍最基本的LBP算法和其扩展。

参考:

LBP算法介绍

基本LBP

最基本的LBP算法与一些处理图像的算法相同,都是通过定义一个基本的算子,将算子作用在整张图像上,通过滑动窗口,来提取图像的纹理。最原始的LBP算子定义为3*3的窗口,将中心的像素值作为阈值,将其邻域与阈值比较,若邻域像素值大于等于阈值,则标记为1,否则标记为0。算法规定,将窗口左上角的像素点作为起始点,顺时针旋转,与阈值进行比较,得到一串8位的二进制数(全部都是0与1),将该二进制数转化为十进制,十进制数应该介于0至255之间,刚好与8位图像的灰度值对应,我们就将该十进制数作为该窗口中心点的像素值保存起来。当我们遍历整张图像后,就会得到一张新的图像,该图像就为提取出的纹理图。

如下图处理人脸上的这样一个像素,执行LBP算法需要以下步骤:

  1. 将图像灰度化处理,提取像素点相邻8个邻域的灰度值
  2. 将8个邻域的灰度值分别与中心灰度值作对比,比之大标记为1,反之标记为0
  3. 得到8个2值的点分别为10001101,将其转换回10进制141

则最后得到的141便是原来的像素点在LBP特征空间中的值。

image

对整幅图的所有像素点进行这样一个操作,可以得到一个LBP特征图片。

image

圆形LBP

在原始的LBP算法中,窗口的半径是固定的,这样就没有办法满足我们提取不同尺寸和频率纹理的需求,于是LBP算法进行改进,提出了圆形LBP算法。圆形LBP算法能够计算任意半径大小的邻域,并且将正方形扩展成圆形,在圆形上可以规定任意数量等间隔的采样点。

image

转换不变性

对于一般的LBP算法,我们通常都是取左上角作为起始点,当图片旋转时,我们可以理解为将LBP算子旋转,而图片不动,而LBP算子的旋转可以理解为算子中起始点的旋转。假设使用的LBP算子中有8个采样点,我们使每个采样点都变成起始点,顺时针进行计算,这样我们就得到了8个二进制数,我们将其转化为十进制数,取其中的最小的作为该中心点的像素值。这样无论图片怎样旋转,我们得到的LBP值都是一样的。

image

但是注意,LBP算法只具有旋转不变性,当图片进行镜像翻转时,得到的LBP值会发生变化。

等价模式

等价模式:当某个局部二进制模式所对应的循环二进制数从0到1或从1到0最多有两次跳变时,该局部二进制模式所对应的二进制就称为一个等价模式。

比如:00000000,11111111,11110010,10111111都是等价模式。

一个LBP算子可以产生不同的二进制模式,对于 LBP将会产生2p种模式。比如7∗7邻域内有236种模式。如此多的二值模式对于信息的提取和识别都是不利的。Ojala等认为,在实际图像中,绝大多数LPB模式最多只包含两次从1到0或从0到1的跳变。

混合模式:除了等价模式之外的称为混合模式。

改进后的LPB模式数由2 p(p为邻域集内的采集点数 ) 降维为p∗(p−1)+2 。维数减少,可以降低高频噪声的影响。Ojala认为等价模式占总模式中的绝大数。

对于边缘点的处理

对于图像边缘的点,由于通常不能用上述方法来处理成LBP特征点,一般来说有以下几种处理方法:

  1. 将边缘的提取不到周围特征的点不进行LBP处理,结果LBP特征图片的像素点会比之前少
  2. 将边缘不存在的邻域点值设置为0
  3. 将边缘的提取不到周围特征的点用原像素点的值取代

用Scikit实现LBP算法

官方示例

scikit-image是一个包含了若干图像特征提取方法的Python库,在scikit中包含了LBP算法。

SourceCode: https://github.com/scikit-image/scikit-image/blob/main/skimage/feature/texture.py#L287-L350

下面给出一个官网上的例子:


from skimage.transform import rotate
from skimage.feature import local_binary_pattern
from skimage import data
from skimage.color import label2rgb

# 设置LBP参数,选取半径为3的圆,取8*3的点
radius = 3
n_points = 8 * radius


def overlay_labels(image, lbp, labels):
    mask = np.logical_or.reduce([lbp == each for each in labels])
    return label2rgb(mask, image=image, bg_label=0, alpha=0.5)


def highlight_bars(bars, indexes):
    for i in indexes:
        bars[i].set_facecolor('r')


image = data.brick()
lbp = local_binary_pattern(image, n_points, radius, METHOD)


def hist(ax, lbp):
    n_bins = int(lbp.max() + 1)
    return ax.hist(lbp.ravel(), density=True, bins=n_bins, range=(0, n_bins),
                   facecolor='0.5')


# 绘制LBP直方图
fig, (ax_img, ax_hist) = plt.subplots(nrows=2, ncols=3, figsize=(9, 6))
plt.gray()

titles = ('edge', 'flat', 'corner')
w = width = radius - 1
edge_labels = range(n_points // 2 - w, n_points // 2 + w + 1)
flat_labels = list(range(0, w + 1)) + list(range(n_points - w, n_points + 2))
i_14 = n_points // 4            # 1/4th of the histogram
i_34 = 3 * (n_points // 4)      # 3/4th of the histogram
corner_labels = (list(range(i_14 - w, i_14 + w + 1)) +
                 list(range(i_34 - w, i_34 + w + 1)))

label_sets = (edge_labels, flat_labels, corner_labels)

for ax, labels in zip(ax_img, label_sets):
    ax.imshow(overlay_labels(image, lbp, labels))

for ax, labels, name in zip(ax_hist, label_sets, titles):
    counts, _, bars = hist(ax, lbp)
    highlight_bars(bars, labels)
    ax.set_ylim(top=np.max(counts[:-1]))
    ax.set_xlim(right=n_points + 2)
    ax.set_title(name)

ax_hist[0].set_ylabel('Percentage')
for ax in ax_img:
    ax.axis('off')

image

这里可以看到,对于砖块图片的边缘,面,以及角点,在LBP的只放图中对应的直方图区域是不同的,可以利用LBP的这种性质对图片区域进行分割和特征的识别。

opencv的LBPH识别器

opencv含有基于LBPH(H指Histogram)face识别器,可以拿来做面部识别,代码示例

X_train , X_test , y_train , y_test = train_test_split(faces , labels , test_size = test_size,
                                                          stratify = labels , random_state = random_state)
    
# define and train the LBP model
recognizer = cv2.face.LBPHFaceRecognizer_create(
radius = 2 , neighbors = 16 , grid_x = 9 , grid_y  = 9)

recognizer.train(X_train , y_train)

# initialize our predictions and confidence lists
predictions = []
confidence = []

# loop over the test data
for i in range(0 , len(X_test)):
# classify the face and update the predictions
# and confidence scores
(prediction , conf) = recognizer.predict(X_test[i])
predictions.append(prediction)
confidence.append(confidence)
posted @ 2022-07-31 15:14  Asp1rant  阅读(1734)  评论(0编辑  收藏  举报