肤色检测算法 - 基于二次多项式混合模型的肤色检测。

来源:互联网 发布:油菜花系统数据库 编辑:程序博客网 时间:2024/04/19 12:42

分类: 图像处理 533人阅读 评论(1) 收藏 举报
二次多项式混合模型皮肤检测肤色检测

  由于CSDN博客和博客园的编辑方面有不一致的地方,导致文中部分图片错位,为不影响浏览效果,建议点击打开链接 

      由于能力有限,算法层面的东西自己去创新的很少,很多都是从现有的论文中学习,然后实践的。

      本文涉及的很多算法,在网络上也有不少同类型的文章,但是肯定的一点就是,很多都是不配代码的,或者所附带的代码都是象征性的,速度慢,不优雅,不具有实用价值,本文努力解决这些问题。

      文中各算法出现的顺序并不代表算法的优越性,仅仅是作者随机排布的而已。

      1、二次多项式混合模型

           二次多项式混合模型首先有SORIANO提出,此后CHIANG对此进行了改进。改进后的模型由两个R-G平面的二次多项式和一个圆方程构成:

      

  在以上三个方程的基础上,肤色区域可以通过一下规则实现:

               

     上述算法的参考论文:Adaptive skin color modeling using the skin locus.pdf  

            A novel method for detecting lips,eyes and faces in real time

  以及百度文库相关文章:基于混合肤色模型的快速人脸检测算法 

     上式中,小写r,g,b(未涉及)为对R/G/B(byte类型的数据,0-255)进行归一化后的数据,即:

              

     如上所示,算法中涉及到了不少的浮点运算,以及大量的乘法,如果按照源汁原味的来编写代码,程序的效率可想而知。因此,我们着手于算法的优化。

     首先,我们来看四个判断条件,由于判断条件是不分先后,需要同时满足的地方才是区域,因此应该把简单的判断条件放在最前面判断。

     首先看如果符合了判断条件R4,条件R3中的R>G肯定是已经成立的,则只需要判断G是否大于B,这是优化手段1。   

     然后我们来看R2的优化,为方便表达,我们这里令Sum=R+G+B,将判断条件R2展开:

                     

     上式子最后一步同时乘以156, 理论上说156×0.33=51.48,不应该取52的,不过这个0.33本来就是个经验数据,谁说不能是1/3呢。

     到此,我们看到在式子的最右侧还有个浮点数0.0624,如果不消除该数据,算法速度依旧会有大的影响,常常研究移位的朋友肯定对0.0625这个数字很熟悉,1/16=0.0625,不是吗,懂了吗,还不懂,看代码吧(这里的式子很多都是经验公式,因此,稍微修改一些参数对结果基本无影响)。

     上述这样做的目的,无非是将浮点数的运算全部转换为整数的运算。

  最后来看式R1的优化,R1实际上也是两个条件,把他分开来,分别称为R11及R12,对于R11,同样展开:

                

     现在大部分的PC都还是32位的系统,因此,使用32位的整数类数据类型速度是最快的,因此,如果上述放大系数的取夺就必须主要使得计算式两边的值都在int.MinValue和 int.MaxValue之间,比如上式,>号左侧算式的肯能最大取值为10000×255×765,是小于int.MaxValue所能表达的范围的,因此放大系数是合理的。

     对于R12的展开我想应该不需要我在去贴出来了吧。

     算法部分参考代码:

