Find color name when have Hue in android

2019-03-22 07:24发布

I only care about 12 colors:

red: RGB: 255, 0, 0
pink: RGB: 255, 192, 203
violet: RGB: 36, 10, 64
blue: RGB: 0, 0, 255
green: RGB: 0, 255, 0
yellow: RGB: 255, 255, 0
orange: RGB: 255, 104, 31
white: RGB: 255, 255, 255
black: RGB: 0, 0, 0
gray: RGB: 128, 128, 128
tea: RGB: 193, 186, 176
cream: RGB: 255, 253, 208

When i read the pixel of bitmap, i can get the Hue value:

int picw = mBitmap.getWidth();
    int pich = mBitmap.getHeight();
    int[] pix = new int[picw * pich];
    float[] HSV = new float[3];

    // get pixel array from source
    mBitmap.getPixels(pix, 0, picw, 0, 0, picw, pich);

    int index = 0;
    // iteration through pixels
    for(int y = 0; y < pich; ++y) {
        for(int x = 0; x < picw; ++x) {
            // get current index in 2D-matrix
            index = y * picw + x;               
            // convert to HSV
            Color.colorToHSV(pix[index], HSV);
            // increase Saturation level
            //HSV[0] = Hue
            Log.i(getCallingPackage(), String.valueOf(HSV[0]));
        }
    }

Now i want to know what color of this pixel (only in 12 above colors)?

I use HSV to see the range of color. When i have a color which doesn't in this list, i want to name it as similarly color in my list How can i do it?

Thanks you so much

2条回答
We Are One
2楼-- · 2019-03-22 08:01

Based on your comments it seems you're basically trying to reduce the bitmap's full colour palette to only the 12 you specified. Obviously for every pixel in the bitmap the 'best match' from those 12 should be picked.

I still don't see why you would need the HSV values, as it's just a different representation of the RGB components - it doesn't actually change the problem, or its solution.

A straightforward approach to find the best match for any RGB colour would look something like as follows.

First build some sort of a list containing the colours you want to match against. I've used a Map, since you mentioned you (also) wanted to know the name of the colour, not just the RGB value.

Map<String, Integer> mColors = new HashMap<String, Integer>();
mColors.put("red", Color.rgb(255, 0, 0));
mColors.put("pink", Color.rgb(255, 192, 203));
mColors.put("voilet", Color.rgb(36, 10, 64));
mColors.put("blue", Color.rgb(0, 0, 255));
mColors.put("green", Color.rgb(0, 255, 0));
mColors.put("yellow", Color.rgb(255, 255, 0));
mColors.put("orange", Color.rgb(255, 104, 31));
mColors.put("white", Color.rgb(255, 255, 255));
mColors.put("black", Color.rgb(0, 0, 0));
mColors.put("gray", Color.rgb(128, 128, 128));
mColors.put("tea", Color.rgb(193, 186, 176));
mColors.put("cream", Color.rgb(255, 253, 208));

Then just make a method that will tell you the best match. You can call this from within your second for loop and pass it the current pixel colour. I've added some inline comments to explain the different steps, but it's really quite trivial.

private String getBestMatchingColorName(int pixelColor) {
    // largest difference is 255 for every colour component
    int currentDifference = 3 * 255;
    // name of the best matching colour
    String closestColorName = null;
    // get int values for all three colour components of the pixel
    int pixelColorR = Color.red(pixelColor);
    int pixelColorG = Color.green(pixelColor);
    int pixelColorB = Color.blue(pixelColor);

    Iterator<String> colorNameIterator = mColors.keySet().iterator();
    // continue iterating if the map contains a next colour and the difference is greater than zero.
    // a difference of zero means we've found an exact match, so there's no point in iterating further.
    while (colorNameIterator.hasNext() && currentDifference > 0) {
        // this colour's name
        String currentColorName = colorNameIterator.next();
        // this colour's int value
        int color = mColors.get(currentColorName);
        // get int values for all three colour components of this colour
        int colorR = Color.red(color);
        int colorG = Color.green(color);
        int colorB = Color.blue(color); 
        // calculate sum of absolute differences that indicates how good this match is 
        int difference = Math.abs(pixelColorR - colorR) + Math.abs(pixelColorG - colorG) + Math.abs(pixelColorB - colorB);
        // a smaller difference means a better match, so keep track of it
        if (currentDifference > difference) {
            currentDifference = difference;
            closestColorName = currentColorName;
        }
    }
    return closestColorName;
}

The results for a quick test using some of the predefined Color constants:

Color.RED (-65536) -> red (-65536)
Color.GREEN (-16711936) -> green (-16711936)
Color.BLUE (-16776961) -> blue (-16776961)
Color.BLACK (-16777216) -> black (-16777216)
Color.WHITE (-1) -> white (-1)
Color.GRAY (-7829368) -> gray (-8355712)
Color.YELLOW (-256) -> yellow (-256)
Color.MAGENTA (-65281) -> pink (-16181)

The first number inbetween the brackets is the actual int value for the Color constant, the second is the int value for the best match found, with the name right in front of it.

The result for Color.MAGENTA also illustrates why you should not just compare the colour's int value directly. The actual int value is -65281, which is quite close to the value for Color.RED (-65536). However, the best match based on the different components is 'pink', which has a -16181 value. Obviously this makes complete sense knowing that a colour is defined as 4 bytes:

Colors are represented as packed ints, made up of 4 bytes: alpha, red, green, blue. (...) The components are stored as follows (alpha << 24) | (red << 16) | (green << 8) | blue.

Source: android.graphics.Color reference.

// Edit: with HSV values it seems to work fine too. I did get a different result for 'magenta' as closest match though - violet, in stead of pink. You might want to double check the values and breakpoint some stuff. For instance, I can imagine it might be better to normalize the 'H' part. That's up to you...

private String getBestMatchingHsvColor(int pixelColor) {
    // largest difference is 360(H), 1(S), 1(V)
    float currentDifference = 360 + 1 + 1;
    // name of the best matching colour
    String closestColorName = null;
    // get HSV values for the pixel's colour
    float[] pixelColorHsv = new float[3];
    Color.colorToHSV(pixelColor, pixelColorHsv);

    Iterator<String> colorNameIterator = mColors.keySet().iterator();
    // continue iterating if the map contains a next colour and the difference is greater than zero.
    // a difference of zero means we've found an exact match, so there's not point in iterating further.
    while (colorNameIterator.hasNext() && currentDifference > 0) {
        // this colour's name
        String currentColorName = colorNameIterator.next();
        // this colour's int value
        int color = mColors.get(currentColorName);
        // get HSV values for this colour
        float[] colorHsv = new float[3];
        Color.colorToHSV(color, colorHsv);
        // calculate sum of absolute differences that indicates how good this match is 
        float difference = Math.abs(pixelColorHsv[0] - colorHsv[0]) + Math.abs(pixelColorHsv[1] - colorHsv[1]) + Math.abs(pixelColorHsv[2] - colorHsv[2]);
        // a smaller difference means a better match, so store it
        if (currentDifference > difference) {
            currentDifference = difference;
            closestColorName = currentColorName;
        }
    }
    return closestColorName;
}
查看更多
Luminary・发光体
3楼-- · 2019-03-22 08:07

Since you already have pixel color value in int. You can extract the RGB value using following methods

int green = Color.green(pix[i]);
int red   = Color.red(pix[i]);
int blue  = Color.blue(pix[i]);

And then compare with the RGB values you have

查看更多
登录 后发表回答