Import Image and apply pattern

Hi,

How can I import a black an white image and apply a patter like the image that I attached in this message?
Hope someone can help,

Thanks !

rd

1 Like

Itā€™s interesting because it looks like this video from Daniel Shiffmanā€™s channel :

1 Like

HI @josephh :slight_smile:

Exactly ! I followed that tutorial and now Iā€™m trying to import an image and to apply the reaction diffusion,

Cheers

@alice

Glad to see that itā€™s what you were looking for, donā€™t hesitate to post your solution :slight_smile:

Hi @alice ā€“ were you able to get the tutorial working?

Hi !
still working on It :frowning:

If you have code in progress that you would like to share with more specific questions, feel free to do that here.

2 Likes

Hi @jeremydouglass , thanks, I managed to have the code and Iā€™m sharing with you It!
Now I have the Reaction Diffusion based on points, what I want to do now is to add a black and white image inside the code and in the ā€œcondition loopā€ I want to apply the reaction diffusion only where the color is blackā€¦ Do you have any idea how to do It ?
Iā€™m sending you the code and a reference image ( the image can be any in black and white)

import java.awt.Color;

Cell[][] grid;
Cell[][] prev;

void setup() {
  size(300, 300);
  grid = new Cell[width][height];
  prev = new Cell[width][height];

  for (int i = 0; i < width; i++) {
    for (int j = 0; j < height; j ++) {
      float a = 1;
      float b = 0;
      grid[i][j] = new Cell(a, b);
      prev[i][j] = new Cell(a, b);
    }
  }

  for (int n = 0; n < 50; n++) {
    int startx = int(random(20, width-20));
    int starty = int(random(20, height-20));

    for (int i = startx; i < startx+10; i++) {
      for (int j = starty; j < starty+10; j ++) {
        float a = 5;
        float b = 5;
        grid[i][j] = new Cell(a, b);
        prev[i][j] = new Cell(a, b);
      }
    }
  }
}

float dA = 1.0;
float dB = 0.5;
float feed = 0.08;  
float k = 0.06;

class Cell {
  float a;
  float b;

  Cell(float a_, float b_) {
    a = a_;
    b = b_;
  }
}

void update() {
  for (int i = 1; i < width-1; i++) {
    for (int j = 1; j < height-1; j ++) {

      Cell spot = prev[i][j];
      Cell newspot = grid[i][j];

      float a = spot.a;
      float b = spot.b;

      float laplaceA = 0;     
      laplaceA += a*-1;
      laplaceA += prev[i+1][j].a*0.2;
      laplaceA += prev[i-1][j].a*0.2;
      laplaceA += prev[i][j+1].a*0.2;
      laplaceA += prev[i][j-1].a*0.2;
      laplaceA += prev[i-1][j-1].a*0.05;
      laplaceA += prev[i+1][j-1].a*0.05;
      laplaceA += prev[i-1][j+1].a*0.05;
      laplaceA += prev[i+1][j+1].a*0.05;

      float laplaceB = 0;
      laplaceB += b*-1;
      laplaceB += prev[i+1][j].b*0.2;
      laplaceB += prev[i-1][j].b*0.2;
      laplaceB += prev[i][j+1].b*0.2;
      laplaceB += prev[i][j-1].b*0.2;
      laplaceB += prev[i-1][j-1].b*0.05;
      laplaceB += prev[i+1][j-1].b*0.05;
      laplaceB += prev[i-1][j+1].b*0.05;
      laplaceB += prev[i+1][j+1].b*0.05;

      newspot.a = a + (dA*laplaceA - a*b*b + feed*(1-a))*1;
      newspot.b = b + (dB*laplaceB + a*b*b - (k+feed)*b)*1;

      newspot.a = constrain(newspot.a, 0, 1);
      newspot.b = constrain(newspot.b, 0, 1);
    }
  }
}  

void swap() {
  Cell[][] temp = prev;
  prev = grid;
  grid = temp;
}  

void draw() {
  //println(frameRate);

  for (int i = 0; i < 1; i++) {
    update();
    swap();
  }

  loadPixels();
  for (int i = 1; i < width-1; i++) {
    for (int j = 1; j < height-1; j ++) {
      Cell spot = grid[i][j];
      float a = spot.a;
      float b = spot.b;
      int pos = i + j * width;
      //pixels[pos] = color((a-b)*255);


      color aColor = color (0, 0, 0);
      color bColor = color (255, 255, 255);
      pixels[pos] = lerpColor(aColor, bColor, (a-b));
    }
  }
  updatePixels();



 }

3

1 Like

Iā€™m trying to follow some tutorials online to undertstand how to do It, If you have any tricks please share ! :slight_smile:

Thanks!

Sure. Just load a PImage, then copy its darkest pixels into your Cells. Thatā€™s it!

Add this line to your header:

PImage img;

This to the top of your setup loop:

void setup(){
  size(512, 512); // match image size here -- then use that to make Cells

  img = loadImage("https://processing.org/img/processing3-logo.png");
  img.loadPixels();

and in your Cell initialization loop, change a=1; b=0; to this:

      if(brightness(img.get(i,j))>16){  // values lighter than 16 are not drawn
        a = 1;
        b = 0;
      } else {
        a = 5;
        b = 5;
      }

You can delete the second (random dot creation) loop entirely, now that you have image data instead.

Here is the modified sketch:

// reaction diffusion -- add image
// https://discourse.processing.org/t/import-image-and-apply-pattern/12229/8

import java.awt.Color;

PImage img;

Cell[][] grid;
Cell[][] prev;

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

  grid = new Cell[width][height];
  prev = new Cell[width][height];

  img = loadImage("https://processing.org/img/processing3-logo.png");
  img.loadPixels();

  float a = 1;
  float b = 0;
  for (int i = 0; i < width; i++) {
    for (int j = 0; j < height; j ++) {
      if (brightness(img.get(i, j))>16) {
        a = 1;
        b = 0;
      } else {
        a = 5;
        b = 5;
      }
      grid[i][j] = new Cell(a, b);
      prev[i][j] = new Cell(a, b);
    }
  }
}

float dA = 1.0;
float dB = 0.5;
float feed = 0.08;  
float k = 0.06;

class Cell {
  float a;
  float b;

  Cell(float a_, float b_) {
    a = a_;
    b = b_;
  }
}

void update() {
  for (int i = 1; i < width-1; i++) {
    for (int j = 1; j < height-1; j ++) {

      Cell spot = prev[i][j];
      Cell newspot = grid[i][j];

      float a = spot.a;
      float b = spot.b;

      float laplaceA = 0;     
      laplaceA += a*-1;
      laplaceA += prev[i+1][j].a*0.2;
      laplaceA += prev[i-1][j].a*0.2;
      laplaceA += prev[i][j+1].a*0.2;
      laplaceA += prev[i][j-1].a*0.2;
      laplaceA += prev[i-1][j-1].a*0.05;
      laplaceA += prev[i+1][j-1].a*0.05;
      laplaceA += prev[i-1][j+1].a*0.05;
      laplaceA += prev[i+1][j+1].a*0.05;

      float laplaceB = 0;
      laplaceB += b*-1;
      laplaceB += prev[i+1][j].b*0.2;
      laplaceB += prev[i-1][j].b*0.2;
      laplaceB += prev[i][j+1].b*0.2;
      laplaceB += prev[i][j-1].b*0.2;
      laplaceB += prev[i-1][j-1].b*0.05;
      laplaceB += prev[i+1][j-1].b*0.05;
      laplaceB += prev[i-1][j+1].b*0.05;
      laplaceB += prev[i+1][j+1].b*0.05;

      newspot.a = a + (dA*laplaceA - a*b*b + feed*(1-a))*1;
      newspot.b = b + (dB*laplaceB + a*b*b - (k+feed)*b)*1;

      newspot.a = constrain(newspot.a, 0, 1);
      newspot.b = constrain(newspot.b, 0, 1);
    }
  }
}  

void swap() {
  Cell[][] temp = prev;
  prev = grid;
  grid = temp;
}  

