Cat on the Wall : An image processing with a Reaction-Diffusion.

A Reaction-Diffusion pattern on an cat image.
Image manipulation example with https://www.pexels.com/photo/orange-white-cat-115011/

Cats with Gray-Scott model.

An image manipulation type creative coding made with Processing.
It creates a Reaction-Diffusion pattern on an image. This is similar works to 'Mewze' but uses a Gray-Scott model.

You can tune the image manipulation result with these values.
'cultureTimeMax' : How many time calculates a Reaction-Diffusion calculation.
'cellSize' : The size of lines and points in the pattern.
  int   cultureTimeMax = 20000;
  float cellSize       = 2.0;

And there may be various ideas on how to draw the Reaction-Diffusion result.
  public void observe()

You can use your own photo with this code.

You can hard coding the path of your photo image file or you can give command line parameter like this.

/your_processing_installed_path/processing-java --force --sketch=/your_sketch_path/ --run /your_photo_file_path

Processing example code.

Please feel free to use this example code.
To see another works based on my code is my pleasure. And my honor.


// Cat on the Wall.
// @author @deconbatch
// @version 0.2
// Processing 3.2.1
// 0.1 2018.09.24
// 0.2 2018.12.23

/* main -------------------- */
void setup() {
  size(1080, 1080);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  rectMode(CENTER);
  smooth();
  noLoop();
  noStroke();
}

void draw() {
  int   baseCanvas     = 980;
  int   cultureTimeMax = 20000;
  float cellSize       = 2.0;

  ImageLoader img = new ImageLoader(baseCanvas, cellSize);
  Ecosystem   eco = new Ecosystem(img.load(), cellSize, cultureTimeMax);

  eco.initSchale();
  eco.culture();
  eco.observe();
  
  saveFrame("frames/0001.png");
  exit();
}

/* load & resize image -------------------- */
public class ImageLoader {

  PImage imgInit;

  ImageLoader(int baseCanvas, float cellSize) {

    if (args == null) {
      // you can use your photo in ./data/your_image.jpg
      imgInit = loadImage("your_image.jpg");
    } else {
      // args[0] must be image path
      imgInit = loadImage(args[0]);
    }

    float rateSize = baseCanvas / cellSize / max(imgInit.width, imgInit.height);
    imgInit.resize(floor(imgInit.width * rateSize), floor(imgInit.height * rateSize));

  }

  public PImage load() {
    return imgInit;
  }

}

/* controle cells  -------------------- */
public class Ecosystem {

  private Cell[][] cells;
  private float[][][] initVs;
  private float hueBase;
  private float cellSize;
  private int cultureTimeMax;
  private int   canvasW;
  private int   canvasH;
  private PImage img;
  
  Ecosystem(PImage img, float cellSize, int cultureTimeMax) {

    this.img            = img;
    this.cellSize       = cellSize;
    this.cultureTimeMax = cultureTimeMax;
    hueBase             = random(360.0);

    canvasW  = img.width;
    canvasH  = img.height;
    
    cells    = new Cell[canvasW][canvasH];
    initVs   = new float[2][canvasW][canvasH];

    // constract cells
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        cells[x][y] = new Cell();
        initVs[0][x][y] = random(1.0);
        initVs[1][x][y] = random(1.0);
        cells[x][y].setU(initVs[0][x][y]);
        cells[x][y].setV(initVs[1][x][y]);
      }
    }
    
    // set 8 neighbor cells
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        cells[x][y].setNeighbor(new Cell[] {
            cells[max(x-1,0)][max(y-1,0)],
            cells[min(x+1,canvasW-1)][max(y-1,0)],
            cells[max(x-1,0)][min(y+1,canvasH-1)],
            cells[min(x+1,canvasW-1)][min(y+1,canvasH-1)],

            cells[x][max(y-1,0)],
            cells[x][min(y+1,canvasH-1)],
            cells[max(x-1,0)][y],
            cells[min(x+1,canvasW-1)][y]
          });
      }
    }
  }

  public void initSchale() {
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {
        color c = img.pixels[canvasW * y + x];
        cells[x][y].setHue(hue(c));
        cells[x][y].setSat(saturation(c));
        cells[x][y].setBri(brightness(c));
        cells[x][y].setU(map(brightness(c), 0.0, 100.0, 0.0, 0.5));
        cells[x][y].setV(map(brightness(c), 0.0, 100.0, 1.0, 0.0));
        // magic numbers
        cells[x][y].setFeed(map(hue(c) * saturation(c), 0.0, 36000.0, 0.05, 0.10));
        cells[x][y].setKill(map(brightness(c), 0.0, 100.0, 0.056, 0.0665));
      }
    }
  }

  public void culture() {
    for (int cultureTime = 0; cultureTime < cultureTimeMax; cultureTime++) {
      for (int x = 0; x < canvasW; x++) {
        for (int y = 0; y < canvasH; y++) {
          cells[x][y].laplacian();
        }
      }
      for (int x = 0; x < canvasW; x++) {
        for (int y = 0; y < canvasH; y++) {
          cells[x][y].react();
        }
      }
    }
  }

  public void observe() {

    background(0.0, 0.0, 0.0, 100.0);

    // draw cells
    // There is some good chance of making various drawings
    pushMatrix();
    blendMode(BLEND);
    translate((width - img.width * cellSize) / 2.0, (height - img.height * cellSize) / 2.0);

    // forground
    blendMode(SCREEN);
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {

        fill(
             cells[x][y].getLaplacianHue() % 360.0,
             cells[x][y].getLaplacianSat(),
             (cells[x][y].getLaplacianBri() * 0.5 + cells[x][y].getStandardU() * 50.0) * 1.0,
             100.0
             );
        rect(
             x * cellSize,
             y * cellSize,
             cellSize * cells[x][y].getStandardU() * 2.0,
             cellSize * cells[x][y].getStandardU() * 2.0
             );
      }
    }

    // draw black in gap to enhance contrast
    blendMode(DIFFERENCE);    
    for (int x = 0; x < canvasW; x++) {
      for (int y = 0; y < canvasH; y++) {

        fill(
             cells[x][y].getLaplacianHue() % 360.0,
             cells[x][y].getLaplacianSat(),
             cells[x][y].getStandardV() * 100.0,
             100.0
             );
        rect(
             x * cellSize,
             y * cellSize,
             cellSize * cells[x][y].getStandardV() * 2.0,
             cellSize * cells[x][y].getStandardV() * 2.0
             );

      }
    }
    popMatrix();

    // casing
    pushMatrix();
    translate(width / 2.0, height / 2.0);
    fill(0.0, 0.0, 0.0, 0.0);
    strokeWeight(100.0);
    stroke(0.0, 0.0, 100.0, 100.0);
    rect(0.0, 0.0, img.width * cellSize + 95.0, img.height * cellSize + 95.0);
    strokeWeight(10.0);
    stroke(0.0, 0.0, 20.0, 100.0);
    rect(0.0, 0.0, img.width * cellSize + 95.0, img.height * cellSize + 95.0);
    noStroke();
    popMatrix();
    
  }

}

