Browse Source

fixed a bug where fxLens would give wrong results if a target lens image had a direction of 1 or 3, added fxSlicer

master
Victor Giers 1 year ago
parent
commit
8bb9509e45
2 changed files with 424 additions and 2 deletions
  1. 423
    1
      effects.pde
  2. 1
    1
      secondapplet.pde

+ 423
- 1
effects.pde View File

@@ -3349,7 +3349,7 @@ class LENS extends Shader {
if ((int)params.get(0).value == pos) {
limg = canvas.get();
} else {
limg = gui.shaderList.get((int)params.get(0).value).canvas.get();
limg = gui.shaderList.get((int)params.get(0).value).result.get();
}

power_vals[0] = params.get(1).value;
@@ -3451,6 +3451,428 @@ class LENS extends Shader {
}


/*

SLICER
*/


class SLICER extends Shader {


ArrayList<Edge> edges = new ArrayList<Edge>();
SegmentNode[] elements;
Map<Integer, S> m = new HashMap<Integer, S>();
int max_patterns = 20; // CAN I KILL THIS?

float max_rotation = TWO_PI; // random or fixed rotation up to specified angle, 0 - TWO_PI, 0 - no rotation
boolean fixed_rotation = true; // fixed or random rotation
int thr_min = -1; // you can change color of segment for specified brightness threshold
int thr_max = -1; // set to -1 if you don't want to use it, values from 0 to 255
color[] palette = { #000000, #E7CBB3, #CEC6AF, #A0C3BC, #7C7F84, #ffffff }; // choose colors for palette you want to use with threshold, colors will be choosen randomly


// SEPARATE config
float max_shift = 2; // max expected shift, using gaussian random, so bigger number, more distortion

int type = ROTATE; // choose type of distortion: PATTERNS, SEPARATE, ROTATE, SHIFTCOPY, SORT
boolean do_blend = false; // blend result with original?
int blend_type = SUBTRACT; // list here: https://processing.org/reference/blend_.html

// segmentation config START
float threshold = 500.0; // higher number - bigger segments
int min_comp_size = 200; // minimal segment size (in pixels), minimum 10
int blur = 0; // sometimes it's good to blur image to have less sharp segment edges, 0 = off, blur > 0 - blur kernel size
int stat_type = DIST; // edge calculation method

final static int DIST = 0;
final static int HUE = 1;
final static int BRIGHTNESS = 2;
final static int SATURATION = 3;
final static int ABSDIST = 4;

// do not touch it
PImage img, mimg; // load image to this variable
int num;
// segmentation config END

// do not touch, list of types
final static int PATTERNS = 0; // fill segments with patterns
final static int SEPARATE = 1; // separate segments (black background visible)
final static int ROTATE = 2; // rotate content of the segments
final static int SHIFTCOPY = 3; // copy content from shifted image
final static int SORT = 4; // sort segments by color value

// working buffer
PGraphics buffer;
SLICER() {
name = "fxSlicer";
params.add(new Param("mask layer", INTVAL, 0, shaderListLength-1, new int[]{RANDOM}));
params.add(new Param("max rotation", FLOATVAL, 0, TWO_PI, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("min threshold", INTVAL, -1, 255, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("max threshold", INTVAL, -1, 255, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("max shift", FLOATVAL, 0, 5, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("type", INTVAL, 0, 5, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("do blend", INTVAL, 0, 1, new int[]{RANDOM, SQUAR}));
params.add(new Param("blend type", INTVAL, 0, blends.length-1, new int[]{RANDOM}));
params.add(new Param("threshold", FLOATVAL, 100, 2000, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("min comp size", INTVAL, 10, 500, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("blur", INTVAL, 0, 6, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("stat type", INTVAL, 0, 6, new int[]{SAWTOOTH, TRIANG, SINE, TAN, TANINVERSE, RAMPUPDOWN, RAMP, RAMPINVERSE}));
params.add(new Param("fixed rotation", INTVAL, 0, 1, new int[]{RANDOM, SQUAR}));
params.add(new Param("direction", INTVAL, 0, 3, new int[]{RANDOM}));
directionParamIndex = 13;

shaderListLength = gui.shaderList.size();

img = createImage(canvas.width, canvas.height, ARGB);
mimg = createImage(canvas.width, canvas.height, ARGB);

buffer = createGraphics(img.width, img.height);
buffer.beginDraw();
buffer.noStroke();
buffer.smooth(8);
buffer.background(0);
buffer.endDraw();
}

int rw, rh, shaderListLength;
void apply() {
if (canvas.width != rw || canvas.height != rh) {
rw = canvas.width;
rh = canvas.height;
buffer = createGraphics(rw, rh);
buffer.beginDraw();
buffer.noStroke();
buffer.endDraw();

img.resize(rw, rh);
mimg.resize(rw, rh);
}

if (shaderListLength != gui.shaderList.size()) {
shaderListLength = gui.shaderList.size();
changeParam(0, new Param("mask layer", INTVAL, 0, shaderListLength-1, new int[]{RANDOM}));
}
img = canvas.get();
if ((int)params.get(0).value == pos) {
mimg = canvas.get();
} else {
mimg = gui.shaderList.get((int)params.get(0).value).result.get();
}


max_rotation = params.get(1).value;
thr_min = (int)params.get(2).value;
thr_max = (int)params.get(3).value;
max_shift = params.get(4).value;
type = (int)params.get(5).value;
do_blend = boolean((int)params.get(6).value);
blend_type = blends[(int)params.get(7).value];
threshold = params.get(8).value;
min_comp_size = (int)params.get(9).value;
blur = (int)params.get(10).value;
stat_type = (int)params.get(11).value;
fixed_rotation = boolean((int)params.get(12).value);

println("");
println("Processing...");
edges.clear();
makeEdges();
calculateSegmentation();


buffer.beginDraw();
m.clear();

if (type == PATTERNS) preparePatterns();

for (int x=0; x<img.width; x++)
for (int y=0; y<img.height; y++) {
Integer segment = findEnd(y*img.width+x);

S segm;
if (m.containsKey(segment)) {
segm = m.get(segment);
if (type == SORT) sortHelper(segm, x, y);
} else {
segm = new S();
segm.rots = sin(fixed_rotation?max_rotation:random(max_rotation));
segm.rotc = cos(fixed_rotation?max_rotation:random(max_rotation));
segm.c = img.get(x, y);
segm.dx = (int)(max_shift*randomGaussian());
segm.dy = (int)(max_shift*randomGaussian());
segm.x = x;
segm.y = y;
if (brightness(segm.c)>=thr_min && brightness(segm.c)<=thr_max) segm.c = palette[(int)random(palette.length)];
if (type == PATTERNS) {
segm.pat = patterns[(int)random(max_patterns)];
segm.pats = random(0.5, 10);
}
if (type == SORT) {
segm.xy = random(1)<0.5;
int size = elements[segment].size;
segm.clrs = new color[size];
segm.positions = new int[size];
segm.iters = 0;
sortHelper(segm, x, y);
}
m.put(segment, segm);
}

if (type == PATTERNS) {
int vx = segm.x-x;
int vy = segm.y-y;
float sinr = segm.rots;
float cosr = segm.rotc;
int imgx = int(segm.pat.width+x+(cosr*vx-sinr*vy))%segm.pat.width;
int imgy = int(segm.pat.height+x+(sinr*vx+cosr*vy))%segm.pat.height;
buffer.fill(segm.pat.get(imgx, imgy));
buffer.rect(x, y, 1, 1);
} else if (type == SEPARATE) {
buffer.fill(segm.c);
buffer.rect(x+segm.dx, y+segm.dy, 1, 1);
} else if (type == SHIFTCOPY) {
int imgx = (2*x-segm.x)%img.width;
int imgy = (2*y-segm.y)%img.height;
buffer.fill(img.get(imgx, imgy));
buffer.rect(x, y, 1, 1);
} else if (type == ROTATE) {
int vx = segm.x-x;
int vy = segm.y-y;
float sinr = segm.rots;
float cosr = segm.rotc;
int imgx = int(img.width+x+(cosr*vx-sinr*vy))%img.width;
int imgy = int(img.height+y+(sinr*vx+cosr*vy))%img.height;
buffer.fill(img.get(imgx, imgy));
buffer.rect(x, y, 1, 1);
}
}

if (type == SORT) {
for (Integer key : m.keySet()) {
S segm = m.get(key);
segm.clrs = sort(segm.clrs);
segm.positions = sort(segm.positions);
}

for (Integer key : m.keySet()) {
S segm = m.get(key);
for (int i=0; i<segm.positions.length; i++) {
int x, y;
if (segm.xy) {
x = (segm.positions[i] >> 16) & 0xffff;
y = segm.positions[i] & 0xffff;
} else {
y = (segm.positions[i] >> 16) & 0xffff;
x = segm.positions[i] & 0xffff;
}
buffer.fill(segm.clrs[i]);
buffer.rect(x, y, 1, 1);
}
}
}

if (do_blend)
buffer.blend(img, 0, 0, img.width, img.height, 0, 0, buffer.width, buffer.height, blend_type);

buffer.endDraw();
canvas.beginDraw();
canvas.image(buffer, canvas.width/2, canvas.height/2);
canvas.endDraw();
}


void sortHelper(S segm, int x, int y) {
int xyval;
if (segm.xy)
xyval = (x << 16) | y;
else
xyval = (y << 16) | x;
segm.clrs[segm.iters] = img.get(x, y);
segm.positions[segm.iters++] = xyval;
}

final float getStat(color c1, color c2) {
switch(stat_type) {
case HUE:
abs(hue(c1)-hue(c2));
case BRIGHTNESS:
abs(brightness(c1)-brightness(c2));
case SATURATION:
abs(saturation(c1)-saturation(c2));
case ABSDIST:
return abs(red(c1)-red(c2)) + abs(green(c1)-green(c2)) + abs(blue(c1)-blue(c2));
default:
return sq(red(c1)-red(c2)) + sq(green(c1)-green(c2)) + sq(blue(c1)-blue(c2));
}
}
void makeEdges() {
PImage img2 = mimg.get(0, 0, img.width, img.height);
if (blur > 0) img2.filter(BLUR, blur);
// make grid each point connected to neighbours
for (int x=0; x<img2.width; x++)
for (int y=0; y<img2.height; y++) {
color c = img2.get(x, y);

if (x<img2.width-1) {
Edge e = new Edge();
e.a = y*img2.width+x;
e.b = y*img2.width+x+1;
e.weight = getStat(c, img2.get(x+1, y));
edges.add(e);
}

if (y<img2.height-1) {
Edge e = new Edge();
e.a = y*img2.width+x;
e.b = (y+1)*img2.width+x;
e.weight = getStat(c, img2.get(x, y+1));
edges.add(e);
}

if ( (x<img2.width-1) && (y<img2.height-1)) {
Edge e = new Edge();
e.a = y*img2.width+x;
e.b = (y+1)*img2.width+x+1;
e.weight = getStat(c, img2.get(x+1, y+1));
edges.add(e);
}

if ( (x<img2.width-1) && (y>0)) {
Edge e = new Edge();
e.a = y*img2.width+x;
e.b = (y-1)*img2.width+x+1;
e.weight = getStat(c, img2.get(x+1, y-1));
edges.add(e);
}
}

// sort edges
Collections.sort(edges);
}

int findEnd(int x) {
int y = x;
while (y != elements[y].parent) y = elements[y].parent;
elements[x].parent = y;
return y;
}

void joinSegments(int x, int y) {
if (elements[x].rank > elements[y].rank) {
elements[y].parent = x;
elements[x].size += elements[y].size;
} else {
elements[x].parent = y;
elements[y].size += elements[x].size;
if (elements[x].rank == elements[y].rank) elements[y].rank++;
}
num--;
}


void calculateSegmentation() {
int no_vertices = img.width*img.height;
num = no_vertices;

elements = new SegmentNode[no_vertices];
// init nodes
for (int i=0; i<no_vertices; i++) {
SegmentNode s = new SegmentNode();
s.rank = 0;
s.size = 1;
s.parent = i;
elements[i]=s;
}

float[] thresholds = new float[no_vertices];
Arrays.fill(thresholds, threshold);

for (Edge edge : edges) {
int a = findEnd(edge.a);
int b = findEnd(edge.b);

if (a!=b) {
if (edge.weight <= thresholds[a] && edge.weight <= thresholds[b]) {
joinSegments(a, b);
a = findEnd(a);
thresholds[a] = edge.weight + threshold/elements[a].size;
}
}
}

for (Edge edge : edges) {
int a = findEnd(edge.a);
int b = findEnd(edge.b);
if ( (a != b) && ((elements[a].size < min_comp_size) || (elements[b].size < min_comp_size))) {
joinSegments(a, b);
}
}

println("Segments: " +num);
}


PImage[] patterns;
void preparePatterns() {
patterns = new PImage[max_patterns];
for (int i=0; i<max_patterns; i++) {
patterns[i] = getSelfPattern();
}
}

PImage getSelfPattern() {
int x = (int)random(img.width-50);
int y = (int)random(img.height-50);
int sx = (int)random(50, img.width-x-1);
int sy = (int)random(50, img.height-y-1);
return img.get(x, y, sx, sy);
}

PImage getLayerPattern(int layer) { //use me
int x = (int)random(gui.shaderList.get(layer).canvas.width-50);
int y = (int)random(gui.shaderList.get(layer).canvas.height-50);
int sx = (int)random(50, gui.shaderList.get(layer).canvas.width-x-1);
int sy = (int)random(50, gui.shaderList.get(layer).canvas.height-y-1);
return gui.shaderList.get(layer).canvas.get(x, y, sx, sy);
}
}
// general class to save distortions for particular segment
class S {
color c; // color of the segment root point
int x, y; // position of segment root point
int dx, dy; // segment shift
float rots, rotc, pats;
PImage pat;
// sort
color[] clrs;
int[] positions;
boolean xy;
int iters;
}
class Edge implements Comparable {
int a, b;
float weight;

int compareTo(Object o) {
Edge e = (Edge)o;
return this.weight<e.weight?-1:this.weight>e.weight?1:0;
}
}
class SegmentNode {
int rank, parent, size;
}













+ 1
- 1
secondapplet.pde View File

@@ -617,7 +617,7 @@ public class SecondApplet extends PApplet {
shaderManager.addShader(new LENS());
break;
case(28):
//shaderManager.addShader(new SLICER());
shaderManager.addShader(new SLICER());
break;
/*
case(4):

Loading…
Cancel
Save