建议在阅读完LBP学习笔记(1)后再阅读下面的内容更容易上手
一.简述LBP旋转不变模式
旋转不变局部二值模式是指在局部二值模式(LBP)特征的基础上,通过对LBP特征进行旋转不变处理,得到具有旋转不变性的特征表示。由于LBP特征具有灰度不变性和旋转不变性等优点,在图像处理和计算机视觉领域中得到了广泛的应用。
旋转不变的LBP特征可以通过等价模式法和旋转不变权值法进行计算。通过等价模式法可以得到LBP特征的等价模式数目,从而消除了图像旋转对特征提取的影响。而通过旋转不变权值法,则可以对参考点周围的像素赋予不同的权值,以抑制某些旋转不变的影响。
在不使用旋转不变模式时,LBP特征计算简单,但会受到图像旋转和灰度变化等因素的影响,导致特征提取的不准确。采用旋转不变模式,可以避免这些因素的干扰,提高LBP特征在实际应用场景中的鲁棒性。
二.代码实现(关键函数及完整代码)
1.遍历像素,获取旋转不变模式特征
# 获取图像的LBP旋转不变模式特征
def lbp_revolve(self, image_array):
revolve_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)[0]
revolve_key = self.get_min_for_revolve(sum)
revolve_array[i, j] = self.revolve_map[revolve_key]
return revolve_array
sum是原始图片的灰度图经过LBP原始特征算法处理后得到的二进制序列
revolve_key是sum经过不断旋转得到的最小十进制数
之后将revolve_key作为revolve_map字典的键,将它对应的值(即像素值)赋值给revolve_array[i,j]
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
笔记(1)有图解
3.不断旋转二进制序列得到二进制序列的最小十进制值
# 获取二进制序列进行不断环形旋转得到新的二进制序列的最小十进制值
def get_min_for_revolve(self, arr):
values = []
circle = arr
circle.extend(arr)
for i in range(0, 8):
j = 0
sum = 0
bit_num = 0
while j < 8:
sum += circle[i + j] << bit_num
bit_num += 1
j += 1
values.append(sum)
return min(values)
传入的arr就是calute_basic_lbp中的sum,一个二进制序列,circle.extend(arr)就是在circle序列后面再加上一个一样的序列,这样每8个元素为一组遍历,遍历八组,就可以看成是序列在不断旋转最后转回到原来的序列排列,while循环是计算每一组二进制序列的十进制数,添加到values列表里并返回最小的十进制数。(<<是位左移运算符,作用是将该值向左移动bit_num位,如果circle[i+j]的值是3,bit_num是3,那么该代码行将会使sum的值增加24)
4.创建旋转不变模式36种特征字典
def __init__(self):
# revolve_map为旋转不变模式的36种特征值从小到大进行序列化编号得到的字典
self.revolve_map = {0: 0, 1: 1, 3: 2, 5: 3, 7: 4, 9: 5, 11: 6, 13: 7, 15: 8, 17: 9, 19: 10, 21: 11, 23: 12, 25: 13,
27: 14, 29: 15, 31: 16, 37: 17, 39: 18, 43: 19, 45: 20, 47: 21, 51: 22, 53: 23, 55: 24, 59: 25,
61: 26, 63: 27, 85: 28, 87: 29, 91: 30, 95: 31, 111: 32, 119: 33, 127: 34, 255: 35}
八位的二进制数不断旋转后后对应的十进制只有36种,所以把这36种的十进制值作为字典的键,键对应的值是像素值(0~35)
5.完整代码
import numpy as np
import cv2
from PIL import Image
from pylab import *
class LBP:
def __init__(self):
# revolve_map为旋转不变模式的36种特征值从小到大进行序列化编号得到的字典
self.revolve_map = {0: 0, 1: 1, 3: 2, 5: 3, 7: 4, 9: 5, 11: 6, 13: 7, 15: 8, 17: 9, 19: 10, 21: 11, 23: 12, 25: 13, 27: 14, 29: 15, 31: 16, 37: 17, 39: 18, 43: 19, 45: 20, 47: 21, 51: 22, 53: 23,55: 24,59: 25, 61: 26, 63: 27, 85: 28, 87: 29, 91: 30, 95: 31, 111: 32, 119: 33, 127: 34, 255: 35}
# 图像的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
# 获取二进制序列进行不断环形旋转得到新的二进制序列的最小十进制值
def get_min_for_revolve(self, arr):
values = []
circle = arr
circle.extend(arr)
for i in range(0, 8):
j = 0
sum = 0
bit_num = 0
while j < 8:
sum += circle[i + j] << bit_num
bit_num += 1
j += 1
values.append(sum)
return min(values)
# 获取图像的LBP旋转不变模式特征
def lbp_revolve(self, image_array):
revolve_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)[0]
revolve_key = self.get_min_for_revolve(sum)
revolve_array[i, j] = self.revolve_map[revolve_key]
return revolve_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_revolve_hist(self, img_array):
self.show_hist(img_array, [36], [0, 36])
# 显示图像
def show_image(self, image_array):
plt.imshow(image_array, cmap='Greys_r')
plt.show()
if __name__ == '__main__':
image = cv2.imread('F:/out_picture/train/001.jpg')
image_array = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
lbp = LBP()
plt.imshow(image_array, cmap='Greys_r') # 去掉参数就是热量图了
plt.title('Original')
plt.show()
#获取图像旋转不变LBP特征,并显示其统计直方图与特征图像
revolve_array = lbp.lbp_revolve(image_array)
lbp.show_revolve_hist(revolve_array)
lbp.show_image(revolve_array)