package patternRecognition.textures.glzm.glszm;
import imageTiTi.reducer.ColorReducer;
import java.awt.image.BufferedImage;
import arrayTiTi.ArrayNew;
import arrayTiTi.ArrayOperations;
import morphee.levelings.Leveling;
import patternRecognition.ComputableFeatures;
/**
*
Description: This class fill the Multiple Gray Level Size Zone Matrix (MGLSZM) and its features.
* List of features (same than Run Length Matrix). They are moments (orders -2 a 2) and variances.
* - F0 => Small Zone Emphasis, SZE.
* - F1 => Large Zone Emphasis, LZE.
* - F2 => Low Gray level Zone Emphasis, LGZE.
* - F3 => High Gray level Zone Emphasis, HGZE.
* - F4 => Small Zone Low Gray level Emphasis, SZLGE.
* - F5 => Small Zone High Gray level Emphasis, SZHGE.
* - F6 => Large Zone Low Gray level Emphasis, LZLGE.
* - F7 => Large Zone High Gray Level Emphasis, LZHGE.
* - F8 => Gray Level Non Uniformity, GLNU.
* - F9 => Size Zone Non Uniformity, SZNU.
* - F10 => Zone Percentage, ZPC.
* - F11 => Barycentre sur les niveaux de gris.
* - F12 => Barycentre sur les tailles de zones.
* - F13 => Variance ponderee sur les niveaux de gris.
* - F14 => Variance ponderee sur tailles des zones.
* - F15 => Orientation.
*
* Package(s) required: imageTiTi, morphee, utils.
* Copyright: Copyright (c) 2006-Today.
* Updates:
* June 24 2010 => Creation.
*
*
*
* BibTeX:
*
* @conference{ThibaultICIP11,
* address = {Brussel, Belgium},
* author = {Guillaume Thibault and Jesus Angulo and Fernand Meyer},
* booktitle = {IEEE International Conference on Image Processing (ICIP)},
* pages = {53--56},
* title = {Advanced Statistical Matrices for Texture Characterization:
* Application to DNA Chromatin and Microtubule Network Classification},
* year = {2011}
* }
*
*
*
*
* @author Guillaume THIBAULT
* @version 1.0
*/
public class MGlszm implements ComputableFeatures
{
/** La classe qui calcule la matrice de tailles de zones.*/
protected GrayLevelSizeZoneMatrix glszm = new GrayLevelSizeZoneMatrix() ;
/** Nombre de niveaux de gris de l'image.*/
protected int GrayLevelMax = -1 ;
/** Nombre de tailles de zones a prendre en compte. Si FixedSize=true ce sera la largeur de la matrice, sinon c'est le pas.*/
protected int nbSizes = -1 ;
/** Largeur de la matrice. Si FixedSize Alors Width=nbSizes, Sinon Width=TailleZoneMax/nbSizes+1.*/
protected int Width = -1 ;
/** La largeur doit elle etre fixe ? */
protected boolean FixedSize = false ;
/** Tableau representant la size zone matrix.*/
protected double[][] matrix = null ;
/** Tableau de coefficients qui permet de ponderer les differents niveaux de gris pour le calcul de la matrice finale.*/
protected double[] Coefficients = null ;
/** The array which contains features.*/
private double[] Features = new double[16] ;
/** The sum of all elements in the matrix.*/
private double Sum = 0.0 ;
/** Features names.*/
private String[] FeaturesNames = new String[]{"SZE", "LZE", "LGZE", "HGZE", "SZLGE", "SZHGE", "LZLGE", "LZHGE",
"GLNU", "SZNU", "ZPC", "BARYGL", "BARYS", "VARGL", "VARS", "ORIE"} ;
/** The color reducer to use.*/
protected ColorReducer reducer = null ;
/** The leveling to use.*/
protected Leveling leveling = null ;
/** The forbidden value during processing.*/
protected int ForbiddenValue = -1 ;
/** Is the 8-connexty used?*/
protected boolean EightConnex = true ;
/** The number of thread to use.*/
protected int nbCPU = 1 ;
public MGlszm()
{
}
/** This method realizes the processing. Do not call the method "FillMatrix" (useless because it is automatically called).
* @param image The image to characterize.
* @param nbSizes Number of size of zones. If FixedSize=true it will be the matrix width, else it will be the step.
* @param FixedSize Is the width fixed?
* @param reducer This class reduce the number of gray level.
* @param leveling The leveling to use before the color reduction. If null, any leveling realized.
* @param ForbiddenValue The backgorund value or value to not take into account during processing. If negative, so the entire
* texture is processed.
* @param EightConnex Use 8-connexity for labeling?
* @param nbCPU Number of thread to use for processing.*/
public void Compute(BufferedImage image, int nbSizes, boolean FixedSize, ColorReducer reducer,
Leveling leveling, int ForbiddenValue, boolean EightConnex, int nbCPU)
{
int i, x, y, n, nb, width, step, nbCoefs ;
double[][] m = null ;
switch ( image.getType() )
{
case BufferedImage.TYPE_BYTE_GRAY : GrayLevelMax = 256 ;
if ( Coefficients == null || Coefficients.length != 8 ) AllocCoefficients(8) ;
nbCoefs = Coefficients.length ;
if ( nbCoefs != 8 ) throw new Error("Wrong number of coefficients (8 waited for BYTE_GRAY images): " + nbCoefs) ;
break ;
case BufferedImage.TYPE_USHORT_GRAY : GrayLevelMax = 65536 ;
if ( Coefficients == null || Coefficients.length != 16 ) AllocCoefficients(16) ;
nbCoefs = Coefficients.length ;
if ( nbCoefs != 16 ) throw new Error("Wrong number of coefficients (16 waited for USHORT_GRAY images): "+nbCoefs) ;
break ;
default : throw new IllegalArgumentException("Only gray level images supported.") ;
}
matrix = null ;
this.nbSizes = nbSizes ;
this.FixedSize = FixedSize ;
for (n=2, nb=0 ; n <= GrayLevelMax ; n*=2, nb++)
{
glszm.FillMatrix(image, n, nbSizes, FixedSize, reducer, leveling, ForbiddenValue, EightConnex, nbCPU) ;
m = glszm.matrix ;
if ( matrix == null ) // Le niveau de gris le plus bas produit les plus grandes zones, donc on initialise le premier.
{
Width = m[0].length ;
matrix = new double[GrayLevelMax][Width] ;
ArrayOperations.Fill(matrix, 0.0) ;
}
step = GrayLevelMax / n ;
width = m[0].length ;
for (x=0 ; x < width ; x++) // On parcours la matrice courante (pour n niveaux de gris)
for (y=0 ; y < n ; y++)
for (i=0 ; i < step ; i++) // On Žtale ses valeurs dans la matrice finale.
matrix[step*y+i][x] += Coefficients[nb] * m[y][x] ;
m = null ;
}
ComputeFeatures() ;
}
/** This method realizes the processing.
* CAUTION: parameters must be set (method parameters) or the method FillMatrix (with all parameters) must be called beforehand.
* @param image The image to characterize.
* @param nbCPU Number of thread to use for processing.*/
public void Compute(BufferedImage image, int nbCPU)
{
Compute(image, nbSizes, FixedSize, reducer, leveling, ForbiddenValue, EightConnex, nbCPU) ;
}
/** This method realizes processing for a stack of images; processing of each image and average on features.
* @param images The image to process.
* @param nbSizes Number of size of zones. If FixedSize=true it will be the matrix width, else it will be the step.
* @param FixedSize Is the width fixed?
* @param reducer This class reduce the number of gray level.
* @param leveling The leveling to use before the color reduction. If null, any leveling realized.
* @param ForbiddenValue The background value or value to not take into account during processing. If negative, so the entire
* texture is processed.
* @param EightConnex Use 8-connexity for labeling?
* @param nbCPU Number of thread to use for processing.*/
public void ComputeFromStack(BufferedImage[] images, int nbSizes, boolean FixedSize, ColorReducer reducer,
Leveling leveling, int ForbiddenValue, boolean EightConnex, int nbCPU)
{
int i, j ;
int[] nbNanF1 = new int[Features.length] ;
double[] F1 = new double[Features.length] ; // Tableaux temporaires contenant la somme des caractŽristiques.
for (i=0 ; i < images.length ; i++)
{
Compute(images[i], nbSizes, FixedSize, reducer, leveling, ForbiddenValue, EightConnex, nbCPU) ;
for (j=0 ; j < F1.length ; j++)
if ( Double.isNaN(Features[j])) nbNanF1[j]++ ; // On compte le nombre de Nan pour ajuster les calculs.
else F1[j] += Features[j] ; // On sauvegarde les rŽsultats.
}
for (j=0 ; j < F1.length ; j++) Features[j] = F1[j] / (double)(images.length-nbNanF1[j]) ;
F1 = null ;
nbNanF1 = null ;
}
/** Methode qui permet d'allouer le tableau et d'affecter les coefficients par un appel AllocCoefficients(size, 2.0, 0.0).
* @param size La taille du tableau.*/
private void AllocCoefficients(int size)
{
AllocCoefficients(size, 2.0, 0.0) ;
}
/** Methode qui permet d'allouer le tableau de coefficients et d'affecter des valeurs (gaussienne).
* @param size La taille du tableau.
* @param sigma L'ecart type a utiliser pour la calcul de la gaussienne.
* @param decalage Le decalage des valeurs => On decale le centre.
* Cela permet de ponderer de maniere asymetrique les niveaux de gris.*/
private void AllocCoefficients(int size, double sigma, double decalage)
{
int x ;
double sum, d2, val ;
double middle = (double)(size-1) / 2.0 + decalage ;
if ( Coefficients == null || Coefficients.length != size)
{
Coefficients = null ;
Coefficients = new double[size] ;
}
sum = 0 ;
for (x=0 ; x < size ; x++)
{
d2 = Math.pow(middle-(double)x, 2.0) / (2.0*Math.pow(sigma, 2.0)) ;
val = Math.exp(-d2) ;
Coefficients[x] = val ;
sum += val ;
}
for (x=0 ; x < size ; x++) // On normalise pour que la somme des valeurs soit 1.
Coefficients[x] /= sum ;
}
/** This method sets parameters.
* @param parameters int nbSizes, boolean FixedSize, ColorReducer reducer, Leveling leveling, int ForbiddenValue,
* boolean EightConnex, int nbCPU.*/
public void Parameters(Object... parameters)
{
if ( parameters.length != 7 ) throw new IllegalArgumentException("7 parameters required.") ;
nbSizes = ((Integer)parameters[0]).intValue() ;
FixedSize = ((Boolean)parameters[1]).booleanValue() ;
reducer = (ColorReducer)parameters[2] ;
leveling = (Leveling)parameters[3] ;
ForbiddenValue = ((Integer)parameters[4]).intValue() ;
EightConnex = ((Boolean)parameters[5]).booleanValue() ;
nbCPU = ((Integer)parameters[6]).intValue() ;
}
/* -------------------------------------------- Calcul des caractŽristiques -------------------------------------------- */
/** This method handles the computation of features.*/
private void ComputeFeatures()
{
Sum = 0.0 ;
for (int y=0 ; y < GrayLevelMax ; y++)
for (int x=0 ; x < Width ; x++)
Sum += matrix[y][x] ;
Features[0] = SZE() ;
Features[1] = LZE() ;
Features[2] = LGZE() ;
Features[3] = HGZE() ;
Features[4] = SZLGE() ;
Features[5] = SZHGE() ;
Features[6] = LZLGE() ;
Features[7] = LZHGE() ;
Features[8] = GLNU() ;
Features[9] = SZNU() ;
Features[10] = ZPC() ;
Features[11] = BARYGL() ;
Features[12] = BARYS() ;
Features[13] = VARGL(Features[11]) ;
Features[14] = VARS(Features[12]) ;
Features[15] = ORIE(Features[11], Features[12]) ;
}
/** F0 - Small Zone Emphasis, SZE.
* @return SZE.*/
private double SZE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] / Math.pow(s+1, 2.0) ;
return val / Sum ;
}
/** F1 - Large Zone Emphasis, LZE.
* @return LZE.*/
private double LZE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] * Math.pow(s+1, 2.0) ;
return val / Sum ;
}
/** F2 - Low Gray level Zone Emphasis, LGZE.
* @return LGZE.*/
private double LGZE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] / Math.pow(n+1, 2.0) ;
return val / Sum ;
}
/** F3 - High Gray level Zone Emphasis, HGZE.
* @return HGZE.*/
private double HGZE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] * Math.pow(n+1, 2.0) ;
return val / Sum ;
}
/** F4 - Small Zone Low Gray level Emphasis, SZLGE.
* @return SZLGE.*/
private double SZLGE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] / (Math.pow(n+1, 2.0) * Math.pow(s+1, 2.0)) ;
return val / Sum ;
}
/** F5 - Small Zone High Gray level Emphasis, SZHGE.
* @return SZHGE.*/
private double SZHGE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] * Math.pow((double)(n+1)/(double)(s+1), 2.0) ;
return val / Sum ;
}
/** F6 - Large Zone Low Gray level Emphasis, LZLGE.
* @return LSLGLE.*/
private double LZLGE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] * Math.pow((double)(s+1)/(double)(n+1), 2.0) ;
return val / Sum ;
}
/** F7 - Large Zone High Gray level Emphasis, LZHGE.
* @return LZHGE.*/
private double LZHGE()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += matrix[n][s] * Math.pow(n+1, 2.0) * Math.pow(s+1, 2.0) ;
return val / Sum ;
}
/** F8 - Gray Level Non Uniform, GLNU. Spectral homogeneity.
* @return GLNU.*/
private double GLNU()
{
double v, val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
{
v = 0.0 ;
for (int s=0 ; s < Width ; s++)
v += matrix[n][s] ;
val += v*v ;
}
return val / Sum ;
}
/** F9 - Size Zone Non Uniform, SZNU. Uniformity.
* @return SZNU.*/
private double SZNU()
{
double v, val = 0.0 ;
for (int s=0 ; s < Width ; s++)
{
v = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
v += matrix[n][s] ;
val += v*v ;
}
return val / Sum ;
}
/** F10 - Zone Percentage, ZPC. Iso-size equality.
* @return ZPC.*/
private double ZPC()
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += (double)(s+1) * matrix[n][s] ;
return Sum / val ;
}
/** F11 - The barycenter on gray level.
* @return The barycenter on gray level.*/
private double BARYGL()
{
double mean = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
mean += (double)(n+1) * matrix[n][s] ;
return mean / Sum ;
}
/** F12 - The barycenter on sizes.
* @return The barycenter on sizes.*/
private double BARYS()
{
double mean = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
mean += (double)(s+1) * matrix[n][s] ;
return mean / Sum ;
}
/** F13 - This method computes the variance on gray level.
* @param mean The average on gray level.
* @return The variance.*/
private double VARGL(double mean)
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += Math.pow((double)(n+1) * matrix[n][s] - mean, 2.0) ;
return Math.sqrt(val / Sum) ;
}
/** F14 - This method computes the variance on sizes.
* @param mean The average on sizes.
* @return The variance.*/
private double VARS(double mean)
{
double val = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
val += Math.pow((double)(s+1) * matrix[n][s] - mean, 2.0) ;
return Math.sqrt(val / Sum) ;
}
/** F15 - This method computes the orientation of non null values of the matrix.
* @param bgl Barycenter on gray level.
* @param bs Barycenter on sizes.
* @return The orientation [-Pi/2, Pi/2].*/
private double ORIE(double bgl, double bs)
{
double a, b, c, m ;
double m11 = 0.0, m02 = 0.0, m20 = 0.0 ;
for (int n=0 ; n < GrayLevelMax ; n++)
for (int s=0 ; s < Width ; s++)
{
m = matrix[n][s] ;
m11 += (double)(n+1) * (double)(s+1) * m ;
m02 = Math.pow(n+1, 2.0) * m ;
m20 = Math.pow(s+1, 2.0) * m ;
}
a = m20/Sum - bs*bs ;
b = 2.0 * (m11/Sum - bgl*bs) ;
c = m02/Sum - bgl*bgl ;
return Math.atan(b/(a-c))/2.0 ;
}
/* ---------------------------------------------------- Les getters ---------------------------------------------------- */
/** This method returns the features.
* @return An array containing features.*/
public double[] Features()
{
return Features ;
}
/** This method returns one feature.
* @param i The feature number.
* @return The feature value.*/
public double Feature(int i)
{
return Features[i] ;
}
/** This method returns an array which contain features names.
* @return An array of String.*/
public String[] FeaturesNames()
{
return FeaturesNames ;
}
/** This method sets coefficients/weights for the matrix fusion.
* @param Coefficients The weights array (will be cloned).*/
public void Coefficients(double[] Coefficients)
{
this.Coefficients = null ;
this.Coefficients = ArrayNew.Clone(Coefficients) ;
}
/** This method returns the array of coefficients.
* @return Array of coefficients.*/
public double[] Coefficients()
{
return Coefficients ;
}
}