package patternRecognition.textures.glzm.glszm;
import arrayTiTi.ArrayOperations;
import dv.DV;
import imageTiTi.ImageTools;
import imageTiTi.reducer.ColorReducer;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import measures.cclh.ConnectedComponentLabeling;
import morphee.levelings.Leveling;
import patternRecognition.PreComputable;
/**
*
Description: this class features on the "Size Zone Matrix".
* 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 => Barycenter on gray level.
* - F12 => Barycenter on size zones.
* - F13 => Weighted variance on gray level.
* - F14 => Weighted variance on size zones.
* - F15 => Orientation.
*
* Package(s) required: dv, imageTiTi, measures, morphee.
* Copyright: Copyright (c) 2006-Today.
* Updates:
* February 27 2011, 1.1 => Add interfaces PreComputable and Computable.
* 20 Avril 2010 => Ajout des indices F13 a F15 et correction/traduction des noms.
* 07 Fevrier 2010 => Suppression des caracteristiques Haralick.
* 21 Janvier 2010 => Ajout de la methode "Compute(DV, int, boolean) pour effectuer le calcul sur des volumes.
* 19 Novembre 2009 => Ajout de la methode ComputeFromStack qui permet de faire les calculs sur une pile d'images
* (projet Loreal, CMM/ENSMP).
* 10 Avril 2008 => Creation.
*
*
*
* BibTeX:
*
* @conference{Thibault09,
* address = {Minsk, Belarus},
* author = {Guillaume Thibault and Bernard Fertil and Claire Navarro and Sandrine Pereira and Pierre Cau
* and Nicolas Levy and Jean Sequeira and Jean-Luc Mari},
* booktitle = {Pattern Recognition and Information Processing (PRIP)},
* pages = {140--145},
* title = {Texture Indexes and Gray Level Size Zone Matrix. Application to Cell Nuclei Classification},
* year = {2009}
* }
*
*
* @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.1
*/
public class GlszmFeatures extends GrayLevelSizeZoneMatrix implements PreComputable
{
/** 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"} ;
/** This array will contain the list of size zones after the call of the method PreCompute.*/
private int[] sizes ;
/** This array will contain the color of zones after reduction.*/
private int[] colors ;
/** This array will contain the list of connected component used in the window (framework).*/
private boolean[] used ;
public GlszmFeatures()
{
super() ;
}
/** This method realizes the processing. Do not call the method "FillMatrix" (useless because it is automatically called).
* @param image The image to characterize.
* @param nbGrayLevel The number of gray level after reduction (matrix height).
* @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 nbGrayLevel, int nbSizes, boolean FixedSize, ColorReducer reducer,
Leveling leveling, int ForbiddenValue, boolean EightConnex, int nbCPU)
{
FillMatrix(image, nbGrayLevel, nbSizes, FixedSize, reducer, leveling, ForbiddenValue, EightConnex, nbCPU) ; // On remplit :)
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, nbGrayLevel, nbSizes, FixedSize, reducer, leveling, ForbiddenValue, EightConnex, nbCPU) ;
ComputeFeatures() ;
}
/** This method realizes the processing.
* @param ImageReduced The original image after color reduction.
* @param Ccl The class which realizes labeling of connected components.*/
public void Compute(BufferedImage ImageReduced, ConnectedComponentLabeling Ccl)
{
if ( !ImageTools.isGrayLevel(ImageReduced) ) throw new IllegalArgumentException("Only gray level images supported.") ;
FillMatrix(ImageReduced, Ccl) ; // On remplit la matrice :)
ComputeFeatures() ;
}
/** This method realizes the processing. Do not call the method "FillMatrix" (useless because it is automatically called).
* @param volume The volume to characterize.
* @param ForbiddenValue The background value or value to not take into account during processing. If negative, so the entire
* 3D texture is processed.
* @param TwentySixConnex Use 26-connexity for labeling?*/
public void Compute(DV volume, int ForbiddenValue, boolean TwentySixConnex)
{
FillMatrix(volume, ForbiddenValue, TwentySixConnex) ; // On remplit la matrice :)
ComputeFeatures() ;
}
/* ------------------------------------------------- Interface PreComputable ------------------------------------------------- */
/** This method realizes the preprocessings.
* @param image The image to process.
* @param nbCPU Number of thread to use for processing.*/
public void PreCompute(BufferedImage image, int nbCPU)
{
if ( nbCPU < 0 ) throw new Error("Parameters must be set before the call to this method.") ;
int x, y ;
int height = image.getHeight() ;
int width = image.getWidth() ;
BufferedImage reduced = reducer.Reduce(image, nbGrayLevel, ForbiddenValue) ;
ccl.Label(reduced, ForbiddenValue, EightConnex) ;
int counter = ccl.NumberOfConnectedComponent() ;
int[][] labels = ccl.Labels() ;
WritableRaster wrred = reduced.getRaster() ;
if ( sizes == null || sizes.length != counter+1 )
{
colors = null ;
colors = new int[counter+1] ;
sizes = null ;
sizes = new int[counter+1] ;
used = null ;
used = new boolean[counter+1] ;
}
if ( FixedSize && ( matrix == null || matrix.length != nbGrayLevel || matrix[0].length != Width) )
{
Width = nbSizes ;
matrix = new double[nbGrayLevel][Width] ;
}
ArrayOperations.Fill(colors, 0) ;
ArrayOperations.Fill(sizes, 0) ;
ArrayOperations.Fill(used, false) ;
for (y=0 ; y < height ; y++)
for (x=0 ; x < width ; x++)
if ( wrred.getSample(x, y, 0) > 0 )
{
sizes[labels[y][x]]++ ;
colors[labels[y][x]] = wrred.getSample(x, y, 0) ;
}
wrred = null ;
labels = null ;
}
/** This method computes characteristics according preprocessings (pre-computation).
* @param startx The X coordinate of the starting point (corner bottom left).
* @param starty The Y coordinate of the starting point (corner bottom left).
* @param endx The X coordinate of the ending point (corner top right).
* @param endy The Y coordinate of the ending point (corner top right).*/
public void FastCompute(int startx, int starty, int endx, int endy)
{
if ( sizes == null ) throw new Error("Method PreCompute must be called before this one.") ;
int[][] labels = ccl.Labels() ;
ArrayOperations.Fill(used, false) ;
for (int y=starty ; y <= endy ; y++)
for (int x=startx ; x <= endx ; x++)
used[labels[y][x]] = true ;
used[0] = false ;
FillMatrix(sizes, colors, used, nbCPU) ;
ComputeFeatures() ;
}
/* -------------------------------------------------- Features -------------------------------------------------- */
/** This method handles the computation of features.*/
private void ComputeFeatures()
{
Sum = 0.0 ;
for (int y=0 ; y < nbGrayLevel ; 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]) ;
}
/** This method realizes processing for a stack of images; processing of each image and average on features.
* @param images The image to process.
* @param nbGrayLevel The number of gray level after reduction (matrix height).
* @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 nbGrayLevel, 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], nbGrayLevel, 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 ;
}
/* -------------------------------------------- Calcul des caractŽristiques -------------------------------------------- */
/** F0 - Small Zone Emphasis, SZE.
* @return SZE.*/
private double SZE()
{
double val = 0.0 ;
for (int n=0 ; n < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; n++)
v += matrix[n][s] ;
val += v*v ;
}
return val / Sum ;
}
/** F10 - Zone Percentage, ZPC. Equality of iso-sizes.
* @return ZPC.*/
private double ZPC()
{
double val = 0.0 ;
for (int n=0 ; n < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 < nbGrayLevel ; 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 ;
}
/* ---------------------------------------------------- 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 ;
}
}