/* Gray-Scott model -------------------- */
public class Cell {
  // magic numbers
  private float diffusionU = 0.001;
  private float diffusionV = 0.000238;
  private float dt         = 1.0;
  private float dxPow      = pow(0.1, 2);

  private float feed;
  private float kill;
  private float valU;
  private float valV;
  private float valHue;
  private float valSat;
  private float valBri;

  private float lapU;
  private float lapV;
  private float lapHue;
  private float lapSat;
  private float lapBri;

  private Cell[] neighbor;

  Cell() {
    feed   = 0.0;
    kill   = 0.0;
    valU   = 0.0;
    valV   = 0.0;
    valHue = 0.0;
    valSat = 0.0;
    valBri = 0.0;
    resetLaplacian();
  }

  private void resetLaplacian() {
    lapU   = 0.0;
    lapV   = 0.0;
    lapHue = 0.0;
    lapSat = 0.0;
    lapBri = 0.0;
  }

  public void setFeed(float init) {
    feed = init;
  }

  public void setKill(float init) {
    kill = init;
  }

  public void setV(float init) {
    valV = init;
  }

  public void setU(float init) {
    valU = init;
  }

  public void setHue(float init) {
    valHue = init;
  }

  public void setSat(float init) {
    valSat = init;
  }

  public void setBri(float init) {
    valBri = init;
  }

  public void setNeighbor(Cell[] pNeighbor) {
    neighbor = new Cell[pNeighbor.length];
    for (int i = 0; i < pNeighbor.length; ++i) {
      neighbor[i] = pNeighbor[i];
    }
  }

  public float getReactU() {
    return valU;
  }

  public float getReactV() {
    return valV;
  }

  public float getStandardU() {
    return constrain(valU, 0.0, 1.0);
  }

  public float getStandardV() {
    return constrain(valV, 0.0, 1.0);
  }

  public float getLaplacianHue() {
    return valHue;
  }

  public float getLaplacianSat() {
    return valSat;
  }

  public float getLaplacianBri() {
    return valBri;
  }

  public void laplacian() {
    float sumU = 0.0;
    float sumV = 0.0;
    float sumHue = 0.0;
    float sumSat = 0.0;
    float sumBri = 0.0;
    for (int i = 0; i < neighbor.length; ++i) {
      sumU   += neighbor[i].getReactU();
      sumV   += neighbor[i].getReactV();
      sumHue += neighbor[i].getLaplacianHue();
      sumSat += neighbor[i].getLaplacianSat();
      sumBri += neighbor[i].getLaplacianBri();
    }
    lapU   = (sumU - valU * neighbor.length) / dxPow;
    lapV   = (sumV - valV * neighbor.length) / dxPow;
    lapHue = (sumHue - valHue * neighbor.length) / dxPow;
    lapSat = (sumSat - valSat * neighbor.length) / dxPow;
    lapBri = (sumBri - valBri * neighbor.length) / dxPow;
  }

  public void react() {
    float reaction = valU * valV * valV;
    float inflow   = feed * (1.0 - valU);
    float outflow  = (feed + kill) * valV;

    valU   += dt * (diffusionU * lapU - reaction + inflow);
    valV   += dt * (diffusionV * lapV + reaction - outflow);
    valHue += dt * diffusionV * lapHue * 0.5;
    valSat += dt * diffusionV * lapSat;
    valBri += dt * diffusionV * lapBri;

    resetLaplacian();
  }

}

/*
Copyright (C) 2018- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see 
*/




Yet another example images.

A Reaction-Diffusion pattern on an cat image.
Image manipulation example with https://www.pexels.com/photo/calico-cat-1359300/

A Reaction-Diffusion pattern on an cat image.
Image manipulation example with https://www.pexels.com/photo/animal-kitten-cat-pet-7517/


No comments :

Post a Comment