不使用java.awt.Color(在Google App Engine上不允许)将HSV(Java中的HSB)转换为RGB

11

尽管我已经找到了一个解决方案,但我认为我应该发布这个问题,因为当我搜索时,没有现成的Java实现。

使用HSV而不是RGB允许生成具有相同饱和度和亮度的颜色(这正是我想要的)。

Google App Engine不允许使用java.awt.Color,因此无法执行以下操作来在HSV和RGB之间进行转换:

Color c = Color.getHSBColor(hue, saturation, value);
String rgb = Integer.toHexString(c.getRGB());

编辑:根据Nick Johnson的评论,我移动了我的答案。

Ex animo,- Alexander。


回答自己的问题是可以的,但您应该将答案发布为一个答案,而不是问题的一部分。 - Nick Johnson
谢谢Nick,我明天会这样做并编辑问题。(目前我遇到了一个错误:“声望低于100的用户在提问后8小时内不能回答自己的问题。您可以在6小时后自我回答。在此之前,请使用评论或编辑您的问题”) - yngling
8个回答

19

我对颜色数学一无所知,但是我可以提供这个代码的另一种结构作为替代方案。这个结构让我感到舒适,因为它让我清楚地看到每个6种情况只是一个不同的值、t和p的排列组合。(此外,我对长长的if-else链有一种非理性的恐惧。)

public static String hsvToRgb(float hue, float saturation, float value) {

    int h = (int)(hue * 6);
    float f = hue * 6 - h;
    float p = value * (1 - saturation);
    float q = value * (1 - f * saturation);
    float t = value * (1 - (1 - f) * saturation);

    switch (h) {
      case 0: return rgbToString(value, t, p);
      case 1: return rgbToString(q, value, p);
      case 2: return rgbToString(p, value, t);
      case 3: return rgbToString(p, q, value);
      case 4: return rgbToString(t, p, value);
      case 5: return rgbToString(value, p, q);
      default: throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value);
    }
}

public static String rgbToString(float r, float g, float b) {
    String rs = Integer.toHexString((int)(r * 256));
    String gs = Integer.toHexString((int)(g * 256));
    String bs = Integer.toHexString((int)(b * 256));
    return rs + gs + bs;
}

这确实更漂亮了,只是多了一个方法调用的成本。 - yngling
我很乐观,如果这可以带来重大的性能提升,编译器或JIT都会内联额外的方法。 - Peter Recore
6
抱歉翻出了一个两年前的帖子,但我想知道是否有人能为我确认一件事 - rgbToString 函数应该乘以 255 而不是 256 吗?否则,当 r1.0 时,rs 将会变成十六进制的 100,这是错误的。 - devrobf
是的,虽然如果你只是简单地乘以255,那么像0.9999这样的结果将被截断为254,这也是不正确的。一个正确的算法应该实现在java.awt.Color.HSBtoRGB()中存在的四舍五入。 - piepera
3
如果你的色调值恰好为1,那么h很容易是6,会抛出一个不必要的异常。 - ehartwell
请查看 https://github.com/sualeh/SchemaCrawler/blob/550b542f5f1b9981029bef60576f63b1a93feb5c/schemacrawler-api/src/main/java/sf/util/Color.java 这个完整实现上面的代码,它考虑了圆形结果,并且色调可以是任意浮点数。 - Sualeh Fatehi

5

您应该使用 Oracle 提供的 HSBtoRGB 实现,将其源代码复制到您的项目中。java.awt.Color 是开源的。Peter Recore 和 Yngling 提供的算法不够稳健,会返回像“256, 256, 0”这样的非法 RGB 值,适用于某些输入。Oracle 的实现是稳健的,请使用它。


5
使用ColorUtils工具类进行颜色处理。
HSLToColor(float\[\] hsl) 

And

[RGBToHSL(int r, int g, int b, float\[\] hsl)]

易于相互转换的方法!

例如:

float[] hsl = new float[]{1.5, 2.0, 1.5};
int color = ColorUtils.HSLToColor(hsl);

现在获取颜色。
float[] hslStub = new float[3];
float[] hslFromColor = ColorUtils.colorToHSL(color, hslStub);

现在获取HSL值。
这是源代码,点击此处查看。

2
解决方案在这里找到:http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ Martin Ankerl提供了一篇关于这个主题的好文章,并提供了Ruby脚本。对于那些太忙(或懒)来用Java实现它的人,这是我编写的一个(我相信它可以更有效地编写,请随意评论):
public static String hsvToRgb(float hue, float saturation, float value) {
    float r, g, b;

    int h = (int)(hue * 6);
    float f = hue * 6 - h;
    float p = value * (1 - saturation);
    float q = value * (1 - f * saturation);
    float t = value * (1 - (1 - f) * saturation);

    if (h == 0) {
        r = value;
        g = t;
        b = p;
    } else if (h == 1) {
        r = q;
        g = value;
        b = p;
    } else if (h == 2) {
        r = p;
        g = value;
        b = t;
    } else if (h == 3) {
        r = p;
        g = q;
        b = value;
    } else if (h == 4) {
        r = t;
        g = p;
        b = value;
    } else if (h <= 6) {
        r = value;
        g = p;
        b = q;
    } else {
        throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value);
    }

    String rs = Integer.toHexString((int)(r * 255));
    String gs = Integer.toHexString((int)(g * 255));
    String bs = Integer.toHexString((int)(b * 255));
    return rs + gs + bs;
}

