为什么 scikit-image 中的 local_binary_pattern 函数为不同的模式提供相同的值?
Why does the local_binary_pattern function in scikit-image provide same value for different patterns?
我正在使用 scikit-image 包中的 local_binary_pattern
函数。我想计算半径 1 内 8 个邻居的旋转不变均匀 LBP。这是我的 Python 代码:
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)
这是输出
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.]]
让我感到困惑的是 lbp
中的某些相同值并不对应于相同的统一模式。例如,lbp[1, 1]
和lbp[2, 2]
都是6
,但是image[1, 1]
的LBP是:
1 0 0
1 x 1
1 1 1
image[2, 2]
的 LBP 是:
1 1 1
1 x 0
1 1 1
根据 lbp
中的值,我假设 local_binary_pattern
函数使用 'greater or equal to' 与邻居进行比较。
image[1, 1]
和image[2, 2]
的LBP都是统一的。但是image[1, 1]
和image[2, 2]
怎么会有相同的LBP值呢?
旋转不变的LBP不直接使用邻居的像素值,而是在一个圆上插值(为了旋转不变性)。参见 https://github.com/scikit-image/scikit-image/blob/master/skimage/feature/_texture.pyx#L156
又见原LBP论文http://vision.stanford.edu/teaching/cs231b_spring1415/papers/lbp.pdf,其中提到“不落下的邻居的灰度值
正好在像素的中心是通过插值估计的。"
为了提高对 LBP 描述符旋转的鲁棒性,将方形邻域替换为圆形邻域。在由八个像素形成的圆形邻域中,对角线上的四个邻域与像素中心不重合。这些邻居的强度值通常通过双线性插值计算。下图以图形方式解释了为什么在您的示例图像中,某些 LBP3×3 模式与 LBP8,1 模式不同。
代码
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])
我正在使用 scikit-image 包中的 local_binary_pattern
函数。我想计算半径 1 内 8 个邻居的旋转不变均匀 LBP。这是我的 Python 代码:
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)
这是输出
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.]]
让我感到困惑的是 lbp
中的某些相同值并不对应于相同的统一模式。例如,lbp[1, 1]
和lbp[2, 2]
都是6
,但是image[1, 1]
的LBP是:
1 0 0
1 x 1
1 1 1
image[2, 2]
的 LBP 是:
1 1 1
1 x 0
1 1 1
根据 lbp
中的值,我假设 local_binary_pattern
函数使用 'greater or equal to' 与邻居进行比较。
image[1, 1]
和image[2, 2]
的LBP都是统一的。但是image[1, 1]
和image[2, 2]
怎么会有相同的LBP值呢?
旋转不变的LBP不直接使用邻居的像素值,而是在一个圆上插值(为了旋转不变性)。参见 https://github.com/scikit-image/scikit-image/blob/master/skimage/feature/_texture.pyx#L156
又见原LBP论文http://vision.stanford.edu/teaching/cs231b_spring1415/papers/lbp.pdf,其中提到“不落下的邻居的灰度值 正好在像素的中心是通过插值估计的。"
为了提高对 LBP 描述符旋转的鲁棒性,将方形邻域替换为圆形邻域。在由八个像素形成的圆形邻域中,对角线上的四个邻域与像素中心不重合。这些邻居的强度值通常通过双线性插值计算。下图以图形方式解释了为什么在您的示例图像中,某些 LBP3×3 模式与 LBP8,1 模式不同。
代码
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])