使用Python實現圖像LBP特征提取的操作方法
一、LBP特征介紹
LBP特征叫做局部二值模式,常用于紋理特征提取,并在紋理分類中具有較強的區(qū)分能力。它主要是利用結構法思想分析固定窗口特征,再利用統(tǒng)計法做整體的特征提取,是一種理論簡單但功能強大的紋理分析算法。
LBP的基本思想:它利用圖像中的每一個點和鄰域中點的灰度值的差異構成圖像的細節(jié)紋理,用其中心像素的灰度值作為閾值,與它的鄰域中的像素灰度值相比較得到一個_8bit的二進制碼_來表達局部紋理特征。
二、LBP特征描述
原始的LBP算子定義為在3×3的窗口內,以窗口中心像素為閾值,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大于中心像素值,則該像素點的位 置被標記為1,否則為0。這樣,3×3鄰域內的8個點經比較可產生8位二進制(通常轉換為十進制數即LBP碼,共256種),即得到該窗口中心像素點的LBP 值,并用這個值來反映該區(qū)域的紋理信息。

對于一幅大小是W*H的圖像,因為邊緣像素無法計算8位的LBP值,所以將LBP值轉換為灰度圖像時,它的大小是(W-2)*(H-2).
三、一些改進版本的LBP
1.圓形LBP算子
圓形LBP算子即采用以中心點為圓心的圓形鄰域代替上文中的正方形鄰域。鄰域尺寸可以由半徑R和采樣點P確定。

2.旋轉不變的LBP算子
上文中可以看出,LBP算子是灰度不變的,但卻_不是旋轉不變的,圖像的旋轉就會得到不同的LBP值。因此將LBP算子進行了擴展,提出了具有旋轉不變性的LBP算子,即不斷旋轉圓形鄰域得到一系列初始定義的LBP值,取其最小值作為該鄰域的LBP值_。
舉一個簡單的例子,對于11110000來說,其旋轉后能夠得到11100001、10000111等值。這一個算子的旋轉不變的LBP值就是其旋轉后能得到的最小值00001111。
3.LBP等價模式
為了解決LBP模式過多的問題,提出了“等價模式”這一個概念。當某個LBP所對應的循環(huán)二進制數從0到1或從1到0最多有兩次跳變時,該LBP所對應的二進制就稱為一個等價模式類。如00000000(0次跳變),00000111(只含一次從0到1的跳變),10001111(先由1跳到0,再由0跳到1,共兩次跳變)都是等價模式類。
這里對“模式”進行具體闡述:“模式”可以理解為LBP特征值的范圍、種類等等。比如常用的3*3大小的正方形LBP算子的LBP模式就是256。引入這個“等價模式”主要是想要減少模式的數量,方便運算。
在統(tǒng)計時可以將這些等價模式對應的LBP的值按照大小進行映射;將其他不等價的模式歸為一類。
比如,一種常用的操作就是將256中LBP算子中58種等價模式按照大小映射到0-57中,將其余的不屬于等價模式的值規(guī)定為58。比如0被映射為0,255(也是一個等價模式,0次跳變)就被映射為57。具體操作可以再下面的代碼中進一步查看。當然,讀者也可以根據需要自己定義其他的映射法則。
經過這種等價操作,模式數量從2P種減少為 P(P-1)+2種。
四、提取LBP算子的步驟
首先將檢測窗口劃分為16×16的小區(qū)域(cell)
對于每個cell中的一個像素,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大于中心像素值,則該像素點的位置被標記為1,否則為0。這樣,3×3鄰域內的8個點經比較可產生8位二進制數,即得到該窗口中心像素點的LBP值;
然后計算每個cell的直方圖,即每個數字(假定是十進制數LBP值)出現的頻率;然后對該直方圖進行歸一化處理。
最后_將得到的每個cell的統(tǒng)計直方圖連接成為一個特征向量_,也就是整幅圖的LBP紋理特征向量;
最后便可利用SVM或者其他機器學習算法進行分類了。
五、提取效果
下圖展示的效果依次為原圖、圓形LBP算子提取結果、旋轉不變LBP結果和等價模式的提取結果。

