应用统计肤色模型和相对于块原点能量的肤色分割

应用统计肤色模型和相对于块原点能量的肤色分割1. 肤色分割简介(1)统计肤色模型简介

在前面的文章中,我们利用训练数据已经成功计算出训练数据的后验概率P(Cs|v),即已知像素值判断属于肤色类的概率。同时我们获得后验概率大于不同阈值的肤色掩膜。但之前并没有给出统计肤色模型的肤色检测效果,因为测试效果取决于训练数据对应像素所在的RGB颜色空间分布。本篇将给出统计肤色模型的应用效果,同时改进单一使用模型的缺陷。

(2)块原点能量简介

图像中被分割出来的肤色块后面统称为块。根据统计肤色模型在一幅图片中可能获得上千个块,那么对应的会有上千个块的中心,块的中心称为块原点。相对于块原点的能量又两部分组成:一部分为静态能量,定义为像素点到块原点的二次距离;另一部分为动态能量,定义为像素值的后验概率与相等能量的像素点的后验概率的差值。相等能量为实验设定值,处于肤色后验概率上界和下界之间,如果某像素点的肤色后验概率大于相等能量值,该像素点的能量饱和,设为1。同时,如果该像素点的肤色后验概率小于肤色后验概率下界,该像素点的能量为空,设为0。如果该像素点的肤色后验概率在肤色后验概率下界和相等能量值的区间内,则计算得到的像素点能量取决于静态能量和动态能量的加权和。

那么块和块原点能量的存在对统计肤色分割有什么意义呢?统计肤色分割取决于训练数据。如果训练数据过于完善,那么离线学习会很缓慢;同时现实中大多数情况下随光照影响往往不太可能收集到完整的训练数据,并且越完整的数据会造成背景颜色的干扰越剧烈。所以我们需要针对有限的肤色检测场合,收集该场合下充分的肤色训练数据,达到有限场合下完美的肤色检测。这里只是这么一提,笔者从网上的数据库随便找了个数据库就做了,暂时没有考虑那么多问题,如果以后用到还会回头来修改训练数据。大多数复杂环境下,肤色或多或少会受环境背景颜色的影响。所以,这里就有了操纵块的需求:如果统计肤色分割的肤色块中同时存在皮肤块和背景块,那么一定要想办法把背景块消除掉。训练数据库的不完整本来就会使分割出来的肤色块比较琐碎,而背景块的剔除会造成肤色块更加琐碎,最终提取的肤色块效果一定是小块小块并且支离破碎的,这样的肤色分割在后期手势或人脸跟踪中根本没法用,所以就有了块原点能量的需求,相比盲目的形态学计算效果分割得更加完整和精确,因为单一得形态学计算没有考虑肤色和背景噪声的关系。

那么肤色和背景噪声是什么关系?统计模型分割后的肤色块相对于背景噪声块要更加完整,大多数情况下肤色面积要更大(当然如果存在面积巨大的肤色背景,任何不依赖于肤色前景位置的分割算法都是失效的)。所以在这里提出强假设:假设不存在比肤色前景更大的肤色背景。那么基于块原点能量的肤色分割的应用场合一定是:小面积肤色或大面积类肤色或不带肤色的复杂背景下的肤色前景分割。

2. 代码实现(1)读取csv文件

读取csv文件中肤色后验概率上界的肤色掩码和18位的RGB颜色空间中的三通道颜色值对应的属于肤色类的后验概率。

################################################################################print 'read skin posterior probability from csv file'csvFile = open('skin_posterior_thresh.csv', "rb")reader = csv.reader(csvFile)# skin mask according to training skin databasemaskSkin = np.zeros(cRange, np.uint8)# skin posterior probability according to training skin databaseprobSkin = np.zeros(cRange, np.double)# prepare an empty image spaceimgSkin = np.zeros(img.shape, np.uint8)imgSkinEnergy = np.zeros(img.shape, np.uint8)# copy original imageimgContour = imgGray.copy()imgThreshBlob = imgGray.copy() imgEnergy = imgGray.copy()# dataType = 0: skinPix (skin mask); dataType = 1: P(Cs|V)dataType = 0rowNum = 0for row in reader:print len(row)colNum = 0for col in row:if dataType == 0:# restore skin maskmaskSkin[(rowNum – 1) * cRange + colNum] = colelif dataType == 1:# restore posterior probabilityprobSkin[(rowNum – 1) * cRange + colNum] = decimal.Decimal(col)colNum += 1rowNum += 1dataType += 1

(2)获取掩码图像

读取原始图像在RGB颜色空间中的位置;获得对应肤色后验概率上界的肤色掩码彩色图像(肤色部分为原图,非肤色部分为黑色);获得对应肤色后验概率上界的肤色掩码的初始等高线灰度图像;获得对应肤色后验概率下界的块阈值灰度图像。

(3)检测肤色块

查找初始等高线灰度图像的等高线,并获得面积最大登高线,同时在肤色掩码彩色图像上用检测到的所有肤色块(红色块,分割的结果包括背景肤色噪声)覆盖肤色块。

################################################################################print 'blob detection'# find the contours in the maskcontour,_ = cv2.findContours(imgContour.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# print num of contoursprint '%d contours found.' % len(contour)# find maximum areamaxArea = 0.0for cntIdx in range(len(contour)):# each contourcnt = contour[cntIdx]# contour areaarea = cv2.contourArea(cnt)if area > maxArea:maxContour = contour[cntIdx]maxArea = areaprint 'maximum area:', maxAreaprint 'contour length before:', len(contour)# loop over the contoursfor c in contour:# draw all red contourscv2.drawContours(imgSkin,[c],-1,(251,37,26),cv2.cv.CV_FILLED)

(4)删除背景肤色块

删除小于最大肤色块面积的1%的背景颜色块(背景颜色块是指包含大量背景区域的肤色或类肤色块,同时有可能包含少量琐碎的皮肤块)。删除过小的肤色块后琐碎的肤色块少了,但本来属于同一肤色单元的肤色区域会更加琐碎。删除的背景肤色块依然为红色块,未删除的肤色块覆盖为蓝色块。

如果心在远方,只需勇敢前行,梦想自会引路,

应用统计肤色模型和相对于块原点能量的肤色分割

相关文章:

你感兴趣的文章:

标签云: