How can I have Perlin Noise take shape from an image?

Hi everyone!

I hope you are all well, considering the circumstances.

I am a Graphic Designer and have just begun exploring Generative Art and Processing. I’ve watched tutorials about Perlin Noise, which is beautiful on its own. I was wondering if anyone could help me find a way, if possible, to have it take on the forms/shapes of a referenced image.

Thank you!! I hope you all stay safe, stay well, and stay inside!

The following is the current code:

FlowField flowfield;
ArrayList<Particle> particles;

boolean debug = false;

void setup() {
  size(1200, 800);

  flowfield = new FlowField(10);
  flowfield.update();

  particles = new ArrayList<Particle>();
  for (int i = 0; i < 10000; i++) {
    PVector start = new PVector(random(width), random(height));
    particles.add(new Particle(start, random(2, 7)));
  }
  background(0);
  colorMode (HSB, 360, 100, 100);
}

void draw() {
  flowfield.update();

  if (debug) flowfield.display();

  for (Particle p : particles) {
    p.follow(flowfield);
    p.run();
  }
}

public class FlowField {
  PVector[] vectors;
  int cols, rows;
  float inc = 0.1;
  float zoff = 0;
  int scl;

  FlowField(int res) {
    scl = res;
    cols = floor(width / res) + 1;
    rows = floor(height / res) + 1;
    vectors = new PVector[cols * rows];
  }
  void update() {
    float xoff = 0;
    for (int y = 0; y < rows; y++) { 
      float yoff = 0;
      for (int x = 0; x < cols; x++) {
        float angle = noise(xoff, yoff, zoff) * TWO_PI * 2;

        PVector v = PVector.fromAngle(angle);
        v.setMag(1);
        int index = x + y * cols;
        vectors[index] = v;

        xoff += inc;
      }
      yoff += inc;
    }
    zoff += 0.0000000004;
  }
  void display() {
    for (int y = 0; y < rows; y++) { 
      for (int x = 0; x < cols; x++) {
        int index = x + y * cols;
        PVector v = vectors[index];

        stroke(HSB, 360, 360);
        strokeWeight(0.1);
        pushMatrix();
        translate(x * scl, y * scl);
        rotate(v.heading());
        line(0, 0, scl, 0);
        popMatrix();
      }
    }
  }
}

public class Particle {
  PVector pos;
  PVector vel;
  PVector acc;
  PVector previousPos;
  float maxSpeed;

  Particle(PVector start, float maxspeed) {
    maxSpeed = maxspeed;
    pos = start;
    vel = new PVector(0, 0);
    acc = new PVector(0, 0);
    previousPos = pos.copy();
  }
  void run() {
    update();
    edges();
    show();
  }
  void update() {
    pos.add(vel);
    vel.limit(maxSpeed);
    vel.add(acc);
    acc.mult(0);
  }
  void applyForce(PVector force) {
    acc.add(force);
  }
  void show() {
    stroke(180, 360, 360, 5);
    strokeWeight(1);
    line(pos.x, pos.y, previousPos.x, previousPos.y);
    //point(pos.x, pos.y);
    updatePreviousPos();
  }
  void edges() {
    if (pos.x > width) {
      pos.x = 0;
      updatePreviousPos();
    }
    if (pos.x < 0) {
      pos.x = width;    
      updatePreviousPos();
    }
    if (pos.y > height) {
      pos.y = 0;
      updatePreviousPos();
    }
    if (pos.y < 0) {
      pos.y = height;
      updatePreviousPos();
    }
  }
  void updatePreviousPos() {
    this.previousPos.x = pos.x;
    this.previousPos.y = pos.y;
  }
  void follow(FlowField flowfield) {
    int x = floor(pos.x / flowfield.scl);
    int y = floor(pos.y / flowfield.scl);
    int index = x + y * flowfield.cols;

    PVector force = flowfield.vectors[index];
    applyForce(force);
  }
}

Hi! If I understood it correctly by our code you want your flowfield vectors to “follow” the edges or color gradients of a image, is that so? Otherwise, you will have to be more explicit in what do you mean by the noise “taking on the forms/shapes”.

But if that is the case, you will have to load and image file into your sketch, using a object called PImage and a function called loadImage() - https://processing.org/reference/loadImage_.html - and then, for example, if you want the flowfield vectors to follow the edges of that image you will have to research some type of “edge detection” algorithm to then generate vectors to follow those edges or even (I don’t know exactly what you want) be normal to the edges so the particles would accumulate inside or outside the shapes of the image.

Another thing you could do, wich I think would be a bit simpler although I don’t know how it would look like, is to first convert the image to a grayscale image, you can do this via any image editing software or even write a function to this inside processing as as well, and then when generating your vector from the noise funtion you could further multiply them but I number proporcional to the brightness of the pixel on the image with the same relative location (for example, your x and y could map to a x and y to the pixel array of the image as well if have the exact same size as rows*cols), so in that way the particles would be faster or slower depending on where on the image they would be passing by.

I’m sorry If this didn’t help as much but there is more then a ton ways to generate vector fields from a given image, you should probably research this specific topic first in order to choose what you actually want or to get some ideas and then trying to implement it on Processing. :wink:

Hi Daniel!!

Thank you so much for your very thorough response.

It completely puts things into perspective for me, and makes me realize I need to be much more specific in addressing what I’m looking to do, especially being that I am such a Processing noob.

I greatly appreciate your help.

My desired result is for darker grayscale values in the loaded image to translate to a denser accumulation of particles in the flowfield. Do you know of a way to do this?

I am able to load the image (thank you so much for the loadImage function).

Thank you again for your help.

Your welcome! Well, what you do is split the image into rows and cols the same way you split the canvas, and them for each “cell” of the image you could for example make an average of its brightness, and associate with low brightness greater values for a constant, lets say, G, and then write some sort of central force vector, like a gravitacional force, for each cell - im thinking of a class wich makes inside every cell a gravitacional attractor to attract the particles, so you would make an Array or ArrayList of those attractors like for example:

class attractor{
    float G;
    PVector pos;
    attractor(int cellX, int cellY, float G_){
         pos = new PVector((cellX + res)/2, (cellY + res)/2);
         G = G_;
  ...

and then for each particle you calculate the force towards each attractor, being proporcional to that G, like:

for(particle p in particles){
  for(attractor A in attractors){
      PVector dr = new PVector(A.pos.x - p.pos.x, A.pos.y - p.pos.y);
      float r2 = dr.mag()*dr.mag();
      PVector force = dr.copy();
      force.setMag(G/r2);
      p.acc.add(force);
...

Thank you!

Your description makes perfect sense, I am just clueless as to how to incorporate your provided code. I am so new to coding in general.

I would have thought I could use thresholds so that the photo’s pixels become either black or white, nothing in between, and then basically say “follow black, avoid white.” But perhaps it is much more complicated than I anticipated.

Thank you so much for your help Daniel!