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