Image processing with polka-dots
Close-Up Photography of Tabby Cat · Free Stock Photo
Close Up of Brown Tabby Cat · Free Stock Photo
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/
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/
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/
Close Up of Brown Tabby Cat · Free Stock Photo
About this image manipulation.
It's an image manipulation type generative art made with the 'Processing'.
I tried to draw a cat's photo in a polka‐dot pattern.
Oops, typo! To be exact in a 'purrka-dot'. ;-)
I used edge detection in this code to make the result more clearly.
The '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 under the terms of the GPL.
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 <http://www.gnu.org/licenses/>
*/
Older version result images.
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/
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/
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 <http://www.gnu.org/licenses/>
*/





