Files
GenerateMe/wavelets/wavelets.pde
2015-05-23 00:29:53 +02:00

477 lines
13 KiB
Plaintext

// Wavelet transformation based on JWave library
// https://github.com/cscheiblich/JWave/
// Tomasz Sulej, generateme.blog@gmail.com, http://generateme.tumblr.com
// Licence: http://unlicense.org/
// Usage:
// * press SPACE to save
// * click to random change of parameters
// Description
// How does it work - read here: http://en.wikipedia.org/wiki/Wavelet
// In short not mathematical words: image (each channel) is converted to wavelet representation (forward transformation),
// then some of the data are removed or simplified (compression) and transformed back to image. Since some data are lost, reverse transformed image has lower quality
//
import math.jwave.transforms.DiscreteFourierTransform;
import math.jwave.transforms.wavelets.Wavelet;
import math.jwave.transforms.wavelets.WaveletBuilder;
import math.jwave.Transform;
import math.jwave.TransformBuilder;
import math.jwave.compressions.Compressor;
import math.jwave.compressions.CompressorMagnitude;
import math.jwave.compressions.CompressorPeaksAverage;
import math.jwave.exceptions.JWaveException;
// set up filename
String filename = "test";
String fileext = ".jpg";
String foldername = "./";
int max_display_size = 800; // viewing window size (regardless image size)
// configuration
boolean equalize = true; // equalize and normalize histogram?
int colorspace = RGB; // RGB or HSB
boolean same_transform = true ; // true to use the same transform for each channel (transform 1)
// configure each transform
void initialize() {
// inital transform configuration, click to set them to random.
// There are three transformations, one per each channel
// List of all wavelets is under this link (search for 'case' list: https://github.com/cscheiblich/JWave/blob/master/src/math/jwave/transforms/wavelets/WaveletBuilder.java
// Transform 1
Wavelet w1 = WaveletBuilder.create("Haar"); // set wavelet using it's name
GMTrans gmtrans1 = new GMTrans(w1, FTW); // FTW or WPT - transform type, FTW is faster
gmtrans1.compressor_type = CompressorGM; // CompressorGM, CompressorM, CompressorPA
gmtrans1.threshold = 20; // 10-50, only for M and PA compressors
// only for GM processor
gmtrans1.gm_sum = true; // true or false
gmtrans1.cond_type = OR; // OR, AND, XOR
gmtrans1.cond_negate = true; // true or false
gmtrans1.low_x = 2; // 1-10
gmtrans1.low_y = 3; // 1-10
gmtrans1.high_x = 0.01; // 0.01 - 0.5
gmtrans1.high_y = 0.02; // 0.02 - 0.5
// Transform 2
Wavelet w2 = WaveletBuilder.create("Daubechies 4"); // set wavelet using it's name
GMTrans gmtrans2 = new GMTrans(w2, FTW); // FTW or WPT - transform type, FTW is faster
gmtrans2.compressor_type = CompressorM; // CompressorGM, CompressorM, CompressorPA
gmtrans2.threshold = 50; // 10-50, only for M and PA compressors
// only for GM processor
gmtrans2.gm_sum = true; // true or false
gmtrans2.cond_type = AND; // OR, AND, XOR
gmtrans2.cond_negate = false; // true or false
gmtrans2.low_x = 2; // 1-10
gmtrans2.low_y = 3; // 1-10
gmtrans2.high_x = 0.01; // 0.01 - 0.5
gmtrans2.high_y = 0.02; // 0.02 - 0.5
// Transform 3
Wavelet w3 = WaveletBuilder.create("Haar"); // set wavelet using it's name
GMTrans gmtrans3 = new GMTrans(w3, FTW); // FTW or WPT - transform type, FTW is faster
gmtrans3.compressor_type = CompressorGM; // CompressorGM, CompressorM, CompressorPA
gmtrans3.threshold = 20; // 10-50, only for M and PA compressors
// only for GM processor
gmtrans3.gm_sum = true; // true or false
gmtrans3.cond_type = XOR; // OR, AND, XOR
gmtrans3.cond_negate = true; // true or false
gmtrans3.low_x = 2; // 1-10
gmtrans3.low_y = 3; // 1-10
gmtrans3.high_x = 0.01; // 0.01 - 0.5
gmtrans3.high_y = 0.02; // 0.02 - 0.5
// do not touch
gmtrans1.setup();
gmtrans2.setup();
gmtrans3.setup();
transformr = gmtrans1;
transformg = gmtrans2;
transformb = gmtrans3;
if(same_transform) transformb = transformg = transformr;
println("Transform 1");
transformr.printSetup();
println("Transform 2");
transformg.printSetup();
println("Transform 3");
transformb.printSetup();
}
final static int AND = 0;
final static int OR = 1;
final static int XOR = 2;
final static String FTW = "Fast Wavelet Transform";
final static String WPT = "Wavelet Packet Transform";
final static String[] wtypes = {FTW,WPT};
final static int CompressorGM = 0;
final static int CompressorM = 1;
final static int CompressorPA = 2;
boolean do_blend = false; // blend image after process
int blend_mode = OVERLAY; // blend type
// working buffer
PGraphics buffer;
// image
PImage img;
String sessionid;
Wavelet[] wtab;
void setup() {
sessionid = hex((int)random(0xffff),4);
img = loadImage(foldername+filename+fileext);
buffer = createGraphics(img.width, img.height);
buffer.beginDraw();
buffer.noStroke();
buffer.smooth(8);
buffer.background(0);
buffer.fill(128);
buffer.endDraw();
// calculate window size
float ratio = (float)img.width/(float)img.height;
int neww, newh;
if(ratio < 1.0) {
neww = (int)(max_display_size * ratio);
newh = max_display_size;
} else {
neww = max_display_size;
newh = (int)(max_display_size / ratio);
}
size(neww,newh);
wtab = WaveletBuilder.create2arr();
initialize();
processImage2d();
}
void draw() {
// fill for iterative processing
}
void mouseClicked() {
setupWavelets();
processImage2d();
}
GMTrans transformr, transformg, transformb;
void setupWavelets() {
colorspace = random(1)<0.5?RGB:HSB;
same_transform = random(1)<0.5;
Wavelet w1,w2,w3;
if(random(1)<0.3) {
w1 = w2 = w3 = wtab[(int)random(wtab.length)];
} else {
w1 = wtab[(int)random(wtab.length)];
w2 = wtab[(int)random(wtab.length)];
w3 = wtab[(int)random(wtab.length)];
}
String wt1, wt2, wt3;
if(random(1)<0.3) {
wt1 = wt2 = wt3 = wtypes[(int)random(2)];
} else {
wt1 = wtypes[(int)random(2)];
wt2 = wtypes[(int)random(2)];
wt3 = wtypes[(int)random(2)];
}
transformr = new GMTrans(w1,wt1);
transformg = new GMTrans(w2,wt2);
transformb = new GMTrans(w3,wt3);
if(same_transform) transformb = transformg = transformr;
println("Transform 1");
transformr.printSetup();
println("Transform 2");
transformg.printSetup();
println("Transform 3");
transformb.printSetup();
do_blend = random(1)<0.2;
blend_id = (int)random(blends.length);
blend_mode = blends[blend_id];
}
void processImage2d() {
println("");
buffer.beginDraw();
int ww = 1 << (int)ceil(log(img.width)/log(2));
int hh = 1 << (int)ceil(log(img.height)/log(2));
print("Preparing channels ("+(colorspace==RGB?"RGB":"HSB")+")...");
double[][] r = new double[ww][hh];
double[][] g = new double[ww][hh];
double[][] b = new double[ww][hh];
for(int y=0;y<hh;y++) {
for(int x=0;x<ww;x++) {
double rr,gg,bb;
color c = img.get(x,y);
if(colorspace == RGB) {
rr = red(c)/255.0;
gg = green(c)/255.0;
bb = blue(c)/255.0;
} else {
rr = hue(c)/255.0;
gg = saturation(c)/255.0;
bb = brightness(c)/255.0;
}
r[x][y] = rr;
g[x][y] = gg;
b[x][y] = bb;
}
}
println("done");
print("Compression.");
r = transformr.process(r);
print(".");
g = transformg.process(g);
print(".");
b = transformb.process(b);
println("done");
print("Combining channels ("+(colorspace==RGB?"RGB":"HSB")+")...");
if(colorspace == HSB) colorMode(HSB,255);
for(int y=0;y<hh;y++) {
for(int x=0;x<ww;x++) {
buffer.fill( clamp((float)r[x][y]),clamp((float)g[x][y]),clamp((float)b[x][y]) );
buffer.rect(x,y,1,1);
}
}
colorMode(RGB);
println("done");
if(do_blend) {
print("Blending ("+blends_names[blend_id]+")...");
buffer.blend(img,0,0,img.width,img.height,0,0,buffer.width,buffer.height,blend_mode);
println("done");
}
buffer.endDraw();
if(equalize) {
print("Equalize...");
buffer.loadPixels();
equalize(buffer.pixels);
buffer.updatePixels();
println("done");
}
image(buffer,0,0,width,height);
println("Processed");
}
final static int MIN = 0;
final static int MAX = 1;
void equalize(color[] p) {
float[][] hist = new float[3][256];
int[][] look = new int[3][256];
for(int i=0;i<256;i++) {
hist[0][i] = 0;
hist[1][i] = 0;
hist[2][i] = 0;
}
float d = 1.0/p.length;
for(int i=0;i<p.length;i++) {
color c = p[i];
hist[0][ (c >> 16) & 0xff ] += d;
hist[1][ (c >> 8) & 0xff ] += d;
hist[2][ (c) & 0xff ] += d;
}
for(int c=0;c<3;c++) {
float sum = 0.0;
for(int i=0;i<256;i++) {
sum += hist[c][i];
look[c][i] = (int)constrain(floor(sum * 255),0,255);
}
}
int[][] minmax = new int[3][2];
minmax[0][MIN] = 256;
minmax[1][MIN] = 256;
minmax[2][MIN] = 256;
minmax[0][MAX] = -1;
minmax[1][MAX] = -1;
minmax[2][MAX] = -1;
for(int i=0;i<256;i++) {
int r = look[0][i];
int g = look[1][i];
int b = look[2][i];
if(r<minmax[0][MIN]) minmax[0][MIN]=r;
if(r>minmax[0][MAX]) minmax[0][MAX]=r;
if(g<minmax[1][MIN]) minmax[1][MIN]=g;
if(g>minmax[1][MAX]) minmax[1][MAX]=g;
if(b<minmax[2][MIN]) minmax[2][MIN]=b;
if(b>minmax[2][MAX]) minmax[2][MAX]=b;
}
for(int i=0;i<p.length;i++) {
color c = p[i];
int r = (int)map(look[0][ (c >> 16) & 0xff ],minmax[0][MIN],minmax[0][MAX],0,255);
int g = (int)map(look[1][ (c >> 8) & 0xff ],minmax[1][MIN],minmax[1][MAX],0,255);
int b = (int)map(look[2][ (c) & 0xff ],minmax[2][MIN],minmax[2][MAX],0,255);
int cres = 0xff000000 | (r << 16) | (g << 8) | b;
p[i] = cres;
}
}
final float clamp(float c) {
return(constrain(255*c,0,255));
}
void keyPressed() {
// SPACE to save
if(keyCode == 32) {
String fn = foldername + filename + "/res_" + sessionid + hex((int)random(0xffff),4)+"_"+filename+fileext;
buffer.save(fn);
println("Image "+ fn + " saved");
}
}
final static int[] blends = {ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN};
final static String[] blends_names = {"ADD", "SUBTRACT", "DARKEST", "LIGHTEST", "DIFFERENCE", "EXCLUSION", "MULTIPLY", "SCREEN", "OVERLAY", "HARD_LIGHT", "SOFT_LIGHT", "DODGE", "BURN"};
int blend_id;
class GMTrans {
public Transform trans;
public int compressor_type;
public Compressor comp;
public Wavelet wavelet;
public String wavelet_type;
public GMTrans(Wavelet w, String wavelettype) {
trans = TransformBuilder.create(wavelettype, w);
wavelet = w;
wavelet_type = wavelettype;
randomize();
}
public void setup() {
if(compressor_type == CompressorM) comp = new CompressorMagnitude(threshold);
if(compressor_type == CompressorPA) comp = new CompressorPeaksAverage(threshold/5000.0);
}
public void randomize() {
compressor_type = random(1)<0.5?CompressorGM:random(1)<0.5?CompressorM:CompressorPA;
threshold = random(5,50);
gm_sum = random(1)<0.7;
cond_type = (int)random(3);
cond_negate = random(1)<0.5;
low_x = (int)random(1,10);
low_y = (int)random(1,10);
high_x = random(0.05,0.5);
high_y = random(0.05,0.5);
if(random(1)<0.5) {
high_x /= 10.0;
high_y /= 10.0;
}
setup();
}
public void printSetup() {
println(" Wavelet: " + wavelet.getName());
println(" Type: " + wavelet_type);
println(" Compressor: " + (compressor_type==CompressorGM?"CompressorGM":compressor_type==CompressorM?"CompressorM":"CompressorPA"));
if(compressor_type == CompressorGM) {
println(" gm_sum: " + gm_sum);
println(" cond_type: " + (cond_type == AND?"AND":cond_type==OR?"OR":"XOR"));
println(" cond_negate: " + cond_negate);
println(" low_x: " + low_x);
println(" low_y: " + low_y);
println(" high_x: " + high_x);
println(" high_y: " + high_y);
} else {
println(" threshold: " + threshold);
}
}
float threshold;
boolean gm_sum;
int cond_type;
boolean cond_negate = true;
int low_x;
int low_y;
float high_x;
float high_y;
public double[][] process(double[][] input) {
if(compressor_type == CompressorGM)
return trans.reverse(processtrans(trans.forward(input)));
else
return trans.reverse(comp.compress(trans.forward(input)));
}
private final boolean between(int v, float a, float b) { return (v>=a) && (v<=b); }
private final boolean cond(boolean one, boolean two) {
boolean res;
switch (cond_type) {
case AND: res = (one && two);break;
case OR: res = (one || two);break;
default: res = (one ^ two);
}
if(cond_negate) return !res; else return res;
}
private double[][] processtrans(double[][] _r) {
int ww = _r.length;
int hh = _r[0].length;
double[][] rr = new double[ww][hh];
for(int y=0;y<hh;y+=2) {
for(int x=0;x<ww;x+=2) {
if( cond( between(x,low_x,high_x*ww),between(y,low_y,high_y*hh) ) ) {
rr[x][y] = 0.0;
rr[x+1][y+1] = 0.0;
rr[x][y+1] = 0.0;
rr[x+1][y] = 0.0;
} else {
if(gm_sum) {
rr[x][y] = _r[x][y]+(_r[x+1][y]+_r[x][y+1])/2.0;
rr[x+1][y+1] = _r[x+1][y+1] + (_r[x+1][y]+_r[x][y+1])/2.0;
rr[x+1][y] = 0;
rr[x][y+1] = 0;
} else {
rr[x][y] = _r[x][y];
rr[x+1][y+1] = _r[x+1][y+1];
rr[x+1][y] = _r[x+1][y];
rr[x][y+1] = _r[x][y+1];
}
}
}
}
return rr;
}
}