Applying Conway's Game of Life to photo images.

Image manipulation digital art example with the image from
Orange And White Cat Closeup
http://www.photos-public-domain.com/2011/01/07/orange-and-white-cat-closeup/



Applying Conway's Game of Life to photo images.

Image manipulation digital art example with the image from
Two Tabby Cats
http://www.photos-public-domain.com/2016/11/15/two-tabby-cats/


Description of this creative coding digital art.

It's an image manipulation type digital art and creative coding example code written in Processing programming language.

I tried to apply Conway's Game of Life to photo images. Hue, Brightness, Saturation, and Alpha. Brightness is easy to apply out of these, I think.

You can see stable patterns in it.
You can see the stable patterns of Conway's Game of Life.


At first,
    Alive cell : brightness = 80
    Dead cell : brightness = 20
It's not bad.
Applying Conway's Game of Life to photo images.


But I like this. You can see the result above.
    Alive cell : ++brightness
    Dead cell : --brightness

An important point of this code is 'How to find dead/alive border'.

You can use your own photo.
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

Correction of my mistaken code.

Oh! I made a mistake.
I always do. But I realized it this time.

I had to apply Conway's Life Game rule to separate array.
My code influence the rule from the left upper to right lower.

Then
conwaysLifeGame()
 ↓
notConwaysLifeGame()
Fixed! ;-)

Example code of this digital art written in Processing.

Please feel free to use this code, if you like it.
To see another works based on my code is my pleasure. And my honor.


// LifeGameow.
// @author @deconbatch
// @version 0.2
// Processing 3.2.1
// 0.1 2018.01.14
// 0.2 2018.12.23

/* ---------------------------------------------------------------------- */

float canvasW, canvasH;
float rateSize;

void setup() {
  size(1000, 1000);
  
  colorMode(HSB, 360, 100, 100, 100);
  noStroke();
  smooth(8);
  noLoop();

}

void draw() {

  PImage imgInit, imgWork;

  float baseWH        = 900;
  float maxBrightness = 0.0;
  int   borderRatio   = 40; // percentage

  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]);
  }

  // fit to canvas size
  rateSize = baseWH / max(imgInit.width, imgInit.height);
  canvasW  = imgInit.width * rateSize;
  canvasH  = imgInit.height * rateSize;
  imgInit.resize(int(canvasW), int(canvasH));
  imgInit.loadPixels();
  
  float[] calcArray = new float[imgInit.pixels.length]; // for life game calculation
  int[]   briArray  = new int[101]; // pixel count per brightness 0-100
  for (int idxW = 1; idxW < imgInit.width - 1; ++idxW) {  
    for (int idxH = 1; idxH < imgInit.height - 1; ++idxH) {
      int idxPoint = idxH * imgInit.width + idxW;
      calcArray[idxPoint] = brightness(imgInit.pixels[idxPoint]);
      ++briArray[int(brightness(imgInit.pixels[idxPoint]))];
    }
  }

  // life game dead or alive border brightness value
  int borderBrightness = 0;
  int countPixels      = 0;
  int noPixels         = int(canvasW * canvasH);
  for (borderBrightness = 100; borderBrightness > 0; --borderBrightness) {
    countPixels += briArray[borderBrightness];
    if (100 * countPixels / noPixels >= borderRatio) {
      break;
    }      
  }

  blendMode(SCREEN);
  background(0, 0, maxBrightness);
  translate((width - canvasW) / 2, (height - canvasH) / 2);

  imgWork = notConwaysLifeGame(imgInit.copy(), calcArray.clone(), 300, 0.1, borderBrightness);
  image(imgWork, 0.0, 0.0);

  imgWork = notConwaysLifeGame(imgInit.copy(), calcArray.clone(), 120, 0.6, borderBrightness);
  image(imgWork, 0.0, 0.0);

  imgWork = notConwaysLifeGame(imgInit.copy(), calcArray.clone(), 30, 10.0, borderBrightness);
  image(imgWork, 0.0, 0.0);


  saveFrame("frames/####.png");
  exit();

}

/* ---------------------------------------------------------------------- */

PImage notConwaysLifeGame(PImage pImg, float[] pCalcArray, int pCicleNoMax, float pDivLife, int pBorder) {

  float borderAlive = pBorder * 1.0;
  
  for (int cicleNo = 1; cicleNo < pCicleNoMax; ++cicleNo) {
 
    for (int idxW = 1; idxW < pImg.width - 1; ++idxW) {  
      for (int idxH = 1; idxH < pImg.height - 1; ++idxH) {

        int aliveNum = 0;
        int idxPoint = idxH * pImg.width + idxW;
        aliveNum += deadOrAlive(pCalcArray[idxPoint - 1], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint + 1], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint - pImg.width], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint - pImg.width - 1], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint - pImg.width + 1], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint + pImg.width], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint + pImg.width - 1], borderAlive);
        aliveNum += deadOrAlive(pCalcArray[idxPoint + pImg.width + 1], borderAlive);
        
        color cPoint = pImg.pixels[idxPoint];
        float valHue = hue(cPoint);
        float valSat = saturation(cPoint);
        float valBri = brightness(cPoint);
        float valAlp = 100.0;

        // birth
        if (aliveNum == 3) {
          valBri += pDivLife;
          valSat += pDivLife / 2.0;
          pCalcArray[idxPoint] = 100.0;
        }
        // die
        if (aliveNum <= 1 || aliveNum >= 4) {
          valBri -= pDivLife;
          valSat -= pDivLife / 2.0;
          pCalcArray[idxPoint] = 0.0;
        }

        valSat = withinRange(valSat);
        valBri = withinRange(valBri);

        pImg.pixels[idxPoint] = color(valHue, valSat, valBri, valAlp);

      }
    }

  }

  pImg.updatePixels();
  return pImg;

}

int deadOrAlive(float pVal, float pBorder) {
  if (pVal > pBorder) {
    return 1;
  }
  return 0;
}

float withinRange(float val) {
  return max(3.0, min(100.0, val));
}


/*
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 "http://www.gnu.org/licenses/"
*/