Convert from RGB to gradient and back

Question: I have a gradient. Only colors from that gradient will ever be printed to screen. Once I print a pixel to screen I need to then retrieve the RGB value of that pixel. I then need to "rotate" the color to the next spot in the gradient, either up or down. How do I do this programmatically?

Here's some code I put together from other threads.

creates some images of gradients:

PImage img1;
PImage img2;

void setup() {
  size(500, 500);

  color[] gradient1 = {
    color(0, 0, 0),
    color(0, 0, 255),
    color(0, 255, 255),
    color(0, 255, 0),
    color(255, 255, 0),
    color(255, 0, 0)
  };

  color[] gradient2 = {
    0x000000,
    0x132f97,
    0x3e8c84,
    0x89da49,
    0xe8de17,
    0xff8823,
    0xfe0009
  };

  img1 = createGradientImage(300, 20, gradient1);
  img2 = createGradientImage(300, 20, gradient2);
}

void draw() {
  background(226, 225, 215);
  translate(100, 200);
  image(img1, 0, 0);
  image(img2, 0, 40);
}

PImage createGradientImage(int w, int h, color[] colors) {
  PImage img = createImage(w, h, RGB);
  int divideColors = colors.length - 1;
  int stepSize = img.width / divideColors;
  img.loadPixels();
  for (int x=0; x<img.width; x++) {
    color cS = colors[x/stepSize];
    color cE = colors[min((x/stepSize)+1, divideColors)];
    float amt = (float) (x % stepSize) / stepSize;
    color cC = lerpColor(cS, cE, amt);
    for (int y=0; y<img.height; y++) {  
      int index = x + y * img.width;
      img.pixels[index] = cC;
    }
  }
  img.updatePixels();
  return img;
}

Demonstrates the effect where leaving your mouse on a spot in the sketch should "increase the heat value" while all pixels in the sketch simultaneously decrease, creating a fade out effect from one edge of the gradient to the other. This sketch is a poor example because there's a lot of things I'm not doing correctly, it's not supposed to rotate through the entire hue/gradient continuously.

color getHeatMapColor(int value)
{
  return color((0.666 * (255 - value)), 255, value*10, 255);
}

int reduceHeatMapValueBy1(int rgb)
{
  float h = map(map(hue(rgb),0,255,0,169)-1/16,0,169,0,255);  
  float b = brightness(rgb)-1.1/16;
  return color(h, 255, b, 255);
}

int increaseHeatMapValueBy1(int rgb)
{
  float h = map(map(hue(rgb),0,255,0,169)+3,0,169,0,255);  
  float b = brightness(rgb)+3.3;
  return color(h, 255, b, 255);
}

void setup()
{
  size(300, 300, P2D);
  colorMode(HSB);
  noStroke();
  background(0);
  loadPixels();
  imageMode(CENTER);
}

int p = 0;
void draw()
{
  PImage mypixels = get(mouseX-15,mouseY-15,30,30);  
  mypixels.loadPixels();  
  for (int i = 0; i < 30*30; ++i)
  {
    mypixels.pixels[i] = increaseHeatMapValueBy1(mypixels.pixels[i]);
  }
  image(mypixels, mouseX, mouseY);

  if(++p == 10)
  {
    loadPixels();
    for (int i = 0; i < width*height; ++i)
    {
      pixels[i] = reduceHeatMapValueBy1(pixels[i]);
    }  
    updatePixels();
    p = 0;
  }


  //if (p == width*height) 
  //{  
  //  //background(0);
  //  //ellipse(width/2, height/2, width/4, width/4);
  //  p = 0;
  //  loadPixels();
  //}
}
Tagged:

Answers

  • I then need to "rotate" the color

    Any time you are conceptualizing color space in terms of rotating a color wheel, I recommend using a hue-based colorspace such as HSB. Then Red both 0 and 360, and you can continue to add indefinitely, taking modulo (%) of the color as you rotate.

    See the Processing Color Tutorial and the colorMode() function.

    You can then also define gradients in terms of rotation distances or interpolations rather than absolute values.

    I'm not sure how relevant or not the examples you posted are to your own problem. When you say:

    I then need to "rotate" the color to the next spot in the gradient

    Do you mean that you then print the next color in the pixel to the right? Or do you mean that you then replace the pixel with the next color in the following draw frame?

  • edited December 2016

    Like I said it doesn't work exactly how I want, it shouldn't keep rotating through all the hues, red should be maximum. Also it doesn't rotate through the hues correctly. My math in calculating the gradient is bad. Well, I want to be able to create this effect with a selection of gradients. I was thinking I'd store gradients in an array, but the most efficient way of doing this is preferred because I'll be doing this to hundreds of thousands of pixels many times per second.

    I want to try different color schemes. So to answer your question, I need to take every pixel and replace the pixel with a new color value. +1 "color" means go up in the gradient, -1 color means go down in the gradient. 0 is black, and whatever the number of steps is in the gradient, that will be max (red in this case), or whatever the gradient dictates.

    Just imagine how blend modes work. Light blue + light blue = dark blue (when you add two low alpha pixels) usually. I'd like light blue + light blue to = green or something, because green is higher up in the gradient in this case. Blue + green = yellow, maybe. Yellow + yellow = red because now were going really high up in the gradient, or the "heat map".

    Edit: Well, I don't need to add colors, that seems a bit more complicated. I just need to step the pixel color up or down according to a gradient.

  • Ah, a touch heatmap with no color rotation (you don't want it to cycle). Sounds like RGB (not HSB) and a defined set of steps is the way for you to go.

    Is your goal to implement it yourself as a learning project, or do you just want high-performance pixel color shifting? If the second you might want to consider looking into whether you can use optimized libraries such as PixelFlow for gradients:

  • Also, for a simple approach to indexing an ordered collection of Processing colors as a Java object, see this library:

    You could, for example, try managing your data in a 1D or 2D array of numbers that is aligned with the pixels[] array:

    000000000
    000010000
    001222100
    001242100
    000111000
    000000000
    

    ...and then update changed values to pixels[] by copying the value from the corresponding ColorScheme index.

  • edited December 2016

    ooo yes I like that idea.

    I'm not trying to learn per se, I'm trying to create a prototype of an idea. Fast easy results is preferred over efficiency. Did you have a pixelflow feature or example in mind that I could look at to do this? I'm adding the library now and will explore the examples.

  • works!

    color[] createGradient(color[] colors, int steps)
    {
      color[] arrayColor = new color[steps];
      float stepSize = colors.length / (float)steps;  
      for (int i = 0; i < arrayColor.length; ++i)
      {
        float decimal_index_pos = i*stepSize;
        int index_pos = floor(decimal_index_pos);
        float index_prc = decimal_index_pos - floor(decimal_index_pos);
        int index1 = min(index_pos, colors.length-1);
        int index2 = min(index_pos+1, colors.length-1);
        arrayColor[i] = lerpColor(colors[index1], colors[index2], index_prc);
      }
      return arrayColor;
    }
    
    int GetPixelIndex(int x, int y)
    {
      return y * width + x;
    }
    
    color[] gradient1 = {
      color(0, 0, 0),
      color(0, 0, 255),
      color(0, 255, 255),
      color(0, 255, 0),
      color(255, 255, 0),
      color(255, 0, 0)
    };
    
    int[] MyPixelArray; 
    color[] colors;
    
    void setup()
    {
      size(300,300);
      colorMode(RGB);
      frameRate(30);
      noStroke();
      background(0);
      loadPixels();
      imageMode(CENTER);
    
      MyPixelArray = new int[width*height];
    
      colors = createGradient(gradient1,10000);
    }
    
    int num_pixels = width*height;
    int increment = 400;
    int decrement = 10;
    int draw_radius = 40;
    
    void draw()
    {
      for (int i = 0; i < draw_radius*draw_radius; ++i)
      {
        int x = constrain(i%draw_radius+mouseX-draw_radius/2, 0, num_pixels);
        int y = constrain(floor(i/draw_radius)+mouseY-draw_radius/2, 0, num_pixels);
        int pixel_index = constrain(GetPixelIndex(x,y),0,MyPixelArray.length-1);
        MyPixelArray[pixel_index] = constrain(MyPixelArray[pixel_index]+increment,0,colors.length-1);
        pixels[pixel_index] = colors[MyPixelArray[pixel_index]];
      }
    
      for (int i = 0; i < width*height; ++i)
      {
        MyPixelArray[i] = constrain(MyPixelArray[i]-decrement,0,colors.length-1);
        pixels[i] = colors[MyPixelArray[i]];
      }
    
      updatePixels();
    }
    
  • Congrats and nicely done, @elanhickler -- thanks for sharing your working prototype.

Sign In or Register to comment.