title

deconbatch's Land of 1000 Creative Codings. example codes of Processing / p5.js, how-to articles, generative art images, and much more!

Purrka Dots.

A cat photo in polka-dot pattern with this generative art code.
Close-Up Photography of Tabby Cat · Free Stock Photo

This generative art is a cat photo in polka‐dot pattern.
Close Up of Brown Tabby Cat · Free Stock Photo


About this image manipulation.

An image manipulation type generative art made with Processing.

I tried to draw a cat's photo in a polka‐dot pattern.
Oops, typo! To be exact in a 'purrka-dot'. ;-)

Processing example codes (Java).

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


Current version code.

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

/**
 * Purrka Dots.
 * Draw image with color dot.
 * run : processing-java --force --sketch=/path/to/PurrkaDots/ --run "image path"
 * 
 * @author @deconbatch
 * @version 0.4
 * created 0.1 2017.10.22
 * updated 0.2 2017.10.28 Many micro dots + little giant dots, no middle dots
 * updated 0.3 2018.12.23
 * updated 0.4 2019.03.21 rewrote whole codes with same concept, less dots, use edge detection
 * 
 * Processing 3.2.1
 * 2019.03.21
 */

import java.util.Random;

void setup() {

  size(1080, 1080);
  colorMode(HSB, 360, 100, 100, 100);
  smooth();
  noStroke();
  noLoop();

}

void draw() {

  int caseWidth  = 30;
  int baseCanvas = width - caseWidth * 2;

  // I brought dots pattern parameters in class.
  DotsParams bp = new BackgroundParams();
  DotsParams sp = new SpotParams();
  DotsParams ep = new EdgeParams();
  DotsParams dp = new DetailParams();
  
  ImageLoader imgLoader = new ImageLoader(baseCanvas);
  PImage img = imgLoader.load();

  // edge detection
  int edgeAry[][] = detectEdge(img);

  background(0.0, 0.0, 90.0, 100.0);
  translate((width - img.width) / 2, (height - img.height) / 2);

  // draw dots pattern
  putDots(bp, img, edgeAry);
  putDots(sp, img, edgeAry);
  putDots(ep, img, edgeAry);
  putDots(dp, img, edgeAry);
 
  saveFrame("frames/0001.png");
  exit();

}

/**
 * putDots : draw dots.
 * @param  _dp       : dots pattern parameters class.
 * @param  _img      : origimal photo image.
 * @param  _edge     : detented edge information.
 */
private void putDots(DotsParams _dp, PImage _img, int[][] _edge) {

  int   drawCntMax = _dp.drawCntMax();
  int   idxDiv     = _dp.initDiv();
  float baseSiz    = _dp.baseSize();

  Utils ut = new Utils();

  for (int drawCnt = 1; drawCnt < drawCntMax; ++drawCnt) {

    float prmSat = map(drawCnt, 1, drawCntMax, 1.0, 0.4);
    float prmAlp = map(drawCnt, 1, drawCntMax, 1.0, 0.0);
    
    for (int idxH = 0; idxH < _img.height; idxH += idxDiv) {
      for (float idxW = 0; idxW < _img.width; idxW += idxDiv) {
      
        float brushAlp = ut.gaussdist(50.0, 30.0, 20.0) * prmAlp;
        float brushSiz = baseSiz * (0.5 + ut.gaussdist(0.5, 0.5, 0.2));

        int pointW = constrain(round(idxW + ut.gaussdist(0.0, idxDiv * 0.6, idxDiv * 0.3)), 0, _img.width - 1);
        int pointH = constrain(round(idxH + ut.gaussdist(0.0, idxDiv * 0.6, idxDiv * 0.3)), 0, _img.height - 1);

        if (_dp.isTarget(_edge, pointW, pointH)) {
          color cPoint = _img.pixels[pointH * _img.width + pointW];
          fill(hue(cPoint), saturation(cPoint), brightness(cPoint), brushAlp);
          ellipse(pointW, pointH, brushSiz, brushSiz);
        }   

      }
    }
  }
}

/* ---------------------------------------------------------------------- */
/**
 * Utils : utility methods
 */
private class Utils {

  Random rnd;

  Utils() {
    rnd = new Random();
  }

  /**
   * gaussdist : returns Gaussian distributed random.
   * @param  _mean      : mean value of Gaussian distribution
   * @param  _limit     : max value of abs(deviation)
   * @param  _deviation : standard deviation of Gaussian distribution
   * @return float      : _mean - _limit < Gaussian distributed random < _mean + _limit
   */
  private float gaussdist(float _mean, float _limit, float _deviation) {
    if (_limit == 0) {
      return _mean;
    }

    float gauss = (float) rnd.nextGaussian() * _deviation;
    // not good idea
    if (abs(gauss) > _limit) {
      gauss = pow(_limit, 2) / gauss;
    }
    return _mean + gauss;

  }
}

/**
 * detectEdge : detect edge of photo image.
 * @param  _img      : detect edge of thid image.
 * @return int[x][y] : 2 dimmension array. it holds 0 or 1, 1 = edge
 */
private int[][] detectEdge(PImage _img) {

  int edgeAry[][] = new int[_img.width][_img.height];
  for (int idxW = 0; idxW < _img.width; ++idxW) {  
    for (int idxH = 0; idxH < _img.height; ++idxH) {
      edgeAry[idxW][idxH] = 0;
    }
  }
    
  _img.loadPixels();
  for (int idxW = 1; idxW < _img.width - 1; ++idxW) {  
    for (int idxH = 1; idxH < _img.height - 1; ++idxH) {

      int pixIndex = idxH * _img.width + idxW;

      // saturation difference
      float satCenter = saturation(_img.pixels[pixIndex]);
      float satNorth  = saturation(_img.pixels[pixIndex - _img.width]);
      float satWest   = saturation(_img.pixels[pixIndex - 1]);
      float satEast   = saturation(_img.pixels[pixIndex + 1]);
      float satSouth  = saturation(_img.pixels[pixIndex + _img.width]);
      float lapSat = pow(
                         - satCenter * 4.0
                         + satNorth
                         + satWest
                         + satSouth
                         + satEast
                         , 2);

      // brightness difference
      float briCenter = brightness(_img.pixels[pixIndex]);
      float briNorth  = brightness(_img.pixels[pixIndex - _img.width]);
      float briWest   = brightness(_img.pixels[pixIndex - 1]);
      float briEast   = brightness(_img.pixels[pixIndex + 1]);
      float briSouth  = brightness(_img.pixels[pixIndex + _img.width]);
      float lapBri = pow(
                         - briCenter * 4.0
                         + briNorth
                         + briWest
                         + briSouth
                         + briEast
                         , 2);

      // hue difference
      float hueCenter = hue(_img.pixels[pixIndex]);
      float hueNorth  = hue(_img.pixels[pixIndex - _img.width]);
      float hueWest   = hue(_img.pixels[pixIndex - 1]);
      float hueEast   = hue(_img.pixels[pixIndex + 1]);
      float hueSouth  = hue(_img.pixels[pixIndex + _img.width]);
      float lapHue = pow(
                         - hueCenter * 4.0
                         + hueNorth
                         + hueWest
                         + hueSouth
                         + hueEast
                         , 2);

      // bright and saturation difference
      if (
          brightness(_img.pixels[pixIndex]) > 30.0
          && lapSat > 20.0
          ) edgeAry[idxW][idxH] = 1;

      // bright and some saturation and hue difference
      if (
          brightness(_img.pixels[pixIndex]) > 30.0
          && saturation(_img.pixels[pixIndex]) > 10.0
          && lapHue > 100.0
          ) edgeAry[idxW][idxH] = 1;

      // just brightness difference
      if (lapBri > 100.0) edgeAry[idxW][idxH] = 1;

    }
  }

  return edgeAry;
}

/**
 * DotsParams : holding dots pattern parameters.
 */
interface DotsParams {

  /**
   * isTarget : is this point(x, y) drawing target?
   * @return true : draw target
   */
  Boolean isTarget(int _points[][], int _x, int _y);

  /**
   * just returns parameter value
   */
  int   drawCntMax();
  int   initDiv();
  float baseSize();

}

public class BackgroundParams implements DotsParams {
  
  public Boolean isTarget(int _points[][], int _x, int _y) {
    // every point
    return true;
  }

  public int drawCntMax() {
    return 3;
  }
  public int initDiv() {
    return 500;
  }
  public float baseSize() {
    return 150;
  }

}

public class EdgeParams implements DotsParams {

  public Boolean isTarget(int _points[][], int _x, int _y) {
    // only edge is target
    if (_points[_x][_y] == 1) {
      return true;
    }
    return false;
  }

  public int drawCntMax() {
    return 10;
  }
  public int initDiv() {
    return 20;
  }
  public float baseSize() {
    return 30;
  }
  
}

