Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the local_binary_pattern function in scikit-image provide same value for different patterns?

I am using the local_binary_pattern function in the scikit-image package. I would like to compute the rotation invariant uniform LBP of 8 neighbors within radius 1. Here is my Python code:

import numpy as np
from skimage.feature import local_binary_pattern

image = np.array([[150, 137, 137, 146, 146, 148],
                  [145, 144, 144, 144, 142, 144],
                  [149, 144, 144, 143, 153, 147],
                  [145, 144, 147, 150, 145, 150],
                  [146, 146, 139, 148, 144, 148],
                  [129, 139, 142, 150, 146, 140]]).astype(np.uint8)

lbp = local_binary_pattern(image, 8, 1, "uniform")

print("image =")
print(image)
print("lbp =")
print(lbp)

And here is the output

image =
[[150 137 137 146 146 148]
 [145 144 144 144 142 144]
 [149 144 144 143 153 147]
 [145 144 147 150 145 150]
 [146 146 139 148 144 148]
 [129 139 142 150 146 140]]
lbp =
[[ 0.  5.  5.  1.  9.  0.]
 [ 9.  6.  9.  9.  8.  9.]
 [ 0.  8.  6.  8.  0.  3.]
 [ 9.  7.  1.  0.  7.  0.]
 [ 1.  1.  8.  9.  7.  1.]
 [ 3.  4.  9.  0.  2.  3.]]

What confuses me is that some same values in lbp do not correspond to the same uniform pattern. E.g., lbp[1, 1] and lbp[2, 2] are both 6, but the LBP of image[1, 1] is:

1 0 0
1 x 1
1 1 1

The LBP of image[2, 2] is:

1 1 1
1 x 0
1 1 1

where based on the values in lbp, I assume the local_binary_pattern function uses 'greater or equal to' to compare with neighbors.

The LBPs of image[1, 1] and image[2, 2] are both uniform. But how could image[1, 1] and image[2, 2] have the same LBP value?

like image 806
Peter Avatar asked Aug 18 '16 06:08

Peter


Video Answer


1 Answers

In order to improve robustness against rotation of the LBP descriptor the square neighbourhood is replaced by a circular one. In a circular neighbourhood formed by eight pixels the four neighbours on the diagonals do not coincide with pixel centers. The intensity values of those neighbours are commonly computed through bilinear interpolation. The following figure graphically explains why in your sample image some LBP3×3 patterns are different to the LBP8,1 patterns.

square and circular neighbourhoods

Code

w_cen  =  (1-1/np.sqrt(2))**2  # Weights
w_diag  =  (1/np.sqrt(2))**2
w_orto  =  (1-1/np.sqrt(2))*(1/np.sqrt(2))

def bilinear_interpoplation(i_cen, i_diag, i_hor, i_ver):
    return i_cen*w_cen + i_diag*w_diag + i_hor*w_orto + i_ver*w_orto

def circular_neighbourhood(x):
    [I7, I6, I5] = x[0, :]
    [I0, Ic, I4] = x[1, :]
    [I1, I2, I3] = x[2, :]
    I7i = bilinear_interpolation(Ic, I7, I0, I6)
    I5i = bilinear_interpolation(Ic, I5, I4, I6)
    I3i = bilinear_interpolation(Ic, I3, I4, I2)
    I1i = bilinear_interpolation(Ic, I1, I0, I2)
    interpolated = np.array([[I7i, I6, I5i], 
                             [ I0, Ic,  I4], 
                             [I1i, I2, I3i]])
    return interpolated

def binary_pattern(x):
    return np.where(x >= x[1, 1], 1, 0)

def display_lbps(patch):
    interpolated = circular_neighbourhood(patch)
    print('Patch =')
    print(patch)
    print('LBP of patch =')
    print(binary_pattern(patch))
    print('Interpolated patch =')
    print(interpolated)
    print('LBP of interpolated patch =')
    print(binary_pattern(interpolated))

display_lbps(image[0:3, 0:3])
display_lbps(image[1:4, 1:4])
like image 116
Tonechas Avatar answered Nov 14 '22 21:11

Tonechas