for (Y = 0; Y < Height; Y++){    Pointer = Scan0 + Y * Stride;    SkinP = SkinScan0 + Y * SkinStride;    for (X = 0; X < Width; X++)    {        *SkinP = 0;                                 // 非皮肤区域为黑色        Blue = *Pointer; Green = *(Pointer + 1); Red = *(Pointer + 2);        if (Red - Green >= 45)                                              //  符合条件R4        {            if (Green > Blue)                                               //   符合条件R3            {                Sum = Red + Green + Blue;                T1 = 156 * Red - 52 * Sum;                                                 T2 = 156 * Green - 52 * Sum;                if (T1 * T1 + T2 * T2 >= (Sum * Sum) >> 4)                    // 符合条件R2,在32位系统要尽量避免用long类型数据,                   {                    T1 = 10000 * Green * Sum;                    Lower = - 7760 * Red * Red + 5601 * Red * Sum + 1766 * Sum * Sum;         // 把这里的公用的乘法提取出来基本没啥优化的效果                        if (T1 > Lower)                                         // 符合条件R11                    {                        Upper = - 13767 * Red * Red + 10743 * Red * Sum + 1452 * Sum * Sum ;                        if (T1 < Upper)                                     //  符合条件R12                        {                            *SkinP = 255;                     }                    }                }            }        }        Pointer += 3;        SkinP++;    }

  本人特喜欢优化,特别是代码层面的优化,比如上述的 Lower = 5601 * Red * Sum + 1766 * Sum * Sum 这句,偶尔我写成Lower =- Red * Red * 7760+ 5601 * Red * Sum + 1766 * Sum * Sum 这样,然后没事的时候我反汇编了两种写法有什么不同,结果如下:

[csharp] view plaincopyprint?
  1.  Lower =-7760 * Red * Red+ 5601 * Red * Sum + 1766 * Sum * Sum ;         // 把这里的公用的乘法提取出来基本没啥优化的效果  
  2. 00000118  imul        ebx,ecx,0FFFFE1B0h   
  3. 0000011e  imul        ebx,ecx   
  4. 00000121  imul        eax,ecx,15E1h   
  5. 00000127  imul        eax,esi   
  6. 0000012a  add         ebx,eax   
  7. 0000012c  imul        eax,esi,6E6h   
  8. 00000132  imul        eax,esi   
  9. 00000135  add         ebx,eax   

 

[csharp] view plaincopyprint?
  1.  Lower = -Red * Red * 7760 * +5601 * Red * Sum + 1766 * Sum * Sum;         // 把这里的公用的乘法提取出来基本没啥优化的效果  
  2. 00000118  mov         ebx,ecx   
  3. 0000011a  neg         ebx   
  4. 0000011c  imul        ebx,ecx   
  5. 0000011f  imul        ebx,ebx,1E50h   
  6. 00000125  imul        ebx,ebx,15E1h   
  7. 0000012b  imul        ebx,ecx   
  8. 0000012e  imul        ebx,esi   
  9. 00000131  imul        eax,esi,6E6h   
  10. 00000137  imul        eax,esi   
  11. 0000013a  add         ebx,eax   

     可见多了两条汇编语句的。可能这个优化举在这里不合适,因为有个系数-7760,一般谁都不会像上面写,但是如果系数是-1,那就比一定了,比如如果是-Red+Blue 和Blue-Red那就有着截然不同的意义了。

      这个算法的皮肤检测效果还是很不错的,那原文中的图像来举例如下:

           

             原图                                                           梦版图                                                      合成图

 然后贴一张别人博客上的照片的例子(一群帅哥和美女):

 检测结果:

   由于是有选择性的执行,因此程序执行的速度其实和图像的内容有关,同样一副大小的图像,如果皮肤部分站的比例越大,执行的时间可能就会越长,就上述这幅800*600的图像来说,在我I3的笔记本上仅用了4ms就得到了结果,因此速度是相当的快的。

      测试工程随后附上。

     

*****************************基本上我不提供源代码,但是我会尽量用文字把对应的算法描述清楚或提供参考文档**************************

*******************************因为靠自己的努力和实践写出来的效果才真正是自己的东西,人一定要靠自己****************************

***************************作者: laviewpbt   时间: 2013.8.17   联系QQ:  33184777  转载请保留本行信息*************************

肤色检测算法 - 基于不同颜色空间简单区域划分的皮肤检测算法

分类: 图像处理 859人阅读 评论(1) 收藏 举报
肤色识别

      由于CSDN博客和博客园的编辑方面有不一致的地方,导致文中部分图片错位,为不影响浏览效果,建议点击打开链接 

      由于能力有限,算法层面的东西自己去创新的很少,很多都是从现有的论文中学习,然后实践的。

      本文涉及的很多算法,在网络上也有不少同类型的文章,但是肯定的一点就是,很多都是不配代码的,或者所附带的代码都是象征性的,速度慢,不优雅,不具有实用价值,本文努力解决这些问题。

      文中各算法出现的顺序并不代表算法的优越性,仅仅是作者随机排布的而已。

      2、基于RGB颜色空间的简单阈值肤色识别

       在human skin color clustering for face detection一文中提出如下简单的判别算式:

      R>95 And G>40 And B>20 And R>G And R>B And Max(R,G,B)-Min(R,G,B)>15 And Abs(R-G)>15 

    算法非常之简单,同样主要把复杂的判断条件放到后面去判断,能有效的降低程序的执行时间,参考代码:

 

for (Y = 0; Y < Height; Y++){    Pointer = Scan0 + Y * Stride;    SkinP = SkinScan0 + Y * SkinStride;    for (X = 0; X < Width; X++)    {        Blue = *Pointer; Green = *(Pointer + 1); Red = *(Pointer + 2);        if (Red > 95 && Green > 40 && Blue > 20 && Red > Blue && Red > Green && Math.Abs(Red - Green) > 15)        {            if (Blue >= Green)                 {                Max = Blue;                Min = Green;            }            else            {                Max = Green;                Min = Blue;            }            if (Red > Max)                Max = Red;            else if (Red < Min)                Min = Red;            if (Max - Min > 15) *SkinP = 255;        }        Pointer += 3;        SkinP++;    }

  算法效果:

       

                      

        原图                     识别结果图                                                   原图                识别结果图

     由上述结果似乎该算法得到了过多的皮肤区域,然后就是算法更喜欢美女一些(^_^)。

     3、基于YCbCr颜色空间的简单阈值肤色识别

  该算法则更为简单,将图像转换到YCbCr颜色空间,然后按下述计算式判断是否属于皮肤区域:

    (Cb > 77 And Cb < 127)  And (Cr > 133 And Cr < 173)

      关于RGB和YCbCr颜色空间的转换的优化算法,可参考本博客相关文章。

      由于当初写这方面的时候没有注明该算法的出处,现在也没从提起了。

      代码参考:

[csharp] view plaincopyprint?
  1. for (Y = 0; Y < Height; Y++)  
  2. {  
  3.     Pointer = Scan0 + Y * Stride;  
  4.     SkinP = SkinScan0 + Y * SkinStride;  
  5.     for (X = 0; X < Width; X++)  
  6.     {  
  7.         Blue = *Pointer; Green = *(Pointer + 1); Red = *(Pointer + 2);  
  8.         Cb = (-176933 * Red - 347355 * Green + 524288 * Blue + 134217728) >> 20;  
  9.         if (Cb > 77 && Cb < 127)  
  10.         {  
  11.             Cr = (524288 * Red - 439026 * Green - 85262 * Blue + 134217728) >> 20;  
  12.             if (Cr > 133 && Cr < 173) *SkinP = 255;  
  13.         }  
  14.         Pointer += 3;  
  15.         SkinP++;  
  16.     }  
  17. }  

 

      

 

                          

              原图                         识别结果图                                                   原图                识别结果图

     误判的区域还是很大的。

     还有一种是基于YUV颜色空间进行的肤色识别,似乎也不太准确,可参考http://www.doc88.com/p-97381067005.html。

 

 

*****************************基本上我不提供源代码,但是我会尽量用文字把对应的算法描述清楚或提供参考文档**************************

 

*******************************因为靠自己的努力和实践写出来的效果才真正是自己的东西,人一定要靠自己****************************

 

***************************作者: laviewpbt   时间: 2013.8.17   联系QQ:  33184777  转载请保留本行信息*************************

 


 

0 0