package fr.unistra.pelican.algorithms.descriptors.texture; import fr.unistra.pelican.*; import fr.unistra.pelican.algorithms.conversion.RGBToGray; import fr.unistra.pelican.util.Pixel; import fr.unistra.pelican.util.Tools; import fr.unistra.pelican.util.data.DoubleArrayData; /** * Difference between pixels of scan pattern (DBPSP). * * Chuen-Horng Lina, Rong-Tai Chena, and Yung-Kuan Chanb, * "A smart content-based image retrieval system based on color and texture feature" * * ( Get it there : <url>http://dx.doi.org/10.1016/j.imavis.2008.07.004</url> [part 2.2] ) * * @author Régis Witz */ public class DifferenceScanPattern extends Descriptor { /** Input image. */ public Image input; /** Output feature. */ public DoubleArrayData output; /** Algorithm specifications. */ public DifferenceScanPattern() { super(); super.inputs = "input"; super.outputs = "output"; } /** Convolution masks names. */ private char[] cmasks = { 'c','d','e','f' }; /** . */ private Double[] values; @SuppressWarnings("unchecked") @Override /** @see Algorithm */ public void launch() throws AlgorithmException { if ( this.input.getBDim() > 1 ) this.input = RGBToGray.exec( this.input ); this.values = new Double[6]; for ( int i = 0 ; i < 6 ; i++ ) this.values[i] = new Double( 0. ); for( Pixel p : this.input ) for ( Character c : this.cmasks ) this.scan( p,c ); // Is this the right way to normalize ? I seems so, but.. this.values = Tools.vectorNormalize( this.values ); this.output = new DoubleArrayData(); this.output.setDescriptor( ( Class ) DifferenceScanPattern.class ); this.output.setValues( this.values ); } private void scan( Pixel p, char cmask ) { double p1,p2,p3,p4; switch ( cmask ) { // doing : pi = this.input.getPixelXYZTDouble( p.x+dx,p.y+dx,p.z,p.t ); // can throw an ArrayIndexOutOfBoundsException when pi is a "beyond borders" pixel. // so we take care of border pixels by setting them to the same values as // their nearest "inside image" 4-neighbour. case 'c': // upper left convolution mask p4 = this.input.getPixelXYZTDouble( p.x,p.y,p.z,p.t ); try { p3 = this.input.getPixelXYZTDouble( p.x-1,p.y,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p3 = p4; } try { p1 = this.input.getPixelXYZTDouble( p.x-1,p.y-1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p1 = p3; } try { p2 = this.input.getPixelXYZTDouble( p.x,p.y-1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p2 = p4; } break; case 'd': // upper right convolution mask p3 = this.input.getPixelXYZTDouble( p.x,p.y,p.z,p.t ); try { p4 = this.input.getPixelXYZTDouble( p.x+1,p.y,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p4 = p3; } try { p1 = this.input.getPixelXYZTDouble( p.x,p.y-1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p1 = p3; } try { p2 = this.input.getPixelXYZTDouble( p.x+1,p.y-1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p2 = p4; } break; case 'e': // bottom left convolution mask p2 = this.input.getPixelXYZTDouble( p.x,p.y,p.z,p.t ); try { p1 = this.input.getPixelXYZTDouble( p.x-1,p.y,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p1 = p2; } try { p3 = this.input.getPixelXYZTDouble( p.x-1,p.y+1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p3 = p1; } try { p4 = this.input.getPixelXYZTDouble( p.x,p.y+1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p4 = p2; } break; case 'f': // bottom right convolution mask p1 = this.input.getPixelXYZTDouble( p.x,p.y,p.z,p.t ); try { p2 = this.input.getPixelXYZTDouble( p.x+1,p.y,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p2 = p1; } try { p3 = this.input.getPixelXYZTDouble( p.x,p.y+1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p3 = p1; } try { p4 = this.input.getPixelXYZTDouble( p.x+1,p.y+1,p.z,p.t ); } catch ( ArrayIndexOutOfBoundsException ex ) { p4 = p2; } break; default : p1 = p2 = p3 = p4 = 0; } for ( int i = 1 ; i <= 6 ; i++ ) this.values[i-1] += this.delta( i, p1,p2,p3,p4 ); } /** Records the pixel value differences among all scan directions within motifs of scan pattern. * @param i Scan pattern id. * @param p1 Upper left pixel. * @param p2 Upper right pixel. * @param p3 Bottom left pixel. * @param p4 Bottom right pixel. * @return Total pixel value difference of any coordinates (x, y) within the image. */ private double delta( int i, double p1, double p2, double p3, double p4 ) { switch ( i ) { case 1 : return Math.abs( p1-p2 ) + Math.abs( p2-p3 ) + Math.abs( p3-p4 ); case 2 : return Math.abs( p1-p3 ) + Math.abs( p3-p2 ) + Math.abs( p2-p4 ); case 3 : return Math.abs( p1-p3 ) + Math.abs( p3-p4 ) + Math.abs( p4-p2 ); case 4 : return Math.abs( p1-p2 ) + Math.abs( p2-p4 ) + Math.abs( p4-p3 ); case 5 : return Math.abs( p1-p4 ) + Math.abs( p4-p3 ) + Math.abs( p3-p2 ); case 6 : return Math.abs( p1-p4 ) + Math.abs( p4-p2 ) + Math.abs( p2-p3 ); default : return -1.; } } ///////////////// // EXEC METHOD // ///////////////// public static DoubleArrayData exec( Image input ) { return ( DoubleArrayData ) new DifferenceScanPattern().process( input ); } }