Image manipulation with the Vector Field method
Close-Up Photography of Tabby Cat · Free Stock Photo
Close Up of Brown Tabby Cat · Free Stock Photo
Close Up of Brown Tabby Cat · Free Stock Photo
Image manipulation using the Vector Field method.
It's an image manipulation type creative coding example made with the 'Processing'.
This is another implementation of SteaMew. I use custom noise and draw the Vector Field with nice parameters.
This code does not display any images on the screen but generates image file.
The 'Processing' code example.
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.
/** * PussWay. * Draw narrow line with start point color and path way saturation/brightness. * run : processing-java --force --sketch=/path/to/PussWay/ --run "image path" * * @author @deconbatch * @version 0.1 * Processing 3.2.1 * 2019.03.16 */ /** * main */ void setup() { size(1080, 1080); colorMode(HSB, 360.0, 100.0, 100.0, 100.0); smooth(); noLoop(); noStroke(); } void draw() { int caseWidth = 30; int baseCanvas = width - caseWidth * 2; // I brought vector field parameters in class. VectorParams bp = new BackgroundParams(); VectorParams ep = new EdgeParams(); VectorParams dp = new DetailEdgeParams(); ImageLoader imgLoader = new ImageLoader(baseCanvas); PImage img = imgLoader.load(); int edgeAry[][] = detectEdge(img); translate((width - img.width) / 2, (height - img.height) / 2); background(0.0, 0.0, 90.0, 100.0); // draw vector field pattern with nice parameters. drawVector(bp, img, edgeAry); drawVector(ep, img, edgeAry); drawVector(dp, img, edgeAry); casing(caseWidth, img.width, img.height); saveFrame("frames/oioi0001.png"); exit(); } /** * drawVector : draw vector field. * @param _vp : vector field parameters class. * @param _img : origimal photo image. * @param _edge : detented edge information. */ private void drawVector(VectorParams _vp, PImage _img, int[][] _edge) { int plotCntMax = _vp.plotCntMax(); int initDiv = _vp.initDiv(); float baseSiz = _vp.baseSiz(); // draw vector field with nice parameters float noiseDiv = 0.001; float xFrom = random(900.0, 1000.0); float yFrom = random(700.0, 800.0); for (int xInit = 0; xInit < _img.width; xInit += initDiv) { for (int yInit = 0; yInit < _img.height; yInit += initDiv) { if (_vp.isTarget(_edge, xInit, yInit)) { color original = _img.pixels[yInit * _img.width + xInit]; // need to start like this to get nice calculation float xOrigin = xFrom + xInit * 0.05; float yOrigin = yFrom + yInit * 0.05; float xCurrent = xOrigin; float yCurrent = yOrigin; for (int plotCnt = 0; plotCnt < plotCntMax; ++plotCnt) { float pRatio = map(plotCnt, 0, plotCntMax, 0.0, 1.0); float xPrev = xCurrent; float yPrev = yCurrent; xCurrent += cos(TWO_PI * customNoise(yPrev * noiseDiv, yPrev * noiseDiv * 10.0) * 8.0); yCurrent += cos(TWO_PI * customNoise(xPrev * noiseDiv, xPrev * noiseDiv * 10.0) * 8.0); int xCol = floor(constrain(xCurrent - xOrigin + xInit, 0, _img.width - 1)); int yCol = floor(constrain(yCurrent - yOrigin + yInit, 0, _img.height - 1)); color pathway = _img.pixels[yCol * _img.width + xCol]; float eHue = hue(original); float eSat = saturation(pathway); float eBri = brightness(pathway); float eAlp = 100.0 * (1.0 - pRatio); float eSiz = pow(baseSiz * sin(PI * pRatio), 2); fill(eHue % 360.0, eSat, eBri, eAlp); ellipse(xCol, yCol, eSiz, eSiz); } } } } } /** * customNoise : returns -1.0 .. 1.0 almost random but interesting value */ private float customNoise(float _x, float _y) { return pow(sin(_x), 3) * cos(pow(_y, 2)); } /** * casing : draw fancy casing */ private void casing(int _casing, float _w, float _h) { fill(0.0, 0.0, 0.0, 0.0); strokeWeight(_casing + 4.0); stroke(0.0, 0.0, 30.0, 100.0); rect(-_casing * 0.5, -_casing * 0.5, _w + _casing, _h + _casing); strokeWeight(_casing); stroke(0.0, 0.0, 100.0, 100.0); rect(-_casing * 0.5, -_casing * 0.5, _w + _casing, _h + _casing); noStroke(); noFill(); } /** * 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; } /** * 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; } } /** * VectorParams : holding vector field parameters. */ interface VectorParams { /** * 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 plotCntMax(); int initDiv(); float baseSiz(); } public class BackgroundParams implements VectorParams { public Boolean isTarget(int _points[][], int _x, int _y) { // every point return true; } public int plotCntMax() { return 3000; } public int initDiv() { return 50; } public float baseSiz() { return 1.0; } } public class EdgeParams implements VectorParams { public Boolean isTarget(int _points[][], int _x, int _y) { // only edge is target if (_points[_x][_y] == 1) { return true; } return false; } public int plotCntMax() { return 500; } public int initDiv() { return 10; } public float baseSiz() { return 1.1; } } public class DetailEdgeParams extends EdgeParams { public int plotCntMax() { return 300; } public int initDiv() { return 5; } public float baseSiz() { return 1.2; } } /* 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/> */