Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inaccurate HSV conversion on Android

I am trying to create a wallpaper and am using the HSV conversion in the "android.graphics.color" class. I was very surprised when i realized that a conversion of a created HSV color with a specified hue (0..360) to a rgb color (an integer) and a back conversion to a HSV color will not result in the same hue. This is my code:

int c = Color.HSVToColor(new float[] { 100f, 1, 1 });
float[] f = new float[3];
Color.colorToHSV(c, f);
alert(f[0]);

I am starting with a hue of 100 degree and the result is 99.76471. I wonder why there is that (in my opinion) relatively big inaccuracy.

But a much bigger problem is, that when you put that value in the code again, the new result decreases again.

int c = Color.HSVToColor(new float[] { 99.76471f, 1, 1 });
float[] f = new float[3];
Color.colorToHSV(c, f);
alert(f[0]);

If I start with 99.76471, I get 99.52941. This is kind of a problem for me. I did something similar in java with the "java.awt.Color" class where I did not have those problems. Unfortunately, I cannot use this class in android.

like image 881
user2957782 Avatar asked Dec 27 '15 22:12

user2957782


1 Answers

This is an interesting problem. It's not avoidable with the android class because of low float precision. However, I found a similar solution written in javascript here.

If it's important enough for you to want to define your own method/class to do the conversions, here is a Java conversion which should give you better precision:

@Size(3)
/** Does the same as {@link android.graphics.Color#colorToHSV(int, float[])} */
public double[] colorToHSV(@ColorInt int color) {
    //this line copied vertabim
    return rgbToHsv((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF);
}

@Size(3)
public double[] rgbToHsv(double r, double g, double b) {
    final double max = Math.max(r,  Math.max(g, b));
    final double min = Math.min(r, Math.min(g, b));
    final double diff = max - min;
    final double h;
    final double s = ((max == 0d)? 0d : diff / max);
    final double v = max / 255d;
    if (min == max) {
        h = 0d;
    } else if (r == max) {
        double tempH = (g - b) + diff * (g < b ? 6: 0);
        tempH /= 6 * diff;
        h = tempH;
    } else if (g == max) {
        double tempH = (b - r) + diff * 2;
        tempH /= 6 * diff;
        h = tempH;
    } else {
        double tempH = (r - g) + diff * 4;
        tempH /= 6 * diff;
        h = tempH;
    }
    return new double[] { h, s, v };
}

I have to confess ignorance here - I've done quick conversion and not had time to test properly. There might be a more optimal solution, but this should get you started at least.

like image 153
Nick Cardoso Avatar answered Oct 19 '22 01:10

Nick Cardoso