package fr.unistra.pelican.util.vectorial.orders; import java.util.Arrays; import java.util.Comparator; import fr.unistra.pelican.ByteImage; import fr.unistra.pelican.util.Tools; /** * This class represents a lexicographical ordering scheme for double valued pixels in the [0,1] interval. * The input pixels are considered in the HSL order.. * * The priority of the components is customizable either by modifying their order or by division * with an argument alpha, aiming to decrease the dynamic range of the component. * * Interesting new version...that works correctly... * each group of equality (obtained after dividing and rounding off with alpha) is further subdivided * according to the function in the form of a V. * * For now, only the first component can be "softened" with division (H is not yet supported for softening). * * @author E.A. * */ public class ModelBasedHSLAlphaLexicographicalOrdering3 implements VectorialOrdering,Comparator { private double alpha; private double beta; private int order; private double refHue; private double slope; private double offset; private double[] transformed; // buna ihtiyacimiz var cunku bir onceki // piksel degerinin kac oldugunu da bilmek zorundayiz. // tabi bunu yapabilmek icin de 256 farkli aydinlik duzeyi oldugunu // varsayiyorum... private double[] transformed2; // bu da doygunluk icin olsun ayni bicimde public static final int HLS = 0; public static final int HSL = 1; public static final int LSH = 2; public static final int LHS = 3; public static final int SHL = 4; public static final int SLH = 5; public static int syc = 0; /** * Alpha is to be chosen as if the pixels values were in [0,255] * * @param alpha the rounding parameter * @param order the prioritization order of the channels * @param refHue the hue origin */ public ModelBasedHSLAlphaLexicographicalOrdering3(double alpha,double beta,int order,double refHue,double offset,double slope) { if(alpha > 1.0) this.alpha = alpha; else this.alpha = 1.0; if(beta > 1.0) this.beta = beta; else this.beta = 1.0; this.offset = offset; this.slope = slope; // integer value!!! alpha = Math.round(alpha); this.order = order; this.refHue = refHue; // construct the transformed arrays transformed = new double[256]; transformed2 = new double[256]; // first apply an alpha subquantization.. // all values are now \in [0, ceilof 256/alpha] for(int i = 0; i < 256; i++){ transformed[i] = transformed2[i] = Math.ceil(i/alpha); } // now for every group of equality // compute their average frequency for(int i = 1; i < 256; i++){ double avg = 0.0; int syc = 0; for(int k = i; (k < 256 && k < i + alpha); k++){ avg += i / 255.0; syc++; } avg = avg / syc; // elimizde bu takimin ortalamasi var.. // simdi de sigma dan gecirelim double sigma = sigma(avg); // her esitlik takiminin genisligi alpha dir. int width = (int)alpha; int birimSys = (int)Math.round(sigma * width); // orta aydinlik icin...buyuk adimlar //System.err.println(birimSys + " birim"); if(birimSys <= 1){ for(int k = i; (k < 256 && k < i + alpha); k++) transformed[k] = k/alpha; }else if(birimSys == width){ for(int k = i; (k < 256 && k < i + alpha); k++) transformed[k] = Math.ceil(i/alpha); }else{ // aradaki durumlardan soz konusu...evet bakalim. int adimSys = (int)Math.ceil(width/birimSys); for(int m = 0; m < adimSys; m++){ // her adim icin for(int k = i + birimSys * m; (k < 256 && k < i + birimSys * (m+1)); k++) transformed[k] = (i + birimSys * (m+1) - 1)/alpha; } } i += alpha - 1; } // aynen doygunluk icin for(int i = 1; i < 256; i++){ double avg = 0.0; int syc = 0; for(int k = i; (k < 256 && k < i + beta); k++){ avg += i / 255.0; syc++; } avg = avg / syc; // elimizde bu takimin ortalamasi var.. // simdi de sigma dan gecirelim double sigma = sigma2(avg); // her esitlik takiminin genisligi alpha dir. int width = (int)beta; int birimSys = (int)Math.round(sigma * width); // kucuk doyungluk icin buyuk adimlar //System.err.println(birimSys + " birim"); if(birimSys <= 1){ for(int k = i; (k < 256 && k < i + beta); k++) transformed2[k] = k/beta; }else if(birimSys == width){ for(int k = i; (k < 256 && k < i + alpha); k++) transformed2[k] = Math.ceil(i/beta); }else{ // aradaki durumlardan soz konusu...evet bakalim. int adimSys = (int)Math.ceil(width/birimSys); for(int m = 0; m < adimSys; m++){ // her adim icin for(int k = i + birimSys * m; (k < 256 && k < i + birimSys * (m+1)); k++) transformed2[k] = (i + birimSys * (m+1) - 1)/beta; } } i += beta - 1; } } double sigma(double x) { if(x <= 0.5) return 2/(1 + Math.exp(-10 * (x-0.5))); else return 2/(1 + Math.exp(10 * (x-0.5))); } double sigma2(double x) { return 1/(1 + Math.exp(slope * (x-offset))); } /* * (non-Javadoc) * @see fr.unistra.pelican.util.vectorial.orders.VectorialOrdering#max(double[][]) */ public double[] max(double[][] p) { double[] max = p[0]; for(int i = 1; i < p.length; i++){ if(this.compare(max,p[i]) < 0) max = p[i]; } return max; } /* * (non-Javadoc) * @see fr.unistra.pelican.util.vectorial.orders.VectorialOrdering#min(double[][]) */ public double[] min(double[][] p) { double[] min = p[0]; for(int i = 1; i < p.length; i++){ if(this.compare(min,p[i]) > 0) min = p[i]; } return min; } /* * (non-Javadoc) * @see fr.unistra.pelican.util.vectorial.orders.VectorialOrdering#rank(double[][], int) */ public double[] rank(double[][] p,int r) { Arrays.sort(p,this); return p[r]; } /** * Compares the given arguments according to this ordering * * @param o1 first double valued array or vector pixel * @param o2 second double valued array or vector pixel * @return 1,-1 or 0 if o1 is respectively superior, inferior or equal to o2 */ public int compare(Object o1,Object o2) { double[] p1 = null,p2 = null; try{ if(o1.getClass().getName().equals("[D")){ p1 = (double[])o1; p2 = (double[])o2; }else throw new ClassCastException(); }catch(ClassCastException ex){ ex.printStackTrace(); } int tmp1,tmp2; double dtmp1,dtmp2; switch(order){ case LSH: // L rounded.. if(alpha > 1.0){ dtmp1 = Math.round(ByteImage.doubleToByte * p1[2]); dtmp1 = transformed[(int)dtmp1]; dtmp2 = Math.round(ByteImage.doubleToByte * p2[2]); dtmp2 = transformed[(int)dtmp2]; if(Tools.doubleCompare(dtmp1,dtmp2) < 0) return -1; else if(Tools.doubleCompare(dtmp1,dtmp2) > 0) return 1; }else{ if(Tools.doubleCompare(p1[2],p2[2]) < 0) return -1; else if(Tools.doubleCompare(p1[2],p2[2]) > 0) return 1; } // S rounded if(beta > 1.0){ dtmp1 = Math.round(ByteImage.doubleToByte * p1[1]); dtmp1 = transformed[(int)dtmp1]; dtmp2 = Math.round(ByteImage.doubleToByte * p2[1]); dtmp2 = transformed[(int)dtmp2]; if(Tools.doubleCompare(dtmp1,dtmp2) < 0) return -1; else if(Tools.doubleCompare(dtmp1,dtmp2) > 0) return 1; }else{ if(Tools.doubleCompare(p1[1],p2[1]) < 0) return -1; else if(Tools.doubleCompare(p1[1],p2[1]) > 0) return 1; } // saturation and luminance appear to be equal.. // check the real luminance values before proceeding to hue if(Tools.doubleCompare(p1[2],p2[2]) < 0) return -1; else if(Tools.doubleCompare(p1[2],p2[2]) > 0) return 1; // ok...now its hue's game dtmp1 = Math.abs(refHue - p1[0]); dtmp2 = Math.abs(refHue - p2[0]); if(Tools.doubleCompare(dtmp1,0.5) > 0) dtmp1 = 1.0 - dtmp1; if(Tools.doubleCompare(dtmp2,0.5) > 0) dtmp2 = 1.0 - dtmp2; dtmp1 = dtmp2 * 1 / (1 + Math.exp(-5 * (p1[1] - 0.5))); dtmp2 = dtmp2 * 1 / (1 + Math.exp(-5 * (p2[1] - 0.5))); // reverse the order direction so the "closer" to the reference is "bigger".. if(Tools.doubleCompare(dtmp1,dtmp2) < 0) return 1; else if(Tools.doubleCompare(dtmp1,dtmp2) > 0) return -1; // apparently hues are also equal...then once more to real saturation values // before proclaiming equality if(Tools.doubleCompare(p1[1],p2[1]) < 0) return -1; else if(Tools.doubleCompare(p1[1],p2[1]) > 0) return 1; return 0; case SLH: // S rounded.. if(alpha > 1.0){ tmp1 = (int)Math.ceil(p1[1] * 255.0 / alpha); tmp2 = (int)Math.ceil(p2[1] * 255.0 / alpha); if(tmp1 < tmp2) return -1; else if(tmp1 > tmp2) return 1; }else{ if(p1[1] < p2[1]) return -1; else if(p1[1] > p2[1]) return 1; } // L natural ordering if(p1[2] < p2[2]) return -1; else if(p1[2] > p2[1]) return 1; dtmp1 = Math.abs(refHue - p1[0]); dtmp2 = Math.abs(refHue - p2[0]); if(dtmp1 > 0.5) dtmp1 = 1.0 - dtmp1; if(dtmp2 > 0.5) dtmp2 = 1.0 - dtmp2; // reverse the order direction so the "closer" to the reference is "bigger".. if(dtmp1 < dtmp2) return 1; else if(dtmp1 > dtmp2) return -1; return 0; case LHS: // L rounded.. if(alpha > 1.0){ dtmp1 = Math.round(ByteImage.doubleToByte * p1[2]); dtmp1 = transformed[(int)dtmp1]; dtmp2 = Math.round(ByteImage.doubleToByte * p2[2]); dtmp2 = transformed[(int)dtmp2]; if(Tools.doubleCompare(dtmp1,dtmp2) < 0) return -1; else if(Tools.doubleCompare(dtmp1,dtmp2) > 0) return 1; }else{ if(Tools.doubleCompare(p1[2],p2[2]) < 0) return -1; else if(Tools.doubleCompare(p1[2],p2[2]) > 0) return 1; } // ok...now its hue's game dtmp1 = Math.abs(refHue - p1[0]); dtmp2 = Math.abs(refHue - p2[0]); if(Tools.doubleCompare(dtmp1,0.5) > 0) dtmp1 = 1.0 - dtmp1; if(Tools.doubleCompare(dtmp2,0.5) > 0) dtmp2 = 1.0 - dtmp2; dtmp1 = dtmp2 * 1 / (1 + Math.exp(-5 * (p1[1] - 0.5))); dtmp2 = dtmp2 * 1 / (1 + Math.exp(-5 * (p2[1] - 0.5))); // reverse the order direction so the "closer" to the reference is "bigger".. if(Tools.doubleCompare(dtmp1,dtmp2) < 0) return 1; else if(Tools.doubleCompare(dtmp1,dtmp2) > 0) return -1; // hue and luminance appear to be equal.. // check the real luminance values before proceeding to saturation if(Tools.doubleCompare(p1[2],p2[2]) < 0) return -1; else if(Tools.doubleCompare(p1[2],p2[2]) > 0) return 1; // S rounded if(beta > 1.0){ dtmp1 = Math.round(ByteImage.doubleToByte * p1[1]); dtmp1 = transformed[(int)dtmp1]; dtmp2 = Math.round(ByteImage.doubleToByte * p2[1]); dtmp2 = transformed[(int)dtmp2]; if(Tools.doubleCompare(dtmp1,dtmp2) < 0) return -1; else if(Tools.doubleCompare(dtmp1,dtmp2) > 0) return 1; }else{ if(Tools.doubleCompare(p1[1],p2[1]) < 0) return -1; else if(Tools.doubleCompare(p1[1],p2[1]) > 0) return 1; } return 0; case SHL: // S rounded.. if(alpha > 1.0){ tmp1 = (int)Math.ceil(p1[1] * 255.0 / alpha); tmp2 = (int)Math.ceil(p2[1] * 255.0 / alpha); if(tmp1 < tmp2) return -1; else if(tmp1 > tmp2) return 1; }else{ if(p1[1] < p2[1]) return -1; else if(p1[1] > p2[1]) return 1; } dtmp1 = Math.abs(refHue - p1[0]); dtmp2 = Math.abs(refHue - p2[0]); if(dtmp1 > 0.5) dtmp1 = 1.0 - dtmp1; if(dtmp2 > 0.5) dtmp2 = 1.0 - dtmp2; // reverse the order direction so the "closer" to the reference is "bigger".. if(dtmp1 < dtmp2) return 1; else if(dtmp1 > dtmp2) return -1; // L natural ordering if(p1[2] < p2[2]) return -1; else if(p1[2] > p2[2]) return 1; return 0; case HSL: dtmp1 = Math.abs(refHue - p1[0]); dtmp2 = Math.abs(refHue - p2[0]); if(dtmp1 > 0.5) dtmp1 = 1.0 - dtmp1; if(dtmp2 > 0.5) dtmp2 = 1.0 - dtmp2; // reverse the order direction so the "closer" to the reference is "bigger".. if(dtmp1 < dtmp2) return 1; else if(dtmp1 > dtmp2) return -1; // S natural ordering if(p1[1] < p2[1]) return -1; else if(p1[1] > p2[1]) return 1; // L natural ordering if(p1[2] < p2[2]) return -1; else if(p1[2] > p2[2]) return 1; return 0; case HLS: dtmp1 = Math.abs(refHue - p1[0]); dtmp2 = Math.abs(refHue - p2[0]); if(dtmp1 > 0.5) dtmp1 = 1.0 - dtmp1; if(dtmp2 > 0.5) dtmp2 = 1.0 - dtmp2; // reverse the order direction so the "closer" to the reference is "bigger".. if(dtmp1 < dtmp2) return 1; else if(dtmp1 > dtmp2) return -1; // L natural ordering if(p1[2] < p2[2]) return -1; else if(p1[2] > p2[2]) return 1; // S natural ordering if(p1[1] < p2[1]) return -1; else if(p1[1] > p2[1]) return 1; return 0; default: System.err.println("not available"); return 0; } } /* * (non-Javadoc) * @see fr.unistra.pelican.util.vectorial.orders.VectorialOrdering#max(double[], double[]) */ public double[] max(double[] p,double[] r) { if(compare(p,r) == 1) return p; else return r; } /* * (non-Javadoc) * @see fr.unistra.pelican.util.vectorial.orders.VectorialOrdering#min(double[], double[]) */ public double[] min(double[] p,double[] r) { if(compare(p,r) == 1) return r; else return p; } }