完整源码链接:https://github.com/LamyaLi/cvLab
在检测到人脸并定位面部关键特征点之后,主要的人脸区域就可以被裁剪出来,经过预处理之后,馈入后端的识别算法。识别算法要完成人脸特征的提取,并与库存的已知人脸进行比对,完成最终的分类。
本实验要做的就是人脸识别工作的第一步:人脸检测与框定
主要利用的人脸特征是肤色,高宽比( “三庭五眼 ”规则高度和宽度比例应该在( 0.6, 2)内” ),眼部特征
基于肤色的人脸分割主要分为三大部分:(1)预处理,针对噪声,光照带来的影响进行消除。(2)基于肤色模型的肤色分割。(3)连通域分析,人脸区域定位。
肤色分布符合高斯分布(如图)
我们可以根据肤色的均值和方差建立肤色的高斯模型。 其中Cb,Cr的均值和协方差如下:
Mean = [117.4316 148.5599]
C = [97.0946 24.4700,24.4700 141.9966]
给定一个图像的一个像素点x,可以计算它是人皮肤的概率p(x),概率小于0.22,的点取灰度值0,否则取255,
得到一幅二值图像,其中白色的区域是人皮肤。
根据以上原理分析,在适宜步骤处加上去噪,形态学处理,效果更好。总流程图如下:
流程图:
img_ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
y,cr,cb=cv2.split(img_ycrcb)
cr_gaussian=cv2.GaussianBlur(cr,(5,5),0)
cb_gaussian=cv2.GaussianBlur(cb,(5,5),0)
阈值划分形成二值图像
见原理分析中,利用肤色高斯模型,为简化计算,不需要每次都计算概率,取了一个,cr与cb的范围,能到达一样的效果
skin=np.zeros_like(cr)
for i in range(img.shape[0]):
for j in range(img.shape[1]):
if y[i][j]<70:
skin[i][j] = 0
elif cr_gaussian[i][j]>133 and cr_gaussian[i][j]<173 and cb_gaussian[i][j]>77 and cb_gaussian[i][j]<127:
skin[i][j]=255
else:
skin[i][j]=0
用开操作处理上步得到的,二值图像,可以强调眼部等细节,去掉一些噪声
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
skin_opening = cv2.morphologyEx(skin, cv2.MORPH_OPEN, kernel)
skin_labeled= measure.label(skin_opening,connectivity = 2)##八邻域
dst=color.label2rgb(skin_labeled)
for region in measure.regionprops(skin_labeled):
min_row, min_col, max_row, max_col=region.bbox
if (max_row - min_row)/img.shape[1]>1/15 and (max_col - min_col)/img.shape[0]>0.05:
height_width_ratio = (max_row - min_row) / (max_col - min_col)
if height_width_ratio>0.6 and height_width_ratio<2.0:
if iseyes(skin_opening,min_row, min_col, max_row, max_col):
#print(height_width_ratio)
count_face = count_face+1
img = cv2.rectangle(img, (min_col, min_row), (max_col, max_row), (0, 255, 0), 2)
def iseyes(img_two, minr, minc, maxr, maxc):
##如果区域内有两个以上的空框是眼睛
part = np.zeros(((maxr - minr), (maxc - minc)))
for i in range(minr, maxr):
for j in range(minc, maxc):
if img_two[i, j] == 0:
part[i - minr, j - minc] = 255
else:
part[i - minr, j - minc] = 0
part_labeled, num = measure.label(part, return_num=True, connectivity=1) ##八邻域
global img
img_copy=img.copy()
count=0
for region2 in measure.regionprops(part_labeled):
min_row2, min_col2, max_row2, max_col2 = region2.bbox
w=max_col2-min_col2
h=max_row2-min_row2
total_w=maxc-minc
total_h=maxr-minr
w_ratio=w/total_w
h_ratio=h/total_h
if w_ratio<1/3 and h_ratio<0.2 and w_ratio>0.05 and h_ratio>1/30 and w>=h:
count=count+1
img_copy = cv2.rectangle(img_copy, (min_col2 + minc, min_row2 + minr), (max_col2 + minc, max_row2 + minr),(0, 255, 0), 2)
print(count)
if count>=1:
img=img_copy
return True
return False
当一张图片脸数量很多时,效果可能不太好,因为,筛选过程中,为去除杂质,将尺寸与图像尺寸比例较小的框去掉了,如图