public class SpotParams extends EdgeParams {

  public int drawCntMax() {
    return 5;
  }
  public int initDiv() {
    return 100;
  }
  public float baseSize() {
    return 60;
  }
  
}

public class DetailParams extends EdgeParams {

  public int drawCntMax() {
    return 20;
  }
  public int initDiv() {
    return 4;
  }
  public float baseSize() {
    return 2;
  }
  
}

/**
 * ImageLoader : load and resize image
 */
public class ImageLoader {

  PImage imgInit;
  String imgPass;

  ImageLoader(int baseCanvas) {

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

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

    println(int(imgInit.width)); // Image width
    println(int(imgInit.height)); // Image height

  }

  /**
   * load : return loaded image
   */
  public PImage load() {
    return imgInit;
  }

}

/*
Copyright (C) 2019- 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 
*/





Older version result images.


It makes a cat photo into polka-dot pattern.

Image manipulation with the image from
Cat and Kitten looking out Door
http://www.photos-public-domain.com/2010/08/28/cat-and-kitten-looking-out-door/


It makes a cat photo into polka-dot pattern.

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


It makes a cat photo into polka-dot pattern.

Image manipulation with the image with
Fat Cat with Greens in Sunbeam
http://www.photos-public-domain.com/2010/08/28/fat-cat-with-greens-in-sunbeam/


Older version code.



// Purrka Dots.
// @author @deconbatch
// @version 0.3
// Processing 3.2.1
// 0.1 2017.10.22
// 0.2 2017.10.28 Many micro dots + little giant dots, no middle dots
// 0.3 2018.12.23

import java.util.Random;

/* ---------------------------------------------------------------------- */
class Utils {

  Random obj_random;

  Utils() {
    obj_random = new Random();
  }

  float gaussdist(float pmean, float plimit, float pdevi) {
    /**
       Gaussian distribution
       1.parameters.
       pmean  : mean value
       plimit : max value of abs(deviation)
       ex. plimit >= 0
       pmean = 0.5, plimit = 0.5 -> return value = from 0.0 to 1.0
       pdevi  : standard deviation value
       ex. good value? -> pdevi = plimit / 2
       2.return.
       gaussian distribution
    **/

    if (plimit == 0) {
      return pmean;
    }

    float gauss = (float) obj_random.nextGaussian() * pdevi;
    // not good idea
    if (abs(gauss) > plimit) {
      gauss = pow(plimit, 2) / gauss;
    }

    return pmean + gauss;
    
  }

}

/* ---------------------------------------------------------------------- */
abstract class PshapeElement {

  PShape anElement;
  float elementColor, elementSaturation, elementBright, elementAlpha;
  
  PshapeElement() {
    anElement = pscreateElement();
    elementColor = 0;
    elementSaturation = 0;
    elementBright = 0;
    elementAlpha = 0;
  }

  abstract PShape pscreateElement();

  void setElementFill(float pcolor, float psaturation, float pbright, float palpha) {
    elementColor = pcolor;
    elementSaturation = psaturation;
    elementBright = pbright;
    elementAlpha = palpha;
    resetColor();
  }

  void resetColor() {
    anElement.setFill(color(elementColor, elementSaturation, elementBright, elementAlpha));
  }

  void changeColor(float scolor) {
    elementColor = scolor;
    resetColor();
  }

  void changeBright(float sbright) {
    elementBright = sbright;
    resetColor();
  }

  void resetSize() {
    anElement.resetMatrix();
  }

  void changeSize(float scaleX, float scaleY) {
    anElement.scale(scaleX, scaleY);
  }

  void rotate(float radX) {
    anElement.rotate(radX);
  }

  void show() {
    shape(anElement);
  }

}

/* ---------------------------------------------------------------------- */
class Brush extends PshapeElement {
  
  Brush() {
    super();
  }

  PShape pscreateElement() {

    noStroke();
    //    strokeWeight(0.02);
    //    stroke(0.0, 0.0, 90.0, 5.0);
    fill(0.0, 0.0, 0.0, 0.0);
    PShape psDp = createShape(ELLIPSE, 0.0, 0.0, 1.0, 1.0);
    return psDp;

  }

}

/* ---------------------------------------------------------------------- */
void drawCircles() {

  int idxSetMax = 5000;
  int idxSetDiv = 1;
  
  float marginW = (width - img.width) / 2.0;
  float marginH = (height - img.height) / 2.0;

  float idxHStart = marginH + img.height / 6.0;
  float idxHEnd = 60 + height - (marginH + img.height / 6.0);
  float idxHDiv = img.height / 3.0;
  float idxWStart = marginW;
  float idxWEnd = width - marginW;

  float nsHueStart = random(10.0);
  float nsSatStart = random(10.0);
  float nsAlpStart = random(10.0);

  for (int idxSet = 1; idxSet <= idxSetMax; idxSet += idxSetDiv) {

    // many micro dots
        if (idxSet > 1200 & idxSet % 10 == 0) {
          ++idxSetDiv;
        }
    
    float nsSat = nsSatStart;
    float prmSat = map(idxSet, 1, idxSetMax, 1.0, 0.8);
    float prmAlp = map(idxSet, 1, idxSetMax, 1.0, 0.1);
    float idxWDiv = pow(map(idxSet, 1, idxSetMax, 1.0, 8.0), 3); // little giant dots
    

    for (float idxH = idxHStart; idxH < idxHEnd; idxH += idxHDiv) {

      float nsHue = nsHueStart;
      float nsAlp = nsAlpStart;

      for (float idxW = idxWStart; idxW < idxWEnd; idxW += idxWDiv * 0.5) {

        float brushSiz = (0.1 + abs(ut.gaussdist(0.0, 10.0, 0.1))) * idxWDiv;
        float brushHue = ut.gaussdist(map(noise(nsHue), 0.0, 1.0, 190.0, 420.0), 60.0, 20.0) % 360.0; // avoid green
        float brushSat = map(noise(nsSat), 0.0, 1.0, 40.0, 60.0) * prmSat;
        float brushBri = 90;
        float brushAlp = map(noise(nsAlp), 0.0, 1.0, 20.0, 100.0) * prmAlp;
      
        float pointW = idxW + ut.gaussdist(0.0, brushSiz * 3.0, brushSiz);
        float pointH = idxH + ut.gaussdist(0.0, img.height / 2.5, img.height / 8.0);

        // photo area
        if (pointW >= marginW & pointW < img.width + marginW & pointH >= marginH & pointH < img.height + marginH) {

          color cPoint = img.pixels[floor(pointH - marginH) * img.width + floor(pointW - marginW)];

          // avoid green
          float zeroOrigin = (brushHue + hue(cPoint) + 170) % 360; // 190 = 0, 420 = 230
          float zeroTo230 = (zeroOrigin + 230) % 230; // 0-360 -> 0-230
          brushHue = (zeroTo230 + 190) % 360; // 0-230 -> 0-60, 190-360
          brushSat *= map(saturation(cPoint), 0.0, 100.0, 0.8, 1.1) * map(brightness(cPoint), 0.0, 100.0, 1.2, 0.5);
          brushBri *= map(brightness(cPoint), 0.0, 100.0, 0.8, 1.1);
          brushAlp *= map(brightness(cPoint), 0.0, 100.0, 1.5, 0.1);

        }
      
        pushMatrix();
        translate(pointW, pointH);
        brush.resetSize();
        brush.changeSize(brushSiz, brushSiz);
        brush.setElementFill(brushHue, brushSat, brushBri, brushAlp);
        brush.show();
        popMatrix();
        
        nsHue += 0.0015 * idxWDiv / 3.0;
        nsSat += 0.012 * idxWDiv;
        nsAlp += 0.016 * idxWDiv;

      }
    }
  }
}

/* ---------------------------------------------------------------------- */
Utils ut;
PImage img;
PshapeElement brush;

void setup() {
  size(1080, 1080);
  float baseWH = 980;
  colorMode(HSB, 360, 100, 100, 100);
  smooth(8);
  noLoop();

  ut = new Utils();  
  brush = new Brush();

  if (args == null) {
    // you can use your photo in ./data/your_image.jpg
    img = loadImage("your_image.jpg");
  } else {
    // args[0] must be image path
    img = loadImage(args[0]);
  }
  
  // fit to canvas size
  float rateSize = baseWH / max(img.width, img.height);
  float canvasW = img.width * rateSize;
  float canvasH = img.height * rateSize;
  img.resize(int(canvasW), int(canvasH));
  img.loadPixels();

}

void draw() {

  background(0.0, 0.0, 90.0, 100.0);
  translate(0.0, 0.0);
  
  drawCircles();

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

}

/*
Copyright (C) 2017- 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 
*/






Post a Comment

0 Comments