Creating Forces from Image

edited March 2016 in Questions about Code

Hi all,

I'm new to the forum and fairly new to Processing. I've scoured around and founds lots of help so far, but now I'm at a point where I'm not totally sure where to head.

I'd like to load an image, analyze each pixel's RGB value, and based on those values move particles in a certain direction.

So... a little pseudo code to explain my thoughts.

Load image, get it's pixels
Go through each pixel, separate it's RGB values
If R < 128, velocity.x = -1
If R > 128, velocity.x = 1
If G < 128, velocity.y = -1
If G > 128, velocity.y = 1
Ignore the B values, I don't really need them.

position.add(speed) to move the particle. 

Initially I was going using the get() function so each particle feeds it's position, looks at the color behind it, analyzes it, and moves accordingly, but that's waaaaay to slow for hundreds of particles.

I'm thinking of loading all of the pixel values into an array (does it need to be a 2D array, for x and y values?) at the start so it's all in memory, and then each particle reads where it's at on the screen, finds the corresponding image pixel index, and just pulls the vector out, without having to re-analyze the image on every frame since it's not changing.

I've been working with vectors for a bit, but I don't have much experience with arrays. Is this a good approach? Can anyone point me in the right direction?

Tagged:

Answers

  • Answer ✓

    The image is already in an array called pixels[] and yes, it's much faster than get()

    https://processing.org/reference/pixels.html

  • edited March 2016

    Okay, I'm getting somewhere. I decided to use a PVector array. Here's what I have for that (I got a start from a sketch I found on here a while a go, but I can't find the author for credit):

    PImage img;
    PVector [] m;
    PVector pos = new PVector(80, 650, 0);
    PVector velocity = new PVector(0, 0, 0);
    int j;
    float maxSpeed = 2.0;
    float minSpeed = 0.1;
    int location;
    
    void setup() {
      size(800, 800);
      background(0);
      img = loadImage("VectorField01.jpg");
      loadPixels();
      //loadPixelArray();
      m = loadPixelArray();
      println(m[50]);
      println(pixels.length);
    }
    
    PVector [] loadPixelArray() {
      PVector [] c = new PVector[pixels.length];
    
      for (int x = 0; x < img.width; x++) {
        for (int y = 0; y < img.height; y++) {
          int loc = x + y * img.width;
          float r, g, b;
    
          r = constrain(red(img.pixels[loc]), 0, 255);
          g = constrain(green(img.pixels[loc]), 0, 255);
          b = constrain(blue(img.pixels[loc]), 0, 255);
    
          if (r < 128) {
            r = map(r, 0, 128, -1*maxSpeed, -1*minSpeed);
          } else {
            r = map(r, 128, 255, minSpeed, maxSpeed);
          }
    
          if (g < 128) {
            g = map(g, 0, 128, maxSpeed, minSpeed);
          } else {
            g = map(g, 128, 255, -1*minSpeed, -1*maxSpeed);
          }
    
          c[loc] = new PVector(r, g, b);
        }
      }
      return c;
    }
    

    Now I think I just have to figure out how to have each particle index its position and read from the loadPixelArray to get its acceleration.

    Sorry about the formatting in the post, I'm not sure what I'm doing wrong.

  • GoToLoop, thank you!

  • So I've tried a different approach of splitting RGB into dual arrays. It works, but since I have to convert the x and y positions into integers in order to grab the values from the arrays it's causing jitter. Or is it caused from my if/else statements? Is there a way around this?

    PImage img;
    float minSpeed = 0.2;
    float maxSpeed = 4.0;
    float posX, posY, accX, accY;
    
    void setup() {
      size(800, 800);
      smooth();
      background(0);
      img = loadImage("VectorField03.png");
      loadPixelArrays();
    
      posX= 100;  //Initial starting position
      posY = 600;
    }
    
    void draw() {
      fill(0, 20);
      rect(0, 0, width, height);
    
      //Maps RGB values to speed
      if (redValues[int(posX)][int(posY)] < 128.0) {
        redValues[int(posX)][int(posY)] = map(redValues[int(posX)][int(posY)], 0.0, 128.0, -1*maxSpeed, -1*minSpeed);
      } else {
        redValues[int(posX)][int(posY)] = map(redValues[int(posX)][int(posY)], 128.0, 255.0, minSpeed, maxSpeed);
      }
    
      if (greenValues[int(posX)][int(posY)] < 128.0) {
        greenValues[int(posX)][int(posY)] = map(greenValues[int(posX)][int(posY)], 0.0, 128.0, -1*maxSpeed, -1*minSpeed);
      } else {
        greenValues[int(posX)][int(posY)] = map(greenValues[int(posX)][int(posY)], 128.0, 255.0, minSpeed, maxSpeed);
      }
    
      if (blueValues[int(posX)][int(posY)] < 128.0) {
        blueValues[int(posX)][int(posY)] = map(blueValues[int(posX)][int(posY)], 0, 128.0, -1*maxSpeed, -1*minSpeed);
      } else {
        blueValues[int(posX)][int(posY)] = map(blueValues[int(posX)][int(posY)], 128, 255, minSpeed, maxSpeed);
      }
    
      accX = redValues[int(posX)][int(posY)];
      accY = greenValues[int(posX)][int(posY)];
    
      //Moves point
      posX+=accX;
      posY+=accY;
    
      stroke(255);
      strokeWeight(5);
      line(posX, posY, posX-accX, posY-accY);
    
      //Reset acceleration to zero
      accX = 0.0;
      accY = 0.0;
    }
    
    
    float [][] redValues = new float[800][800];
    float [][] greenValues = new float[800][800];
    float [][] blueValues = new float[800][800];
    
    void loadPixelArrays() {
      for (int x = 0; x <img.width; x++) {
        for (int y = 0; y < img.height; y++) {
          redValues[x][y] = constrain(red(img.pixels[x + y*800]), 0, 255); 
          greenValues[x][y] = constrain(green(img.pixels[x + y*800]), 0, 255); 
          blueValues[x][y] = constrain(blue(img.pixels[x + y*800]), 0, 255);
        }
      }
    }
    

    VectorField03

  • edited April 2016

    Function int() can convert whole arrays as well:
    https://Processing.org/reference/intconvert_.html

    You don't need constrain(). By default red(), green() & blue() return a range of 0 to 255 already.

    Actually you don't need them at all. You're already using pixels[]. And they're very slow too.

    This utility function returns an array w/ the 3 RGB colors split:

    static final int[] to_RGB_array(final color c, final int... target) {
      final int r = c >> 020 & 0xff;
      final int g = c >> 010 & 0xff;
      final int b = c        & 0xff;
    
      if (target == null || target.length < 3)  return new int[] {r, g, b};
    
      target[0] = r;
      target[1] = g;
      target[2] = b;
    
      return target;
    }
    
Sign In or Register to comment.