void draw() {
  //println(frameRate);

  for (int i = 0; i < 1; i++) {
    update();
    swap();
  }

  loadPixels();
  for (int i = 1; i < width-1; i++) {
    for (int j = 1; j < height-1; j ++) {
      Cell spot = grid[i][j];
      float a = spot.a;
      float b = spot.b;
      int pos = i + j * width;
      //pixels[pos] = color((a-b)*255);


      color aColor = color (0, 0, 0);
      color bColor = color (255, 255, 255);
      pixels[pos] = lerpColor(aColor, bColor, (a-b));
    }
  }
  updatePixels();
}

2 Likes

The following are suggestions on style that wonā€™t affect what your code draws:

Your Cell class is good but not strictly necessary. Processing already has a built in class for holding two floats that you could use insteadā€“it is PVector, and you can store your floats in vec.x, vec.y rather than cell.a, cell.b if you wish.

You could also move aColor, bColor to the header and define them in setup ā€“ no need to declare repeatedly in an inner loop.

Likewise, no need to shuffle spot.a and spot.b variables around into other variables ā€˜aā€™ ā€˜bā€™ ā€“ just use them directly.

{
  Cell spot = grid[i][j];
  pixels[i + j * width] = lerpColor(aColor, bColor, (spot.a-spot.b));
}
3 Likes

Hi @jeremydouglass ! THANK YOU :slight_smile: !!
I actually donā€™t know how to use PVector as you explainedā€¦If you donā€™t mind can you please show me how do It in my code , Iā€™m curious to see how you do It and to learn to do It,

Anyway thanks a lot for your help !!

Just replace ā€œCellā€ with ā€œPVectorā€ and .a .b with .x .y. Thatā€™s it, really.

However, you donā€™t gain anything by this in your current sketch. If in the future you wanted to do other things to your cell values ā€“ like scale them all at the same time, for example ā€“ then PVector has lots of pre-built-in methods to help you loop over your cells and do a/b math to them. If you go in a different direction, then having Cell is convenient for simply adding your own methods.

3 Likes

Hi @jeremydouglass , Thanks a lot for your help !
Iā€™ll try for sure ! :smile:
You have been so helpful, I appreciate It !

Thanks again :slight_smile:

1 Like

Thanks everybody for your precious help, so far Iā€™m playing with the code to have different results.
What Iā€™m trying to do now is to apply the reaction diffusion only in one part of the grid ( example right or left ) , something like the image that I uploaded 4 ,

PS: Do you think the coefficent Time can help to have the pattern only in some parts ?

This is the image that I have imported in the code : 2

  import java.awt.Color;

PImage img;

Cell[][] grid;
Cell[][] prev;

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

  grid = new Cell[width][height];
  prev = new Cell[width][height];

  img = loadImage("2.PNG");
  img.loadPixels();

  float a = 1;
  float b = 0;
  for (int i = 0; i < width; i++) {
    for (int j = 0; j < height; j ++) {
      if (brightness(img.get(i, j))>16) {
        a = 1;
        b = 0;
      } else {
        a = 5;
        b = 5;
      }
      grid[i][j] = new Cell(a, b);
      prev[i][j] = new Cell(a, b);
    }
  }
}

float dA = 1.0;
float dB = 0.5;
float feed = 0.08;  
float k = 0.06;

class Cell {
  float a;
  float b;

  Cell(float a_, float b_) {
    a = a_;
    b = b_;
  }
}

