Skip to content

Commit

Permalink
Refactorized ImageTracer.java into smaller java files, and imported a…
Browse files Browse the repository at this point in the history
…n external algorithm to replace the existent Quantization algorithm.
  • Loading branch information
miguelemosreverte committed Jul 5, 2017
1 parent c906e01 commit ba545c9
Show file tree
Hide file tree
Showing 9 changed files with 1,489 additions and 812 deletions.
Binary file modified ImageTracer.jar
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added jankovicsandras/imagetracer/ImageTracer.class
Binary file not shown.
878 changes: 66 additions & 812 deletions jankovicsandras/imagetracer/ImageTracer.java

Large diffs are not rendered by default.

711 changes: 711 additions & 0 deletions jankovicsandras/imagetracer/Quantize.java

Large diffs are not rendered by default.

133 changes: 133 additions & 0 deletions jankovicsandras/imagetracer/SVGUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package jankovicsandras.imagetracer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Map.Entry;

import jankovicsandras.imagetracer.ImageTracer.IndexedImage;

public class SVGUtils {




////////////////////////////////////////////////////////////
//
// SVG Drawing functions
//
////////////////////////////////////////////////////////////

public static float roundtodec (float val, float places){
return (float)(Math.round(val*Math.pow(10,places))/Math.pow(10,places));
}


// Getting SVG path element string from a traced path
public static void svgpathstring (StringBuilder sb, String desc, ArrayList<Double[]> segments, String colorstr, HashMap<String,Float> options){
float scale = options.get("scale"), lcpr = options.get("lcpr"), qcpr = options.get("qcpr"), roundcoords = (float) Math.floor(options.get("roundcoords"));
// Path
sb.append("<path ").append(desc).append(colorstr).append("d=\"" ).append("M ").append(segments.get(0)[1]*scale).append(" ").append(segments.get(0)[2]*scale).append(" ");

if( roundcoords == -1 ){
for(int pcnt=0;pcnt<segments.size();pcnt++){
if(segments.get(pcnt)[0]==1.0){
sb.append("L ").append(segments.get(pcnt)[3]*scale).append(" ").append(segments.get(pcnt)[4]*scale).append(" ");
}else{
sb.append("Q ").append(segments.get(pcnt)[3]*scale).append(" ").append(segments.get(pcnt)[4]*scale).append(" ").append(segments.get(pcnt)[5]*scale).append(" ").append(segments.get(pcnt)[6]*scale).append(" ");
}
}
}else{
for(int pcnt=0;pcnt<segments.size();pcnt++){
if(segments.get(pcnt)[0]==1.0){
sb.append("L ").append(roundtodec((float)(segments.get(pcnt)[3]*scale),roundcoords)).append(" ")
.append(roundtodec((float)(segments.get(pcnt)[4]*scale),roundcoords)).append(" ");
}else{
sb.append("Q ").append(roundtodec((float)(segments.get(pcnt)[3]*scale),roundcoords)).append(" ")
.append(roundtodec((float)(segments.get(pcnt)[4]*scale),roundcoords)).append(" ")
.append(roundtodec((float)(segments.get(pcnt)[5]*scale),roundcoords)).append(" ")
.append(roundtodec((float)(segments.get(pcnt)[6]*scale),roundcoords)).append(" ");
}
}
}// End of roundcoords check

sb.append("Z\" />");

// Rendering control points
for(int pcnt=0;pcnt<segments.size();pcnt++){
if((lcpr>0)&&(segments.get(pcnt)[0]==1.0)){
sb.append( "<circle cx=\"").append(segments.get(pcnt)[3]*scale).append("\" cy=\"").append(segments.get(pcnt)[4]*scale).append("\" r=\"").append(lcpr).append("\" fill=\"white\" stroke-width=\"").append(lcpr*0.2).append("\" stroke=\"black\" />");
}
if((qcpr>0)&&(segments.get(pcnt)[0]==2.0)){
sb.append( "<circle cx=\"").append(segments.get(pcnt)[3]*scale).append("\" cy=\"").append(segments.get(pcnt)[4]*scale).append("\" r=\"").append(qcpr).append("\" fill=\"cyan\" stroke-width=\"").append(qcpr*0.2).append("\" stroke=\"black\" />");
sb.append( "<circle cx=\"").append(segments.get(pcnt)[5]*scale).append("\" cy=\"").append(segments.get(pcnt)[6]*scale).append("\" r=\"").append(qcpr).append("\" fill=\"white\" stroke-width=\"").append(qcpr*0.2).append("\" stroke=\"black\" />");
sb.append( "<line x1=\"").append(segments.get(pcnt)[1]*scale).append("\" y1=\"").append(segments.get(pcnt)[2]*scale).append("\" x2=\"").append(segments.get(pcnt)[3]*scale).append("\" y2=\"").append(segments.get(pcnt)[4]*scale).append("\" stroke-width=\"").append(qcpr*0.2).append("\" stroke=\"cyan\" />");
sb.append( "<line x1=\"").append(segments.get(pcnt)[3]*scale).append("\" y1=\"").append(segments.get(pcnt)[4]*scale).append("\" x2=\"").append(segments.get(pcnt)[5]*scale).append("\" y2=\"").append(segments.get(pcnt)[6]*scale).append("\" stroke-width=\"").append(qcpr*0.2).append("\" stroke=\"cyan\" />");
}// End of quadratic control points
}

}// End of svgpathstring()






// Converting tracedata to an SVG string, paths are drawn according to a Z-index
// the optional lcpr and qcpr are linear and quadratic control point radiuses
public static String getsvgstring (IndexedImage ii, HashMap<String,Float> options){
// SVG start
int w = (int) (ii.width * options.get("scale")), h = (int) (ii.height * options.get("scale"));
String viewboxorviewport = options.get("viewbox")!=0 ? "viewBox=\"0 0 "+w+" "+h+"\" " : "width=\""+w+"\" height=\""+h+"\" ";
StringBuilder svgstr = new StringBuilder("<svg "+viewboxorviewport+"version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" ");
if(options.get("desc")!=0){ svgstr.append("desc=\"Created with ImageTracer.java version "+ImageTracer.versionnumber+"\" "); }
svgstr.append(">");

// creating Z-index
TreeMap <Double,Integer[]> zindex = new TreeMap <Double,Integer[]>();
double label;
// Layer loop
for(int k=0; k<ii.layers.size(); k++) {

// Path loop
for(int pcnt=0; pcnt<ii.layers.get(k).size(); pcnt++){

// Label (Z-index key) is the startpoint of the path, linearized
label = (ii.layers.get(k).get(pcnt).get(0)[2] * w) + ii.layers.get(k).get(pcnt).get(0)[1];
// Creating new list if required
if(!zindex.containsKey(label)){ zindex.put(label,new Integer[2]); }
// Adding layer and path number to list
zindex.get(label)[0] = new Integer(k);
zindex.get(label)[1] = new Integer(pcnt);
}// End of path loop

}// End of layer loop

// Sorting Z-index is not required, TreeMap is sorted automatically

// Drawing
// Z-index loop
String thisdesc = "";
for(Entry<Double, Integer[]> entry : zindex.entrySet()) {
if(options.get("desc")!=0){ thisdesc = "desc=\"l "+entry.getValue()[0]+" p "+entry.getValue()[1]+"\" "; }else{ thisdesc = ""; }
svgpathstring(svgstr,
thisdesc,
ii.layers.get(entry.getValue()[0]).get(entry.getValue()[1]),
tosvgcolorstr(ii.palette[entry.getValue()[0]]),
options);
}

// SVG End
svgstr.append("</svg>");

return svgstr.toString();

}// End of getsvgstring()


static String tosvgcolorstr (byte[] c){
return "fill=\"rgb("+(c[0]+128)+","+(c[1]+128)+","+(c[2]+128)+")\" stroke=\"rgb("+(c[0]+128)+","+(c[1]+128)+","+(c[2]+128)+")\" stroke-width=\"1\" opacity=\""+((c[3]+128)/255.0)+"\" ";
}


}
102 changes: 102 additions & 0 deletions jankovicsandras/imagetracer/SelectiveBlur.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package jankovicsandras.imagetracer;

import jankovicsandras.imagetracer.ImageTracer.ImageData;

public class SelectiveBlur {


// Gaussian kernels for blur
static double[][] gks = { {0.27901,0.44198,0.27901}, {0.135336,0.228569,0.272192,0.228569,0.135336}, {0.086776,0.136394,0.178908,0.195843,0.178908,0.136394,0.086776},
{0.063327,0.093095,0.122589,0.144599,0.152781,0.144599,0.122589,0.093095,0.063327}, {0.049692,0.069304,0.089767,0.107988,0.120651,0.125194,0.120651,0.107988,0.089767,0.069304,0.049692} };


// Selective Gaussian blur for preprocessing
static ImageData blur (ImageData imgd, float rad, float del){
int i,j,k,d,idx;
double racc,gacc,bacc,aacc,wacc;
ImageData imgd2 = new ImageData(imgd.width,imgd.height,new byte[imgd.width*imgd.height*4]);

// radius and delta limits, this kernel
int radius = (int)Math.floor(rad); if(radius<1){ return imgd; } if(radius>5){ radius = 5; }
int delta = (int)Math.abs(del); if(delta>1024){ delta = 1024; }
double[] thisgk = gks[radius-1];

// loop through all pixels, horizontal blur
for( j=0; j < imgd.height; j++ ){
for( i=0; i < imgd.width; i++ ){

racc = 0; gacc = 0; bacc = 0; aacc = 0; wacc = 0;
// gauss kernel loop
for( k = -radius; k < (radius+1); k++){
// add weighted color values
if( ((i+k) > 0) && ((i+k) < imgd.width) ){
idx = ((j*imgd.width)+i+k)*4;
racc += imgd.data[idx ] * thisgk[k+radius];
gacc += imgd.data[idx+1] * thisgk[k+radius];
bacc += imgd.data[idx+2] * thisgk[k+radius];
aacc += imgd.data[idx+3] * thisgk[k+radius];
wacc += thisgk[k+radius];
}
}
// The new pixel
idx = ((j*imgd.width)+i)*4;
imgd2.data[idx ] = (byte) Math.floor(racc / wacc);
imgd2.data[idx+1] = (byte) Math.floor(gacc / wacc);
imgd2.data[idx+2] = (byte) Math.floor(bacc / wacc);
imgd2.data[idx+3] = (byte) Math.floor(aacc / wacc);

}// End of width loop
}// End of horizontal blur

// copying the half blurred imgd2
byte[] himgd = imgd2.data.clone();

// loop through all pixels, vertical blur
for( j=0; j < imgd.height; j++ ){
for( i=0; i < imgd.width; i++ ){

racc = 0; gacc = 0; bacc = 0; aacc = 0; wacc = 0;
// gauss kernel loop
for( k = -radius; k < (radius+1); k++){
// add weighted color values
if( ((j+k) > 0) && ((j+k) < imgd.height) ){
idx = (((j+k)*imgd.width)+i)*4;
racc += himgd[idx ] * thisgk[k+radius];
gacc += himgd[idx+1] * thisgk[k+radius];
bacc += himgd[idx+2] * thisgk[k+radius];
aacc += himgd[idx+3] * thisgk[k+radius];
wacc += thisgk[k+radius];
}
}
// The new pixel
idx = ((j*imgd.width)+i)*4;
imgd2.data[idx ] = (byte) Math.floor(racc / wacc);
imgd2.data[idx+1] = (byte) Math.floor(gacc / wacc);
imgd2.data[idx+2] = (byte) Math.floor(bacc / wacc);
imgd2.data[idx+3] = (byte) Math.floor(aacc / wacc);

}// End of width loop
}// End of vertical blur

// Selective blur: loop through all pixels
for( j=0; j < imgd.height; j++ ){
for( i=0; i < imgd.width; i++ ){

idx = ((j*imgd.width)+i)*4;
// d is the difference between the blurred and the original pixel
d = Math.abs(imgd2.data[idx ] - imgd.data[idx ]) + Math.abs(imgd2.data[idx+1] - imgd.data[idx+1]) +
Math.abs(imgd2.data[idx+2] - imgd.data[idx+2]) + Math.abs(imgd2.data[idx+3] - imgd.data[idx+3]);
// selective blur: if d>delta, put the original pixel back
if(d>delta){
imgd2.data[idx ] = imgd.data[idx ];
imgd2.data[idx+1] = imgd.data[idx+1];
imgd2.data[idx+2] = imgd.data[idx+2];
imgd2.data[idx+3] = imgd.data[idx+3];
}
}
}// End of Selective blur

return imgd2;

}// End of blur()
}
Loading

0 comments on commit ba545c9

Please sign in to comment.