Please create an account to participate in the Slashdot moderation system

 



Forgot your password?
typodupeerror
×
Programming IT Technology

Dynamically Picking High Contrast Colors? 10

FocaJonathan asks: "We are creating a Web-based app that pulls down color attributes from a user specified Web page and builds a custom page for users trying to maintain the color scheme from the specified page. Most of the time we can simply grab stuff from the body tag and look for tables and font colors used. However, periodically somebody does something wacky and we end up with the same color for a foreground and background color. When this happens, we would like to automatically select a pleasing, high contrast color given the one we already have. As dynamic page generation grows this will be a useful question, but I'm sure somebody must have explored this for map highlighting, dynamic copyright additions, or some visual design project. Any ideas out there?" The problem many people have been struggling with for years, though: How do you technically define "pleasing"?
This discussion has been archived. No new comments can be posted.

Dynamically Picking High Contrast Colors?

Comments Filter:
  • Really this comes down to applied art theory. You need to come up with a color model, and then by constraining things you can come up with a solid solution fairly quickly.

    Basically there are two factors that will matter to you. First is contrast, you don't want two very light or two very dark colors. The second is the actual color (hue). If you remember the color wheel, that would help a lot, because you can think of the situation like two cones stuck together at their bases. At the tips, are black and white, at the middle are the maximum color values. The goal therefore is for the two points inside the cones to be sufficiently far apart to be easily visually distinguishable.

    What looks good? Again to color wheel! Generally, you want to pick a complementary color to your current one. The complementary colors (artists should jump in and correct me as necessary) are the two on the wheel that form a equalateral triangle. Picking either of these should result in more pleasing choices. However, if you initial color is a bad color to begin with, the complements probably won't be good colors either.

    Writing this code isn't hard, but sit down with a pencil and paper and make sure you understand your color model first. Then code it up and tweak the parameters until good results start coming out. I'll post a reply to this with an example perl script that generates randomly colored xterms with sufficient contrast based on a variation of the CIE L*a*b model.

    - Mike

  • Well, the 'Plain Old Text' option doesn't seem to work quite right in that it snarfs up all but the last function definitions in the perl code. So, plan B is to download the script from http://members.xoom.com/Mikem42/randxterm [xoom.com]

    Sorry for the difficulities,

    - Mike

  • #!/usr/bin/perl
    # This script randomly chooses colors until two of sufficient contrast
    # are found, and displays an xterm with those foreground and background
    # colors. Contrast is determined based on a variation of the CIE L*a*b*
    # color model.

    srand;

    $lightness_scaling = 1.55; # the higher the scaling factor, the more lightness
    # counts in the distance function. Make small to get
    # more light/dark difference betwee BG and FG.

    $contrast_threshold = 120; # pick a higher threshold for more contrast

    $num_fg_iterations = 30;# pick a higher number for more attempts at an acceptabl
    e
    # fg color before abandoning bg color choice.

    do {
    # pick a background color
    $BH = 360.0 * rand(1); # Hue
    $BL = rand(1)/3.0 + rand(1)/3.0 + rand(1)/3.0; # Lightness
    # make sure we're not outside double cone...
    $BS = rand(1)*2*(0.5-abs($BL-0.5)); # Saturation

    ($BR, $BG, $BB) = hls2rgb($BH, $BL, $BS);
    ($BX, $BY, $BZ) = rgb2xyz($BR, $BG, $BB);
    ($BL, $Ba, $Bb) = xyz2Lab($BX, $BY, $BZ);

    $i = 0;
    do {
    # pick a foreground color
    $FH = 360.0 * rand(1); # Hue
    # $FL = rand(1)/3.0 + rand(1)/3.0 + rand(1)/3.0; # Lightness
    $FL = rand(1); # Lightness
    # make sure we're not outside double cone...
    $FS = rand(1)*2*(0.5-abs($FL-0.5)); # Saturation

    ($FR, $FG, $FB) = hls2rgb($FH, $FL, $FS);
    ($FX, $FY, $FZ) = rgb2xyz($FR, $FG, $FB);
    ($FL, $Fa, $Fb) = xyz2Lab($FX, $FY, $FZ);
    $i++;

    #printf("FG: %02X %02X %02X : %f %f %f\n", 255*$FR, 255*$FG, 255*$FB,
    # $FL, $Fa, $Fb);
    #printf("BG: %02X %02X %02X : %f %f %f\n", 255*$BR, 255*$BG, 255*$BB,
    # $BL, $Ba, $Bb);
    #printf("contrast: %f\n\n", contrast($BL, $Ba, $Bb, $FL, $Fa, $Fb));

    } while ((contrast($BL, $Ba, $Bb, $FL, $Fa, $Fb) 360) {
    $hue1 -= 360;
    }
    if ($hue1 0.008856) {
    $L = 116 * ($Y/$Yn)**(1/3) - 16;
    } else {
    $L = 903.3 * ($Y/$Yn);
    }

    $a = 500 * ((($X/$Xn)**(1/3)) - (($Y/$Yn)**(1/3)));
    $b = 200 * ((($Y/$Yn)**(1/3)) - (($Z/$Zn)**(1/3)));

    return ($L, $a, $b);
    }

    sub contrast {
    my ($L1, $a1, $b1, $L2, $a2, $b2) = @_;
    my ($contrast);

    $contrast = sqrt( $lightness_scaling*($L1-$L2)**2 + ($a1-$2)**2 + ($b1-$b2)*
    *2);
    return $contrast;
    }
  • you can simply invert the bitmask of the colour selected to get a contrasting colour...FFFFF = black so invert to get white etc etc.
  • You could try just using grey 080808 as a pivot point and using the hex value with same absolute value when subtracted from 080808.

    For example, for 08FF08 use 080008 as:
    080808-08FF08 = 000800 = 080808-080008

    A MOD function should do this well.

    The problem is color values close to 080808 since you could end up with an 090909 foreground on a 070707 background which wouldn't be very readable.

    OT: I love HEX, it should be mandatory to take a semester in assembler to get a CS degree. Of course when I used to program in assembler, color hadn't been invented yet.

  • For example, for 88FF88 use 888888 as:
    888888-88FF88 = 008800 = 888888-880088
  • but you could go through all the colors and pick a nice high contrast color for each one (presumalby with a small special-purpose app). However, there are 2**24 possible colors in HTML, so you'd probably want to skip some of the low order bits, or something. If you skipped the lowest 3 bits, you get 2**(24 - 9) == 2**15 colors to check. That would take about 54 hours to do [averaging 6 seconds per check]... though I guess you'd only have to do it once. Of course the task is quite parrelizable... just give the applicationto the marketing people (finally they'll come in useful!). However once it's done you can write up a C program that, given a certain 24-bit color code as argv[1], prints out a cooresponding contrasting color. Or store them in a file or whatever.
  • I've spent quite a while playing with this sort of thing. Basically, the problem is that the three components of the RGB colour model don't have equal brightness. For instance green (#00ff00) is about 10 times brighter than blue (#0000ff).

    The first script StyleMangler [webarchitects.co.uk] is a randomised css file that generates a different stylesheet each time it's loaded. The second script StylePicker [webarchitects.co.uk] is a modified version that allows you to randomise different aspects of the css until you are satisfied with the results.

  • Pick a pallet of colors you like and get a contrast by shifting the brightness.

    Example: 737373 is five shades brighter than 232323 and is the same basic color. This kind of 'complimentary' coloring using different brightness of the same hues is a simple solution.

    --
  • The simple formula for maximum brightness contrast:
    1. Transform your background color into black and white using the well-known formula .3r + .6g + .1b
    2. If you get > 50% (> 127), choose black; otherwise, choose white.

"What man has done, man can aspire to do." -- Jerry Pournelle, about space flight

Working...