Opencv之LBP特征(算法)


LBP(Local Binary Pattern),即局部二進制模式,對一個像素點以半徑r畫一個圈,在圈上取K個點(一般為8),這K個點的值(像素值大於中心點為1,否則為0)組成K位二進制數。此即局部二進制模式,實際中使用的是LBP特征譜的直方統計圖。在舊版的Opencv里,使用CvHaarClassifierCascade函數,只支持Harr特征。新版使用CascadeClassifier類,還可以支持LBP特征。Opencv的人臉識別使用的是Extended LBP(即circle_LBP),其LBP特征值的模式為256(0-255)種。

優點:

1,旋轉不變性(局部二進制循環左移或右移其表示不變)

2,一定程度上消除了光照變化的問題

3,紋理特征維度低,計算速度快

缺點:

1,當光照變化不均勻時,各像素間的大小關系被破壞,對應的LBP算子也就發生了變化

2,通過引入旋轉不變的定義,使LBP算子更具魯棒性。但這也使得LBP算子丟失了方向信息(如使局部二進制左移或右移,結果是一樣的,但是圖像不一樣)

 以下介紹若干中LBP:

 

1,原始LBP。基於方框選取中心點周圍8個像素,構成8位二進制

 

# 以下不再重復這個部分
import cv2
import numpy as np

image_path=your_img_path
# 原始LBP算法:選取中心點周圍的8個像素點,大於中心點為1,小於為0,將這些1或0順時針串成8位二進制,即最終表示
def origin_LBP(img):
    dst = np.zeros(img.shape,dtype=img.dtype)
    h,w=img.shape
    start_index=1
    for i in range(start_index,h-1):
        for j in range(start_index,w-1):
            center = img[i][j]
            code = 0
#             順時針,左上角開始的8個像素點與中心點比較,大於等於的為1,小於的為0,最后組成8位2進制
            code |= (img[i-1][j-1] >= center) << (np.uint8)(7)  
            code |= (img[i-1][j  ] >= center) << (np.uint8)(6)  
            code |= (img[i-1][j+1] >= center) << (np.uint8)(5)  
            code |= (img[i  ][j+1] >= center) << (np.uint8)(4)  
            code |= (img[i+1][j+1] >= center) << (np.uint8)(3)  
            code |= (img[i+1][j  ] >= center) << (np.uint8)(2)  
            code |= (img[i+1][j-1] >= center) << (np.uint8)(1)  
            code |= (img[i  ][j-1] >= center) << (np.uint8)(0)  
            dst[i-start_index][j-start_index]= code
    return dst
# 讀入灰度圖
gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# LBP處理
org_lbp = origin_LBP(gray)
cv2.imshow('img', gray) cv2.imshow(
'org_lbp', org_lbp) # 若針對視頻取圖片,delay=k時表示下一幀在kms后選取 cv2.waitKey(0)

 

2,Extended LBP

 

# 使用圓形選取框替代矩形框選:給定半徑為r(半徑越小,紋理越細),在此圓上選擇K個點(選取點越多,亮度越高),同樣,逆/順時針組成K為二進制
# 稱為extend LBP
def circular_LBP(img, radius=3, neighbors=8):
    h,w=img.shape
    dst = np.zeros((h-2*radius, w-2*radius),dtype=img.dtype)
    for i in range(radius,h-radius):
        for j in range(radius,w-radius):
            # 獲得中心像素點的灰度值
            center = img[i,j]
            for k in range(neighbors):
                # 計算采樣點對於中心點坐標的偏移量rx,ry
                rx = radius * np.cos(2.0 * np.pi * k / neighbors)
                ry = -(radius * np.sin(2.0 * np.pi * k / neighbors))
                # 為雙線性插值做准備
                # 對采樣點偏移量分別進行上下取整
                x1 = int(np.floor(rx))
                x2 = int(np.ceil(rx))
                y1 = int(np.floor(ry))
                y2 = int(np.ceil(ry))
                # 將坐標偏移量映射到0-1之間
                tx = rx - x1
                ty = ry - y1
                # 根據0-1之間的x,y的權重計算公式計算權重,權重與坐標具體位置無關,與坐標間的差值有關
                w1 = (1-tx) * (1-ty)
                w2 =    tx  * (1-ty)
                w3 = (1-tx) *    ty
                w4 =    tx  *    ty
                 # 根據雙線性插值公式計算第k個采樣點的灰度值
                neighbor=img[i+y1,j+x1] * w1 + img[i+y2,j+x1] *w2 + img[i+y1,j+x2] *  w3 +img[i+y2,j+x2] *w4
                # LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                dst[i-radius,j-radius] |= (neighbor>center)  <<  (np.uint8)(neighbors-k-1)
    return dst

gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
circul_1_8 = circular_LBP(gray,1,8)
circul_3_8 = circular_LBP(gray,3,8)
circul_3_6 = circular_LBP(gray,3,6)
# 最好是先計算完,統一顯示
cv2.imshow('img', gray)
cv2.imshow('r1k8', circul_1_8)
cv2.imshow('r3k8', circul_3_8)
cv2.imshow('r3k6', circul_3_6)
cv2.waitKey(0)
cv2.destroyAllWindows()

其中,雙線性插值公式為:

 

 3,加入旋轉不變性

 

# 在圓形選取框基礎上,加入旋轉不變操作
def rotation_invariant_LBP(img, radius=3, neighbors=8):
    h,w=img.shape
    dst = np.zeros((h-2*radius, w-2*radius),dtype=img.dtype)
    for i in range(radius,h-radius):
        for j in range(radius,w-radius):
            # 獲得中心像素點的灰度值
            center = img[i,j]
            for k in range(neighbors):
                # 計算采樣點對於中心點坐標的偏移量rx,ry
                rx = radius * np.cos(2.0 * np.pi * k / neighbors)
                ry = -(radius * np.sin(2.0 * np.pi * k / neighbors))
                # 為雙線性插值做准備
                # 對采樣點偏移量分別進行上下取整
                x1 = int(np.floor(rx))
                x2 = int(np.ceil(rx))
                y1 = int(np.floor(ry))
                y2 = int(np.ceil(ry))
                # 將坐標偏移量映射到0-1之間
                tx = rx - x1
                ty = ry - y1
                # 根據0-1之間的x,y的權重計算公式計算權重,權重與坐標具體位置無關,與坐標間的差值有關
                w1 = (1-tx) * (1-ty)
                w2 =    tx  * (1-ty)
                w3 = (1-tx) *    ty
                w4 =    tx  *    ty
                # 根據雙線性插值公式計算第k個采樣點的灰度值
                neighbor = img[i+y1,j+x1] * w1 + img[i+y2,j+x1] *w2 + img[i+y1,j+x2] *  w3 +img[i+y2,j+x2] *w4
                # LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                dst[i-radius,j-radius] |= (neighbor>center)  <<  (np.uint8)(neighbors-k-1)
    # 進行旋轉不變處理
    for i in range(dst.shape[0]):
        for j in range(dst.shape[1]):
            currentValue = dst[i,j]
            minValue = currentValue
            for k in range(1, neighbors):
                # 對二進制編碼進行循環左移,意思即選取移動過程中二進制碼最小的那個作為最終值
                temp = (np.uint8)(currentValue>>(neighbors-k)) |  (np.uint8)(currentValue<<k)
                if temp < minValue:
                    minValue = temp
            dst[i,j] = minValue

    return dst       

gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
rotation_invariant = rotation_invariant_LBP(gray,3,8)
cv2.imshow('img', gray)
cv2.imshow('ri', rotation_invariant)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

4,等價模式

 

def get_shifts(data):
    '''
    計算跳變次數,即二進制碼相鄰2位不同,總共出現的次數
    '''
    count = 0
    binaryCode = "{0:0>8b}".format(data)
     
    for i in range(1,len(binaryCode)):
        if binaryCode[i] != binaryCode[(i-1)]:
            count+=1
    return count
def create_table(img):
    # LBP特征值對應圖像灰度編碼表,直接默認采樣點為8位
    temp = 1
    table =np.zeros((256),dtype=img.dtype)
    for i in range(256):
#         跳變小於3定義為等價模式類,共58,混合類算做1種
        if get_shifts(i)<3:
            table[i] = temp
            temp+=1
    return table


# 等價模式類:二進制碼跳變次數小於3,8位二進制碼共58種等價模式,其他256-58種為混合類。混合類的LBP特征將置為0,所以最終圖像偏暗
def uniform_pattern_LBP(img,table,radius=3, neighbors=8):
    h,w=img.shape
    dst = np.zeros((h-2*radius, w-2*radius),dtype=img.dtype)
    for i in range(radius,h-radius):
        for j in range(radius,w-radius):
            # 獲得中心像素點的灰度值
            center = img[i,j]
            for k in range(neighbors):
                # 計算采樣點對於中心點坐標的偏移量rx,ry
                rx = radius * np.cos(2.0 * np.pi * k / neighbors)
                ry = -(radius * np.sin(2.0 * np.pi * k / neighbors))
                # 為雙線性插值做准備
                # 對采樣點偏移量分別進行上下取整
                x1 = int(np.floor(rx))
                x2 = int(np.ceil(rx))
                y1 = int(np.floor(ry))
                y2 = int(np.ceil(ry))
                # 將坐標偏移量映射到0-1之間
                tx = rx - x1
                ty = ry - y1
                # 根據0-1之間的x,y的權重計算公式計算權重,權重與坐標具體位置無關,與坐標間的差值有關
                w1 = (1-tx) * (1-ty)
                w2 =    tx  * (1-ty)
                w3 = (1-tx) *    ty
                w4 =    tx  *    ty
                # 根據雙線性插值公式計算第k個采樣點的灰度值
                neighbor = img[i+y1,j+x1] * w1 + img[i+y2,j+x1] *w2 + img[i+y1,j+x2] *  w3 +img[i+y2,j+x2] *w4
                # LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                dst[i-radius,j-radius] |= (neighbor>center)  <<  (np.uint8)(neighbors-k-1)
                # 進行LBP特征的UniformPattern編碼
                # 8位二進制碼形成后,查表,對屬於混合類的特征置0
                if k==neighbors-1:
                    dst[i-radius,j-radius] = table[dst[i-radius,j-radius]]
    return dst
          
gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
table=create_table(gray)
uniform_pattern = uniform_pattern_LBP(gray,table,3,8)
cv2.imshow('img', gray)
cv2.imshow('up', uniform_pattern)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

5,MB_LBP:先對像素做區域平均處理,再使用原始LBP

 

# 先對像素分割,用一個小區域的平均值代替這個區域,再用LBP特征處理
def multi_scale_block_LBP(img,scale):
    h,w= img.shape
    
    # cellSize表示一個cell大小
    cellSize = int(scale / 3)
    offset = int(cellSize / 2)
    cellImage = np.zeros((h-2*offset, w-2*offset),dtype=img.dtype)
      
    for i in range(offset,h-offset):
        for j in range(offset,w-offset):
            temp = 0
            for m in range(-offset,offset+1):
                for n in range(-offset,offset+1):  
                    temp += img[i+n,j+m]
#             即取一個cell里所有像素的平均值
            temp /= (cellSize*cellSize)
            cellImage[i-offset,j-offset] = np.uint8(temp)
#     再對平均后的像素做LBP特征處理
    dst = origin_LBP(cellImage)
    return dst

gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
mb_3 = multi_scale_block_LBP(gray,3)  
mb_9 = multi_scale_block_LBP(gray,9)  
mb_15 = multi_scale_block_LBP(gray,15)  
cv2.imshow('img', gray)
cv2.imshow('mb_3', mb_3)
cv2.imshow('mb_9', mb_9)
cv2.imshow('mb_15', mb_15)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

5,LBPH,Local Binary Patterns Histograms

此處基於等價模式,再使用像素各分割塊的直方統計圖,拼接為最后的特征向量

 

# 先使用等價模式預處理圖像,降維。再分割圖像,對每個分割塊進行直方統計(降維后的類別為59),返回密度向量,再拼接各個分割塊對應的密度向量
# 最終返回grid_x*grid_y*numPatterns維的特征向量,作為圖像的LBPH特征向量
def getLBPH(img_lbp,numPatterns,grid_x,grid_y,density):
    '''
    計算LBP特征圖像的直方圖LBPH
    '''
    h,w=img_lbp.shape
    width = int(w / grid_x)
    height = int(h / grid_y)
    # 定義LBPH的行和列,grid_x*grid_y表示將圖像分割的塊數,numPatterns表示LBP值的模式種類
    result = np.zeros((grid_x * grid_y,numPatterns),dtype=float)
    resultRowIndex = 0
    # 對圖像進行分割,分割成grid_x*grid_y塊,grid_x,grid_y默認為8
    for i in range(grid_x):
        for j in range(grid_y):
            # 圖像分塊
            src_cell = img_lbp[i*height:(i+1)*height,j*width:(j+1)*width]
            # 計算直方圖
            hist_cell = getLocalRegionLBPH(src_cell,0,(numPatterns-1),density)
            #將直方圖放到result中
            result[resultRowIndex]=hist_cell
            resultRowIndex+=1
    return np.reshape(result,(-1))

def getLocalRegionLBPH(src,minValue,maxValue,density=True):
    '''
    計算一個LBP特征圖像塊的直方圖
    '''
    data = np.reshape(src,(-1))
    # 計算得到直方圖bin的數目,直方圖數組的大小
    bins = maxValue - minValue + 1;
    # 定義直方圖每一維的bin的變化范圍
    ranges = (float(minValue),float(maxValue + 1))
#     density為True返回的是每個bin對應的概率值,bin為單位寬度時,概率總和為1
    hist, bin_edges = np.histogram(src, bins=bins, range=ranges, density=density)
    return hist

uniform_pattern = uniform_pattern_LBP(gray,table,3,8)
#等價模式58種,混合模式算1種
lbph = getLBPH(uniform_pattern,59,8,8,True)

參考博客:https://blog.csdn.net/lk3030/article/details/84034963

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM