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

Millie Discussion :

[MAJ] Plugin Canny


Sujet :

Millie

  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 [MAJ] Plugin Canny
    Une réimplémentation du filtre de canny. Rend obsolète l'opérateur millie.operator.detection.CannyOperator.

    Pour l'instant, j'utilise un appel "en dur" au plugin FastGaussianBlurPlugin, en attendant que le FastGaussianBlur soit dispo sous forme d'opérateur.

    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
     
    import java.awt.image.BufferedImage;
    import java.awt.image.Raster;
    import java.awt.image.WritableRaster;
    import java.util.LinkedList;
     
    import millie.automation.Automatable;
    import millie.plugins.PluginInfo;
    import millie.plugins.appimage.GenericAppImagePlugin;
    import millie.plugins.core.blur.FastGaussianBlurPlugin;
    import millie.plugins.parameters.CheckBoxParameter;
    import millie.plugins.parameters.IntSliderParameter;
    import millie.se.operator.GrayOperator;
    import millie.se.operator.GrayOperator.SelectedColor;
     
    /**
     * Canny Filter (edge detector)
     * 
     * @author Xavier Philippeau
     *
     */
    @PluginInfo(name="Détection de Canny", category="new", description="Détection de Canny")
    public class CannyFilterPlugin extends GenericAppImagePlugin implements Automatable {
     
    	private int width=0, height=0;
     
    	public CannyFilterPlugin() {
    		setReinitializable(true);
    		setLongProcessing(true);
    		addParameter(new IntSliderParameter("radius", "Sampling radius", 0, 10, 2));
    		addParameter(new IntSliderParameter("low", "Hysteresis low value (%)", 0, 100, 30));
    		addParameter(new IntSliderParameter("high", "Hysteresis high value (%)", 0, 100, 60));
    		addParameter(new CheckBoxParameter("logscale", "Use Log-Scale", true));
    	}
     
    	@Override
    	public BufferedImage filter() throws Exception {
    		int radius = getIntValue("radius");
    		int lowvalue = getIntValue("low");
    		int highvalue = getIntValue("high");
    		boolean logscale = getBooleanValue("logscale");
     
    		if (lowvalue>highvalue)
    			throw new IllegalArgumentException("Low > High");
     
    		// input image
    		BufferedImage input = this.getInputImage();
    		this.width = input.getWidth();
    		this.height = input.getHeight();
     
    		// convert to graylevel if needed
    		int numBands = input.getRaster().getNumBands();
    		if (numBands>1) {
    			GrayOperator grayOp = new GrayOperator(SelectedColor.ALL);
    			input = grayOp.compute(input);
    		}
     
    		// blur input image (if requested)
    		if (radius>0) {
    			FastGaussianBlurPlugin fgblur = new FastGaussianBlurPlugin();
    			fgblur.getParameter("rayon").setValue(radius);
    			fgblur.setInputImage( input );
    			input = fgblur.filter();
    		}
     
    		// Canny detection
    		BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
    		canny(input.getRaster(), output.getRaster(), 0, lowvalue, highvalue, logscale);
    		return output;
    	}
     
     
    	/**
             * Compute the local gradient
             * 
             * @param raster Image raster
             * @param x x coord of the computation (int)
             * @param y y coord of the computation (int)
             * @return norme and value of the gradient
             */
    	private double[] gradient(Raster raster, int x, int y, int band) {
    		// limit to image dimension
    		if (x<0) x=0; else if (x>=width)  x=width-1;
    		if (y<0) y=0; else if (y>=height) y=height-1;
     
    		int px = x - 1;  // previous x
    		int nx = x + 1;  // next x
    		int py = y - 1;  // previous y
    		int ny = y + 1;  // next y
     
    		// limit to image dimension
    		if (px < 0)	px = 0;
    		if (nx >= width) nx = width - 1;
    		if (py < 0)	py = 0;
    		if (ny >= height) ny = height - 1;
     
    		// Intesity of the 8 neighbors
    		int Ipp = raster.getSample( px,py, band);
    		int Icp = raster.getSample( x,py,  band);
    		int Inp = raster.getSample( nx,py, band);
    		int Ipc = raster.getSample( px, y, band);
    		int Inc = raster.getSample( nx, y, band);
    		int Ipn = raster.getSample( px,ny, band);
    		int Icn = raster.getSample( x,ny,  band);
    		int Inn = raster.getSample( nx,ny, band);
     
    		// Local gradient
    		double gradx = ((Inc-Ipc)*2 + (Inn-Ipp) + (Inp-Ipn))/4.0; // horizontal + 2 diagonals
    		double grady = ((Icn-Icp)*2 + (Inn-Ipp) + (Ipn-Inp))/4.0; // vertical + 2 diagonals
     
    		if (gradx==0 && grady==0)
    			return new double[] { 0, 0, 0 };
     
    		double norme = Math.sqrt(gradx*gradx+grady*grady);  
    		return new double[] { norme, gradx/norme, grady/norme };
    	}
     
     
    	/**
             * Compute the norm of the local gradient (subpixel with bilinear interpolation)
             * 
             * @param gradient gradient map
             * @param x x coord of the computation (double)
             * @param y y coord of the computation (double)
             * @return interpolated value of the norm of the gradient
             */
    	private double gradientNorm(double[][][] gradient, double x, double y) {
    		// fractional part of coordinates
    		double wx = x - (int) x;
    		double wy = y - (int) y;
     
    		// limit to image dimension
    		int cx = (int) x, cy = (int) y;
    		if (cx < 0)	cx = 0;	else if (cx >= width) cx = width - 1;
    		if (cy < 0)	cy = 0; else if (cy >= height) cy = height - 1;
     
    		int nx = cx + 1, ny = cy + 1;
    		if (nx >= width) nx = width - 1;
    		if (ny >= height) ny = height - 1;
     
    		//  norm of the 4 neighbours
    		double Gpp = gradient[cx][cy][0];
    		double Gnp = gradient[nx][cy][0];
    		double Gpn = gradient[cx][ny][0];
    		double Gnn = gradient[nx][ny][0];
     
    		// bilinear interpolation
    		double wpp=(1-wx)*(1-wy), wnp= wx*(1-wy), wpn=(1-wx)*wy, wnn=wx*wy;
    		double norm = wpp*Gpp + wnp*Gnp + wpn*Gpn + wnn*Gnn;
     
    		return norm;
    	}
     
     
    	/**
             * compute if a position is a local maxima
             * 
             * @param c Image map
             * @param x x coord of the computation
             * @param y y coord of the computation
             * @return true if position is a local maxima
             */
    	private boolean isLocalMaxima(double[][][] gradient, int x, int y) {
     
    		// gradient at current position
    		double[] grad = gradient[x][y];
     
    		// gradient direction
    		double gx = grad[1]; //Math.cos( angle );
    		double gy = grad[2]; //Math.sin( angle );
     
    		// gradient value at next position in the gradient direction
    		double nx = x + gx;
    		double ny = y + gy;
    		double gradn = gradientNorm(gradient,nx,ny);
     
    		// gradient value at previous position in the gradient direction
    		double px = x - gx;
    		double py = y - gy;
    		double gradp = gradientNorm(gradient,px,py);
     
    		// is the current gradient value a local maxima ?
    		if (grad[0]>gradn  && grad[0]>=gradp) return true;
    		return false;
    	}
     
    	/**
             * @param input Input image raster 
             * @param output Output image raster
             * @param band band of the raster to process
             * @param lowThreshold Hysteresis low value (0...100%) 
             * @param highThreshold Hysteresis hight value (0...100%)
             * @param logscale use log-scale
             */
    	private void canny(Raster input, WritableRaster output, int band, int lowThreshold, int highThreshold, boolean logscale) {
    		int LOWSTATE=92, HIGHSTATE=255;
     
    		// list of pixels above highThreshold
    		LinkedList<int[]> highpixels = new LinkedList<int[]>();
     
    		// precompute gradient
    		double[][][] gradient = new double[width][height][];
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				gradient[x][y] = gradient(input, x, y, band);
     
    		// low/high intesity value of gradient
    		double glow = gradient[0][0][0], ghigh = glow;
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++) {
    				if (gradient[x][y][0]<glow)  glow=gradient[x][y][0];
    				if (gradient[x][y][0]>ghigh) ghigh=gradient[x][y][0];
     			}
     
    		// rescale values of gradient in [0,100]
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++) {
    				double d  = (gradient[x][y][0]-glow)/(ghigh-glow);
    				// use log-scale if requested
    				if (logscale) 
    					gradient[x][y][0] = 100.0*Math.max(0 , 1.16*Math.pow(d, 0.333)-0.16);
    				else
    					gradient[x][y][0] = 100.0*d;
    			}
     
    		// gradient thresholding
    		for (int y=0; y<height; y++) {
    			for (int x=0; x<width; x++) {
     
    				// gradient intesity
    				double g = gradient[x][y][0];
     
    				// low-threshold -> not an edge
    				if (g<lowThreshold) continue;
     
    				// not a local maxima -> not an edge
    				if (!isLocalMaxima(gradient, x, y)) continue;
     
    				// high-threshold -> is an edge
    				if (g>highThreshold) {
    					output.setSample(x, y, band, HIGHSTATE);
    					highpixels.addLast(new int[]{x,y});
    					continue;
    				}
     
    				// between thresholds -> "unknown state" (depends of neighbors)
    				output.setSample(x, y, band, LOWSTATE);
    			}
    		}
     
    		// edge continuation
    		int[] dx8 = new int[] {-1, 0, 1, 1, 1, 0,-1,-1};
    		int[] dy8 = new int[] {-1,-1,-1, 0, 1, 1, 1, 0};
     
    		// analyze 8 neighboors of each pixels in HIGHSTATE 
    		while(!highpixels.isEmpty()) {
    			int[] pixel = highpixels.removeFirst();
    			int x=pixel[0], y=pixel[1];
     
    			// move pixels from LOWSTATE to HIGHSTATE
    			for(int k=0;k<8;k++) {
    				if (safeGetSample(output, x+dx8[k], y+dy8[k], band)==LOWSTATE) {
    					output.setSample(x+dx8[k], y+dy8[k], band, HIGHSTATE);
    					highpixels.addLast(new int[]{x+dx8[k], y+dy8[k]});
    				}
    			}
    		}
     
    		// keep only pixels in HIGHSTATE
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				if (output.getSample(x, y, band)!=HIGHSTATE) 
    					output.setSample(x,y,band,0);
    				else
    					output.setSample(x,y,band,(int)(gradient[x][y][0]*2.55));
    	}
     
    	private static int safeGetSample(Raster raster, int x, int y, int band) {
    		if(x<0 || x>=raster.getWidth() ||y<0 || y>=raster.getHeight())
    			return 0;
    		return raster.getSample(x,y,band);
    	}
    }
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  2. #2
    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
    Je l'ai integré et j'ai cassé ton filtre en deux (ainsi que le FastGaussianBlur) :

    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
     
    package millie.plugins.core.detection;
     
    import java.awt.image.BufferedImage;
     
    import millie.automation.Automatable;
    import millie.plugins.PluginInfo;
    import millie.plugins.appimage.GenericAppImagePlugin;
    import millie.plugins.parameters.CheckBoxParameter;
    import millie.plugins.parameters.IntSliderParameter;
    import millie.se.operator.CannyOperator;
     
    @PluginInfo(name="Détection de Canny", category="Analyse", description="Détection de Canny")
    public class CannyPlugin extends GenericAppImagePlugin implements Automatable {
     
    	public CannyPlugin() {
    		setReinitializable(true);
    		setLongProcessing(true);
    		addParameter(new IntSliderParameter("radius", "Sampling radius", 0, 10, 2));
    		addParameter(new IntSliderParameter("low", "Hysteresis low value (%)", 0, 100, 30));
    		addParameter(new IntSliderParameter("high", "Hysteresis high value (%)", 0, 100, 60));
    		addParameter(new CheckBoxParameter("logscale", "Use Log-Scale", true));
    	}
     
     
    	@Override
    	public BufferedImage filter() throws Exception {
    		int radius = getIntValue("radius");
    		int lowvalue = getIntValue("low");
    		int highvalue = getIntValue("high");
    		boolean logscale = getBooleanValue("logscale");
     
    		if (lowvalue>highvalue)
    			throw new IllegalArgumentException("Low > High");
     
    		CannyOperator op = new CannyOperator(radius, lowvalue, highvalue, logscale);
    		return op.compute(getInputImage());
    	}
     
    }

    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
     
    /**
     * 
     */
    package millie.se.operator;
     
    import java.awt.image.BufferedImage;
    import java.awt.image.ColorModel;
    import java.awt.image.Raster;
    import java.awt.image.WritableRaster;
    import java.util.LinkedList;
     
    import millie.se.operator.GrayOperator.SelectedColor;
     
    /**
     * Canny Filter (edge detector)
     * 
     * @author Xavier Philippeau
     */
    public class CannyOperator extends SimpleOperator {
     
    	private int radius;
    	private int lowvalue;
    	private int highvalue;
    	private boolean logscale;
     
    	public CannyOperator(int radius, int lowvalue, int highvalue, boolean logscale) {
    		this.radius = radius;
    		this.logscale = logscale;
    		this.highvalue = highvalue;
    		this.lowvalue = lowvalue;
    	}
     
    	public BufferedImage createCompatibleDestImage(BufferedImage src,
    			ColorModel destCM) {
     
       		return new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
    	}
     
    	private int width;
    	private int height;
     
    	@Override
    	public void compute(BufferedImage output, BufferedImage input) {
    		if (lowvalue>highvalue)
    			throw new IllegalArgumentException("Low > High");
     
    		width = input.getWidth();
    		height = input.getHeight();
     
    		// convert to graylevel if needed
    		int numBands = input.getRaster().getNumBands();
    		if (numBands>1) {
    			GrayOperator grayOp = new GrayOperator(SelectedColor.ALL);
    			input = grayOp.compute(input);
    		}
     
    		// blur input image (if requested)
    		if (radius>0) {
    			FastGaussianBlurOperator fgblur = new FastGaussianBlurOperator(radius);
    			input = fgblur.compute(input);
    		}
     
    		// Canny detection
    		canny(input.getRaster(), output.getRaster(), 0, lowvalue, highvalue, logscale);
    	}
     
     
    	/**
             * Compute the local gradient
             * 
             * @param raster Image raster
             * @param x x coord of the computation (int)
             * @param y y coord of the computation (int)
             * @return norme and value of the gradient
             */
    	private double[] gradient(Raster raster, int x, int y, int band) {
    		// limit to image dimension
    		if (x<0) x=0; else if (x>=width)  x=width-1;
    		if (y<0) y=0; else if (y>=height) y=height-1;
     
    		int px = x - 1;  // previous x
    		int nx = x + 1;  // next x
    		int py = y - 1;  // previous y
    		int ny = y + 1;  // next y
     
    		// limit to image dimension
    		if (px < 0)	px = 0;
    		if (nx >= width) nx = width - 1;
    		if (py < 0)	py = 0;
    		if (ny >= height) ny = height - 1;
     
    		// Intesity of the 8 neighbors
    		int Ipp = raster.getSample( px,py, band);
    		int Icp = raster.getSample( x,py,  band);
    		int Inp = raster.getSample( nx,py, band);
    		int Ipc = raster.getSample( px, y, band);
    		int Inc = raster.getSample( nx, y, band);
    		int Ipn = raster.getSample( px,ny, band);
    		int Icn = raster.getSample( x,ny,  band);
    		int Inn = raster.getSample( nx,ny, band);
     
    		// Local gradient
    		double gradx = ((Inc-Ipc)*2 + (Inn-Ipp) + (Inp-Ipn))/4.0; // horizontal + 2 diagonals
    		double grady = ((Icn-Icp)*2 + (Inn-Ipp) + (Ipn-Inp))/4.0; // vertical + 2 diagonals
    		double norme = Math.sqrt(gradx*gradx+grady*grady);  
     
    		return new double[] { norme, gradx/norme, grady/norme };
    	}
     
     
    	/**
             * Compute the norm of the local gradient (subpixel with bilinear interpolation)
             * 
             * @param gradient gradient map
             * @param x x coord of the computation (double)
             * @param y y coord of the computation (double)
             * @return interpolated value of the norm of the gradient
             */
    	private double gradientNorm(double[][][] gradient, double x, double y) {
    		// fractional part of coordinates
    		double wx = x - (int) x;
    		double wy = y - (int) y;
     
    		// limit to image dimension
    		int cx = (int) x, cy = (int) y;
    		if (cx < 0)	cx = 0;	else if (cx >= width) cx = width - 1;
    		if (cy < 0)	cy = 0; else if (cy >= height) cy = height - 1;
     
    		int nx = cx + 1, ny = cy + 1;
    		if (nx >= width) nx = width - 1;
    		if (ny >= height) ny = height - 1;
     
    		//  norm of the 4 neighbours
    		double Gpp = gradient[cx][cy][0];
    		double Gnp = gradient[nx][cy][0];
    		double Gpn = gradient[cx][ny][0];
    		double Gnn = gradient[nx][ny][0];
     
    		// bilinear interpolation
    		double wpp=(1-wx)*(1-wy), wnp= wx*(1-wy), wpn=(1-wx)*wy, wnn=wx*wy;
    		double norm = wpp*Gpp + wnp*Gnp + wpn*Gpn + wnn*Gnn;
     
    		return norm;
    	}
     
     
    	/**
             * compute if a position is a local maxima
             * 
             * @param c Image map
             * @param x x coord of the computation
             * @param y y coord of the computation
             * @return true if position is a local maxima
             */
    	private boolean isLocalMaxima(double[][][] gradient, int x, int y) {
     
    		// gradient at current position
    		double[] grad = gradient[x][y];
     
    		// gradient direction
    		double gx = grad[1]; //Math.cos( angle );
    		double gy = grad[2]; //Math.sin( angle );
     
    		// gradient value at next position in the gradient direction
    		double nx = x + gx;
    		double ny = y + gy;
    		double gradn = gradientNorm(gradient,nx,ny);
     
    		// gradient value at previous position in the gradient direction
    		double px = x - gx;
    		double py = y - gy;
    		double gradp = gradientNorm(gradient,px,py);
     
    		// is the current gradient value a local maxima ?
    		if (grad[0]>gradn  && grad[0]>=gradp) return true;
    		return false;
    	}
     
    	/**
             * @param input Input image raster 
             * @param output Output image raster
             * @param band band of the raster to process
             * @param lowThreshold Hysteresis low value (0...100%) 
             * @param highThreshold Hysteresis hight value (0...100%)
             * @param logscale use log-scale
             */
    	private void canny(Raster input, WritableRaster output, int band, int lowThreshold, int highThreshold, boolean logscale) {
    		int LOWSTATE=92, HIGHSTATE=255;
     
    		// list of pixels above highThreshold
    		LinkedList<int[]> highpixels = new LinkedList<int[]>();
     
    		// precompute gradient
    		double[][][] gradient = new double[width][height][];
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				gradient[x][y] = gradient(input, x, y, band);
     
    		// low/high intesity value of gradient
    		double glow = gradient[0][0][0], ghigh = glow;
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++) {
    				if (gradient[x][y][0]<glow)  glow=gradient[x][y][0];
    				if (gradient[x][y][0]>ghigh) ghigh=gradient[x][y][0];
     			}
     
    		// rescale values of gradient in [0,100]
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++) {
    				double d  = (gradient[x][y][0]-glow)/(ghigh-glow);
    				// use log-scale if requested
    				if (logscale) 
    					gradient[x][y][0] = 100.0*Math.max(0 , 1.16*Math.pow(d, 0.333)-0.16);
    				else
    					gradient[x][y][0] = 100.0*d;
    			}
     
    		// gradient thresholding
    		for (int y=0; y<height; y++) {
    			for (int x=0; x<width; x++) {
     
    				// gradient intesity
    				double g = gradient[x][y][0];
     
    				// low-threshold -> not an edge
    				if (g<lowThreshold) continue;
     
    				// not a local maxima -> not an edge
    				if (!isLocalMaxima(gradient, x, y)) continue;
     
    				// high-threshold -> is an edge
    				if (g>highThreshold) {
    					output.setSample(x, y, band, HIGHSTATE);
    					highpixels.addLast(new int[]{x,y});
    					continue;
    				}
     
    				// between thresholds -> "unknown state" (depends of neighbors)
    				output.setSample(x, y, band, LOWSTATE);
    			}
    		}
     
    		// edge continuation
    		int[] dx8 = new int[] {-1, 0, 1, 1, 1, 0,-1,-1};
    		int[] dy8 = new int[] {-1,-1,-1, 0, 1, 1, 1, 0};
     
    		// analyze 8 neighboors of each pixels in HIGHSTATE 
    		while(!highpixels.isEmpty()) {
    			int[] pixel = highpixels.removeFirst();
    			int x=pixel[0], y=pixel[1];
     
    			// move pixels from LOWSTATE to HIGHSTATE
    			for(int k=0;k<8;k++) {
    				if (safeGetSample(output, x+dx8[k], y+dy8[k], band)==LOWSTATE) {
    					output.setSample(x+dx8[k], y+dy8[k], band, HIGHSTATE);
    					highpixels.addLast(new int[]{x+dx8[k], y+dy8[k]});
    				}
    			}
    		}
     
    		// keep only pixels in HIGHSTATE
    		for (int y=0; y<height; y++)
    			for (int x=0; x<width; x++)
    				if (output.getSample(x, y, band)!=HIGHSTATE) output.setSample(x,y,band,0);
    	}
     
    	private static int safeGetSample(Raster raster, int x, int y, int band) {
    		if(x<0 || x>=raster.getWidth() ||y<0 || y>=raster.getHeight())
    			return 0;
    		return raster.getSample(x,y,band);
    	}
     
     
     
    }
    Je ne répondrai à aucune question technique en privé

  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 millie Voir le message
    Je l'ai integré et j'ai cassé ton filtre en deux (ainsi que le FastGaussianBlur)
    Je me posais justement la question si je devais en faire un opérateur. D'un coté ca semblait logique, mais de l'autre c'etait pas pratique à coder puisque ce n'est pas chargeable dynamiquement à l'instar des Plugins. (ou alors est-ce possible ?)
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  4. #4
    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 pseudocode Voir le message
    Je me posais justement la question si je devais en faire un opérateur. D'un coté ca semblait logique, mais de l'autre c'etait pas pratique à coder puisque ce n'est pas chargeable dynamiquement à l'instar des Plugins. (ou alors est-ce possible ?)
    Ce que je viens de poster, il y a un opérateur ET un plugin.


    D'un côté, l'opérateur est un "SimpleOperator" qui correspond aux opérateurs qui sont définis dans la bibliothèque standard (BufferedImageOp).
    Donc, c'est juste pour offrir les services du filtres à d'autres filtres (comme ConvolveOp etc.). Mais faut voir si ça risque de servir ?

    De l'autre côté, le plugin associé est tout petit car il ne contient que l'appel à l'opérateur.

    EDIT : Après, logiquement, ça ne sert à rien de découper si le filtre ne risque pas d'être integré ailleurs (genre mes filtres photos, je pense pas vraiment m'en servir ailleurs)
    Je ne répondrai à aucune question technique en privé

  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
    Citation Envoyé par millie Voir le message
    Ce que je viens de poster, il y a un opérateur ET un plugin.


    D'un côté, l'opérateur est un "SimpleOperator" qui correspond aux opérateurs qui sont définis dans la bibliothèque standard (BufferedImageOp).
    Donc, c'est juste pour offrir les services du filtres à d'autres filtres (comme ConvolveOp etc.). Mais faut voir si ça risque de servir ?

    De l'autre côté, le plugin associé est tout petit car il ne contient que l'appel à l'opérateur.
    Oui, j'ai bien compris le principe

    Le truc c'est que si je veux mettre a jour le code, je doit recompiler tout le projet puisque c'est un opérateur. (ou alors jouer à cache-cache avec le classloader).

    Sinon c'est effectivement une bonne chose d'avoir des opérateurs. Mais peut-etre devrait on se limiter a des trucs réutilisables (gradient, laplace, variance, ...). Je ne sais pas trop.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  6. #6
    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 pseudocode Voir le message
    Sinon c'est effectivement une bonne chose d'avoir des opérateurs. Mais peut-etre devrait on se limiter a des trucs réutilisables (gradient, laplace, variance, ...). Je ne sais pas trop.
    Ah oui, c'est parce que je l'intègre que je casse en 2. Sinon, il faudrait soit :
    - avoir l'opérateur dans le jar du plugins
    - ne pas casser

    Maintenant, il faudrait effectivement voir ce que l'on peut considérer comme réutilisable.
    Je ne répondrai à aucune question technique en privé

  7. #7
    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 millie Voir le message
    Ah oui, c'est parce que je l'intègre que je casse en 2. Sinon, il faudrait soit :
    - avoir l'opérateur dans le jar du plugins
    - ne pas casser

    Maintenant, il faudrait effectivement voir ce que l'on peut considérer comme réutilisable.
    C'est possible d'avoir l'opérateur dans le jar du plugin ? Tu ne charges pas que les classes qui héritent de Plugin ?
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  8. #8
    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 pseudocode Voir le message
    C'est possible d'avoir l'opérateur dans le jar du plugin ? Tu ne charges pas que les classes qui héritent de Plugin ?
    Je suis obligé de tout charger (rien que pour voir si ça hérite de plugin par exemple). C'est encore plus le cas avec le nouveau système, car on peut créer son propre système de plugin manager. J'avais même commencé à faire un chargeur de plugin ImageJ (il suffit de déposer le jar dans plugins et de balancer des classes compilées ImageJ dans le répertoire).

    Par contre, niveau ordre de priorité. Pour l'instant, il semblerait que le plugin n'a pas la priorité sur le reste.
    (je parle dans le cas où la classe est définie 2 fois à deux endroits).
    Je ne répondrai à aucune question technique en privé

  9. #9
    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
    Bon, cette version de plugin qui utilise le FastGaussianBlur semble marcher correctement (du moins tant que le radius reste faible, genre 3 à 5)

    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  10. #10
    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 pseudocode Voir le message
    C'est possible d'avoir l'opérateur dans le jar du plugin ? Tu ne charges pas que les classes qui héritent de Plugin ?
    J'avais oublié de préciser que depuis le début, tu peux même mettre des jars dans le dossier plugins (genre JAI) et ils sont pris en compte à la volée.

    C'était comme ça avec le Decoder de TIFF

    => le truc sympa, ce serait de pouvoir mettre plusieurs dossiers de plugins, et quand un .class est modifié, le reprendre à chaud
    Je ne répondrai à aucune question technique en privé

  11. #11
    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
    J'ai par hasard trouvé pourquoi les fichiers des plugins qui sont également défini dans les bibliothèques n'étaient pas rafraichi.

    Du coup, il est désormais possible de pointer vers plusieurs dossiers de plugins, et il est possible de pointer vers un dossier qui contient des .class (notamment les bin des projets)

    Ca permet de ne pas avoir à générer de jars en dév
    Je ne répondrai à aucune question technique en privé

  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
    Citation Envoyé par millie Voir le message
    J'ai par hasard trouvé pourquoi les fichiers des plugins qui sont également défini dans les bibliothèques n'étaient pas rafraichi.

    Du coup, il est désormais possible de pointer vers plusieurs dossiers de plugins, et il est possible de pointer vers un dossier qui contient des .class (notamment les bin des projets)

    Ca permet de ne pas avoir à générer de jars en dév
    Ah, ca peut etre sympa. Quoique générer un jar avec eclipse (voir ant) c'etait quand meme pas la mort.


    Par contre j'aimerai bien savoir pourquoi j'ai des artefacts dans le fastblur avec des grands rayons.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  13. #13
    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
    J'avais repris le Fast blur (qui n'est pas véritablement gaussien en fait) ici :

    http://incubator.quasimondo.com/proc...lur_deluxe.php

    Apparement, ça peut lui arriver de ressembler à un box blur

    Cela dit, je pourrais faire un FastGaussianBlur avec ton algo et un FastBlur tout court avec celui ci
    Je ne répondrai à aucune question technique en privé

  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
    Citation Envoyé par millie Voir le message
    J'avais repris le Fast blur (qui n'est pas véritablement gaussien en fait) ici :

    http://incubator.quasimondo.com/proc...lur_deluxe.php

    Apparement, ça peut lui arriver de ressembler à un box blur
    C'est étrange. Dans le principe je ne vois pas pourquoi ca créerait des artefacts. Ca a l'air de créer un kernel "triangulaire".

    Code : 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
    ... a b c d e f g h ...
        |___________|
    
    blur(0) = a+2b+3c+4d+3e+2f+g
    rightsum(0) = e+f+g
    leftsum(0) = a+b+c+d
    
    
    ... a b c d e f g h ...
          |___________|
    
    blur(1) = blur(0) - leftsum(0) + rightsum(0) + h = b+2c+3d+4e+3f+2g+h
    rightsum(1) = rightsum(0) - e + h = f+g+h
    leftsum(1) = leftsum(0) - a + e = b+c+d+e
    
    
    etc.
    C'est peut-être dû a des erreurs de discrétisation, etant donné que l'implémentation utilise seulement des calculs sur les entiers et des tables de lookup. Mais je vois pas où.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  15. #15
    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
    Oups !

    En fait, il n'y a pas de problème avec l'opérateur de flou. Les artefacts sont simplement dus à l'utilisation de l'échelle Log sur des images très floutées (donc avec beaucoup de petites valeurs).
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  16. #16
    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
    J'ai mis ta nouvelle version en tant que Filtre/plugin, et j'ai dégagé l'opérateur
    Je ne répondrai à aucune question technique en privé

  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 millie Voir le message
    J'ai mis ta nouvelle version en tant que Filtre/plugin, et j'ai dégagé l'opérateur
    Z'etes bien aimable chef.

    C'est marrant, cette version du filtre me semble meilleure que celle que j'utilise au boulot. Si mes algos publics sont meilleurs que les privés, ca va pas aller.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  18. #18
    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


    Enfin, je repassais un peu sur tout vu que je ferai une release demain.
    Donc j'en ai profité pour remettre à jour le filtre là
    Je ne répondrai à aucune question technique en privé

  19. #19
    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
    Bon, finalement j'obtiens la meme qualité de résultat que le filtre Canny-Deriche du plugin imageJ. C'est pas si mal pour un filtre fait sur du temps libre.

    Par contre j'explose totalement les 2 filtres watershed de imageJ. Mais je crois qu'il doit y avoir des bugs dans leur code.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  20. #20
    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
    Moi, ça va. Je commence à arriver à un point où je prend mon appli 80% du temps pour retoucher mes photos.
    C'est déjà ça. Mais dès qu'on a besoin de faire des sélections, c'est mort
    Je ne répondrai à aucune question technique en privé

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. passage 6.7 a 6.9.1 :: plus de maj plugins
    Par bmayer dans le forum NetBeans
    Réponses: 6
    Dernier message: 26/11/2010, 17h31
  2. Obligatoire : lisez les règles du forum : MAJ 06/08/2010
    Par Anomaly dans le forum Mode d'emploi & aide aux nouveaux
    Réponses: 0
    Dernier message: 03/07/2008, 13h46
  3. plugin winamp
    Par darkfrag dans le forum API, COM et SDKs
    Réponses: 7
    Dernier message: 03/08/2002, 10h34
  4. [web]maj de pages
    Par Gogoye dans le forum Web
    Réponses: 3
    Dernier message: 17/06/2002, 18h25

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