分区人脸二值化

来源:互联网 发布:mac qq 讨论组 改昵称 编辑:程序博客网 时间:2024/06/07 23:39

根据需求的描述,我们需要在照相机拍摄的相片中完成:对于画面中人物的脸部,使用机器人手臂握笔绘出。对于图像处理这个模块的功能,我们需要做的是把输入的相片转化为一张仅包含人物脸部及五官轮廓的二值图输出给后面的模块。

因为光照的不同,所以人脸肤色的平均亮度也是不同的,所以我们要找到一种自适应的阈值分割方法来进行二值化图像。

为了更加好地处理图片,我们先对图片进行一系列的预处理。我们首先把背景去掉,采用一个漫水填充的方法可以很容易地去除背景。然后为了简化处理的像素级,我们可以采用opencv内置的人脸检测模型把人脸裁剪出来。

在这里,我们采用的肤色+分区+边缘的融合方法。为了方便计算人脸皮肤的平均亮度,我们首先用肤色模型来来定位皮肤:RGB色彩空间中亮度由RGB三个值确定,而且RGB空间中对肤色很难聚类。而根据现有的肤色模型中,绝大多数的算法都是将RGB图像转换到YCbCr色彩空间中进行处理,而且在YCbCr空间中,亮度Y是单独的一维。在YCbCr的色彩空间中,最简单的聚类方法就是以(77<= Cr<=127, 140<= Cb<=173)为条件进行肤色聚类,大多数肤色会被聚类在这个区间中。理想当中我们的肤色模型可以得到肤色二值图如下:


 

 

得到肤色图之后,我们先对肤色图进行简单的修剪:

 

为了让二值图更加细致,我们采用了分区的方法,将人脸图分为25*25个格子,每一个格子根据相关信息来进行选择阈值。

在选择阈值之前,我们先对图片做一个Canny算法的边缘提取操作,如下图:



其中白色的点就是边缘点。为什么要提取这个操作呢,后面再来解释

阈值的选择:选择阈值的要合理,在这里我初步建立了一个阈值选择公式:

(1+edgesPointPercent)*gamma*(lamda*max_th+bata*min_th)

其中:edgesPointPercent是当前格子区域中边缘点所点像素百分比。

         Gamma,lamda,bata为一个参数,其中lamda+bata= 1

        min_th = min(alpha,theta)max_th = max(alpha,theta)
               alptha 为 区域的皮肤平均亮度,thata为总体皮肤平均
在这里我来解释一下这个公式的依据:
下面以一副照片为例:
    

可以看上图的ABCD四个区域 ,可以预见,因为肤色模型总是有点误差,所以会把一些头发的一些亮度较低的区域给误识别成肤色,导致总体的肤色平均亮度实际偏低,但这完全没影响,值为v1。我们首先研究的是D区域和C区域可以看见,明显要把D给二值化为黑色也就是0,把C给二值化为白色也就是255,所以我们可以画出一副图来:

可以看到要把C设为255,则阈值要设在1号线和2号线之间,要把D设为0,则要把阈值设在2号线和3号线之间,所以有

min_th = min(alpha,theta)
max_th = max(alpha,theta)

确定下最大最小值,再给lamda和bata一个适合的值,就给有相应的阈值了。

而那个edgesPointPercent又发挥了什么作用呢,在这们请看一下A区域和B区域,明显,B区域是眉毛,而A区域只是一个阴影比较大的地方,在这里,我们可以认为他们两的平均亮度差不多,同时位于总体平均亮度的上方,但是眉毛明显应该二值化为黑色啊,所以这个参数发挥作用了。再看这个边缘图,可以看出B区域是没有边缘点的,而A区域是有边缘点的,这时候我们就可以根据在这个区域内边缘点的百分比来放大Gamma这个参数,也就相当于放大阈值,所以如果这个区域内的边缘点比较多,则阈值会大,则被二值化成0黑色的概率会很高。在这里,对这个百分比进行了一个小小的放大作用:

temp = point/sum      #这里就是单位区域内的边缘点占比
if temp > 0 and temp<=0.05:
    return 5*temp
elif temp> 0.05 and temp<=0.1:
    return 3*temp
elif temp>0.1andtemp<=0.2:
    return 1.5*temp
elif temp>0.3andtemp<=0.4:
    return 1.1*temp
else:
    return temp

就是如果有边缘点了,我们就把这个百分比提高3倍,否则就返回0(那个else)

如果在光照不是很暗的情况下,实验的效果还是相对较好的,结果集中也展现了几副处理结果较差的图片!

这样下来的结果还可以接受:

 

 

       

原创粉丝点击