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 |
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