void update() {
  for (int i = 1; i < width-1; i++) {
    for (int j = 1; j < height-1; j ++) {

      Cell spot = prev[i][j];
      Cell newspot = grid[i][j];

      float a = spot.a;
      float b = spot.b;

      float laplaceA = 0;     
      laplaceA += a*-1;
      laplaceA += prev[i+1][j].a*0.2;
      laplaceA += prev[i-1][j].a*0.2;
      laplaceA += prev[i][j+1].a*0.2;
      laplaceA += prev[i][j-1].a*0.2;
      laplaceA += prev[i-1][j-1].a*0.05;
      laplaceA += prev[i+1][j-1].a*0.05;
      laplaceA += prev[i-1][j+1].a*0.05;
      laplaceA += prev[i+1][j+1].a*0.05;

      float laplaceB = 0;
      laplaceB += b*-1;
      laplaceB += prev[i+1][j].b*0.2;
      laplaceB += prev[i-1][j].b*0.2;
      laplaceB += prev[i][j+1].b*0.2;
      laplaceB += prev[i][j-1].b*0.2;
      laplaceB += prev[i-1][j-1].b*0.05;
      laplaceB += prev[i+1][j-1].b*0.05;
      laplaceB += prev[i-1][j+1].b*0.05;
      laplaceB += prev[i+1][j+1].b*0.05;

      newspot.a = a + (dA*laplaceA - a*b*b + feed*(1-a))*1;
      newspot.b = b + (dB*laplaceB + a*b*b - (k+feed)*b)*1;

      newspot.a = constrain(newspot.a, 0, 1);
      newspot.b = constrain(newspot.b, 0, 1);
    }
  }
}  

void swap() {
  Cell[][] temp = prev;
  prev = grid;
  grid = temp;
}  

void draw() {
  //println(frameRate);

  for (int i = 0; i < 1; i++) {
    update();
    swap();
  }

  loadPixels();
  for (int i = 1; i < width-1; i++) {
    for (int j = 1; j < height-1; j ++) {
      Cell spot = grid[i][j];
      float a = spot.a;
      float b = spot.b;
      int pos = i + j * width;
      //pixels[pos] = color((a-b)*255);


      color aColor = color (0, 0, 0);
      color bColor = color (255, 255, 255);
      pixels[pos] = lerpColor(aColor, bColor, (a-b));
    }
  }
  updatePixels();
}

please format your forum code with the </> button, or with three ticks ``` ā€“ not < and >

Here is where you are currently copying your reaction diffusion onto the visible canvas.

for (int i = 1; i < width-1; i++) {
  for (int j = 1; j < height-1; j ++) {
    // ...
    pixels[pos] = lerpColor(aColor, bColor, (a-b));

It sounds like you only want to do that pixel copy if (i, j) is inside one or more defined rectangles. You could use point-rect collision detection to check whether this is true.

You could calculate this each time for each pixel ā€“ is the pixel inside the rectangle? If so, copy the value to the canvas.

for (int i = 1; i < width-1; i++) {
  for (int j = 1; j < height-1; j ++) {
    if(pointRect(i, j, rx, ry, rw, rh){
      // ...
      pixels[pos] = lerpColor(aColor, bColor, (a-b));
    }
  }
  updatePixels();
}

Or, to manage complex regions like in your right-hand example (multiple rectanges, triangles, circles, etc.) you could use a mask image. Then you will be computing the whole image every time, but only showing part of it through the mask onto the canvas.

A third approach is manual masking. Create a PGraphics, and draw some black on it (rectangles, triangles, whatever). Then compute and copy your reaction diffusion pixel to the canvas ONLY if the corresponding pixel on your PGraphics is marked. This means you neednā€™t bother computing any pixel that isnā€™t marked on your manual mask.

    if(myMask.get(i,j) != emptypixel){
      // ...
      pixels[pos] = lerpColor(aColor, bColor, (a-b));
    }

HI @jeremydouglass , thanks again for your quick reply, I have tried the third approch with mask : I made 2 images with the same size and I imported both inside the code , one as a image and one as a mask, I got the error that ā€œemptyPixels cannot be resolved to a variableā€ , this is how I changed the code :

import java.awt.Color;

PImage img, maskImage;

Cell[][] grid;
Cell[][] prev;

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

 grid = new Cell[width][height];
 prev = new Cell[width][height];

 img = loadImage("1.png");
 img.loadPixels();
 maskImage = loadImage("mask.png");
 img.mask(maskImage);

 float a = 1;
 float b = 0;
 for (int i = 0; i < width; i++) {
   for (int j = 0; j < height; j ++) {
     if (brightness(img.get(i, j))>16) {
       a = 1;
       b = 0;
     } else {
       a = 5;
       b = 5;
     }
     grid[i][j] = new Cell(a, b);
     prev[i][j] = new Cell(a, b);
   }
 }
}

float dA = 1.0;
float dB = 0.5;
float feed = 0.08;  
float k = 0.06;

class Cell {
 float a;
 float b;

 Cell(float a_, float b_) {
   a = a_;
   b = b_;
 }
}

void update() {
 for (int i = 1; i < width-1; i++) {
   for (int j = 1; j < height-1; j ++) {

     Cell spot = prev[i][j];
     Cell newspot = grid[i][j];

     float a = spot.a;
     float b = spot.b;

     float laplaceA = 0;     
     laplaceA += a*-1;
     laplaceA += prev[i+1][j].a*0.2;
     laplaceA += prev[i-1][j].a*0.2;
     laplaceA += prev[i][j+1].a*0.2;
     laplaceA += prev[i][j-1].a*0.2;
     laplaceA += prev[i-1][j-1].a*0.05;
     laplaceA += prev[i+1][j-1].a*0.05;
     laplaceA += prev[i-1][j+1].a*0.05;
     laplaceA += prev[i+1][j+1].a*0.05;

     float laplaceB = 0;
     laplaceB += b*-1;
     laplaceB += prev[i+1][j].b*0.2;
     laplaceB += prev[i-1][j].b*0.2;
     laplaceB += prev[i][j+1].b*0.2;
     laplaceB += prev[i][j-1].b*0.2;
     laplaceB += prev[i-1][j-1].b*0.05;
     laplaceB += prev[i+1][j-1].b*0.05;
     laplaceB += prev[i-1][j+1].b*0.05;
     laplaceB += prev[i+1][j+1].b*0.05;

     newspot.a = a + (dA*laplaceA - a*b*b + feed*(1-a))*1;
     newspot.b = b + (dB*laplaceB + a*b*b - (k+feed)*b)*1;

     newspot.a = constrain(newspot.a, 0, 1);
     newspot.b = constrain(newspot.b, 0, 1);
   }
 }
}  

void swap() {
 Cell[][] temp = prev;
 prev = grid;
 grid = temp;
}  

void draw() {
 //println(frameRate);

 for (int i = 0; i < 1; i++) {
   update();
   swap();
 }

 loadPixels();
 for (int i = 1; i < width-1; i++) {
   for (int j = 1; j < height-1; j ++) {
     if (maskImage.get(i, j) != emptypixel) {
       Cell spot = grid[i][j];
       float a = spot.a;
       float b = spot.b;
       int pos = i + j * width;
       //pixels[pos] = color((a-b)*255);


       color aColor = color (0, 0, 0);
       color bColor = color (255, 255, 255);
       pixels[pos] = lerpColor(aColor, bColor, (a-b));
     }
   }
   updatePixels();
 }
}

these 2 images are my mask ( black rectangle) and the image :

mask

1

1 Like

You are mixing the second and third approach. If you want the manual approach, then you donā€™t need to apply your mask() ā€“ as you will be doing that manually.

First, create a global color variable ā€“ that isnā€™t a processing built-in, it is just a normal integer where you are keeping the color value that you intend to ignore in your manual mask.

color emptypixel;

Now define what color on your mask means empty:

 maskImage = loadImage("mask.png");
 //img.mask(maskImage);
 maskImage.loadPixels();
 emptypixel = maskImage.get(3,3); // define what an empty pixel is

Now the rest of your code will work. If the mask color is white, donā€™t bother to compute the Cell.

However, your example images arenā€™t going to do anything because your input image (two black and white halves isnā€™t reactive. Make sure there is texture within your active zones.

import java.awt.Color;

PImage img, maskImage;

Cell[][] grid;
Cell[][] prev;

color emptypixel;

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

 grid = new Cell[width][height];
 prev = new Cell[width][height];

 img = loadImage("https://processing.org/img/processing3-logo.png");
 img.resize(width, height);
 img.loadPixels();
 maskImage = loadImage("mask.png");
 //img.mask(maskImage);
 maskImage.loadPixels();
 emptypixel = maskImage.get(10,10); // define what an empty pixel is

 float a = 1;
 float b = 0;
 for (int i = 0; i < width; i++) {
   for (int j = 0; j < height; j ++) {
     if (brightness(img.get(i, j))>16) {
       a = 1;
       b = 0;
     } else {
       a = 5;
       b = 5;
     }
     grid[i][j] = new Cell(a, b);
     prev[i][j] = new Cell(a, b);
   }
 }
}

float dA = 1.0;
float dB = 0.5;
float feed = 0.08;  
float k = 0.06;

class Cell {
 float a;
 float b;

 Cell(float a_, float b_) {
   a = a_;
   b = b_;
 }
}

void update() {
 for (int i = 1; i < width-1; i++) {
   for (int j = 1; j < height-1; j ++) {

     Cell spot = prev[i][j];
     Cell newspot = grid[i][j];

     float a = spot.a;
     float b = spot.b;

     float laplaceA = 0;     
     laplaceA += a*-1;
     laplaceA += prev[i+1][j].a*0.2;
     laplaceA += prev[i-1][j].a*0.2;
     laplaceA += prev[i][j+1].a*0.2;
     laplaceA += prev[i][j-1].a*0.2;
     laplaceA += prev[i-1][j-1].a*0.05;
     laplaceA += prev[i+1][j-1].a*0.05;
     laplaceA += prev[i-1][j+1].a*0.05;
     laplaceA += prev[i+1][j+1].a*0.05;

     float laplaceB = 0;
     laplaceB += b*-1;
     laplaceB += prev[i+1][j].b*0.2;
     laplaceB += prev[i-1][j].b*0.2;
     laplaceB += prev[i][j+1].b*0.2;
     laplaceB += prev[i][j-1].b*0.2;
     laplaceB += prev[i-1][j-1].b*0.05;
     laplaceB += prev[i+1][j-1].b*0.05;
     laplaceB += prev[i-1][j+1].b*0.05;
     laplaceB += prev[i+1][j+1].b*0.05;

     newspot.a = a + (dA*laplaceA - a*b*b + feed*(1-a))*1;
     newspot.b = b + (dB*laplaceB + a*b*b - (k+feed)*b)*1;

     newspot.a = constrain(newspot.a, 0, 1);
     newspot.b = constrain(newspot.b, 0, 1);
   }
 }
}  

void swap() {
 Cell[][] temp = prev;
 prev = grid;
 grid = temp;
}  

void draw() {
 //println(frameRate);

 for (int i = 0; i < 1; i++) {
   update();
   swap();
 }

 loadPixels();
 for (int i = 1; i < width-1; i++) {
   for (int j = 1; j < height-1; j ++) {
     if (maskImage.get(i, j) != emptypixel) {
       Cell spot = grid[i][j];
       float a = spot.a;
       float b = spot.b;
       int pos = i + j * width;
       //pixels[pos] = color((a-b)*255);
       color aColor = color (0, 0, 0);
       color bColor = color (255, 255, 255);
       pixels[pos] = lerpColor(aColor, bColor, (a-b));
     }
   }
   updatePixels();
 }
}

ā€¦or maybe I misunderstood, and you wanted the two halves to be the mask, and the little shapes to be the image. That would work.

Note also that with EDIT your diffusion is evolving the same way underneath, in update(), but being copied selectively. If you used the mask code in update instead, multiple small windows it mayevolve differently than if you were computing the diffusion across the whole grid and then only revealing part of it as in approach 2. Instead, you would be computing a bunch of small diffusions that are never developing beyond their own edges ā€“ and thus never interacting with each other. On the other hand, that is much faster if you are only computing the pixels that you show.

HI @jeremydouglass ,
I just realised that with the black and white mask I canā€™t have a gradual change of the pattern, please have a look at this image to show you what Iā€™m trying to do : 6 3

Looks like I need to have a gradient colour as a mask to have that result ? I actually want to see the black rectangles on the left and slowly when I go on the right there is a gradual born of the pattern when the rectangles are blackā€¦

mmm Iā€™m trying again ,

Thanks!