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