// 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> 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(rminmax[0][MAX]) minmax[0][MAX]=r; if(gminmax[1][MAX]) minmax[1][MAX]=g; if(bminmax[2][MAX]) minmax[2][MAX]=b; } for(int i=0;i> 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