Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm to generate RGB graduated colors in PHP

I'm interesting in algorithms to generate 'n' graduated colors between two given colors, that generate smooth transitions between each of them.

I tried letting static two channels, for example R and G, and incremental change B, but sometimes the difference between two colors are harder than the neighbors have.

I want to check different algorithms and analyze their weakness and strenghts.


I wrote this code and it seems logic, but the transitions between some colors are harder than between other (e.g.between 0 and 1 is harder than between 1 and 2):

<?php
$c1 = array(128,175,27); // Color 1
$c2 = array(255,255,140); // Color 2
$nc = 5; // Number of colors to display.
$dc = array(($c2[0]-$c1[0])/($nc-1),($c2[1]-$c1[1])/($nc-1),($c2[2]-$c1[2])/($nc-1)); // Step between colors

for ($i=0;$i<$nc;$i++){
    echo '<div style="width:200px;height:50px;background-color:rgb('.round($c1[0]+$dc[0]*$i).','.round($c1[1]+$dc[1]*$i).','.round($c1[2]+$dc[2]*$i).');">'.$i.'</div>'; // Output
}
?>

Are there a better algorithm to do this?


I bring an example: In the code above I used $c1=array(192,5,248); and $c2 = array(142,175,240); and $nc = 10; and obtained this image:

graduated colors example

The RGB values of 0,1,8 and 9 are:

  • 0 = 192,5,248
  • 1 = 186,24,247
  • 8 = 148,156,241
  • 9 = 142,175,240

If you look there is a diference between neighbor colors of 6,19,1. But the visually transition between 0 and 1 is softer than the transition between 8 and 9. And for HSV is the same thing. It is something with some colors that do its transition harder or softer.

like image 901
Memochipan Avatar asked Nov 21 '11 16:11

Memochipan


2 Answers

In HSV, generate n colors whose hue changes linearly from the first color to the second color. Convert each HSV value to RGB.

like image 163
mbeckish Avatar answered Nov 16 '22 19:11

mbeckish


In the following image you can see the output of a piece of code I wrote to compare transitions between two colors using RGB and HSV splitting in equal size steps:

enter image description here

I found the transitions using HSV are afected by Hue and depend of the distance between colors. If you choose two colors with the same hue is interesting to see that HSV transitions are more clear than in RGB, because you are only playing with saturation and value (black) and no adding colors like in RGB.

<?php
// Configuration.
$nc = 6; // Number of colors.
$w = 300; // Width of divs.
$a = 50; // Height of divs.

// Colors
/* In RGB */
$c1 = array(rand(0,255),rand(0,255),rand(0,255));
$c2 = array(rand(0,255),rand(0,255),rand(0,255)); 
//$c1 = array(128,175,27); // Color 1: Whit these colors is not change.
//$c2 = array(255,255,140); // Color 2: Whit these colors is not change.
// $c1 = array(0,0,0); // Color 1: White.
// $c2 = array(255,255,255); // Color 2: Black.
/* In HSV */
$h3 = array(rand(0,360),rand(0,100),rand(0,100)); 
$h4 = array(rand(0,360),rand(0,100),rand(0,100)); 
//$h3 = array(145,50,50); // Color 3: To see the influence of Hue.
//$h4 = array(145,0,100); // Color 4: To see the influence of Hue.

// HTML
$html .= '<div style="margin:auto;width:'.($w*2).'px;">';
// RGB to RGB split
$c = graduateRGB($c1,$c2,$nc);
$html .= customHTML($w,$a,$c,'RGB->RGBs');
// RGB to HSV split
$h1 = RGBtoHSV($c1);
$h2 = RGBtoHSV($c2);
$h = graduateHSV($h1,$h2,$nc);
$html .= customHTML($w,$a,$h,'RGB->HSVs');
// HSV to HSV split
$h = graduateHSV($h3,$h4,$nc);
$html .= customHTML($w,$a,$h,'HSV->HSVs');
// HSV to RGB split
$c3 = HSVtoRGB($h3);
$c4 = HSVtoRGB($h4);
$c = graduateRGB($c3,$c4,$nc);
$html .= customHTML($w,$a,$c,'HSV->RGBs');
// Output
$html .= '</div>';
echo $html;

/* FUNCIONES DE GRADUACIÓN */
// Dados dos colores RGB (0-255,0-255,0-255) y un número de colores deseados, regresa un array con todos los colores de la gradación.   
function graduateRGB($c1,$c2,$nc){
    $c = array();
    $dc = array(($c2[0]-$c1[0])/($nc-1),($c2[1]-$c1[1])/($nc-1),($c2[2]-$c1[2])/($nc-1));
    for ($i=0;$i<$nc;$i++){
        $c[$i][0]= round($c1[0]+$dc[0]*$i);
        $c[$i][1]= round($c1[1]+$dc[1]*$i);
        $c[$i][2]= round($c1[2]+$dc[2]*$i);
    }
    return $c;
}
// Dados dos colores HSV (0-360,0-100,0-100) y un número de colores deseados, regresa un array con todos los colores de la gradación en RGB.    (Hay un detalle con esta función y es que la transición se podría hacer por el lado contrario del círculo cromático)
function graduateHSV($h1,$h2,$nc){
    $h = array();
    $dh = array(($h2[0]-$h1[0])/($nc-1),($h2[1]-$h1[1])/($nc-1),($h2[2]-$h1[2])/($nc-1));
    for ($i=0;$i<$nc;$i++){
        $h[$i][0]= $h1[0]+$dh[0]*$i;
        $h[$i][1]= $h1[1]+$dh[1]*$i;
        $h[$i][2]= $h1[2]+$dh[2]*$i;
        $h[$i] = HSVtoRGB($h[$i]);
    }
    return $h;
}

/* FUNCIONES DE CONVERSIÓN. */
// Convierte a HSV (0-360,0-100,0-100) colores en RGB (0-255,0-255,0-255).
function RGBtoHSV(array $rgb) {

    $f = 0.00000001; // Factor de corrección para evitar la división por cero.

    list($R,$G,$B) = $rgb;

    $R = $R==0?$f:$R/255;
    $G = $G==0?$f:$G/255;
    $B = $B==0?$f:$B/255;

    $V = max($R,$G,$B);
    $X = min($R,$G,$B);
    $S = ($V-$X)/$V;

    $V_X = $V-$X==0?$f:$V-$X;

    $r = ($V-$R)/($V_X);
    $g = ($V-$G)/($V_X);
    $b = ($V-$B)/($V_X);    

    if ($R == $V)
        $H = $G==$X?(5+$b):(1-$g);
    elseif ($G == $V)
        $H = $B==$X?(1+$r):(3-$b);
    else
        $H = $R==$X?(3+$g):(5-$r);

    $H /= 6;

    $H = round($H*360);
    $S = round($S*100);
    $V = round($V*100);

    return array($H, $S, $V);
}

// Convierte a RGB (0-255,0-255,0-255) colores en HSV (0-360,0-100,0-100).
function HSVtoRGB(array $hsv) {
    list($H,$S,$V) = $hsv;

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

    //1
    $H *= 6;
    //2
    $I = floor($H);
    $F = $H - $I;
    //3
    $M = $V * (1 - $S);
    $N = $V * (1 - $S * $F);
    $K = $V * (1 - $S * (1 - $F));
    //4
    switch ($I) {
        case 0:
            list($R,$G,$B) = array($V,$K,$M);
            break;
        case 1:
            list($R,$G,$B) = array($N,$V,$M);
            break;
        case 2:
            list($R,$G,$B) = array($M,$V,$K);
            break;
        case 3:
            list($R,$G,$B) = array($M,$N,$V);
            break;
        case 4:
            list($R,$G,$B) = array($K,$M,$V);
            break;
        case 5:
        case 6: //for when $H=1 is given
            list($R,$G,$B) = array($V,$M,$N);
            break;
    }

    $R = round($R*255);
    $G = round($G*255);
    $B = round($B*255);

    return array($R, $G, $B);
}

// Función con un HTML de muestra para la visualización de colores, podría ser cualquier otro.
function customHTML($w,$a,$c,$header){
    $html = '<div style="float:left;text-align:center;"><h2>'.$header.'</h2>';
    foreach ($c as $color){
        $html .= '<div style="width:'.$w.'px;height:'.$a.'px;background-color:rgb('.$color[0].','.$color[1].','.$color[2].');">RGB '.$color[0].','.$color[1].','.$color[2].'</div>';
    }
    $html .= '</div>';
    return $html;
}
?>
like image 45
Memochipan Avatar answered Nov 16 '22 21:11

Memochipan