完整源码链接:https://github.com/LamyaLi/cvLab

一、人脸检测技术原理

1、原理总说
  • 在检测到人脸并定位面部关键特征点之后,主要的人脸区域就可以被裁剪出来,经过预处理之后,馈入后端的识别算法。识别算法要完成人脸特征的提取,并与库存的已知人脸进行比对,完成最终的分类。

  • 本实验要做的就是人脸识别工作的第一步:人脸检测与框定

  • 主要利用的人脸特征是肤色,高宽比( “三庭五眼 ”规则高度和宽度比例应该在( 0.6, 2)内” ),眼部特征

  • 基于肤色的人脸分割主要分为三大部分:(1)预处理,针对噪声,光照带来的影响进行消除。(2)基于肤色模型的肤色分割。(3)连通域分析,人脸区域定位。

2、基于肤色高斯模型实现人脸区域分割提取的原理
  • 通过比较RGB,HSV,Ycbcr空间,发现Ycbcr和HSV空间在进行人脸肤色分割方面由于肤色范围紧密(如图),不易受光照其他物体干扰(基于肤色模型的,如果背景中有与人脸颜色类似的物体,且距离较近很容易产生干扰,影响人脸区域定位的准确性,这也是这一算法不能解决的问题)。但是RGB与HSV空间的转换相比RGB到Ycbcr空间转换来说较为复杂些,所以我们采用Ycbcr空间进行人脸肤色的建模与分割。

在这里插入图片描述

  • 肤色分布符合高斯分布(如图)

    在这里插入图片描述

    我们可以根据肤色的均值和方差建立肤色的高斯模型。 其中Cb,Cr的均值和协方差如下:

    Mean = [117.4316 148.5599]
    C = [97.0946 24.4700,24.4700 141.9966]

在这里插入图片描述

给定一个图像的一个像素点x,可以计算它是人皮肤的概率p(x),概率小于0.22,的点取灰度值0,否则取255,

得到一幅二值图像,其中白色的区域是人皮肤。

3、根据人脸特征筛选
  • 人脸区域定位,使用连通区域分析,获取二值图像最小外接矩形,即为人脸区域。 采用的是八邻域连通。
  • 高宽比( “三庭五眼 ”规则高度和宽度比例应该在( 0.6, 2)内” )不符合要求的去掉
  • 符合要求的区域也可能是胳膊,服饰之类的。我们在这个区域内找眼睛。在肤色的二值图像里,眼珠位置一般是黑色,所以我们将这个区域图像取反,再进行连通区域分析,按眼睛与脸的外接矩形尺寸比例筛选得到眼睛。如果在这个区域里能找到一个以上眼睛,判定这个区域是脸
  • 将脸区域框起来

二、人脸识别程序流程

根据以上原理分析,在适宜步骤处加上去噪,形态学处理,效果更好。总流程图如下:

在这里插入图片描述

三、实现步骤

1、利用肤色高斯模型完成人脸区域提取

流程图:

在这里插入图片描述

  • 转成ycrcb色彩空间,
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
2、人脸区域形态学处理

用开操作处理上步得到的,二值图像,可以强调眼部等细节,去掉一些噪声

kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))  
skin_opening = cv2.morphologyEx(skin, cv2.MORPH_OPEN, kernel)

在这里插入图片描述在这里插入图片描述

3、人脸区域的筛选和标定
  • 将连通区域标定,
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

四、结果展示

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

当一张图片脸数量很多时,效果可能不太好,因为,筛选过程中,为去除杂质,将尺寸与图像尺寸比例较小的框去掉了,如图

在这里插入图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