LBP指局部二值模式(Local Binary Pattern),是一种用来描述图像局部特征的算子,具有灰度不变性和旋转不变性等显著优点。LBP常应用于人脸识别和目标检测中,在OpenCV中有使用LBP特征进行人脸识别的接口,也有用LBP特征训练目标检测分类器的方法,OpenCV实现了LBP特征的计算,但没有提供一个单独的计算LBP特征的接口。也就是说OpenCV中使用了LBP算法,但是没有提供函数接口。
LBP特征用图像的局部领域的联合分布 T T T 来描述图像的纹理特征,如果假设局部邻域中像素个数为 P ( P > 1 ) P(P >1) P(P>1),那么纹理特征的联合分布 T T T 可以表述成:
T = t ( g c , g 0 , … , g p − 1 ) p = 0 , … , P − 1 (2-1) T=t\left(g_{c}, g_{0}, \ldots, g_{p-1}\right) \quad p=0, \ldots, P-1\tag{2-1} T=t(gc,g0,…,gp−1)p=0,…,P−1(2-1)
其中, g c g_c gc 表示相应局部邻域的中心像素的灰度值, g p g_p gp 表示以中心像素圆心,以R为半径的圆上的像素的灰度值。
假设中心像素和局部邻域像素相互独立,那么这里可以将上面定义式写成如下形式:
T = t ( g c , g 0 − g c , … , g p − 1 − g c ) p = 0 , … , P − 1 ≈ t ( g c ) t ( g 0 − g c , … , g p − 1 − g c ) (2-2) \begin{aligned} T &=t\left(g_{c}, g_{0}-g_{c}, \ldots, g_{p-1}-g_{c}\right) \quad p=0, \ldots, P-1 \ & \approx t\left(g_{c}\right) t\left(g_{0}-g_{c}, \ldots, g_{p-1}-g_{c}\right) \end{aligned}\tag{2-2} T=t(gc,g0−gc,…,gp−1−gc)p=0,…,P−1 ≈t(gc)t(g0−gc,…,gp−1−gc)(2-2)
其中 t ( g c ) t(g_c) t(gc)决定了局部区域的整体亮度,对于纹理特征,可以忽略这一项,最终得到:
T ≈ t ( g 0 − g c , … , g p − 1 − g c ) p = 0 , … , P − 1 (2-3) T \approx t\left(g_{0}-g_{c}, \ldots, g_{p-1}-g_{c}\right) \quad p=0, \ldots, P-1\tag{2-3} T≈t(g0−gc,…,gp−1−gc)p=0,…,P−1(2-3)
上式说明,将纹理特征定义为邻域像素和中心像素的差的联合分布函数,因为 g p − g c g_p − g_c gp−gc是基本不受亮度均值影响的,所以从上式可以看出,此时统计量T 是一个跟亮度均值,即灰度级无关的值。
最后定义特征函数如下: T ≈ t ( s ( g 0 − g c ) , … , s ( g p − 1 − g c ) ) p = 0 , … , p − 1 s ( x ) = [ 1 , x ≥ 0 0 , x < 0 ] (2-4) \begin{array}{l} T \approx t \left(s\left(g_{0}-g_{c}\right), \ldots, s\left(g_{p-1}-g_{c}\right)\right) \\p=0, \ldots, p-1 \\ s(x)=\left[ \begin{array}{l} 1, x \geq 0 \\ 0, x<0 \end{array}\right] \end{array}\tag{2-4} T≈t(s(g0−gc),…,s(gp−1−gc))p=0,…,p−1s(x)=[1,x≥00,x<0](2-4)
定义灰度级不变LBP为:
L B P P , R = ∑ p = 0 P − 1 s ( g p − g c ) 2 p (2-5) L B P_{P, R}=\sum_{p=0}^{P-1} s\left(g_{p}-g_{c}\right) 2^{p}\tag{2-5} LBPP,R=p=0∑P−1s(gp−gc)2p(2-5)
即二进制编码公式。
通俗解释:
原始的LBP算子定义在像素 3 ∗ 3 3*3 3∗3的邻域内,以邻域中心像素为阈值,相邻的8个像素的灰度值与邻域中心的像素值进行比较,若周围像素大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,
3 ∗ 3 3*3 3∗3邻域内的8个点经过比较可产生8位二进制数,将这8位二进制数依次排列形成一个二进制数字,这个二进制数字就是中心像素的LBP值,LBP值共有28种可能,因此LBP值有256种可能。中心像素的LBP值反映了该像素周围区域的纹理信息。
注意:计算LBP特征的图像必须是灰度图,如果是彩色图,需要先转换成灰度图
图 示 为 L B P 计 算 示 意 图 图示为LBP计算示意图 图示为LBP计算示意图
基本的 LBP算子的最大缺陷在于它只覆盖了一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的需要。为了适应不同尺度的纹理特征,并达到灰度级和旋转不变性的要求,Ojala等对 LBP算子进行了改进,将 3×3邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域,改进后的 LBP算子允许在半径为 R的圆形邻域内有任意多个像素点。从而得到了诸如半径为R的圆形区域内含有P个采样点的LBP算子,表示为LBPRP;
图 示 为 圆 形 L B P 示 意 图 图示为圆形LBP示意图 图示为圆形LBP示意图
对于给定中心点 ( x c , y c ) (x_c,y_c) (xc,yc),其邻域像素位置为 ( x p , y p ) (x_p,y_p) (xp,yp), p ∈ P p∈P p∈P,其采样点 ( x p , y p ) (x_p,y_p) (xp,yp)用如下公式计算:
x p = x c + Rcos ( 2 π p P ) y p = y c + Rsin ( 2 π p P ) (2-6) \begin{array}{l} x_{p}=x_{c}+\operatorname{Rcos}\left(\frac{2 \pi p}{P}\right) \\ y_{p}=y_{c}+\operatorname{Rsin}\left(\frac{2 \pi p}{P}\right) \end{array}\tag{2-6} xp=xc+Rcos(P2πp)yp=yc+Rsin(P2πp)(2-6)
R是采样半径,p是第p个采样点,P是采样数目。
LPB特征是灰度不变,但不是旋转不变的,同一幅图像,进行旋转以后,其特征将会有很大的差别,影响匹配的精度。Ojala在LBP算法上,进行改进,实现了具有旋转不变性的LPB的特征。
实现方法:不断旋转圆形邻域得到一系列初始定义的LPB值,取最小值作为该邻域的值。
L B P P R r i = min ( R O R ( L B P P , R r i , i ) ∣ i = 0 , 1 , … , P − 1 ) (2-7) L B P_{P R}^{ri}=\min \left(R O R\left(L B P_{P, R}^{ri}, i\right) | i=0,1, \ldots, P-1\right)\tag{2-7} LBPPRri=min(ROR(LBPP,Rri,i)∣i=0,1,…,P−1)(2-7)
其中 L B P P R r i L B P_{P R}^{ri} LBPPRri表示具有旋转不变性的LBP特征。 R O R ( x , i ) ROR(x, i) ROR(x,i)为旋转函数,表示将 x x x右循环 i i i位。
图 示 为 求 取 旋 转 不 变 的 L P B 特 征 示 意 图 图示为求取旋转不变的LPB特征示意图 图示为求取旋转不变的LPB特征示意图
Uniform Pattern,也被称为等价模式或均匀模式,由于一个LBP特征有多种不同的二进制形式,对于半径为R的圆形区域内含有P个采样点的LBP算子将会产生2P种模式。很显然,随着邻域集内采样点数的增加,二进制模式的种类是以指数形式增加的。例如:5×5邻域内20个采样点,有2^20=1,048,576种二进制模式。这么多的二进制模式不利于纹理的提取、分类、识别及存取。例如,将LBP算子用于纹理分类或人脸识别时,常采用LBP模式的统计直方图来表达图像的信息,而较多的模式种类将使得数据量过大,且直方图过于稀疏。因此,需要对原始的LBP模式进行降维,使得数据量减少的情况下能最好的表示图像的信息。
为了解决二进制模式过多的问题,提高统计性,Ojala提出了采用一种“等价模式”(Uniform Pattern)来对LBP算子的模式种类进行降维。Ojala等认为,在实际图像中,绝大多数LBP模式最多只包含两次从1到0或从0到1的跳变。因此,Ojala将“等价模式”定义为:当某个LBP所对应的循环二进制数从0到1或从1到0最多有两次跳变时,该LBP所对应的二进制就称为一个等价模式类。如00000000(0次跳变),00000111(只含一次从0到1的跳变),10001111(先由1跳到0,再由0跳到1,共两次跳变)都是等价模式类。除等价模式类以外的模式都归为另一类,称为混合模式类,例如10010111(共四次跳变)。通过这样的改进,二进制模式的种类大大减少,而不会丢失任何信息。模式数量由原来的2P种减少为 P ( P-1)+2种,其中P表示邻域集内的采样点数。对于3×3邻域内8个采样点来说,二进制模式由原始的256种减少为58种,即:它把值分为59类,58个uniform pattern为一类,其它的所有值为第59类。这样直方图从原来的256维变成59维。这使得特征向量的维数更少,并且可以减少高频噪声带来的影响。
具体实现:采样点数目为8个,即LBP特征值有28种,共256个值,正好对应灰度图像的0-255,因此原始的LBP特征图像是一幅正常的灰度图像,而等价模式LBP特征,根据0-1跳变次数,将这256个LBP特征值分为了59类,从跳变次数上划分:跳变0次—2个,跳变1次—0个,跳变2次—56个,跳变3次—0个,跳变4次—140个,跳变5次—0个,跳变6次—56个,跳变7次—0个,跳变8次—2个。共9种跳变情况,将这256个值进行分配,跳变小于2次的为等价模式类,共58个,他们对应的值按照从小到大分别编码为1—58,即它们在LBP特征图像中的灰度值为1—58,而除了等价模式类之外的混合模式类被编码为0,即它们在LBP特征中的灰度值为0,因此等价模式LBP特征图像整体偏暗。
显而易见的是,上述提取的LBP算子在每个像素点都可以得到一个LBP“编码”,那么,对一幅图像(记录的是每个像素点的灰度值)提取其原始的LBP算子之后,得到的原始LBP特征依然是“一幅图片”(记录的是每个像素点的LBP值)。
LBP的应用中,如纹理分类、人脸分析等,一般都不将LBP图谱作为特征向量用于分类识别,而是采用LBP特征谱的统计直方图作为特征向量用于分类识别。
因为,从上面的分析我们可以看出,这个“特征”跟位置信息是紧密相关的。直接对两幅图片提取这种“特征”,并进行判别分析的话,会因为“位置没有对准”而产生很大的误差。后来,研究人员发现,可以将一幅图片划分为若干的子区域,对每个子区域内的每个像素点都提取LBP特征,然后,在每个子区域内建立LBP特征的统计直方图。如此一来,每个子区域,就可以用一个统计直方图来进行描述;整个图片就由若干个统计直方图组成;
例如:一幅100*100像素大小的图片,划分为10*10=100个子区域(可以通过多种方式来划分区域),每个子区域的大小为10*10像素;在每个子区域内的每个像素点,提取其LBP特征,然后,建立统计直方图;这样,这幅图片就有10*10个子区域,也就有了10*10个统计直方图,利用这10*10个统计直方图,就可以描述这幅图片了。之后,我们利用各种相似性度量函数,就可以判断两幅图像之间的相似性了;
(1)首先将检测窗口划分为16×16的小区域(cell);
(2)对于每个cell中的一个像素,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3*3邻域内的8个点经比较可产生8位二进制数,即得到该窗口中心像素点的LBP值;
(3)然后计算每个cell的直方图,即每个数字(假定是十进制数LBP值)出现的频率;然后对该直方图进行归一化处理。
(4)最后将得到的每个cell的统计直方图进行连接成为一个特征向量,也就是整幅图的LBP纹理特征向量;
然后便可利用SVM或者其他机器学习算法进行分类了
#coding:utf-8
import cv2 as cv
# 读取原始图像
img= cv.imread('*.png')
#face_detect = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
face_detect = cv.CascadeClassifier("lbpcascade_frontalface_improved.xml")
# 检测人脸
# 灰度处理
gray = cv.cvtColor(img, code=cv.COLOR_BGR2GRAY)
# 检查人脸 按照1.1倍放到 周围最小像素为5
face_zone = face_detect.detectMultiScale(gray, scaleFactor = 2, minNeighbors = 2) # maxSize = (55,55)
print ('识别人脸的信息:\n',face_zone)
# 绘制矩形和圆形检测人脸
for x, y, w, h in face_zone:
# 绘制矩形人脸区域
cv.rectangle(img, pt1 = (x, y), pt2 = (x+w, y+h), color = [0,0,255], thickness=2)
# 绘制圆形人脸区域 radius表示半径
cv.circle(img, center = (x + w//2, y + h//2), radius = w//2, color = [0,255,0], thickness = 2)
# 设置图片可以手动调节大小
cv.namedWindow("Easmount-CSDN", 0)
# 显示图片
cv.imshow("Easmount-CSDN", img)
# 等待显示 设置任意键退出程序
cv.waitKey(0)
cv.destroyAllWindows()
https://blog.csdn.net/qq_33810188/article/details/
https://github.com/datawhalechina/team-learning/blob/master