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
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
// 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;
double gy = g;
// 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
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
// for each tile in the image
for (int y=0; y
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
while(iter.hasNext()) {
int[] p = iter.next();
for(int[] n:corners) {
if (n==p) continue;
int dist = (int)Math.sqrt( (p-n)*(p-n)+(p-n)*(p-n) );
if( dist>minDistance) continue;
if (n
iter.remove();
break;
}
}
// Display corners over the image (cross)
for (int[] p:corners) {
for (int dx=-2; dx<=2; dx++) {
if (p+dx<0 || p+dx>=width) continue;
c2.set(p+dx,p,255);
}
for (int dy=-2; dy<=2; dy++) {
if (p+dy<0 || p+dy>=height) continue;
c2.set(p,p+dy,255);
}
//System.out.println("corner found at: "+p+","+p+" ("+p+")");
}
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
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
