IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez Discussion :

[Image] Détecteur de Harris pour ImageJ


Sujet :

Contribuez

  1. #1
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut [Image] Détecteur de Harris pour ImageJ
    Continuons avec l'extraction des points caractéristiques. Après les contours (Canny), passons maintenant aux coins avec Harris.

    Le détecteur de Harris est un détecteur assez simple qui permet d'extraire les "coins" des contours. Les points récupérés sont souvent utilisés dans les algorithmes de reconnaissance de forme.



    plus d'info: http://en.wikipedia.org/wiki/Corner_detection

    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
     
    import ij.IJ;
    import ij.ImagePlus;
    import ij.gui.GenericDialog;
    import ij.plugin.filter.PlugInFilter;
    import ij.process.ByteProcessor;
    import ij.process.ImageProcessor;
     
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
     
     
    /**
     * Harris Corner Detector
     * 
     * @author Xavier Philippeau
     *
     */
    public class Harris_ implements PlugInFilter {
     
    	// corners list
    	List<int[]> corners = new ArrayList<int[]>();
     
    	// halfwindow size (integration, kernels)
    	private int halfwindow = 0;
     
    	// variance of gaussians
    	private double gaussiansigma = 0;
     
    	// corner filtering
    	private int minDistance = 0;
    	private int minMeasure = 0;
     
    	// Gradient kernel
    	GradientVector gradient = new GradientVector();
     
    	//	 About...
    	private void showAbout() {
    		IJ.showMessage("Harris...","Harris Corner Detector by Pseudocode");
    	}
     
    	public int setup(String arg, ImagePlus imp) {
     
    		// about...
    		if (arg.equals("about")) {
    			showAbout(); 
    			return DONE;
    		}
     
    		// else...
    		if (imp==null) return DONE;
     
    		// Configuration dialog.
    		GenericDialog gd = new GenericDialog("Parameters");
    		gd.addNumericField("Half-Window size",1,0);
    		gd.addNumericField("Gaussian sigma",1.4,1);
    		gd.addNumericField("Min Harris measure for corners",10,0);
    		gd.addNumericField("Min distance between corners",8,0);
     
    		int  halfwindow = 0;
    		double gaussiansigma = 0;
    		int minMeasure = 0;
    		int minDistance = 0;
     
    		while(true) {
    			gd.showDialog();
    			if ( gd.wasCanceled() )	return DONE;
     
    			halfwindow     = (int) gd.getNextNumber();
    			gaussiansigma  = (double) gd.getNextNumber();
    			minMeasure     = (int) gd.getNextNumber();
    			minDistance    = (int) gd.getNextNumber();
     
    			if (halfwindow<=0) continue;
    			if (gaussiansigma<=0) continue;
    			if (minMeasure<0) continue;
    			if (minDistance<0) continue;
    			break;
    		}
    		gd.dispose();
     
    		this.halfwindow = halfwindow;
    		this.gaussiansigma = gaussiansigma;
    		this.minMeasure = minMeasure;
    		this.minDistance = minDistance;
     
    		return PlugInFilter.DOES_8G;
    	}
     
    	public void run(ImageProcessor ip) {
     
    		// ImageProcessor -> ByteProcessor conversion
    		ByteProcessor bp = new ByteProcessor(ip.getWidth(),ip.getHeight());
    		for (int y = 0; y < ip.getHeight(); y++) {
    			for (int x = 0; x < ip.getWidth(); x++) {
    				bp.set(x,y,ip.getPixel(x,y));
    			}
    		}
     
    		// canny filter
    		ByteProcessor newbp = filter( bp, this.minMeasure, this.minDistance );
     
    		// ByteProcessor -> ImageProcessor conversion
    		ImageProcessor out = new ByteProcessor(ip.getWidth(),ip.getHeight());
    		for (int y = 0; y < ip.getHeight(); y++) {
    			for (int x = 0; x < ip.getWidth(); x++) {
    				out.set(x,y,newbp.get(x,y));
    			}
    		}
    		ImagePlus newImg = new ImagePlus("Canny Filter Result", out);
    		newImg.show();
     
    	}
     
    	// -------------------------------------------------------------------------
     
    	/**
             * Gaussian window function
             * 
             * @param x x coordinates
             * @param y y coordinates
             * @param sigma2 variance
             * @return value of the function
             */
    	private double gaussian(double x, double y, double sigma2) {
    		double t = (x*x+y*y)/(2*sigma2);
    		double u = 1.0/(2*Math.PI*sigma2);
    		double e = u*Math.exp( -t );
    		return e;
    	}
     
    	/**
             * compute harris measure for a pixel
             * 
             * @param c Image map
             * @param x x coord of the computation
             * @param y y coord of the computation
             * @return harris measure 
             */
    	private double harrisMeasure(ByteProcessor c, int x, int y) {
    		double m00=0, m01=0, m10=0, m11=0;
     
    		// Harris estimator
    		// ----------------
    		//
    		// k = det(A) - lambda * trace(A)^2
    		//
    		// Where A is the second-moment matrix 
    		//
    		//           | Lx²(x+dx,y+dy)    Lx.Ly(x+dx,y+dy) |
    		// A =  Sum  |                                    | * Gaussian(dx,dy)
    		//     dx,dy | Lx.Ly(x+dx,y+dy)  Ly²(x+dx,y+dy)   |
    		//
    		// and lambda = 0.06  (totaly empirical :-)
     
    		for(int dy=-halfwindow;dy<=halfwindow;dy++) {
    			for(int dx=-halfwindow;dx<=halfwindow;dx++) {
    				int xk = x + dx;
    				int yk = y + dy;
    				if (xk<0 || xk>=c.getWidth()) continue;
    				if (yk<0 || yk>=c.getHeight()) continue;
     
    				// gradient value
    				double[] g = gradient.getVector(c,xk,yk);
    				double gx = g[0];
    				double gy = g[1];
     
    				// gaussian window
    				double gw = gaussian(dx,dy,gaussiansigma);
     
    				// second-moment matrix elements
    				m00 += gx * gx * gw;
    				m01 += gx * gy * gw;
    				m10 = m01;
    				m11 += gy * gy * gw;
    			}
    		}
     
    		// Harris corner measure 
    		double harris = m00*m11 - m01*m10 - 0.06*(m00+m11)*(m00+m11);
     
    		// scaled down
    		return harris/(256*256);
    	}
     
    	/**
             * return the the measure at pixel (x,y) if the pixel is a spatial Maxima, else return -1
             * 
             * @param c original image 
             * @param x x coordinates of pixel
             * @param y y coordinates of pixel
             * @return the measure if the pixel is a spatial Maxima, else -1
             */
    	private double spatialMaximaofHarrisMeasure(ByteProcessor c, int x, int y) {
    		int n=8;
    		int[] dx = new int[] {-1,0,1,1,1,0,-1,-1};
    		int[] dy = new int[] {-1,-1,-1,0,1,1,1,0};
    		double w =  harrisMeasure(c,x,y);
    		for(int i=0;i<n;i++) {
    			double wk = harrisMeasure(c,x+dx[i],y+dy[i]);
    			if (wk>=w) return -1;
    		}
    		return w;
    	}
     
    	/**
             * Perfom Harris Corner Detection
             * 
             * @param c Image map
             * @param tilesize size of a tile 
             * @param nmbmax max number of corner to keep
             * @return filtered image map
             */
    	public ByteProcessor filter(ByteProcessor c, int minMeasure, int minDistance) {
    		int width = c.getWidth();
    		int height = c.getHeight();
     
    		// copy of the original image (at little darker)
    		ByteProcessor c2 = new ByteProcessor(width,height);
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				c2.set(x,y,(int)(c.get(x,y)*0.80));
     
    		// for each tile in the image
    		for (int y=0; y<height; y++) {
    			for (int x=0; x<width; x++) {
    				// harris measure
    				int h = (int)spatialMaximaofHarrisMeasure(c, x, y);
     
    				// add the corner to the list
    				if (h>=minMeasure) corners.add( new int[]{x,y,h} );
    			}
    		}
     
    		// remove corners to close to each other (keep the highest measure)
    		Iterator<int[]> iter = corners.iterator();
    		while(iter.hasNext()) {
    			int[] p = iter.next();
    			for(int[] n:corners) {
    				if (n==p) continue;
    				int dist = (int)Math.sqrt( (p[0]-n[0])*(p[0]-n[0])+(p[1]-n[1])*(p[1]-n[1]) );
    				if( dist>minDistance) continue;
    				if (n[2]<p[2]) continue;
    				iter.remove();
    				break;
    			}
    		}
     
    		// Display corners over the image (cross)
    		for (int[] p:corners) {
    			for (int dx=-2; dx<=2; dx++) {
    				if (p[0]+dx<0 || p[0]+dx>=width) continue;
    				c2.set(p[0]+dx,p[1],255);
    			}
    			for (int dy=-2; dy<=2; dy++) {
    				if (p[1]+dy<0 || p[1]+dy>=height) continue;
    				c2.set(p[0],p[1]+dy,255);
    			}
    			//System.out.println("corner found at: "+p[0]+","+p[1]+" ("+p[2]+")");
    		}		
     
    		return c2;
    	}
     
    	public class GradientVector {
     
    		int halfwindow = 1; // 3x3 kernel
    		double sigma2 = 1.4;
     
    		double[][] kernelGx = new double[2*halfwindow+1][2*halfwindow+1];
    		double[][] kernelGy = new double[2*halfwindow+1][2*halfwindow+1];
     
    		// Constructor
    		public GradientVector() {
    			for(int y=-halfwindow;y<=halfwindow;y++) {
    				for(int x=-halfwindow;x<=halfwindow;x++) {
    					kernelGx[halfwindow+y][halfwindow+x] = Gx(x, y);
    					kernelGy[halfwindow+y][halfwindow+x] = Gy(x, y);
    				}
    			}
    		}
     
    		// Kernel functions (gaussian 1st order partial derivatives)
    		private double Gx(int x, int y) {
    			double t = (x*x+y*y)/(2*sigma2);
    			double d2t = -x / sigma2;
    			double e = d2t * Math.exp( -t );
    			return e;
    		}
     
    		private double Gy(int x, int y) {
    			double t = (x*x+y*y)/(2*sigma2);
    			double d2t = -y / sigma2;
    			double e = d2t * Math.exp( -t );
    			return e;
    		}
     
    		// return the Gradient Vector for pixel(x,y) 
    		public double[] getVector(ByteProcessor c, int x, int y) {
    			double gx=0, gy=0;
    			for(int dy=-halfwindow;dy<=halfwindow;dy++) {
    				for(int dx=-halfwindow;dx<=halfwindow;dx++) {
    					int xk = x + dx;
    					int yk = y + dy;
    					double vk = c.getPixel(xk,yk); // <-- value of the pixel
    					gx += kernelGx[halfwindow-dy][halfwindow-dx] * vk;
    					gy += kernelGy[halfwindow-dy][halfwindow-dx] * vk;
    				}
    			}
     
    			double[] gradientVector = new double[] { gx, gy };
     
    			return gradientVector;
    		}	
    	}
    }

    Vous trouverez une version "stand-alone" de ce code au Post #14

    Et également une version sous forme de Plugin pour l'application de Millie ici -> HarrisDetectionPlugin.jar
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  2. #2
    Membre éclairé
    Avatar de parp1
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    829
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Calvados (Basse Normandie)

    Informations forums :
    Inscription : Mai 2005
    Messages : 829
    Points : 872
    Points
    872
    Par défaut
    Bien joué, moi je l'avais implémenté en Python pour faire un suivi de point sur un film de prothèse. C'est assez robuste, faut il choisir le bon seuil pour avoir les points les plus robuste !

    Bravo encore
    [SIZE="2"]Dis moi qui tu suis, je te dirais qui je Hais!
    Heureux est l'étudiant, qui comme la rivière suit son cours sans sortir de son lit

    Mon premier Tutoriel


    A 80% des cas je résouts mon problème en rédigeant une nouvelle discussion, du coup je ne poste que 20% de mes problèmes...

  3. #3
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par parp1
    Bien joué, moi je l'avais implémenté en Python pour faire un suivi de point sur un film de prothèse. C'est assez robuste, faut il choisir le bon seuil pour avoir les points les plus robuste !

    Bravo encore
    Merci. Je n'ai pas vraiment de merite. Cet algo est largement connu, je n'ai fait que le réecrire en Java.

    En fait, a l'instar de millie, je fais un peu de menage dans mes archives de code. J'en profite pour remettre au propre et poster les algos usuels.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  4. #4
    Membre éclairé
    Avatar de parp1
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    829
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Calvados (Basse Normandie)

    Informations forums :
    Inscription : Mai 2005
    Messages : 829
    Points : 872
    Points
    872
    Par défaut
    Dès lors ou tu passes du temps a faire quelque chose, et que tu mets en ligne tu as du merite, ce que je veux dire par la... tu pourrais te faire ta petite ToolBox pour le traitement d'image, mais non tu en fait profité les copains.
    [SIZE="2"]Dis moi qui tu suis, je te dirais qui je Hais!
    Heureux est l'étudiant, qui comme la rivière suit son cours sans sortir de son lit

    Mon premier Tutoriel


    A 80% des cas je résouts mon problème en rédigeant une nouvelle discussion, du coup je ne poste que 20% de mes problèmes...

  5. #5
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Nouvelle version du filtre:

    - utilisation d'une fenetre gaussienne lors du calcul de la matrice de harris (second-moment).
    - recherche du maxima local de la mesure de Harris (8 pixels voisins)
    - filtrage des points trop proche ou ayant une mesure trop faible

    Bref de meilleurs résultats, au detriment des perfs (la recherche du maxima local implique de calculer 9 mesures)
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2007
    Messages : 32
    Points : 24
    Points
    24
    Par défaut
    y a t'il une version C++ de cet algorithme?

  7. #7
    Rédacteur

    Avatar de millie
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7 015
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7 015
    Points : 9 818
    Points
    9 818
    Par défaut
    Citation Envoyé par kruskal21
    y a t'il une version C++ de cet algorithme?
    En vérité, avoir une version C++ ne veut pas dire grand chose. Il est nécessaire de spécifier la bibliothèque que tu utilises pour traiter tes images.

    De plus, la discussion est ici et pas dans le domaine Java car le code permet assez facilement (cela dit, ça peut prendre un peu de temps), en connaissant un peu Java, de pouvoir le transcrire dans le langage et la bibliothèque de ton choix.
    Je ne répondrai à aucune question technique en privé

  8. #8
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par kruskal21
    y a t'il une version C++ de cet algorithme?
    Faut demander gentiment à Millie de le porter dans la MillieLib...
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  9. #9
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    177
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2006
    Messages : 177
    Points : 106
    Points
    106
    Par défaut re
    Annule et remplace :

    Voici maintenant mon lanceur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    		String filename = "baboon.jpg";
     
    		ImagePlus ipl = new ImagePlus(filename);
    		ImageProcessor ipr = ipl.getProcessor();
    		Harris_ harris = new Harris_();
    		harris.setup("", ipl);
    		harris.run(ipr);
    j'avais pas saisi le fait de mettre un harris.setup pour que cela fonctionne.
    Maintenant c'est bon ! Sinon l'argument 1 de setup() est facultatif ??

    Bonne soirée

  10. #10
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par Methode Voir le message
    j'avais pas saisi le fait de mettre un harris.setup pour que cela fonctionne.
    Maintenant c'est bon ! Sinon l'argument 1 de setup() est facultatif ??
    Le classe présentée ici est un PLUGIN pour ImageJ. Elle n'est pas conçue pour être appelable depuis un main(), mais pour être utilisée par le programme executable "ImageJ" qui se charge d'appeler les methodes de l'interface "setup()" et "run()"

    Cela dit, il est possible de recupérer le code des autres methodes.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  11. #11
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    177
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2006
    Messages : 177
    Points : 106
    Points
    106
    Par défaut mon code harris
    Bonjour,

    J'ai tenté de faire ma propre implémentation du détecteur de Harris.
    Cependant sur une image entière le temps de calcul est très long et les résultats pas très probants...

    Quelqu'un saurait-il dire ce qui ne vas vraiment pas ?

    Pour le faire fonctionner :
    - API Jama (Java Matrix)
    - API JAI
    nécessaires...

    Deux classes :
    - ImageNM.java
    - Harris.java (classe main[])

    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
     
    package imageprocessing;
     
    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
     
    import javax.media.jai.JAI;
    import javax.media.jai.PlanarImage;
     
    import Jama.Matrix;
    import Jama.SingularValueDecomposition;
     
    public class Harris {
     
    	private static ImageNM img = new ImageNM("lena_b&w.jpg");
    	private static int minDistance = 2;
    	private static int nbpts = 8;
     
    	/**
             * @param args
             */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
     
    		int width = img.getWidth();
    		int height = img.getHeight();
    		int size = height * width;
     
    		// Conversion niveaux de gris
    		img.convert2GrayLevel();		
     
    		// Calcul des gradients de Sobel X et Y : on obtient les images Ix et Iy
    		PlanarImage img_sobx = img.SobelX();
    		PlanarImage img_soby = img.SobelY();
     
    		// Calcul des images Ixx Iyy et Ixy
    		PlanarImage sobxx = JAI.create("multiply", img_sobx, img_sobx);
    		PlanarImage sobyy = JAI.create("multiply", img_soby, img_soby);
    		PlanarImage sobxy = JAI.create("multiply", img_sobx, img_soby);
     
    		ImageNM img_sobxx = new ImageNM(sobxx);
    		ImageNM img_sobyy = new ImageNM(sobyy);
    		ImageNM img_sobxy = new ImageNM(sobxy);
     
    		// Lissage des images Ixx Iyy Ixy par un noyau gaussien
    		PlanarImage gimg_sobxx = img_sobxx.convolveByGaussian();
    		PlanarImage gimg_sobyy = img_sobyy.convolveByGaussian();
    		PlanarImage gimg_sobxy = img_sobxy.convolveByGaussian();
     
    		double[] A = new double[3];
    		double[] B = new double[3];
    		double[] C = new double[3];
     
    		double[] Trace = new double[size];
    		double[] Det = new double[size];
    		double[] R = new double[size];
     
     
    		// Calcul des coins de harris pour chaque pixel
    		for (int i = 0; i < width; i++) {
    			for (int j = 0; j < height; j++) {
    				gimg_sobxx.getData().getPixel(i, j, A);
    				gimg_sobyy.getData().getPixel(i, j, B);
    				gimg_sobxy.getData().getPixel(i, j, C);
     
    				double[][] data = { { A[0], C[0] }, { C[0], B[0] } };
     
    				Matrix M = new Matrix(data);
    				SingularValueDecomposition singularvalues = M.svd();
    				double[] singularvaluetab = singularvalues.getSingularValues();
     
    				Trace[j * width + i] = singularvaluetab[0]
    						+ singularvaluetab[1];
    				Det[j * width + i] = singularvaluetab[0] * singularvaluetab[1];
    				R[j * width + i] = Det[j * width + i] - 0.06
    						* Math.pow((Trace[j * width + i]), 2);
     
    			}
    		}
     
    		double[] nb = new double[8];
    		List<Rmark> listR = new ArrayList<Rmark>();
     
    		// Extraction des maxima locaux pour chaque pixel
    		for (int i = 0; i < R.length; i++) {
     
    			nb[0] = getNeighbours(R, i, -1, 1);
    			nb[1] = getNeighbours(R, i, 0, 1);
    			nb[2] = getNeighbours(R, i, 1, 1);
    			nb[3] = getNeighbours(R, i, -1, 0);
    			nb[4] = getNeighbours(R, i, 1, 0);
    			nb[5] = getNeighbours(R, i, -1, -1);
    			nb[6] = getNeighbours(R, i, 0, -1);
    			nb[7] = getNeighbours(R, i, 1, -1);
     
    			boolean added = false;
    			for (int j = 0; j < nb.length; j++) {
    				if ((R[i] > nb[j]) && (R[i] > 0) && !added){
    					listR.add(new Rmark(R[i],i%width,i/width));
    					added = true;
    				}
     
    			}
     
    		}
     
    		// tri les mesures de harris dans l'ordre décroissant
    		java.util.Collections.sort(listR, java.util.Collections.reverseOrder());
     
    		// Supprime les coins trop proches (répétabilité)
    		Iterator&lt;Rmark> iter = listR.iterator();
    		while(iter.hasNext()){
    			Rmark rmark = iter.next();
    			for(Rmark rmark2 : listR){
    				if((rmark.i==rmark2.i)&&(rmark.j==rmark2.j)) continue;
    				int dist = (int)Math.sqrt((rmark.i-rmark2.i)*(rmark.i-rmark2.i)+(rmark.j-rmark2.j)*(rmark.j-rmark2.j));
    				if(dist>minDistance) continue;
    				if(rmark2.interest<rmark.interest) continue;
    				iter.remove();
    				break;
    			}	
    		}
     
    		// sélectionne les nbpts les plus représentatifs d'un coin
    		List<Rmark> sorted_selected_RList = new ArrayList<Rmark>();	
    		sorted_selected_RList = listR.subList(0, nbpts);	
     
    		// Affiche des croix '+' sur l'image
    		PlanarImage pimg= img.getImage();
     
    		BufferedImage bimg = pimg.getAsBufferedImage();
    		Graphics2D g2d = bimg.createGraphics();
     
    		g2d.setColor(new Color(255,0,0));
    		for (Rmark rmark : sorted_selected_RList) {
    			int x=rmark.i;
    			int y=rmark.j;
     
    			g2d.drawLine(x-2,y,x+2,y);
    			g2d.drawLine(x,y-2,x,y+2);
    		}
     
    		ImageNM tempimg = new ImageNM(bimg);
    		tempimg.displayImage();
     
    		System.out.println(sorted_selected_RList.toString());
     
    	}
     
     
    	private static double getNeighbours(double[] src1d, int i, int ox, int oy) {
    		int x, y;
    		double result;
     
    		x = (i % img.getWidth()) + ox; // d_w and d_h are assumed to be set
    											// to the
    		y = (i / img.getWidth()) + oy; // width and height of scr1d
     
    		if ((x < 0) || (x >= img.getWidth()) || (y < 0)
    				|| (y >= img.getHeight())) {
    			result = 0;// (int) (Math.random()*src1d.length);
    		} else {
    			result = src1d[y * img.getWidth() + x];// & 0x000000ff;
    		}
    		return result;
    	}
     
    	static class Rmark implements Comparable<Rmark>{
    		double interest;
    		int i,j;
     
    		public Rmark(double interest,int i, int j) {
    			this.interest = interest;
    			this.i = i;
    			this.j = j;
    		}
     
    		public int compareTo(Rmark o) {
    			return (int)Math.round(interest-o.interest);
    		}
     
    		public String toString(){
    			return "coord : ("+i+" ,"+j+")"; 
    		}
     
     
    	}
     
     
     
    }

    et la classe ImageNM :

    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
     
    package imageprocessing;
     
    import java.awt.BorderLayout;
    import java.awt.Container;
    import java.awt.image.BufferedImage;
    import java.awt.image.ColorModel;
    import java.awt.image.Raster;
    import java.awt.image.renderable.ParameterBlock;
    import java.io.File;
    import java.io.IOException;
     
    import javax.media.jai.JAI;
    import javax.media.jai.KernelJAI;
    import javax.media.jai.PlanarImage;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
     
    import com.sun.media.jai.widget.DisplayJAI;
     
    public class ImageNM {
     
    	private PlanarImage image;
     
    	private String imagename;
     
    	private int width, height;
     
    	private int size;
     
    	private ColorModel cm;
     
    	public ImageNM(File imagefileHandle) {
    		try {
    			this.imagename = imagefileHandle.getCanonicalPath();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		try {
    			image = readImage(imagename);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		width = image.getWidth();
    		height = image.getHeight();
    		size = width * height;
    	}
     
    	public ImageNM(String filename) {
    		imagename = filename;
    		try {
    			image = readImage(filename);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		width = image.getWidth();
    		height = image.getHeight();
    		size = width * height;
    	}
     
    	public ImageNM(BufferedImage imbuf) {
    		imagename = "temp";
     
    		// 1st solution BufferedImage --> PlanarImage
    		// ParameterBlock pb = new ParameterBlock();
    		// pb.add(imbuf);
    		//
    		// image = (PlanarImage) JAI.create("awtImage", pb);
     
    		// 2nd solution BufferedImage --> PlanarImage
    		image = PlanarImage.wrapRenderedImage(imbuf);
    		width = imbuf.getWidth();
    		height = imbuf.getHeight();
    		size = width * height;
    	}
     
    	public ImageNM(PlanarImage plimage, String plimagename) {
    		imagename = plimagename;
    		image = plimage;
    		width = plimage.getWidth();
    		height = plimage.getHeight();
    		size = width * height;
    	}
     
    	public ImageNM(PlanarImage plimage) {
    		imagename = "temp";
    		image = plimage;
    		width = plimage.getWidth();
    		height = plimage.getHeight();
    		size = width * height;
    	}
     
    	public PlanarImage readImage(String filename) throws IOException {
    		PlanarImage image = JAI.create("fileload", filename);
    		return image;
    	}
     
    	public void writeImage(PlanarImage imageout, String nameout) {
    		JAI.create("filestore", imageout, nameout);
    	}
     
    	public void displayImage() {
     
    		JFrame frame = new JFrame("Image " + imagename);
    		frame.setSize(width, height);
     
    		Container contentPane = frame.getContentPane();
    		contentPane.setLayout(new BorderLayout());
    		DisplayJAI dj = new DisplayJAI(image);
    		contentPane.add(new JScrollPane(dj), BorderLayout.CENTER);
     
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.setVisible(true);
    	}
     
     
    	public int[] getPixel(int x, int y) {
    		int[] pixel = new int[image.getNumBands()];
    		image.getData().getPixel(x, y, pixel);
    		return pixel;
    	}
     
    	public int[] getGrayPixels(int x, int y, int w, int h) {
     
    		Raster raster = image.getData();
     
    		if (raster.getNumBands() == 3) {
    			PlanarImage grayImage = convert2GrayLevel();
    			Raster rastergray = grayImage.getData();
    			int[] pixels = new int[(w - x) * (h - y)];
     
    			rastergray.getPixels(x, y, w, h, pixels);
     
    			return pixels;
    		} else {
    			int[] graypixels = new int[(w - x) * (h - y)];
    			raster.getPixels(x, y, w, h, graypixels);
    			return graypixels;
    		}
     
    	}
     
     
    	public int[] getRGBPixels(int x, int y, int w, int h) {
     
    		Raster raster = image.getData();
     
    		if (raster.getNumBands() == 1) {
    			return getGrayPixels(x,y,w,h);
    		} else {
    			int pixels[][] = new int[raster.getNumBands()][(w - x) * (h - y)];
    			// added
    			int rgbpixels[] = new int[(w - x) * (h - y)];
     
    			for (int i = 0; i < raster.getNumBands(); i++) {
    				raster.getSamples(x, y, w, h, i, pixels[i]);
    			}
     
    			for (int i = 0; i < (w - x) * (h - y); i++) {
    				rgbpixels[i] = (pixels[0][i] << 16) | (pixels[1][i] << 8)
    						| (pixels[2][i]);
    			}
     
    			return rgbpixels;
    		}
    	}
     
    	public PlanarImage blurImage() {
     
    		float[] kernelData = { 1.0F / 14.0F, 2.0F / 14.0F, 1.0F / 14.0F,
    				2.0F / 14.0F, 2.0F / 14.0F, 2.0F / 14.0F, 1.0F / 14.0F,
    				2.0F / 14.0F, 1.0F / 14.0F };
     
    		KernelJAI kernel = new KernelJAI(3, 3, kernelData);
    		PlanarImage result = JAI.create("convolve", image, kernel);
    		return result;
    	}
     
    	public PlanarImage convolveByMoyen3x3() {
     
    		float[] kernelData = { 1.0F, 1.0F, 1.0F, 1.0F, 1.0F, 1.0F, 1.0F, 1.0F,
    				1.0F };
     
    		KernelJAI kernel = new KernelJAI(3, 3, kernelData);
    		PlanarImage result = JAI.create("convolve", image, kernel);
    		return result;
    	}
     
     
    	public PlanarImage convolveByGaussian() {
     
    		float[] kernelData = { 0.7F / 256F, 3.3F / 256F, 5.5F / 256F,
    				3.3F / 256F, 0.7F / 256F, 3.3F / 256F, 15.0F / 256F,
    				24.7F / 256F, 15F / 256F, 3.3F / 256F, 5.5F / 256F,
    				24.7F / 256F, 40.7F / 256F, 24.7F / 256F, 5.5F / 256F,
    				3.3F / 256F, 15.0F / 256F, 24.7F / 256F, 15F / 256F,
    				3.3F / 256F, 0.7F / 256F, 3.3F / 256F, 5.5F / 256F,
    				3.3F / 256F, 0.7F / 256F };
     
    		KernelJAI kernel = new KernelJAI(5, 5, kernelData);
    		PlanarImage result = JAI.create("convolve", image, kernel);
    		return result;
    	}
     
    	public PlanarImage convolveByLaplacian() {
     
    		float[] kernelData = { 0.0F, -1.0F, 0.0F, -1.0F, 4.0F, -1.0F, 0.0F,
    				-1.0F, 0.0F };
     
    		KernelJAI kernel = new KernelJAI(3, 3, kernelData);
    		PlanarImage result = JAI.create("convolve", image, kernel);
    		return result;
    	}
     
     
    	 /**
         * Filtre de SobelX
         * @return l'image modifi�e
         */
        public PlanarImage SobelX()
        {	    	
     
        	float data_v[] = new float[]
        	                    		{
        	               			   1.0F ,  1.414F , 1.0F ,
        	                            0.0F ,   0.0F   ,  0.0F ,
        	                            1.0F ,   1.414F ,  1.0F
        	                           }; 
     
        	//KernelJAI kernel = new KernelJAI(3, 3, 1, 1, data_v);
        	KernelJAI kernel = new KernelJAI(3,3,data_v);
        	PlanarImage result = JAI.create("convolve", image, kernel);
            return result;
        }
     
        /**
         * Filtre de SobelY
         * @return l'image modifi�e
         */
        public PlanarImage SobelY()
        {	    	
     
        	float data_h[] = new float[]
           	           				{
           	           				1.0F  , 0.0F , -1.0F,
           	           				1.414F, 0.0F , -1.414F,
           	           				1.0F  , 0.0F , -1.0F
           	};
     
        	//KernelJAI kernel = new KernelJAI(3, 3, 1, 1, data_v);
        	KernelJAI kernel = new KernelJAI(3,3,data_h);
        	PlanarImage result = JAI.create("convolve", image, kernel);
            return result;
        }
     
     
    	public PlanarImage edgesByRoberts() {
     
    		float[] data_h = {0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, -1.0F,
    				0.0F };
     
    		float[] data_v = {0.0F, 0.0F, 0.0F, 0.0F, -1.0F, 1.0F, 0.0F, 0.0F,
    				1.0F };
     
    		KernelJAI kern_h = new KernelJAI(3,3,data_h);
         	KernelJAI kern_v = new KernelJAI(3,3,data_v);
    		PlanarImage result = JAI.create("gradientmagnitude", image, kern_h, kern_v);
    		return result;
    	}
     
    	public PlanarImage edgesByPrewitt() {
     
    		float[] data_h = {-1.0F, 0.0F, 1.0F, -1.0F, 0.0F, 1.0F, -1.0F, 0.0F,
    				1.0F };
     
    		float[] data_v = {-1.0F, -1.0F, -1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 1.0F,
    				1.0F };
     
    		KernelJAI kern_h = new KernelJAI(3,3,data_h);
         	KernelJAI kern_v = new KernelJAI(3,3,data_v);
    		PlanarImage result = JAI.create("gradientmagnitude", image, kern_h, kern_v);
    		return result;
    	}
     
     
    	/**
         * Filtre de SobelXY
         * @return l'image modifiée
         */
        public PlanarImage edgesBySobel()
        {
     
        	float data_h[] = new float[]
        	           				{
        	           				1.0F  , 0.0F , -1.0F,
        	           				1.414F, 0.0F , -1.414F,
        	           				1.0F  , 0.0F , -1.0F
        	};
     
         	float data_v[] = new float[]
         		{
    			1.0F ,  1.414F , 1.0F ,
                 0.0F ,   0.0F   ,  0.0F ,
                 1.0F ,   1.414F ,  1.0F
                };
     
    		KernelJAI kern_h = new KernelJAI(3,3,data_h);
         	KernelJAI kern_v = new KernelJAI(3,3,data_v);
    		//operation de detection de contour
     		PlanarImage result ;
            return result = (PlanarImage)JAI.create("gradientmagnitude",image, kern_h, kern_v);
        }
     
     
    	public PlanarImage convert2GrayLevel() {
     
    		Raster raster = image.getData();
     
    		if (raster.getNumBands() == 3) {
    		double[][] matrix = { { 0.114, 0.587, 0.299, 0.0 } };
     
    		// Create the ParameterBlock.
    		ParameterBlock pb = new ParameterBlock();
    		pb.addSource(image);
    		pb.add(matrix);
     
    		// Perform the convolution
    		PlanarImage result = (PlanarImage) JAI.create("bandcombine", pb, null);
    		return result;
    		}
    		else{
    			throw new IllegalArgumentException("Image # bands <> 3");
    		}	
     
    	}
     
    	public int getHeight() {
    		return height;
    	}
     
    	public PlanarImage getImage() {
    		return image;
    	}
     
    	public String getImagename() {
    		return imagename;
    	}
     
    	public int getWidth() {
    		return width;
    	}
     
    	public static void main(String[] args) {
     
    		ImageNM image = new ImageNM("lena_b&w.jpg");
    		image.displayImage();
    		ImageNM imageconvolved = new ImageNM(image.edgesByPrewitt());
    		imageconvolved.displayImage();
     
    	}
     
    }

  12. #12
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Matrix M = new Matrix(data);
    SingularValueDecomposition singularvalues = M.svd();
    double[] singularvaluetab = singularvalues.getSingularValues();

    Dans le detecteur de Harris, on n'a pas besoin de cherchers les valeurs propres de la matrice. On calcule la courbure (R, Determinant et Trace) en utilisant les elements de la matrice M.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  13. #13
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    177
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2006
    Messages : 177
    Points : 106
    Points
    106
    Par défaut re
    Ok je vais changer ça.

    Cependant cela ne devrait pas tellement influencer les résultats (je ne sais si tu as tester le prog?) puisque :

    Trace(M) = λ1+ λ2 = M1,1 + M2,2
    Déterminant(M) = λ1λ2 = M1,1*M2,2 – M1,2*M2,1

    R = Déterminant(M) – k(Trace(M))^2

    Merci à toi

  14. #14
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Le résultat sans doute pas, mais la vitesse surement...

    J'en profite pour poster une version "stand-alone" du filtre, qui précalcule les valeurs filtrés des derivées F*Lx², F*Ly² et F*Lxy pour accelerer le temps de traitement (au detriment de la mémoire).

    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
     
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
     
     
    /**
     * Harris Corner Detector
     *              
     *  k = det(A) - k * trace(A)^2
     * 
     *  Where A is the second-moment matrix 
     * 
     *            | Lx²(x+dx,y+dy)    Lx.Ly(x+dx,y+dy) |
     *  A =  Sum  |                                    | * Gaussian(dx,dy)
     *      dx,dy | Lx.Ly(x+dx,y+dy)  Ly²(x+dx,y+dy)   |
     * 
     *  and k = a/(1+a)^2, 
     *  
     *  where "a" is the mininmum ratio between the two eigenvalues
     *  for a point to be considered as a corner.
     *  
     * @author Xavier Philippeau
     */
    public class HarrisFast  {
     
    	// corner class
    	class Corner {
    		int x,y; // corner position
    		float h; // harris measure
    		public Corner(int x, int y, float h) {
    			this.x=x; this.y=y; this.h=h;
    		}
    	}
     
    	// corners list
    	List<Corner> corners = new ArrayList<Corner>();
     
    	// image
    	private int[][] image;
    	int width,height;
     
    	// precomputed values of the derivatives
    	private float[][] Lx2,Ly2,Lxy;
     
    	/**
             *  Constructor
             */
    	public HarrisFast(int[][] image, int width, int height) {
    		this.image = image;
    		this.width = width;
    		this.height = height;
    	}
     
    	/**
             * Gaussian function
             */
    	private double gaussian(double x, double y, double sigma) {
    		double sigma2 = sigma*sigma;
    		double t = (x*x+y*y)/(2*sigma2);
    		double u = 1.0/(2*Math.PI*sigma2);
    		double e = u*Math.exp( -t );
    		return e;
    	}
     
    	/**
             * Sobel gradient 3x3
             */
    	private float[] sobel(int x, int y) {
    		int v00=0,v01=0,v02=0,v10=0,v12=0,v20=0,v21=0,v22=0;
     
    		int x0 = x-1, x1 = x, x2 = x+1;
    		int y0 = y-1, y1 = y, y2 = y+1;
    		if (x0<0) x0=0;
    		if (y0<0) y0=0;
    		if (x2>=width) x2=width-1;
    		if (y2>=height) y2=height-1;
     
    		v00=image[x0][y0]; v10=image[x1][y0]; v20=image[x2][y0];
    		v01=image[x0][y1];                    v21=image[x2][y1];
    		v02=image[x0][y2]; v12=image[x1][y2]; v22=image[x2][y2];
     
    		float sx = ((v20+2*v21+v22)-(v00+2*v01+v02))/(4*255f);
    		float sy = ((v02+2*v12+v22)-(v00+2*v10+v20))/(4*255f);
    		return new float[] {sx,sy};
    	}
     
     
    	/**
             * Compute the 3 arrays Ix, Iy and Ixy
             */
    	private void computeDerivatives(double sigma){
    		this.Lx2 = new float[width][height];
    		this.Ly2 = new float[width][height];
    		this.Lxy = new float[width][height];
     
    		// gradient values: Gx,Gy
    		float[][][] grad = new float[width][height][];
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				grad[x][y] = sobel(x,y);
     
    		// precompute the coefficients of the gaussian filter
    		int radius = (int)(2*sigma);
    		int window = 1+2*radius;
    		float[][] gaussian = new float[window][window];
    		for(int j=-radius;j<=radius;j++)
    			for(int i=-radius;i<=radius;i++)
    				gaussian[i+radius][j+radius]=(float)gaussian(i,j,sigma);
     
    		// Convolve gradient with gaussian filter:
    		//
    		// Ix2 = (F) * (Gx^2)
    		// Iy2 = (F) * (Gy^2)
    		// Ixy = (F) * (Gx.Gy)
    		//
    		for (int y=0; y<height; y++) {
    			for (int x=0; x<width; x++) {
     
    				for(int dy=-radius;dy<=radius;dy++) {
    					for(int dx=-radius;dx<=radius;dx++) {
    						int xk = x + dx;
    						int yk = y + dy;
    						if (xk<0 || xk>=width) continue;
    						if (yk<0 || yk>=height) continue;
     
    						// gaussian weight
    						double gw = gaussian[dx+radius][dy+radius];
     
    						// convolution
    						this.Lx2[x][y]+=gw*grad[xk][yk][0]*grad[xk][yk][0];
    						this.Ly2[x][y]+=gw*grad[xk][yk][1]*grad[xk][yk][1];
    						this.Lxy[x][y]+=gw*grad[xk][yk][0]*grad[xk][yk][1];
    					}
    				}
    			}
    		}
    	}
     
    	/**
             * compute harris measure for a pixel
             */
    	private float harrisMeasure(int x, int y, float k) {
    		// matrix elements (normalized)
    		float m00 = this.Lx2[x][y]; 
    		float m01 = this.Lxy[x][y];
    		float m10 = this.Lxy[x][y];
    		float m11 = this.Ly2[x][y];
     
    		// Harris corner measure = det(M)-k.trace(M)^2
    		return m00*m11 - m01*m10 - k*(m00+m11)*(m00+m11);
    	}
     
    	/**
             * return true if the measure at pixel (x,y) is a local spatial Maxima
             */
    	private boolean isSpatialMaxima(float[][] hmap, int x, int y) {
    		int n=8;
    		int[] dx = new int[] {-1,0,1,1,1,0,-1,-1};
    		int[] dy = new int[] {-1,-1,-1,0,1,1,1,0};
    		double w =  hmap[x][y];
    		for(int i=0;i<n;i++) {
    			double wk = hmap[x+dx[i]][y+dy[i]];
    			if (wk>=w) return false;
    		}
    		return true;
    	}
     
    	/**
             * compute the Harris measure for each pixel of the image
             */
    	private float[][] computeHarrisMap(double k) {
     
    		// Harris measure map
    		float[][] harrismap = new float[width][height];
     
    		// for each pixel in the image
    		for (int y=0; y<height; y++) {
    			for (int x=0; x<width; x++) {
    				// compute the harris measure
    				double h =  harrisMeasure(x,y,(float)k);
    				if (h<=0) continue;
    				// log scale
    				h = 255 * Math.log(1+h) / Math.log(1+255);
    				// store
    				harrismap[x][y]=(float)h;
    			}
    		}
     
    		return harrismap;
    	}
     
    	/**
             * Perfom the Harris Corner Detection
             * 
             * @param sigma gaussian filter parameter 
             * @param k parameter of the harris measure formula
             * @param minDistance minimum distance between corners
             * @return the orginal image marked with cross sign at each corner
             */
    	public int[][] filter(double sigma, double k, int minDistance) {
     
    		// precompute derivatives
    		computeDerivatives(sigma);
     
    		// Harris measure map
    		float[][] harrismap = computeHarrisMap(k);
     
    		// for each pixel in the harrismap 
    		for (int y=1; y<height-1; y++) {
    			for (int x=1; x<width-1; x++) {
    				// thresholding : harris measure > epsilon
    				float h = harrismap[x][y];
    				if (h<=1E-3) continue;
    				// keep only a local maxima
    				if (!isSpatialMaxima(harrismap, x, y)) continue;
    				// add the corner to the list
    				corners.add( new Corner(x,y,h) );
    			}
    		}
     
    		System.out.println(corners.size()+" potential corners found.");
     
    		// remove corners to close to each other (keep the highest measure)
    		Iterator<Corner> iter = corners.iterator();
    		while(iter.hasNext()) {
    			Corner p = iter.next();
    			for(Corner n:corners) {
    				if (n==p) continue;
    				int dist = (int)Math.sqrt( (p.x-n.x)*(p.x-n.x)+(p.y-n.y)*(p.y-n.y) );
    				if(dist>minDistance) continue;
    				if (n.h<p.h) continue;
    				iter.remove();
    				break;
    			}
    		}
     
    		// output
    		int[][] output =new int[width][height];
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				output[x][y]=(int)(image[x][y]*0.75); // original image (darker)
     
    		// for each corner
    		for (Corner p:corners) {
    			// add the cross sign over the image
    			for (int dt=-3; dt<=3; dt++) {
    				if (p.x+dt>=0 && p.x+dt<width ) output[p.x+dt][p.y]=255;
    				if (p.y+dt>=0 && p.y+dt<height) output[p.x][p.y+dt]=255;
    			}
    			System.out.println("corner found at: "+p.x+","+p.y+" ("+p.h+")");
    		}		
    		System.out.println(corners.size()+" corners found.");
     
    		return output;
    	}
    }

    Pour l'utiliser:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    int[][] input = ... // tableau 2D [x][y] contenant l'image en niveau de gris (0-255)
    int width     = ... // largeur de l'image
    int height    = ... // hauteur de l'image
     
    int sigma   = 1.2;  // parametre du filtre gaussien
    int k       = 0.06; // parametre de la formule de la mesure
    int spacing = 8;    // distance minimum entre 2 coins
     
    int[][] output = new HarrisFast(img,width,height).filter(sigma,k,spacing);
     
    // output[][] : image d'entrée avec les coins marqués par des croix
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  15. #15
    Candidat au Club
    Inscrit en
    Juillet 2008
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 4
    Points : 2
    Points
    2
    Par défaut
    Bonjour,

    j'essaye désespérément de savoir de quoi est capable un détecteur de Harris, et surtout si je vais pouvoir l'utiliser pour mon projet. Voilà j'y connais pas grand chose en "computer vision" et j'aimerais trouver un algo qui puisse me donner les coordonnées de formes géométriques simples (rectangles).
    Naïvement je pense que le détecteur de Harris est la solution à mon pb.

    Voilà donc je tente d'utiliser votre implémentation, mais le résultat qu'il me sort n'est pas très cohérent. Mon niveau en graphic avec java est assez faible, je débute, donc je dois surement passer de mauvaise ressource à Fast Harris.

    Toutes remarques bienvenues !
    Merci

    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    		Image sourceImage;
    		Toolkit toolkit = Toolkit.getDefaultToolkit();
    		sourceImage = toolkit.getImage("E:\\Workspace\\tmp\\test-corners.png");
     
     
    		int[][] input; // tableau 2D [x][y] contenant l'image en niveau de gris (0-255)
    		int width; // largeur de l'image
    		int height; // hauteur de l'image
     
    		int window  = 3;   // taille du filtre gaussien
    		double sigma   = 1.2; // parametre du filtre gaussien
    		int min     = 32;  // courbure minimum pour un coin
    		int spacing = 8;   // distance minimum entre 2 coins
     
    		PixelGrabber grabber = new PixelGrabber(sourceImage, 0, 0, -1, -1, false);
    		grabber.startGrabbing();
    		if (grabber.grabPixels()) {
    			boolean isGrey = isGreyscaleImage(grabber);
                width = grabber.getWidth();
                height = grabber.getHeight();
                byte[] data = (byte[]) grabber.getPixels();
                input = new int[width][height];
     
                for (int x=0; x < width; x++)
        		{
        			for (int y=0; y < height; y++)
        			{
        				int offset = x*height + y;
        				byte val = data[offset];
        				if (val == -1){
        					input[x][y] = (int)255;
        				}else{
        					input[x][y] = (int) val;
        				}
        			}
        		}
     
                int[][] output = new HarrisFast(input,width,height).filter(window,sigma,min,spacing);
     
                int[] outData = new int[width * height];
                for (int x=0; x < width; x++)
        		{
        			for (int y=0; y < height; y++)
        			{
        				int offset = x*height + y;
        				int val = output[x][y];
        				if (val == 255){
        					outData[offset] = -1;
        				}else{
        					outData[offset] = val;
        				}
        			}
        		}
     
                BufferedImage out = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                out.setRGB(0, 0, width, height, outData, 0, width);
                File file = new File("E:\\Workspace\\tmp\\out-harris.png");
                ImageIO.write(out, "png", file);

  16. #16
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    177
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2006
    Messages : 177
    Points : 106
    Points
    106
    Par défaut facteur*1000
    Pour revenir sur le détecteur de Harris dans le code de pseudocode.
    A quoi sert le coef multiplicateur * 1000 lors du calcul des coefficients gaussiens ?

  17. #17
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par Methode Voir le message
    Pour revenir sur le détecteur de Harris dans le code de pseudocode.
    A quoi sert le coef multiplicateur * 1000 lors du calcul des coefficients gaussiens ?
    Hum... au départ je dirais que c'etait pour stocker les valeurs sur des "int" et pas des "double" pour accelerer les calculs. Mais comme grad[][] est un double, ca n'a plus aucun interet.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  18. #18
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    177
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2006
    Messages : 177
    Points : 106
    Points
    106
    Par défaut re
    oki... merci

  19. #19
    Nouveau Candidat au Club
    Inscrit en
    Janvier 2009
    Messages
    1
    Détails du profil
    Informations forums :
    Inscription : Janvier 2009
    Messages : 1
    Points : 1
    Points
    1
    Par défaut comment limiter le nombre de corners
    merci pour le code , personnellement je l'ai tester et ça marche convenablement. Le seul problème que j'ai rencontré c'est comment fixer un seuil maximal pour le nombre de coins sachant que dans votre code le paramètre nmbmax est écrit dans le commentaire mais pas utilisé dans la méthode filtre .Donc je vous demande comment procéder pour limiter le nombre de coin à seuil donné.
    /**
    * Perfom Harris Corner Detection
    *
    * @param c Image map
    * @param tilesize size of a tile
    * @param nmbmax max number of corner to keep
    * @return filtered image map
    */
    public ByteProcessor filter(ByteProcessor c, int minMeasure, int minDistance)

  20. #20
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par tarek.z Voir le message
    merci pour le code , personnellement je l'ai tester et ça marche convenablement. Le seul problème que j'ai rencontré c'est comment fixer un seuil maximal pour le nombre de coins sachant que dans votre code le paramètre nmbmax est écrit dans le commentaire mais pas utilisé dans la méthode filtre .Donc je vous demande comment procéder pour limiter le nombre de coin à seuil donné.
    Oups. Eclipse ne remet pas encore la javadoc a jour automatiquement.

    1. Je vous suggère d'utiliser/adapter le code du post#14 qui est une version un peu plus "optimisée" des calculs.

    2. Une méthode qui marche pas mal, c'est d'utiliser un seuil en pourcentage. Pour cela on calcule toutes les mesures de Harris, on normalise et font un simple test de dépassement.

    C'est la méthode qui est utilisée dans MillieGUI :

    HarrisFastDetectionOperator.java


    La normalisation entre 0 et 100 (en échelle Log et qui pourrait être optimisée ):
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // rescale measures in 0-100
    for (int y=0; y<this.height; y++) {
    	for (int x=0; x<this.width; x++) {
    		double h=harrismap[x][y];
    		if (h<0) h=0; else h = 100 * Math.log(1+h) / Math.log(1+max);
    		harrismap[x][y]=h;
    	}
    }

    Le test de dépassement:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // for each pixel in the hmap, keep the local maxima
    for (int y=1; y<this.height-1; y++) {
    	for (int x=1; x<this.width-1; x++) {
    		double h = harrismap[x][y];
    		if (h<this.minMeasure) continue;
    		if (!isSpatialMaxima(harrismap, (int)x, (int)y)) continue;
    		// add the corner to the list
    		getCorner(canal).add( new Corner(x,y,h) );
    	}
    }
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

Discussions similaires

  1. [image] Filtre Squelette pour ImageJ
    Par pseudocode dans le forum Contribuez
    Réponses: 47
    Dernier message: 12/08/2013, 11h41
  2. [Image] Filtre UnNoise pour ImageJ
    Par pseudocode dans le forum Contribuez
    Réponses: 37
    Dernier message: 07/03/2008, 17h23
  3. [Image] Filtre de Canny pour ImageJ
    Par pseudocode dans le forum Contribuez
    Réponses: 18
    Dernier message: 13/09/2007, 20h01
  4. [Image] Filtre UnNoise pour ImageJ
    Par pseudocode dans le forum Algorithmes et structures de données
    Réponses: 10
    Dernier message: 04/04/2007, 00h38
  5. Réponses: 20
    Dernier message: 19/12/2004, 19h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo