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

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

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

今天要说说肤色提取,大概就暴露了,我要选什么分支了,不多说,不多说 >_<!

肤色提取

开始使用了CSDN上某大神写的一段JAVA代码(用于检测黄色图片),使用了YUV色彩空间。效果还是很不错的。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/**

  * 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色彩空间。

HSV六棱锥

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

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

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

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

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实现

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

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实现统计

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

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==16777215continue;

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

时间: 2024-08-08 09:27:10

【JAVA】图像识别——HSV肤色提取 【转载】的相关文章

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

OSCHINA上看到各种语言的抓妹子图的程序段,拿来跑一跑,都是爬虫的机制,而地址一般都是固定的,格式固定,才能抓到想要的图,这显示不够智能,于是把作者的代码改掉,变成了个下载图片的爬虫.然后问题就来了,大量的图片,不是我想要的,就这想到了图像识别,目前主要的分支有,找相似图,人脸识别,鉴黄等. 今天要说说肤色提取,大概就暴露了,我要选什么分支了,不多说,不多说 >_<! 肤色提取 开始使用了CSDN上某大神写的一段JAVA代码(用于检测黄色图片),使用了YUV色彩空间.效果还是很不错的. /

Java NIO Channel之FileChannel [ 转载 ]

Java NIO Channel之FileChannel [ 转载 ] @author zachary.guo 对于文件 I/O,最强大之处在于异步 I/O(asynchronous I/O),它允许一个进程可以从操作系统请求一个或多个 I/O 操作而不必等待这些操作的完成.发起请求的进程之后会收到它请求的 I/O 操作已完成的通知.异步 I/O 是一种高级性能,当前的很多操作系统都还不具备.因此,文件通道在多数情况下来说总是阻塞式的,因此不能被置于非阻塞模式. FileChannel 对象不能

Java类之间的关联关系(转载)

Java类之间的关联关系 UML类图中的关系分为四种:泛化.依赖.关联.实现:关联关系又可以细化为聚合和组合.一.泛化(Generalization)泛化是父类和子类之间的关系,子类继承父类的所有结构和行为.在子类中可以增加新的结构和行为,也可以覆写父类的行为. 一般用一个带空心箭头的实线表示泛化关系,UML图如下: 泛化对应Java中继承关系,即子类继承父类中出private修饰外的所有东西(变量.方法等).示例代码: public class Animal { } public class

Java Object 序列化与单例模式 [ 转载 ]

Java Object 序列化与单例模式 [ 转载 ] @author Hollis 本文将通过实例+阅读Java源码的方式介绍序列化是如何破坏单例模式的,以及如何避免序列化对单例的破坏. 单例模式,是设计模式中最简单的一种.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案.关于单例模式的使用方式,可以阅读单例模式的七种写法 但是,单例模式真的能够实现实例的唯一性吗? 答

Java基础11 对象引用(转载)

对象引用 我们沿用之前定义的Human类,并有一个Test类: public class Test{    public static void main(String[] args){        Human aPerson = new Human(160);    }  class Human{    public Human(int h){        this.height = h;    }    public int getHeight(){        return this

如何从sun公司官网下载java API文档(转载)

相信很多同人和我一样,想去官网下载一份纯英文的java API文档,可使sun公司的网站让我实在很头疼,很乱,全是英文!所以就在网上下载了别人提供的下载!可是还是不甘心!其实多去看看这些英文的技术网站很有好处!去官网下载的东西感觉也很好! 所以: 1.进入官网http://www.oracle.com/technetwork/java/index.html 2.Oracle主页 -> download下拉菜单里找到Java for development -> 按ctrl+F搜索Java SE

java从字符串中提取数字

1.做一下操作时会一般会用到提取数字操纵: a.列表中有翻页,当新添加的数据不是放在第一条或者最后一条时,需要翻页并循环找到对应的那条数据 b.当新添加的数据放在第一条或者最后一条时,则不需要翻页,只需要直接进入该页面然后直接找到第一条或者最后一条数据即可. 2.例子: 界面: javs代码: /** * java从字符串中提取数字 * str:传递过来的字符串 */ public static List<String> getNUm(String str){ str.trim(); //St

Java基础06 组合(转载)

在一个新类的定义中使用其他对象.这就是组合(composition).组合是在Java中实现程序复用(reusibility)的基本手段之一. 组合与"has-a" 一个对象是另一个对象的数据成员.比如我们看之前提到的充电电筒的例子: 一个充电电筒中的电池.LED灯.按钮…… 都可以是一个对象.我们可以定义一个Battery类来定义和产生电池对象.而在充电电筒的类定义中,可以用一个电池对象作为其数据成员,来代表电池部分的状态. 我们下面定义一个Battery类,并用power来表示其电

Java的命名规范(转载)

Java是一种区分字母的大小写(case-sensitive)的语言,下面谈谈Java语言中包.类.变量等的命名规范. (一)Package(包)的命名: Package的名字应该都是由一个小写单词组成,例如net.ebseries.modules. (二)Class(类)的命名: Class的名字首字母大写,通常由多个单词合成一个类名,要求每个单词的首字母也要大写,例如:DataFile或InfoParser. (三)变量的命名: 变量的名字可大小写混用,但首字符应小写.词由大写字母分隔,限制