package fr.unistra.pelican.algorithms.conversion; import fr.unistra.pelican.*; /** * RGB to polar representation using the L1 norm for both brightness and saturation. * Using the "simplified" hue transformation. * * @author Erchan Abdullah, Régis Witz, Jonathan Weber */ public class RGBToLSH extends Algorithm { /** Input RGB image. */ public Image input; /** Output LSH image. */ public Image output; public boolean scaleToByte=false; /** Constructor. */ public RGBToLSH() { super(); super.inputs = "input"; super.options = "scaleToByte"; super.outputs = "output"; } /** @see fr.unistra.pelican.Algorithm#launch() */ public void launch() throws AlgorithmException { int size = this.input.size(); if ( this.input.getBDim() != 3 ) throw new AlgorithmException( "The input must be a tristumulus RGB image" ); this.output = this.input.newDoubleImage(); this.output.setMask( this.input.getMask() ); this.output.setColor(true); for(int i=0;i<size;i=i+3) { double R = this.input.getPixelDouble( i ); double G = this.input.getPixelDouble( i+1 ); double B = this.input.getPixelDouble( i+2 ); double[] lsh = convert( R,G,B ); this.output.setPixelDouble( i, lsh[0] ); this.output.setPixelDouble( i+1, lsh[1] ); this.output.setPixelDouble( i+2, lsh[2] ); } if (scaleToByte) output=scaleToByte(output); } /** Converts a triplet of RGB in [0,255] into LSH * @param r Red channel value. * @param g Green channel value. * @param b Blue channel value. * @return Array of { L,S,H } values. */ private static double[] convert( double r, double g, double b ) { double[] lsh = new double[3]; lsh[0] = lsh[1] = lsh[2] = 0.0; double max = 0.0, med = 0.0, min = 0.0; if( r >= g && r >= b ) { max = r; if ( g >= b ) { med = g; min = b; } else { med = b; min = g; } } else if( g >= r && g >= b ) { max = g; if( r >= b ) { med = r; min = b; } else { med = b; min = r; } } else if( b >= r && b >= g ) { max = b; if( r >= g ) { med = r; min = g; } else { med = g; min = r; } } // fi // luminance lsh[0] = ( max + med + min ) / 3.0; // saturation if( lsh[0] >= med ) lsh[1] = 1.5 * ( max - lsh[0] ); else lsh[1] = 1.5 * ( lsh[0] - min ); // hue double k = 1.0/6.0; int lambda = 0; if( r > g && g >= b ) lambda = 0; else if( g >= r && r > b ) lambda = 1; else if( g > b && b >= r ) lambda = 2; else if( b >= g && g > r ) lambda = 3; else if( b > r && r >= g ) lambda = 4; else if( r >= b && b > g ) lambda = 5; if ( lsh[1] > 0.0 ) lsh[2] = k * ( lambda + 0.5 - Math.pow(-1,lambda) * ( max+min-2*med ) / ( 2*lsh[1] ) ); else lsh[2] = 0; if( lsh[2] < 0 ) lsh[2] = 0; return lsh; } /** * Scales each band of the resulting LSH image according to the value * intervals L,S,H in [0,1] and returns a valid byteImage * * @return resulting ByteImage */ private static Image scaleToByte(Image lsh) { ByteImage bimg = lsh.newByteImage(); int size = bimg.size(); for(int i=0;i<size;i=i+3) { double d = lsh.getPixelDouble(i); // L bimg.setPixelByte(i, (int) Math.round(d * 255)); // S d = lsh.getPixelDouble(i+1); bimg.setPixelByte(i+1, (int) Math.round(d * 255)); // H d = lsh.getPixelDouble(i+2); bimg.setPixelByte(i+2, (int) Math.round(d * 255)); } return bimg; } /** Realizes the transformation of a tristumulus RGB image into a double valued LSH image * with pixels in the interval [0,1]. Thus it is adequate also for visualisation. * * @param input Tristumulus RGB image. * @return Double valued LSH image. */ public static Image exec( Image input ) { return ( Image ) new RGBToLSH().process(input); } /** Realizes the transformation of a tristumulus RGB image into a double valued LSH image * with pixels in the interval [0,1]. Thus it is adequate also for visualisation. * * @param input Tristumulus RGB image. * @param scaleToByte Scale result to byteImage * @return Double valued LSH image. */ public static Image exec( Image input, boolean scaleToByte ) { return ( Image ) new RGBToLSH().process(input,scaleToByte); } public static void main( String[] args ) { Image img = fr.unistra.pelican.algorithms.io.ImageLoader.exec( "samples/macaws.png" ); img = RGBToLSH.exec(img); img.color = false; fr.unistra.pelican.algorithms.visualisation.Viewer2D.exec(img); } }