【JAVA】图像识别——HSV肤色提取

网上看到各种语言的抓图(采集照片)的程序段,拿来跑一跑,都是爬虫的机制,而地址一般都是固定的,格式固定,才能抓到想要的图,这显示不够智能,于是把作者的代码改掉,变成了个下载图片的爬虫。然后问题就来了,大量的图片,不都是我们想要的,这就想到了图像识别,目前主要的分支有,找相似图,人脸识别,鉴黄等。

今天要说说肤色提取,即提取你个人喜爱的肤色(无种族歧视哦)。最开始使用了CSDN上某大神写的一段JAVA代码(用于检测黄色图片,此处指颜色为黄色的图像),代码如下,使用了YUV色彩空间。效果还是很不错的。


 public static boolean isFlesh(final Color c) {
  if ((c.getRed() > 230) && (c.getGreen() > 170) && (c.getBlue() > 190)) {
   return false;
  }
  LDialyzer yuv = LDialyzer.getYuv(c.getRed(), c.getGreen(), c.getBlue());
  return ((c.getRed() > 40) && (c.getGreen() > 40) && (yuv.y + 16 > 145)
    && (yuv.v + 128 < 173) && (yuv.v + 128 > 133)
    && (yuv.u + 128 < 127) && (yuv.u + 128 > 77));
 }

但是这段代码,上半部分的依据RGB范围直接PASS掉一部分,这确定是有点果断的,仔细观察RGB色彩空间,会发现还是有一部分的偏黄色被排除了。于是考虑使用HSV色彩空间。

1、这里科普一下RGB和HSV的区别,

所谓RGB就是:红(Red)、绿(Green)、蓝(Blue)三种色光原色。RGB色彩模型的混色属于加法混色。每种原色的数值越高,色彩越明亮。 R、G、B都为0时是黑色,都为255时是白色。RGB虽然表示直接,但是R、G、B数值和色彩的三属性没有直接的联系,不能揭示色彩之间的关系。所以在进行配色设计时,RGB模型就不是那么合适了。HSV是指Hue(色相)、Saturation(饱和度)和Value(值)。RGB和CMY颜色模型都是面向硬件的,而HSV颜色模型是面向用户的。

2、HSV六棱锥

H参数表示色彩信息,即所处的光谱颜色的位置。该参数用一角度量来表示,红、绿、蓝分别相隔120度。互补色分别相差180度。

纯度S为一比例值,范围从0到1,它表示成所选颜色的纯度和该颜色最大的纯度之间的比率。S=0时,只有灰度。

V表示色彩的明亮程度,范围从0到1。有一点要注意:它和光强度之间并没有直接的联系。

3、RGB和HSV的转换算法

 max=max(R,G,B)
  min=min(R,G,B)
  if R = max, H = (G-B)/(max-min)
  if G = max, H = 2 + (B-R)/(max-min)
  if B = max, H = 4 + (R-G)/(max-min)
  H = H * 60
  if H < 0, H = H + 360
  V=max(R,G,B)
  S=(max-min)/max
 if s = 0
    R=G=B=V
  else
    H /= 60;
    i = INTEGER(H)
    f = H - i
    a = V * ( 1 - s )
    b = V * ( 1 - s * f )
    c = V * ( 1 - s * (1 - f ) )
    switch(i)
      case 0: R = V; G = c; B = a;
      case 1: R = b; G = v; B = a;
      case 2: R = a; G = v; B = c;
      case 3: R = a; G = b; B = v;
      case 4: R = c; G = a; B = v;
      case 5: R = v; G = a; B = b;

4、RGB转化为HSV的算法的实现:

public static HSV RGB2HSV(RGB rgb){
  float r = (float)rgb.getR()/255;
  float g = (float)rgb.getG()/255;
  float b = (float)rgb.getB()/255;
  float max = max(r, g, b);
  float min = min(r, g, b);
  float h = 0;
  if(r==max)
   h = (g-b)/(max-min);
  if(g==max)
   h = 2+(b-r)/(max-min);
  if(b==max)
   h= 4+(r-g)/(max-min);
  h *=60;
  if(h<0) h +=360;
  HSV hsv = new HSV(h,(max-min)/max,max);
  return hsv;
 }

对于肤色识别 饱和度(S)和亮度(V)就无关紧要了,这样只需得到一个色调(H)的取值范围。

从网上找了找H的取值范围,大概在25~50,为了近一步确定这个数值,做了如下实验。

先截取了一些照片,只要能体现此人的肤色,尽量选择有差异的。

11

5、JAVA实现统计

public static int[] vessel = new int[360];
 public static int[] vesselIndex = new int[360];
 public static void main(String[] args) throws IOException {
  File file = new File("D:\\培养材料");
  File[] listFiles = file.listFiles();
  ArrayList list = new ArrayList();
  for (int i = 0; i < listFiles.length; i++) {
   transition(listFiles[i]);
  }
  for (int i = 0; i < vesselIndex.length; i++) {
   vesselIndex[i] = i;
  }
  for (int i = 0; i < vessel.length; i++) {
   for (int j = i+1; j < vessel.length; j++) {
    if(vessel[i]int temp = vessel[i];
     vessel[i] = vessel[j];
     vessel[j] = temp;
     int tempIndex = vesselIndex[i];
     vesselIndex[i] = vesselIndex[j];
     vesselIndex[j] = tempIndex;
    }
   }
  }
  for (int i = 0; i < vesselIndex.length; i++) {
   System.out.println("H="+vesselIndex[i]+",count:"+vessel[i]);
  }
 }
 private static ArrayList transition(File file) throws IOException{
  System.out.println(file.getName());
  BufferedImage img = ImageIO.read(file);
  ArrayList list = new ArrayList();
  for (int j = 0; j for (int j2 = 0; j2 < img.getHeight(); j2++) {
    int binaryColor = img.getRGB(j, j2);
    if(binaryColor==16777215) continue;
    Color c = new Color(binaryColor);
    RGB rgb = new RGB(c.getRed(), c.getGreen(), c.getBlue());
    HSV hsv = ColorUtils.RGB2HSV(rgb);
    if(!"NaN".equals(String.valueOf(hsv.getH())))
     vessel[(int)hsv.getH()]++;
    list.add(hsv);
    System.out.println(hsv);
   }
  }
  return list;
 }

6、结果:(略掉count=0)此处省去大部分数据

H=15,count:31071

H=18,count:26936

H=16,count:24615

H=13,count:24031

H=17,count:21968

H=26,count:11921

H=10,count:11062

H=90,count:13

H=65,count:12

H=79,count:11

H=357,count:11

H=210,count:10

H=351,count:10

H=251,count:10

H=74,count:9

H=356,count:9

H=53,count:9

H=190,count:8

H=67,count:8

H=300,count:8

H=336,count:4

H=85,count:4

H=84,count:4

H=171,count:3

H=186,count:3

H=173,count:3

H=140,count:3

H=195,count:3

H=349,count:3

H=105,count:3

H=108,count:2

H=189,count:2

H=106,count:2

H=358,count:2

H=260,count:1

H=264,count:1

结果 (略掉未绘制部分)

12

H范围[0,50),很显示以上数据,上下可以再切掉10%~30%。这是当S,V都等于1时的图像,尝试修改S和V的值,范围在[0,1],就可以匹配到因光线等问题,造成的较亮或较暗的图像。而在做肤色匹配时,不考虑S和V,使准确性提高。 

7、判断鲜肉(即筛选你想要的肤色)

public static boolean isFlesh2(Color c){
RGB rgb = new RGB(c.getRed(),c.getGreen(),c.getBlue());
 HSV hsv = ColorUtils.RGB2HSV(rgb);
 if(hsv.getH()>9&&hsv.getH()<43){
  return true;
 }
 return false;
 }

以上内容就是我们本节讲的主要内容,利用Java程序实现了对众多图片按照肤色提取。(帮助你找到心仪的小姐姐)。其实肤色提取对于人脸识别领域也是一项重大的技术,尤其是人脸识别作为生物身份验证手段的潜力逐渐凸显,使得对识别系统的性能要求越来越高。学习了以上内容,让你对肤色及色彩提取更为了解。


登录或注册后发布评论