Thread: Model Color from HSL

Results 1 to 3 of 3
  1. #1 Model Color from HSL 
    Registered Member Aleksandr's Avatar
    Join Date
    Sep 2006
    Age
    33
    Posts
    1,034
    Thanks given
    455
    Thanks received
    134
    Rep Power
    74
    This code will help you get the model color for recoloring your models.

    Code:
    	public static int getModelColorFromHSL(int H, int S, int L) {
    		H = Math.max(0, Math.min(359, H));
    		S = Math.max(0, Math.min(100, S));
    		L = Math.max(0, Math.min(100, L));
    		
    		int newH = interpolate(0, 63, 0, 359, H);
    		int newS = interpolate(0, 7, 0, 100, S);
    		int newL = interpolate(0, 127, 0, 100, L);
    		
    		return (newH << 10) | (newS << 7) | newL;
    	}
    	
    	public static int interpolate(int outputMin, int outputMax, int inputMin, int inputMax, int input) {
    		return outputMin + (outputMax - outputMin) * (input - inputMin) / (inputMax - inputMin);
    	}

    The HSL is bit packed.
    Bit representation of model color is
    Code:
    HHHHHHSSSLLLLLLL
    Al
    Attached image

    There is no RL, just AFK
    Reply With Quote  
     

  2. #2  
    nice


    Join Date
    Jul 2014
    Posts
    740
    Thanks given
    382
    Thanks received
    562
    Rep Power
    4239
    A much better and equally simple approach would be to find the "closest matching color" by looking up each value in the table.
    by 'better' i really mean more accurate.
    First lets look at what your method yields for some randomly chosen HSL values:
    Attached image
    Left side displays the original HSL values, right side displays the converted values using ur method
    It looks noticeably different and we can do much better, first lets generate the HSL -> RGB table:
    Code:
        public static void generatePalette(double brightness) {
            int index = 0;
            for (int y = 0; y < 512; y++) {
                double hue = ((double) (y / 8) / 64.0) + 0.0078125;
                double saturation = ((double) (y & 0x7) / 8.0) + 0.0625;
                for (int x = 0; x < 128; x++) {
                    double lightness = (double) x / 128.0;
                    double red = lightness;
                    double green = lightness;
                    double blue = lightness;
    
                    if (saturation != 0.0) {
                        double a;
    
                        if (lightness < 0.5) {
                            a = lightness * (1.0 + saturation);
                        } else {
                            a = (lightness + saturation) - (lightness * saturation);
                        }
    
                        double b = (2.0 * lightness) - a;
    
                        double fRed = hue + (1.0 / 3.0);
                        double fBlue = hue - (1.0 / 3.0);
    
                        if (fRed > 1.0) fRed--;
                        if (fBlue < 0.0) fBlue++;
    
                        red = hueToRgb(fRed, a, b);
                        green = hueToRgb(hue, a, b);
                        blue = hueToRgb(fBlue, a, b);
                    }
    
                    table[index++] = applyBrightness(((int) (red * 256.0) << 16) | ((int) (green * 256.0) << 8) | (int) (blue * 256.0), brightness);
                }
            }
        }
    Now we need a function that checks how 'different' a color is from another, there are multiple different techniques but the 2 really simple ones work quite well
    1) computing the Euclidean distance of the 2 colors which is defined as
    Code:
    d = sqrt(sq(r2 - r1) + sq(g2 - g1) + sq(b2 - b1))
    2) weighted approach which is defined as
    Code:
    d = sqrt(sq((r2 - r1) * 0.3) + sq((g2 - g1) * 0.59) + sq((b2 - b1) * 0.11))
    NOTE: the sqrt can be omitted in both if ur not interested in the actual d value

    Using the 2nd method, the method for finding the closest color would look like this:
    Code:
        private int findClosestHSL(int rgb, int[] colors) {
            double r1 = ((rgb >> 16) & 0xFF) / 255.0;
            double g1 = ((rgb >> 8) & 0xFF) / 255.0;
            double b1 = (rgb & 0xFF) / 255.0;
            double closestDist = Double.MAX_VALUE;
            int closestHSL = colors[0];
            for (int hsl = 0; hsl < colors.length; hsl++) {
                int color = colors[hsl];
                double r2 = ((color >> 16) & 0xFF) / 255.0;
                double g2 = ((color >> 8) & 0xFF) / 255.0;
                double b2 = (color & 0xFF) / 255.0;
                double d = sq((r2 - r1) * 0.3) + sq((g2 - g1) * 0.59) + sq((b2 - b1) * 0.11);
                if (d < closestDist) {
                    closestDist = d;
                    closestHSL = hsl;
                }
            }
            return closestHSL;
        }
    Using this method yields a much better result compared to the above:
    Attached image

    In case the input to the findClosest() method isn't the actual table and just an array of arbitrary RS HSL values you could also compute the RGB value directly from the HSL value instead of building the whole table and then doing a look-up
    What we need to do is compute x, y from h, s, l, assuming the input is a bitpacked hsl value, we can extract h, s, l easily:
    Code:
            int h = hsl >> 10 & 0x3f;
            int s = hsl >> 7 & 0x7;
            int l = hsl & 0x7f;
    in the code that generates the table, y goes from 0 to 512(exclusive) and x goes from 0 to 128(exclusive)
    Lets start from computing x as it's the simplest
    l is computed as
    Code:
    double lightness = (double) x / 128.0;
    Therefore x is just
    Code:
    int x = l & 0x7F;
    computing y is also quite simple, we can see that h, s is computed as:
    Code:
    double hue = ((double) (y / 8) / 64.0) + 0.0078125;
    double saturation = ((double) (y & 0x7) / 8.0) + 0.0625;
    therefore y can be computed as
    Code:
    int y = (h << 3) | s;
    So our compute method(s) for hsl -> rgb will look like this:
    Code:
        public static int compute(int hsl, double brightness) {
            int h = hsl >> 10 & 0x3f;
            int s = hsl >> 7 & 0x7;
            int l = hsl & 0x7f;
            int x = l & 0x7F;
            int y = (h << 3) | s;
            return compute(x, y, brightness);
        }
    
        private static int compute(int x, int y, double brightness) {
            double hue = ((double) (y / 8) / 64.0) + 0.0078125; // 0.0078125 is 1 / 128
            double saturation = ((double) (y & 0x7) / 8.0) + 0.0625; // 0.0625 is 1 / 16
            double lightness = (double) x / 128.0;
            double red = saturation;
            double green = saturation;
            double blue = saturation;
    
            if (lightness != 0.0) {
                double a;
    
                if (saturation < 0.5) {
                    a = saturation * (1.0 + lightness);
                } else {
                    a = (saturation + lightness) - (saturation * lightness);
                }
    
                double b = (2.0 * saturation) - a;
    
                double fRed = hue + (1.0 / 3.0);
                double fBlue = hue - (1.0 / 3.0);
    
                if (fRed > 1.0) fRed--;
                if (fBlue < 0.0) fBlue++;
    
                red = hueToRgb(fRed, a, b);
                green = hueToRgb(hue, a, b);
                blue = hueToRgb(fBlue, a, b);
            }
            return applyBrightness(((int) (red * 256.0) << 16) | ((int) (green * 256.0) << 8) | (int) (blue * 256.0), brightness);
        }
    Last edited by Suic; 01-15-2022 at 01:54 PM. Reason: typo :S
    Attached image
    Reply With Quote  
     


  3. #3  
    Registered Member Aleksandr's Avatar
    Join Date
    Sep 2006
    Age
    33
    Posts
    1,034
    Thanks given
    455
    Thanks received
    134
    Rep Power
    74
    Quote Originally Posted by Suic View Post
    A much better and equally simple approach would be to find the "closest matching color" by looking up each value in the table.
    by 'better' i really mean more accurate.
    First lets look at what your method yields for some randomly chosen HSL values:
    Attached image
    Left side displays the original HSL values, right side displays the converted values using ur method
    It looks noticeably different and we can do much better, first lets generate the HSL -> RGB table:
    Code:
        public static void generatePalette(double brightness) {
            int index = 0;
            for (int y = 0; y < 512; y++) {
                double hue = ((double) (y / 8) / 64.0) + 0.0078125;
                double saturation = ((double) (y & 0x7) / 8.0) + 0.0625;
                for (int x = 0; x < 128; x++) {
                    double lightness = (double) x / 128.0;
                    double red = lightness;
                    double green = lightness;
                    double blue = lightness;
    
                    if (saturation != 0.0) {
                        double a;
    
                        if (lightness < 0.5) {
                            a = lightness * (1.0 + saturation);
                        } else {
                            a = (lightness + saturation) - (lightness * saturation);
                        }
    
                        double b = (2.0 * lightness) - a;
    
                        double fRed = hue + (1.0 / 3.0);
                        double fBlue = hue - (1.0 / 3.0);
    
                        if (fRed > 1.0) fRed--;
                        if (fBlue < 0.0) fBlue++;
    
                        red = hueToRgb(fRed, a, b);
                        green = hueToRgb(hue, a, b);
                        blue = hueToRgb(fBlue, a, b);
                    }
    
                    table[index++] = applyBrightness(((int) (red * 256.0) << 16) | ((int) (green * 256.0) << 8) | (int) (blue * 256.0), brightness);
                }
            }
        }
    Now we need a function that checks how 'different' a color is from another, there are multiple different techniques but the 2 really simple ones work quite well
    1) computing the Euclidean distance of the 2 colors which is defined as
    Code:
    d = sqrt(sq(r2 - r1) + sq(g2 - g1) + sq(b2 - b1))
    2) weighted approach which is defined as
    Code:
    d = sqrt(sq((r2 - r1) * 0.3) + sq((g2 - g1) * 0.59) + sq((b2 - b1) * 0.11))
    NOTE: the sqrt can be omitted in both if ur not interested in the actual d value

    Using the 2nd method, the method for finding the closest color would look like this:
    Code:
        private int findClosestHSL(int rgb, int[] colors) {
            double r1 = ((rgb >> 16) & 0xFF) / 255.0;
            double g1 = ((rgb >> 8) & 0xFF) / 255.0;
            double b1 = (rgb & 0xFF) / 255.0;
            double closestDist = Double.MAX_VALUE;
            int closestHSL = colors[0];
            for (int hsl = 0; hsl < colors.length; hsl++) {
                int color = colors[hsl];
                double r2 = ((color >> 16) & 0xFF) / 255.0;
                double g2 = ((color >> 8) & 0xFF) / 255.0;
                double b2 = (color & 0xFF) / 255.0;
                double d = sq((r2 - r1) * 0.3) + sq((g2 - g1) * 0.59) + sq((b2 - b1) * 0.11);
                if (d < closestDist) {
                    closestDist = d;
                    closestHSL = hsl;
                }
            }
            return closestHSL;
        }
    Using this method yields a much better result compared to the above:
    Attached image

    In case the input to the findClosest() method isn't the actual table and just an array of arbitrary RS HSL values you could also compute the RGB value directly from the HSL value instead of building the whole table and then doing a look-up
    What we need to do is compute x, y from h, s, l, assuming the input is a bitpacked hsl value, we can extract h, s, l easily:
    Code:
            int h = hsl >> 10 & 0x3f;
            int s = hsl >> 7 & 0x7;
            int l = hsl & 0x7f;
    in the code that generates the table, y goes from 0 to 512(exclusive) and x goes from 0 to 128(exclusive)
    Lets say from computing x as it's the simplest
    l is computed as
    Code:
    double lightness = (double) x / 128.0;
    Therefore x is just
    Code:
    int x = l & 0x7F;
    computing y is also quite simple, we can see that h, s is computed as:
    Code:
    double hue = ((double) (y / 8) / 64.0) + 0.0078125;
    double saturation = ((double) (y & 0x7) / 8.0) + 0.0625;
    therefore y can be computed as
    Code:
    int y = (h << 3) | s;
    So our compute method(s) for hsl -> rgb will look like this:
    Code:
        public static int compute(int hsl, double brightness) {
            int h = hsl >> 10 & 0x3f;
            int s = hsl >> 7 & 0x7;
            int l = hsl & 0x7f;
            int x = l & 0x7F;
            int y = (h << 3) | s;
            return compute(x, y, brightness);
        }
    
        private static int compute(int x, int y, double brightness) {
            double hue = ((double) (y / 8) / 64.0) + 0.0078125; // 0.0078125 is 1 / 128
            double saturation = ((double) (y & 0x7) / 8.0) + 0.0625; // 0.0625 is 1 / 16
            double lightness = (double) x / 128.0;
            double red = saturation;
            double green = saturation;
            double blue = saturation;
    
            if (lightness != 0.0) {
                double a;
    
                if (saturation < 0.5) {
                    a = saturation * (1.0 + lightness);
                } else {
                    a = (saturation + lightness) - (saturation * lightness);
                }
    
                double b = (2.0 * saturation) - a;
    
                double fRed = hue + (1.0 / 3.0);
                double fBlue = hue - (1.0 / 3.0);
    
                if (fRed > 1.0) fRed--;
                if (fBlue < 0.0) fBlue++;
    
                red = hueToRgb(fRed, a, b);
                green = hueToRgb(hue, a, b);
                blue = hueToRgb(fBlue, a, b);
            }
            return applyBrightness(((int) (red * 256.0) << 16) | ((int) (green * 256.0) << 8) | (int) (blue * 256.0), brightness);
        }
    Very nice
    Al
    Attached image

    There is no RL, just AFK
    Reply With Quote  
     


Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Replies: 4
    Last Post: 10-20-2015, 10:43 PM
  2. model grab from 474 cache
    By Romania Clan in forum Requests
    Replies: 2
    Last Post: 05-28-2010, 09:20 AM
  3. some more model antics from me
    By Gary in forum Show-off
    Replies: 21
    Last Post: 01-18-2010, 04:46 AM
  4. Replies: 0
    Last Post: 07-05-2009, 01:01 AM
  5. Replies: 37
    Last Post: 07-04-2008, 08:25 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •