Discussion: [image] Haralick texture features

1. [image] Haralick texture features

Voici un petit programme Java qui calcule quelques caracteristiques d'Haralick sur la matrice de co-occurence.

Un exemple d'applet interactif (qui n'utilise pas du tout ce code) est dispo sur ce site.

 Code java : Sélectionner tout - Visualiser dans une fenêtre à part
```123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265import java.util.Arrays;

public class Haralick {

// matrices de densité spatiale (aka GLCM)
private int MSIZE=256;
private double[][] matrix = new double[MSIZE][MSIZE];

// image source
private Image image = null;

// precalculs
private double[] px_y = new double[MSIZE];  // Px-y(i)
private double[] pxy = new double[2*MSIZE]; // Px+y(i)

// valeurs specifiques de la texture
public double[] features = new double[15];

public Haralick(Image img) {
this.image=img;

int n=4;
int[] dx = new int[] {1, 1, 0,-1};
int[] dy = new int[] {0, 1, 1, 1};

for(int k=0;k<n;k++) {
computeMatrix(dx[k],dy[k]);

precomputation();

this.features[1] += getF1();
this.features[2] += getF2();
this.features[3] += getF3();
this.features[4] += getF4();
this.features[5] += getF5();
this.features[6] += getF6();
this.features[7] += getF7(this.features[6]);
this.features[8] += getF8();
this.features[9] += getF9();
this.features[10]+= getF10();
this.features[11]+= getF11();
}

for(int f=1;f<this.features.length;f++)
this.features[f]/=n;
}

private void precomputation() {
// Px-y(i)
Arrays.fill(px_y, 0);
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
px_y[Math.abs(i-j)]+=matrix[i][j];
}
}

// Px+y(i)
Arrays.fill(pxy, 0);
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
pxy[i+j]+=matrix[i][j];
}
}
}

@Override
public String toString() {
StringBuffer sb = new StringBuffer();
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
sb.append(matrix[i][j]+" ");
}
sb.append("\n");
}
return sb.toString();
}

private void computeMatrix(int dx, int dy) {
// raz matrice
for(int i=0;i<MSIZE;i++)
Arrays.fill(matrix[i], 0);

// calcul des co-occurences
int sum=0;
int height = image.getHeight();
int width = image.getWidth();
for(int y0=0;y0<height;y0++) {
for(int x0=0;x0<width;x0++) {
// pour chaque pixel
int v0 = (int)(MSIZE*this.image.getValue(x0,y0)/256.0);

// on cherche le voisin
int x1 = x0 + dx;
if (x1<0 || x1>=width) continue;
int y1 = y0 + dy;
if (y1<0 || y1>=height) continue;
int v1 = (int)(MSIZE*this.image.getValue(x1,y1)/256.0);

// on incremente la matrice
matrix[v0][v1]++;
matrix[v1][v0]++;
sum+=2;
}
}

// normalisation
for(int j=0;j<MSIZE;j++)
for(int i=0;i<MSIZE;i++)
matrix[i][j]/=sum;
}

// F1 - Second moment angulaire (homogeneite)
private double getF1() {
double h=0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
h+=matrix[i][j]*matrix[i][j];
}
}
return h;
}

// F2 - contraste
private double getF2() {
double contrast=0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
contrast+=(i-j)*(i-j)*matrix[i][j];
}
}
return contrast;
}

// F3 - correlation
private double getF3() {

// optimisation car matrice symetrique
// ==> distribution marginale sur X = distribution marginale sur Y

// moyenne = somme { p(i,j) * i }
double mean = 0;
for(int i=0;i<MSIZE;i++) {
for(int j=0;j<MSIZE;j++) {
mean+=i*matrix[i][j];
}
}

// variance = somme { p(i,j) * (i-moyenne)^2 }
double var=0;
for(int i=0;i<MSIZE;i++) {
for(int j=0;j<MSIZE;j++) {
var+=matrix[i][j]*(i-mean)*(i-mean);
}
}
if (var<=0) return 1;

// correlation = somme { (i-moyenne) * (j-moyenne) * p(i,j) / variance^2 }
double sum = 0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
sum+= matrix[i][j]*(i-mean)*(j-mean);
}
}
double r = sum/(var*var);

return r;
}

// F4 - Variance
private double getF4() {
double mean = 0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
mean+=i*matrix[i][j];
}
}

double variance=0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
variance+=(i-mean)*(i-mean)*matrix[i][j];
}
}
return variance;
}

// F5 - Moment des différences inverses OK
private double getF5() {
double invdiff=0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
double coef = 1.0/(1.0+(i-j)*(i-j));
invdiff+=coef*matrix[i][j];
}
}
return invdiff;
}

// F6 - Moyenne des sommes
private double getF6() {
double sumavg=0;
for(int k=0;k<=2*(MSIZE-1);k++) {
sumavg+=k*pxy[k];
}
return sumavg;
}

// F7 - Variance des sommes
private double getF7(double f6) {
double sumavg=f6;
int sumvar=0;
for(int k=0;k<=2*(MSIZE-1);k++) {
sumvar+=(k-sumavg)*(k-sumavg)*pxy[k];
}
return sumvar;
}

// F8 - Entropie des sources
private double getF8() {
double entropysrc=0;
for(int k=0;k<=2*(MSIZE-1);k++) {
if (pxy[k]==0) continue;
entropysrc+=pxy[k]*Math.log(pxy[k]);
}
return -entropysrc;
}

// F9 - Entropie
private double getF9() {
double entropy=0;
for(int j=0;j<MSIZE;j++) {
for(int i=0;i<MSIZE;i++) {
if (matrix[i][j]==0) continue;
entropy+=matrix[i][j]*Math.log(matrix[i][j]);
}
}
return -entropy;
}

// F10 - variance des differences
private double getF10() {
double mean=0;
for(int k=0;k<=MSIZE-1;k++) {
mean+=k*px_y[k];
}
double var=0;
for(int k=0;k<=MSIZE-1;k++) {
var+=(k-mean)*(k-mean)*px_y[k];
}
return var;
}

// F11 - Entropie des differences
private double getF11() {
double entropydiff=0;
for(int k=0;k<=MSIZE-1;k++) {
if (px_y[k]==0) continue;
entropydiff+=px_y[k]*Math.log(px_y[k]);
}
return -entropydiff;
}

}```

Voici un résumé des formules utilisées:

2. Bonjour à vous,
Je viens de lire ce code, et il y a quelques points pour lesquels j’aurai besoin d’éclaircissement :

Tout d’abord la matrice de cooccurrence appelée Matrix dans ce code. Je pensais que :
Matrix ( i , j ) représente le nombre d’occurrence des pixels i et j l’un à coté de l’autre, d’ailleurs, c’est ce qui a été calculé au départ, mais ensuite il y a eu étape de normalisation que je ne comprends pas. Pourquoi diviser cette matrice par la somme de ses éléments ?
La matrice de cooccurrence par définition, est ce bien le résultat de la méthode MatrixCompute (après normalisation) ?

J’ai essayé de rédiger les formules des paramètres, selon ma compréhension du code,pour mon cas je n’ai fais aucune optimisation de la matrice,c'est-à-dire qu’elle contient 256 lignes et 256 colonnes selon les niveau de gris de 0 à 255. Pouvez vous me dire si les formules suivantes sont mathématiquement correctes :

Merci d'avance.

3. Bonjour,

M(i,j) est le nombre d'occurrence de couple de pixel de valeur (i,j) situé à une distance d. Ce n'est pas forcément cote à cote. Dans ce code, il s'agit des pixels situé à une distance (0,1), (1,1), (1,0) et (-1,1).

Ensuite, il ne faut pas travailler avec des matrices de taille égales au nombre de niveaux de gris de l'image. Il faut diminuer le nombre de couleur afin d'atténuer l'influence du bruit. En général des niveaux de gris de l'image de 16, 32 ou 64 apportent des résultats intéressant. Mais il faut souvent faire des tests en fonctions du type de problème pour trouver le nombre de niveaux de gris optimal.

Pseudocode a directement programmé les formules qu'il a donné. Si tes formules sont différentes des siennes, alors c'est que t'es trompé

4. Envoyé par ToTo13
Bonjour,

M(i,j) est le nombre d'occurrence de couple de pixel de valeur (i,j) situé à une distance d. Ce n'est pas forcément cote à cote. Dans ce code, il s'agit des pixels situé à une distance (0,1), (1,1), (1,0) et (-1,1).
Oui,Oui, je sais, d'ailleurs c'est ce que vous m'avez expliquer ici, quand je disais "l'un à coté de l'autre" c'etait seulement par abus de langage...

Pseudocode a directement programmé les formules qu'il a donné. Si tes formules sont différentes des siennes, alors c'est que t'es trompé
Je n'arrive pas à lire les formules de "Moyenne" et "Variance"...
Un petit agrandissement PseudoCode ?

Et je ne comprends pas la forumule du paramètre f3 :
f3=...(i j) p(i,j)-Vx*Vy...