六、代碼實現
對于初學者來說,手寫代碼實現提取特征效果能夠鍛煉代碼能力和加強對特征的理解,讀者可以參照下面的代碼進行學習。
import cv2
import numpy as np
import matplotlib.pyplot as plt
class LBP():
def __init__(self, img,cell_size):
self.img = img #灰度圖
self.height, self.width = img.shape[:2]
self.cell_size = cell_size
# 傳進來一個int型8位整數 找到旋轉后最小的值
def find_min(self,code):
min = code
for i in range(8):
code = (code << 1) | (code >> 7)
if code < min:
min = code
return min
def lbp_circle(self):
# 圓形LBP算子 這里寫成了P=8,R=1 實際上這個與矩形3*3的LBP算子是一樣的
lbp = np.zeros((self.height, self.width), np.uint8) #統(tǒng)計的lbp是比原來少2行2列的,這里為了方便拆分成cell直接把四周的lbp的值置為0
for i in range(1, self.height - 1):
for j in range(1, self.width - 1):
center = self.img[i, j]
code = 0
code |= (self.img[i - 1, j - 1] >= center) << 7
code |= (self.img[i - 1, j] >= center) << 6
code |= (self.img[i - 1, j + 1] >= center) << 5
code |= (self.img[i, j + 1] >= center) << 4
code |= (self.img[i + 1, j + 1] >= center) << 3
code |= (self.img[i + 1, j] >= center) << 2
code |= (self.img[i + 1, j - 1] >= center) << 1
code |= (self.img[i, j - 1] >= center) << 0
lbp[i, j] = code
return lbp
# 旋轉不變模式
def lbp_uniform(self):
lbp = self.lbp_circle()
# 圓形LBP算子 這里寫成了P=8,R=1 實際上這個與矩形3*3的LBP算子是一樣的
lbp_uniform= np.zeros((self.height, self.width), np.uint8) #統(tǒng)計的lbp是比原來少2行2列的,這里為了方便拆分成cell直接把四周的lbp的值置為0
# 旋轉不變模式 找到旋轉中的最小值
for i in range(1, self.height - 1):
for j in range(1, self.width - 1):
lbp_uniform[i,j] = self.find_min(lbp[i, j])
return lbp_uniform
# 等價模式 這里將等價的模式按照大小映射到0-57中,然后將不等價模式標記為58
def lbp_equivalent(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_equivalent = np.zeros((self.height, self.width), np.uint8) #統(tǒng)計的lbp是比原來少2行2列的,這里為了方便拆分成cell直接把四周的lbp的值置為0
lbp = self.lbp_circle()
for i in range(1, self.height - 1):
for j in range(1, self.width - 1):
if lbp[i, j] in uniform_map:
# 按照字典序賦值
lbp_equivalent[i, j] = uniform_map[lbp[i, j]]
else:
lbp_equivalent[i, j] = 58
return lbp_equivalent
# 統(tǒng)計每個cell的直方圖 并對其進行歸一化 這里使用的是L2范數歸一化
def lbp_histogram(self,lbp):
bin_size = lbp.max() + 1 # 獲取直方圖的維數
# 統(tǒng)計每個cell的直方圖
cell_histogram = np.zeros((self.height // self.cell_size, self.width // self.cell_size, bin_size), np.float32)
for i in range(self.height // self.cell_size):
for j in range(self.width // self.cell_size):
cell = lbp[i * self.cell_size:(i + 1) * self.cell_size, j * self.cell_size:(j + 1) * self.cell_size]
cell_histogram[i, j] = np.bincount(cell.flatten(), minlength=bin_size)
# 對每個cell的直方圖進行歸一化 這里使用的是L2范數歸一化
for i in range(self.height // self.cell_size):
for j in range(self.width // self.cell_size):
cell_histogram[i, j] = cell_histogram[i, j] / np.linalg.norm(cell_histogram[i, j])
return cell_histogram
# 拼接每個cell,得到最后的LBP特征向量
def lbp_feature(self,lbp):
cell_histogram = self.lbp_histogram(lbp)
# 拼接每個cell,得到最后的LBP特征向量 是一個一維向量
lbp_feature = np.zeros((cell_histogram.shape[0] * cell_histogram.shape[1] * cell_histogram.shape[2]), np.float32)
for i in range(cell_histogram.shape[0]):
for j in range(cell_histogram.shape[1]):
for k in range(cell_histogram.shape[2]):
lbp_feature[i * cell_histogram.shape[1] * cell_histogram.shape[2] + j * cell_histogram.shape[2] + k] = cell_histogram[i, j, k]
return lbp_feature
if __name__ == '__main__':
img = cv2.imread('1.png')
# 灰度圖讀取
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
lbp = LBP(img,16) #這里設置cell大小為16*16
lbp_circle = lbp.lbp_circle()
lbp_uniform = lbp.lbp_uniform()
lbp_equivalent = lbp.lbp_equivalent()
print("圓形LBP特征向量:",lbp.lbp_feature(lbp_circle)) # 圓形LBP特征向量
print("旋轉不變LBP特征向量:",lbp.lbp_feature(lbp_uniform)) # 旋轉不變LBP特征向量
print("等價LBP特征向量:",lbp.lbp_feature(lbp_equivalent)) # 等價LBP特征向量
# 顯示原圖和 lbp
img = cv2.imread('1.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 將 BGR 轉換為 RGB 以正確顯示
plt.subplot(2, 2, 1)
plt.imshow(img)
plt.title('Original Image')
plt.subplot(2, 2, 2)
plt.imshow(lbp_circle, cmap='gray',vmin=0, vmax=255)
plt.title('LBP Circle')
plt.subplot(2, 2, 3)
plt.imshow(lbp_uniform, cmap='gray',vmin=0, vmax=255)
plt.title('LBP Uniform')
plt.subplot(2, 2, 4)
plt.imshow(lbp_equivalent, cmap='gray',vmin=0, vmax=255)
plt.title('LBP Equivalent')
# 保存圖像
plt.savefig('1_lbp_result.png')
plt.show()
到此這篇關于使用Python實現圖像LBP特征提取的操作方法的文章就介紹到這了,更多相關Python 圖像LBP提取內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

