【JAVA】圖像識別——HSV膚色提取

OSCHINA上看到各類語言的抓妹子圖的程序段,拿來跑一跑,都是爬蟲的機制,而地址通常都是固定的,格式固定,才能抓到想要的圖,這顯示不夠智能,因而把做者的代碼改掉,變成了個下載圖片的爬蟲。而後問題就來了,大量的圖片,不是我想要的,就這想到了圖像識別,目前主要的分支有,找類似圖,人臉識別,鑑黃等。java

今天要說說膚色提取,大概就暴露了,我要選什麼分支了,很少說,很少說 >_<!算法

膚色提取code

開始使用了CSDN上某大神寫的一段JAVA代碼(用於檢測黃色圖片),使用了YUV色彩空間。效果仍是很不錯的。圖片

/**
  * flesh
  * 
  * @param c
  * @return
  */
 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色彩空間。get

HSV六棱錐it

  H參數表示色彩信息,即所處的光譜顏色的位置。該參數用一角度量來表示,紅、綠、藍分別相隔120度。互補色分別相差180度。io

  純度S爲一比例值,範圍從0到1,它表示成所選顏色的純度和該顏色最大的純度之間的比率。S=0時,只有灰度。class

  V表示色彩的明亮程度,範圍從0到1。有一點要注意:它和光強度之間並無直接的聯繫。List

RGB轉化到HSV的算法file

  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

HSV轉化到RGB的算法

  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;

由算法,寫JAVA實現

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,爲了近一步肯定這個數值,作了以下實驗。

先扣了一些美女圖,只要肉,儘可能選擇有差別的。

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<HSV> list = new ArrayList<HSV>();
  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]<vessel[j]){
     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<HSV> transition(File file) throws IOException{
  System.out.println(file.getName());
  BufferedImage img = ImageIO.read(file);
  ArrayList<HSV> list = new ArrayList<HSV>();
  for (int j = 0; j <img.getWidth(); 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;
 }

 

結果:(略掉count=0)

H=15,count:31071

H=18,count:26936

H=16,count:24615

H=13,count:24031

H=17,count:21968

H=12,count:21211

H=30,count:19438

H=38,count:16740

H=14,count:16470

H=33,count:16404

H=32,count:16217

H=28,count:15231

H=35,count:14929

H=20,count:14714

H=31,count:14353

H=36,count:13654

H=29,count:13515

H=21,count:13311

H=34,count:13133

H=19,count:12595

H=26,count:11921

H=10,count:11062

H=37,count:10669

H=11,count:10422

H=27,count:9726

H=22,count:9010

H=25,count:8629

H=24,count:8548

H=40,count:8375

H=23,count:8240

H=39,count:7295

H=41,count:4262

H=43,count:3365

H=0,count:3229

H=9,count:2628

H=60,count:1983

H=42,count:1469

H=8,count:1453

H=7,count:927

H=44,count:862

H=45,count:742

H=180,count:515

H=51,count:354

H=48,count:263

H=240,count:221

H=330,count:210

H=6,count:198

H=47,count:168

H=50,count:147

H=56,count:137

H=5,count:134

H=63,count:125

H=52,count:116

H=46,count:90

H=69,count:69

H=220,count:59

H=76,count:57

H=70,count:50

H=77,count:44

H=4,count:41

H=64,count:36

H=184,count:32

H=75,count:32

H=72,count:30

H=49,count:29

H=354,count:27

H=353,count:26

H=280,count:25

H=2,count:25

H=150,count:24

H=120,count:23

H=68,count:23

H=352,count:19

H=350,count:17

H=3,count:16

H=55,count:15

H=54,count:14

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=73,count:8

H=348,count:8

H=57,count:8

H=185,count:7

H=345,count:7

H=83,count:7

H=78,count:7

H=66,count:7

H=355,count:6

H=188,count:6

H=228,count:6

H=100,count:5

H=340,count:5

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=174,count:2

H=96,count:2

H=182,count:2

H=183,count:2

H=82,count:2

H=95,count:2

H=165,count:2

H=170,count:2

H=189,count:2

H=106,count:2

H=358,count:2

H=260,count:1

H=264,count:1

H=94,count:1

H=144,count:1

H=88,count:1

H=1,count:1

H=166,count:1

H=342,count:1

H=187,count:1

H=168,count:1

H=110,count:1

H=114,count:1

H=192,count:1

H=172,count:1

H=92,count:1

H=128,count:1

H=175,count:1

H=176,count:1

H=249,count:1

H=135,count:1


分析數據,H的範圍大概在9~43之間

驗證以上分析

public static void main(String[] args) throws IOException {

  BufferedImage dst = new BufferedImage(100, 360 * 5,

    BufferedImage.TYPE_INT_RGB);

  for (int i = 0; i < 100; i++) {

   //for (int j = 0; j < 360 * 5; j++) {

   for (int j = 0; j < 50 * 5; j++) {

    dst.setRGB(i, j, ColorUtils.RGB2Binary(ColorUtils.HSV2RGB(new HSV(j/5, 1, 1))));

   }

  }

  ImageIO.write(dst, "jpg", new File("D:\\hsv1.jpg"));

 }

結果 (略掉未繪製部分)


H範圍[0,50),很顯示以上數據,上下能夠再切掉10%~30%。這是當S,V都等於1時的圖像,嘗試修改S和V的值,範圍在[0,1],就能夠匹配到因光線等問題,形成的較亮或較暗的圖像。而在作膚色匹配時,不考慮S和V,使準確性提升。 

判斷鮮肉

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; }

相關文章
相關標籤/搜索