PS: Auriez vous un lien qui donne une description de chacun des 14 paramètres d'Haralick ?

5. Envoyé par b_reda31
Je n'arrive pas à lire les formules de "Moyenne" et "Variance"...
Un petit agrandissement PseudoCode ?
Bah, ce sont les formules usuelles de moyenne et variance. Comme le dit wikipedia:

µ =

variance =

Et je ne comprends pas la forumule du paramètre f3 :
f3=...(i j) p(i,j)-Vx*Vy...
C'est une propriété des matrices de covariance.

=

6. Envoyé par pseudocode
Un exemple d'applet interactif (qui n'utilise pas du tout ce code) est dispo sur ce site.
Bonjour,J'ai remarqué dans ce site que les valeurs des paramètres calculés ne dépassent pas la valeurs 1....Or en faisant des test sur que j'ai implémenté j'obtiens les valeurs suivantes:
F1 = 0.09 (Homogénéité)
F2 = 1.23 (Constraste)
F3= 0.01 (Corrélation)
F4=3.76 !(Entropie)

Je m'interroge surtout sur les valeurs de f2 et f4 étant supérieurs à 1...Il est possible qu'elles soient correctes à votre avis?

Je voudrai aussi savoir comment interpréter ces résultats?
Pour l'homogénéité je pense qu'elle est égal à 1 si l'image ne contient qu'un seul niveau de gris,donc plus on a une grande surface de même niveau de gris dans l'image et plus f1 tend vers la valeur 1....Mais concernant les autres paramètres je n'ai aucune idée sur comment les interpréter!

7. Envoyé par b_reda31
Je m'interroge surtout sur les valeurs de f2 et f4 étant supérieurs à 1...Il est possible qu'elles soient correctes à votre avis?
D'après les formules, il est effectivement possible d'avoir des valeurs plus grandes que 1.

Je voudrai aussi savoir comment interpréter ces résultats?
Hum. Il est sans doute possible de trouver une interprétation "graphique" aux différentes valeurs. Cependant, on utilise surtout ces valeurs pour "classifier" des images. On s'intéresse donc au l'ecart de valeurs entre 2 images plutôt qu'a la valeur d'une seule image.

8. Bonjour,

je suis entièrement d'accord avec la dernière phrase de Pseudocode.

En data mining, il est extrêmement rare de trouver des variables que l'on puisse utiliser pour discriminer directement des individus. C'est pour cette raison que l'on construit des modèles plus ou moins complexes. A ce moment là, certains paramètres d'évaluation comme le Chi2 te donneront l'importance d'une variable dans le modèle.

Toutefois, si tu souhaites absolument une interprétation graphique du rôle de chaque variable, tu peux utiliser la fonction "Fit Y by X" de JMP.

9. comparer deux ensembles de caractéristiques

Bonjour,

je me pose exactement les mêmes questions existentielles au sujet de la différence de grandeur entre les valeurs de certaines caractéristiques très petites et, effectivement, d'autres bien supérieures à 1.

Si vous voulez classifiez des images (ex : textures) en fonction de leurs caractéristiques de Haralick, quelle métrique utilisez-vous si vous ne pouvez faire aucun a-priori sur la valeur que prendra telle ou telle caractéristique , pour telle ou telle image ? (par exemple trier des images au fur-et-à-mesure de leur arrivée ?).

10. Envoyé par thomL
Bonjour,

je me pose exactement les mêmes questions existentielles au sujet de la différence de grandeur entre les valeurs de certaines caractéristiques très petites et, effectivement, d'autres bien supérieures à 1.

Si vous voulez classifiez des images (ex : textures) en fonction de leurs caractéristiques de Haralick, quelle métrique utilisez-vous si vous ne pouvez faire aucun a-priori sur la valeur que prendra telle ou telle caractéristique , pour telle ou telle image ? (par exemple trier des images au fur-et-à-mesure de leur arrivée ?).
Généralement pour faire de la classification on commence par un apprentissage.

Par exemple on calcule les caractéristiques d'un groupe d'image A et on étudie la distribution des caractéristiques (min, max, moyenne, ecart-type, ...). On fait la meme chose avec un groupe d'image B.

Quand une "nouvelle" image doit être classifiée, on calcule ses caractéristiques et on regarde si on est dans l'intervalle de confiance du groupe A, ou du groupe B, ou aucun des deux groupes.

11. ok, je fais fausse route alors.

je cherche à comparer la "pertinence" de certains indicateurs/caractéristiques/coefficients (features) avec ceux de Haralick.

pour cela je calcule les indicateurs de plusieurs images qui se répartissent en quelques classes (connues), je considère qu'une image correspond à un vecteur d'indicateurs et je calcule le rapport inertie inter / inertie intra : d'un côté pour ceux de Haralick, de l'autre pour les "miens".

mais calculer l'inertie inter et intra, c'est se baser sur une norme euclidienne qui accorde la même importance à tous les indicateurs ...

12. Envoyé par thomL
mais calculer l'inertie inter et intra, c'est se baser sur une norme euclidienne qui accorde la même importance à tous les indicateurs ...
Oui. Mais déjà considérer les valeurs des caractéristiques comme un vecteur, c'est considérer que les caractéristiques forment une base d'un espace. Déjà, j'ai un doute a ce niveau. Ensuite que la base soit orthonormée c'est un deuxième problème.

Pour ma part, j'ai de meilleurs résultats en faisant comme je l'ai dit précédemment.

13. merci tout d'abord pour la rapidité des réponses !

oui, j'imagine bien qu'avec des méthodes sophistiquées on obtient de bons résultats, ...
mon souci c'est que j'ai besoin de quelque chose d'assez "simple" juste pour évaluer la pertinence de caractéristiques (Haralick vs Autres), plusieurs fois.
simple à expliquer, et simple à mettre en oeuvre. (donc les inerties, j'avoue, c'était assez pratique).

Est-ce qu'il y aurait donc une solution "intermédiaire" ?

pour ma part je dois en rester là pour aujourd'hui..
merci encore et bonne soirée

14. Envoyé par thomL
oui, j'imagine bien qu'avec des méthodes sophistiquées on obtient de bons résultats, ...
mon souci c'est que j'ai besoin de quelque chose d'assez "simple" juste pour évaluer la pertinence de caractéristiques (Haralick vs Autres), plusieurs fois.
simple à expliquer, et simple à mettre en oeuvre. (donc les inerties, j'avoue, c'était assez pratique).
Calculer la moyenne et la variance d'une série de valeur c'est pas bien compliqué. Pour la pertinence, plus la variance est faible, plus l'indicateur est pertinent. Il faut également que cet indicateur sépare correctement les groupes. Bref rien de bien compliqué.

15. Envoyé par ManU_05
Je développe actuellement un algorithme de classification SVM à partir de ces attributs de textures et je voudrai normer les vecteurs d'attributs sur une base générale.
Quel intérêt de normer les valeurs avec un SVM ?

16. Envoyé par pseudocode
Quel intérêt de normer les valeurs avec un SVM ?

Bonjour, dslé pour la réponse tardive.
Plusieurs articles de recherche ont montré que la normalisation des vecteurs d'attributs pour la classification SVM produirait d'une part des modèles d'estimation plus performant et d'autre part accélèrerai la convergence de l'algorithme.

17. Envoyé par ManU_05
Bonjour, dslé pour la réponse tardive.
Plusieurs articles de recherche ont montré que la normalisation des vecteurs d'attributs pour la classification SVM produirait d'une part des modèles d'estimation plus performant et d'autre part accélèrerai la convergence de l'algorithme.
Ah. Autant pour l'accélération de la convergence, je peux comprendre. Autant pour la performance d'estimation, je ne vois pas trop.

18. Salut a tous
j'ai un problème avec ce code
 Code : Sélectionner tout - Visualiser dans une fenêtre à part
`(int)(MSIZE*this.image.getValue(x0,y0)/256.0);`
ça marche pas pour moi est ce que j'ai besoin d'importer quelque chose comme java.awt.Image

19. Envoyé par compaq610
Salut a tous
j'ai un problème avec ce code
 Code : Sélectionner tout - Visualiser dans une fenêtre à part
`(int)(MSIZE*this.image.getValue(x0,y0)/256.0);`
ça marche pas pour moi est ce que j'ai besoin d'importer quelque chose comme java.awt.Image
Image c'est une classe personnelle qui est un simple wrapper autour de BufferedImage.

image.getValue(x,y) renvoie simplement la valeur d'intensité du pixel de coordonnées (x,y).

20. Envoyé par pseudocode
Image c'est une classe personnelle qui est un simple wrapper autour de BufferedImage.

image.getValue(x,y) renvoie simplement la valeur d'intensité du pixel de coordonnées (x,y).
oui on fait j'ai besoin de récupérer la valeur d'intensité du pixel de coordonnées (x,y). mais ça marche pas

 Actualités COURS ALGO FAQ ALGO LIVRES ALGO SOURCES ALGO