最后一个else条件应该改为| else if (h <= 6) {。转换应该是(r * 255)等,而不是* 256,因为颜色范围是0到255。 - ehartwell
@ehartwell:根据您的评论进行了编辑。 - Peter O.

1

我的代码用于转换:

     /**
     * @param H
     *            0-360
     * @param S
     *            0-100
     * @param V
     *            0-100
     * @return color in hex string
     */
    public static String hsvToRgb(float H, float S, float V) {

        float R, G, B;

        H /= 360f;
        S /= 100f;
        V /= 100f;

        if (S == 0)
        {
            R = V * 255;
            G = V * 255;
            B = V * 255;
        } else {
            float var_h = H * 6;
            if (var_h == 6)
                var_h = 0; // H must be < 1
            int var_i = (int) Math.floor((double) var_h); // Or ... var_i =
                                                            // floor( var_h )
            float var_1 = V * (1 - S);
            float var_2 = V * (1 - S * (var_h - var_i));
            float var_3 = V * (1 - S * (1 - (var_h - var_i)));

            float var_r;
            float var_g;
            float var_b;
            if (var_i == 0) {
                var_r = V;
                var_g = var_3;
                var_b = var_1;
            } else if (var_i == 1) {
                var_r = var_2;
                var_g = V;
                var_b = var_1;
            } else if (var_i == 2) {
                var_r = var_1;
                var_g = V;
                var_b = var_3;
            } else if (var_i == 3) {
                var_r = var_1;
                var_g = var_2;
                var_b = V;
            } else if (var_i == 4) {
                var_r = var_3;
                var_g = var_1;
                var_b = V;
            } else {
                var_r = V;
                var_g = var_1;
                var_b = var_2;
            }

            R = var_r * 255; // RGB results from 0 to 255
            G = var_g * 255;
            B = var_b * 255;
        }

        String rs = Integer.toHexString((int) (R));
        String gs = Integer.toHexString((int) (G));
        String bs = Integer.toHexString((int) (B));

        if (rs.length() == 1)
            rs = "0" + rs;
        if (gs.length() == 1)
            gs = "0" + gs;
        if (bs.length() == 1)
            bs = "0" + bs;
        return "#" + rs + gs + bs;
    }

在Android上的使用示例:
tv.setBackgroundColor(Color.parseColor((ColorOperations.hsvToRgb(100, 100, 57))));

你好,你还活着吗? - Jitendra Mistry

1

@Peter Recore的答案没有使用四舍五入。

更正确的方法可能是从java.awt.Color中复制内容,以下是Java 6中的样子:

 public static int HSBtoRGB(float hue, float saturation, float brightness) {
        int r = 0, g = 0, b = 0;
        if (saturation == 0) {
            r = g = b = (int) (brightness * 255.0f + 0.5f);
        } else {
            float h = (hue - (float)Math.floor(hue)) * 6.0f;
            float f = h - (float)java.lang.Math.floor(h);
            float p = brightness * (1.0f - saturation);
            float q = brightness * (1.0f - saturation * f);
            float t = brightness * (1.0f - (saturation * (1.0f - f)));
            switch ((int) h) {
            case 0:
                r = (int) (brightness * 255.0f + 0.5f);
                g = (int) (t * 255.0f + 0.5f);
                b = (int) (p * 255.0f + 0.5f);
                break;
            case 1:
                r = (int) (q * 255.0f + 0.5f);
                g = (int) (brightness * 255.0f + 0.5f);
                b = (int) (p * 255.0f + 0.5f);
                break;
            case 2:
                r = (int) (p * 255.0f + 0.5f);
                g = (int) (brightness * 255.0f + 0.5f);
                b = (int) (t * 255.0f + 0.5f);
                break;
            case 3:
                r = (int) (p * 255.0f + 0.5f);
                g = (int) (q * 255.0f + 0.5f);
                b = (int) (brightness * 255.0f + 0.5f);
                break;
            case 4:
                r = (int) (t * 255.0f + 0.5f);
                g = (int) (p * 255.0f + 0.5f);
                b = (int) (brightness * 255.0f + 0.5f);
                break;
            case 5:
                r = (int) (brightness * 255.0f + 0.5f);
                g = (int) (p * 255.0f + 0.5f);
                b = (int) (q * 255.0f + 0.5f);
                break;
            }
        }
        return 0xff000000 | (r << 16) | (g << 8) | (b << 0);
    }

这里的舍入似乎是正确的。

1
使用SWT,您可以使用以下代码片段:
RGB rgb = new RGB(r, g, b);
float[] hsbColor = rgb.getHSB();
rgb = new RGB(hsbColor[0], hsbColor[1], hsbColor[2]);

确认。虽然(int,int,int)构造函数接受RGB,但(float,float,float)构造函数接受HSB。 - Andy Thomas

0

我知道这是一个老问题,但我在这里看到的所有答案都将色调乘以6。这是错误的。我查看了维基百科文章,那里说你必须除以60

这是我用Kotlin编写并测试过的代码示例:

fun hsvToRgb(hsv: FloatArray): IntArray {
    val (hue, saturation, value) = hsv
    val h: Int = (hue / 60).toInt()
    val f = hue / 60 - h
    val p = value * (1 - saturation)
    val q = value * (1 - f * saturation)
    val t = value * (1 - (1 - f) * saturation)

    val rgb = when (h) {
        0    -> floatArrayOf(value, t, p)
        1    -> floatArrayOf(q, value, p)
        2    -> floatArrayOf(p, value, t)
        3    -> floatArrayOf(p, q, value)
        4    -> floatArrayOf(t, p, value)
        5, 6 -> floatArrayOf(value, p, q)
        else -> throw Exception()
    }.map { it * 255 }
    val (r, g, b) = rgb
    return intArrayOf(r.toInt(), g.toInt(), b.toInt())
}

这是我的 Java 实现

public static int[] hsvToRgb(float[] hsv) {
        final float hue = hsv[0];
        final float saturation = hsv[1];
        final float value = hsv[2];
        final int h = (int) hue / 60;
        final float f = hue / 60 - h;
        final float p = value * (1 - saturation);
        final float q = value * (1 - f * saturation);
        final float t = value * (1 - (1 - f) * saturation);

        float[] rgb = switch (h) {
            case 0 -> new float[]{value, t, p};
            case 1 -> new float[]{q, value, p};
            case 2 -> new float[]{p, value, t};
            case 3 -> new float[]{p, q, value};
            case 4 -> new float[]{t, p, value};
            case 5, 6 -> new float[]{value, p, q};
            default -> throw new IllegalStateException();
        };
        rgb[0] = rgb[0] * 255;
        rgb[1] = rgb[1] * 255;
        rgb[2] = rgb[2] * 255;
        return new int[]{(int) rgb[0], (int) rgb[1], (int) rgb[2]};
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接