RustiCat & FabriCat

FabriCat makes a photo into the painting that made with felted fabric.

A FabriCat example with the image from
Gray Tabby cat with Flowers
http://www.photos-public-domain.com/2010/09/03/gray-tabby-cat-with-flowers/


RustiCat makes a photo into the painting that drew with thin lines.

A RustiCat example with the image from
Calico Cat Closeup
http://www.photos-public-domain.com/2011/01/04/calico-cat-closeup/


Description of this image manipulation code.

This image manipulation code was written in Processing(Java) programming language.
This is the revised work of PsychedeliCat.

RustiCat makes a photo into the painting that drew with thin lines. FabriCat makes the painting that made with felted fabric.

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 RustiCat /your_photo_file_path

If you want draw the Fabric one, then

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

Example code of Processing.

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



// RustiCat & FabriCat
// @author @deconbatch
// @version 0.2
// Processing 3.2.1
// 0.1 2017.06.03
// 0.2 2018.12.23

import java.util.Random;

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

  PShape anElement;
  float elementColor, elementSaturation, elementBright, elementAlpha;
  float fctBrightMinus;
  
  PshapeElement() {
    anElement = pscreateElement();
    elementColor = 0;
    elementSaturation = 0;
    elementBright = 0;
    elementAlpha = 0;
    fctBrightMinus = 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.setStroke(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 RoundBrush extends PshapeElement {

  
  RoundBrush() {
    super();
    fctBrightMinus = 0.6;
  }

  PShape pscreateElement() {

    noFill();
    PShape psDp = createShape(ELLIPSE, 0, 0, 6, 9);
    return psDp;

  }

}

/* ---------------------------------------------------------------------- */
class TriBrush extends PshapeElement {
  

  TriBrush() {
    super();
    fctBrightMinus = 0.5;
  }

  PShape pscreateElement() {

    noFill();
    PShape psDp = createShape(
                              TRIANGLE,
                              0, -6,
                              -4, 3,
                              4, 3
                              );
    return psDp;

  }

}

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

  Random obj_random;

  Utils() {
    obj_random = new Random();
  }
  
  float sigmoid(float x, float pcenter, float pgradient) {
    /**
       Sigmoid function
       1.parameters.
       x : 1 > x value > 0
       pcenter : center of curve. 1 - 1 / pcenter
       ex. 10 > pcenter > 1 : 0.9 > center of curve > 0
       pgradient : gradient of curve.
       ex. 20 > pgradient > 4 : steep > gradient of curve > gentle
       2.return.
       1 > y value > 0
    **/

    float modx = pcenter * (x - 1) + 1;
    return (float)(1.0 / (1.0 + Math.exp(-modx * pgradient)));
  }

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

}

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

Utils ut;
PImage img;
PshapeElement brush;

int divX, divY;
float minBright, maxBright, meanBright;
float minSaturate, maxSaturate, meanSaturate;
float canvasW, canvasH;
float rateSize;

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

  ut = new Utils();  

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

  // fit to canvas size
  rateSize = baseWH / max(img.width, img.height);
  canvasW = img.width * rateSize;
  canvasH = img.height * rateSize;
  img.resize(int(canvasW), int(canvasH));
  img.loadPixels();

  // choose brush
  if (args[0].equals("FabriCat")) {
    println("FabriCat");
    brush = new RoundBrush();
  } else {
    println("RustiCat");
    brush = new TriBrush();
  }

  // divide 1 pixel
  divX = 1;
  divY = 1;
  
  minBright    = 100;
  maxBright    = 0;
  meanBright   = 0;
  minSaturate  = 100;
  maxSaturate  = 0;
  meanSaturate = 0;
  int cntDivs  = 0;
  for (int idxW = 0; idxW < img.width; idxW += divX) {  
    for (int idxH = 0; idxH < img.height; idxH += divY) {

      float valBright = brightness(img.pixels[idxH * img.width + idxW]);
      minBright = minBright > valBright ? valBright : minBright;
      maxBright = maxBright < valBright ? valBright : maxBright;
      meanBright += valBright;

      float valSaturate = saturation(img.pixels[idxH * img.width + idxW]);
      minSaturate = minSaturate > valSaturate ? valSaturate : minSaturate;
      maxSaturate = maxSaturate < valSaturate ? valSaturate : maxSaturate;
      meanSaturate += valSaturate;

      ++cntDivs;
      
    }
  }
  meanBright = meanBright / cntDivs;
  meanSaturate = meanSaturate / cntDivs;
  
}

void draw() {

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

  float maxDistance = sqrt(pow(img.width/2, 2) + pow(img.height/2, 2));
  
  float nsRX = random(50);
  float nsRY = random(50);
  float nsDX = random(50);
  float nsDY = random(50);

  float nfRX = nsRX;
  float nfDX = nsDX;

  for (int idxW = 0; idxW < img.width; idxW += divX) {  
    float nfRY = nsRY;
    float nfDY = nsDY;
    for (int idxH = 0; idxH < img.height; idxH += divY) {

      float fctFray = ut.sigmoid(noise(nfDX, nfDY), 3.5, 4);
      float fctProbability = sqrt(pow(img.width/2 - idxW, 2) + pow(img.height/2 - idxH, 2)) / maxDistance;

      if (random(1) > map(fctFray * fctProbability, 0, 1, 0.15, 0.95)) {
      
        color c = img.pixels[idxH * img.width + idxW];
        float valColor    = setColor(c);
        float valSaturate = setSaturation(c);
        float valSize     = 1.0 + fctProbability * map(fctFray, 0.0, 1.0, 0.5, map(valSaturate, 0.0, 100.0, 2.0, 18.0));
        float valBright   = setBrightness(c) - valSize * brush.fctBrightMinus;
        float valRotate   = radians(180 * noise(nfRX, nfRY));
        
        pushMatrix();
        translate(idxW, idxH);
        brush.resetSize();
        brush.changeSize(valSize, valSize);
        brush.rotate(valRotate);
        brush.setElementFill(valColor, valSaturate, valBright, 100);
        brush.show();
        popMatrix();
      
      }

      nfRY += 0.02;
      nfDY += 0.004;

    }

    nfRX += 0.02;
    nfDX += 0.004;
    
  }

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

}

float setColor(color c) {
  float divColor = map(saturation(c) + (50 - meanSaturate), 0, 100, 10, 30);
  return ut.gaussdist(hue(c) + 360, 20 + divColor, divColor) % 360;
}

float setSaturation(color c) {
  return min(100, 14 * sqrt(max(0, min(100, ut.gaussdist(saturation(c) + (50 - meanSaturate), 30, 10) - 5))));
}

float setBrightness(color c) {
  return map(max(0, min(100, ut.gaussdist(brightness(c) + (50 - meanBright), 30, 10))), 0, 100, 10.0, 40.0);
}

/*
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 
*/










No comments :

Post a Comment