Statistics
| Revision:

svn-gvsig-desktop / branches / org.gvsig.desktop-2018a / org.gvsig.desktop.library / org.gvsig.raster / org.gvsig.raster.lib / org.gvsig.raster.lib.buffer.impl / src / main / java / org / gvsig / raster / lib / buffer / impl / operations / edgedetection / firstderivative / FirstDerivativeOperation.java @ 43864

History | View | Annotate | Download (23.2 KB)

1
/* gvSIG. Desktop Geographic Information System.
2
 *
3
 * Copyright ? 2007-2017 gvSIG Association
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
 * MA  02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us
21
 * at info AT gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.raster.lib.buffer.impl.operations.edgedetection.firstderivative;
24

    
25
import java.util.ArrayList;
26
import java.util.Iterator;
27
import java.util.List;
28

    
29
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
30
import org.gvsig.raster.lib.buffer.api.Band;
31
import org.gvsig.raster.lib.buffer.api.BufferLocator;
32
import org.gvsig.raster.lib.buffer.api.BufferManager;
33
import org.gvsig.raster.lib.buffer.api.Kernel;
34
import org.gvsig.raster.lib.buffer.api.NoData;
35
import org.gvsig.raster.lib.buffer.api.exceptions.BandException;
36
import org.gvsig.raster.lib.buffer.api.exceptions.BufferException;
37
import org.gvsig.raster.lib.buffer.api.exceptions.BufferOperationException;
38
import org.gvsig.raster.lib.buffer.api.operations.OperationFactory;
39
import org.gvsig.raster.lib.buffer.spi.exceptions.ProcessingOperationException;
40
import org.gvsig.raster.lib.buffer.spi.operations.AbstractSpecifiedBandsOperation;
41
import org.gvsig.tools.locator.LocatorException;
42

    
43

    
44
/**
45
 * @author fdiaz
46
 *
47
 */
48
public class FirstDerivativeOperation extends AbstractSpecifiedBandsOperation{
49

    
50
    static final public String SOBEL_OPERATOR_STRING= "SOBEL";
51
    static final public String ROBERTS_OPERATOR_STRING= "ROBERTS";
52
    static final public String PREWITT_OPERATOR_STRING= "PREWITT";
53
    static final public String FREICHEN_OPERATOR_STRING= "FREICHEN";
54

    
55
    static public String OPERATOR_PARAM = "operator";
56
    static public String UMBRAL_PARAM = "umbral";
57
    static public String COMPARE_PARAM = "compare";
58

    
59
    private String operator;
60
    private int umbral;
61
    private boolean compare;
62
    private RowProcessor[] rowProcessors;
63
    private Kernel operatorH;
64
    private Kernel operatorV;
65

    
66
    static final private int SIDE_WINDOW = 3;
67
    static final private int HALF_SIDE_WINDOW = 1;
68

    
69

    
70
    // Kernels:------------------------------------------
71
    static final double     sobelH[][]      = {{-1,0,1},{-2,0,2},{-1,0,1}};
72
    static final double     sobelV[][]      = {{-1,-2,-1},{0,0,0},{1,2,1}};
73

    
74
    static final double     robertsH[][]    = {{0,0,-1},{0,1,0},{0,0,0}};
75
    static final double     robertsV[][]    = {{-1,0,0},{0,1,0},{0,0,0}};
76

    
77
    static final double     prewittH[][]    = {{1,0,-1},{1,0,-1},{1,0,-1}};
78
    static final double     prewittV[][]    = {{-1,-1,-1},{0,0,0},{1,1,1}};
79
    static final double     sqr2            = Math.sqrt(2);
80
//    static final double     freiChenH[][]   = {{-1,-sqr2,-1},{0,0,0},{1,sqr2,1}};
81
//    static final double     freiChenV[][]   = {{-1,0,1},{-sqr2,0,sqr2},{-1,0,1}};
82

    
83
    static final double     freiChenH[][]   = {{-1,-1.4D,-1},{0,0,0},{1,1.4D,1}};
84
    static final double     freiChenV[][]   = {{-1,0,1},{-1.4D,0,1.4D},{-1,0,1}};
85

    
86

    
87
    /**
88
     * @param factory
89
     *
90
     */
91
    public FirstDerivativeOperation(OperationFactory factory) {
92
        super(factory);
93
    }
94

    
95
    @Override
96
    public void preProcess() throws BufferOperationException {
97
        super.preProcess();
98
        BufferManager manager = BufferLocator.getBufferManager();
99

    
100
        operator = (String) this.getParameter(OPERATOR_PARAM, SOBEL_OPERATOR_STRING);
101
        
102
        switch (operator) {
103
        case SOBEL_OPERATOR_STRING:
104
            operatorH = manager.createKernel(sobelH);
105
            operatorV = manager.createKernel(sobelV);
106
            break;
107
        case ROBERTS_OPERATOR_STRING:
108
            operatorH = manager.createKernel(robertsH);
109
            operatorV = manager.createKernel(robertsV);
110
            break;
111
        case PREWITT_OPERATOR_STRING:
112
            operatorH = manager.createKernel(prewittH);
113
            operatorV = manager.createKernel(prewittV);
114
            break;
115
        case FREICHEN_OPERATOR_STRING:
116
            operatorH = manager.createKernel(freiChenH);
117
            operatorV = manager.createKernel(freiChenV);
118
            break;
119
        }
120

    
121
        compare = (boolean) this.getParameter(COMPARE_PARAM, false);
122
        umbral = (int) this.getParameter(UMBRAL_PARAM, 0);
123

    
124
        int bands = this.getInputBuffer().getBandCount();
125
        NoData[] noData = this.getInputBuffer().getBandNoData();
126
        if (mustCopyUnprocessedBands()) {
127
            try {
128
                this.setOutputBuffer(
129
                    manager.createBuffer(
130
                            this.getInputBuffer().getRows(), 
131
                            this.getInputBuffer().getColumns(), 
132
                            this.getInputBuffer().getBandTypes(),
133
                            this.getInputBuffer().getBandNoData(), 
134
                            this.getInputBuffer().getProjection(), 
135
                            this.getInputBuffer().getEnvelope()));
136
            } catch (LocatorException | BufferException | CreateEnvelopeException e) {
137
                throw new ProcessingOperationException(e);
138
            }
139
        } else {
140
            try {
141
                this.setOutputBuffer(
142
                    manager.createBuffer(
143
                            this.getInputBuffer().getRows(), 
144
                            this.getInputBuffer().getColumns(), 
145
                            this.getProcessableBandTypesAsArray(),
146
                            this.getProcessableBandNoDatasAsArray(), 
147
                            this.getInputBuffer().getProjection(), 
148
                            this.getInputBuffer().getEnvelope()));
149
            } catch (LocatorException | BufferException | CreateEnvelopeException e) {
150
                throw new ProcessingOperationException(e);
151
            }
152
        }
153

    
154
        rowProcessors = new RowProcessor[bands];
155
        for (int band = 0; band < noData.length; band++) {
156
            if (isProcessableBand(band)) {
157
                int bandType = this.getInputBuffer().getBand(band).getDataType();
158
                switch (bandType) {
159
                case BufferManager.TYPE_BYTE:
160
                    rowProcessors[band] = new ByteRowProcessor(band);
161
                    break;
162
                case BufferManager.TYPE_USHORT:
163
                    rowProcessors[band] = new UShortRowProcessor(band);
164
                    break;
165
                case BufferManager.TYPE_SHORT:
166
                    rowProcessors[band] = new ShortRowProcessor(band);
167
                    break;
168
                case BufferManager.TYPE_INT:
169
                    rowProcessors[band] = new IntRowProcessor(band);
170
                    break;
171
                case BufferManager.TYPE_FLOAT:
172
                    rowProcessors[band] = new FloatRowProcessor(band);
173
                    break;
174
                case BufferManager.TYPE_DOUBLE:
175
                    rowProcessors[band] = new DoubleRowProcessor(band);
176
                    break;
177
                default:
178
                    throw new IllegalArgumentException("Unknow type of band '" + band + "'");
179
                }
180
            }
181
        }
182
        }
183

    
184
    @Override
185
    public void process() throws ProcessingOperationException {
186
        super.process();
187
        for (int band=0; band<this.getInputBuffer().getBandCount(); band++){
188
            if (getBandsToProcess().contains(band)) {
189
                Band bufferBand = this.getInputBuffer().getBand(band);
190
                Band outputBufferBand = this.getOutputBuffer().getBand(band);
191

    
192
                for (int row = 0; row < this.getInputBuffer().getRows(); row++) {
193
                    Object rowBuffer = bufferBand.createRowBuffer();
194
                    bufferBand.fetchRow(row, rowBuffer);
195
                    List<Object> bundle = new ArrayList<>();
196
                    //FIXME: Solo se procesan aquellas filas en las que se pueden crear kernels, el resto ?qu? hacer? (ver abajo)
197
                    if ((row - HALF_SIDE_WINDOW >= 0) && (row + HALF_SIDE_WINDOW < this.getInputBuffer().getRows())) {
198
                        for (int r = Math.max(row - HALF_SIDE_WINDOW, 0); r <= Math.min(row + HALF_SIDE_WINDOW,
199
                            this.getInputBuffer().getRows()-1); r++) {
200
                            Object bundleRow = bufferBand.createRowBuffer();
201
                            bufferBand.fetchRow(r, bundleRow);
202
                            bundle.add(bundleRow);
203
                        }
204

    
205
                        Object outputRowBuffer = outputBufferBand.createRowBuffer();
206
                        // outputBufferBand.fetchRow(row, outputRowBuffer);
207

    
208
                        rowProcessors[band].processRow(rowBuffer, bundle, outputRowBuffer);
209

    
210
                        outputBufferBand.putRow(row, outputRowBuffer);
211
                    } else {
212
                        // FIXME: Si son de tipo BYTE, las copio, si no, no hacemos nada
213
                        if(this.getInputBuffer().getBandTypes()[band]==BufferManager.TYPE_BYTE){
214
                            outputBufferBand.putRow(row, rowBuffer);
215
                        }
216
                    }
217
                }
218
            } else {
219
                if(mustCopyUnprocessedBands()){
220
                    try {
221
                        this.getOutputBuffer().getBand(band).copyFrom(this.getInputBuffer().getBand(band));
222
                    } catch (BandException e) {
223
                        throw new ProcessingOperationException(e);
224
                    }
225
                }
226
            }
227
        }
228
    }
229

    
230
    @Override
231
    public void postProcess()  throws BufferOperationException {
232
        super.postProcess();
233
    }
234

    
235
    interface RowProcessor {
236
        void processRow(Object inputRow, List<Object> bundleRow, Object outputRow);
237
        Number processValue(Kernel kernel);
238
    };
239

    
240
    private abstract class AbstractRowProcessor implements RowProcessor {
241
        int band;
242
        NoData noData;
243

    
244
        public AbstractRowProcessor(int band) {
245
            this.band = band;
246
            noData = getInputBuffer().getBand(band).getNoData();
247
        }
248

    
249
        @Override
250
        public Number processValue(Kernel kernel) {
251

    
252
            int convoResult[] = new int[4];
253
            int out;
254

    
255
            convoResult[0] = (int) operatorH.convolution(kernel);
256
            convoResult[1] = (int) operatorV.convolution(kernel);
257

    
258
            if (compare) {
259
                if (convoResult[0] > convoResult[1])
260
                    out = convoResult[0];
261
                else
262
                    out = convoResult[1];
263
            } else {
264
                out = (int) Math.sqrt(Math.pow(convoResult[0], 2) + Math.pow(convoResult[1], 2));
265
            }
266

    
267
            if (umbral > 0) {
268
                if (out >= (255 - umbral))
269
                    out = 255;
270
                else
271
                    out = 0;
272
            } else {
273
                if (out < 0)
274
                    out = 0;
275
                else if (out > 255)
276
                    out = 255;
277
            }
278

    
279
          return (byte)(out);
280
        }
281
    }
282

    
283
    private class ByteRowProcessor extends AbstractRowProcessor {
284

    
285

    
286
        public ByteRowProcessor(int band) {
287
            super(band);
288
        }
289

    
290
        @Override
291
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
292
            byte[] inputByteRow = (byte[])inputRow;
293
            byte[] outputByteRow = (byte[])outputRow;
294
            for (int i = 0; i < inputByteRow.length; i++) {
295
                // FIXME: Solo se procesan aquellas columnas en las que se pueden crear kernels, el resto ?se copian?
296
                if ((i - HALF_SIDE_WINDOW >= 0) && (i + HALF_SIDE_WINDOW < inputByteRow.length)) {
297
                    double[][] k = new double[SIDE_WINDOW][SIDE_WINDOW];
298
                    int r=0;
299
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
300
                        int c=0;
301
                        byte[] row = (byte[]) iterator.next();
302
                        for (int column = Math.max(i - HALF_SIDE_WINDOW, 0); column <= Math.min(i + HALF_SIDE_WINDOW,
303
                            inputByteRow.length-1); column++) {
304
                            byte value = ((Byte) row[column]).byteValue();
305
                            if (noData.isDefined() && noData.getValue().equals(value)) {
306
                                //FIXME: Si es noData, deber?a no influir en el resultado
307
                                // ?Que valor poner en el kernel?
308
                                k[r][c] = 0;
309
                            } else {
310
                                k[r][c] = (double)(0xFF & value);
311
                            }
312
                            c++;
313
                        }
314
                        r++;
315
                    }
316
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
317
                    if(noData.isDefined() && noData.getValue().equals(inputByteRow[i])){
318
                        outputByteRow[i] = inputByteRow[i];
319
                    } else {
320
                        outputByteRow[i] = processValue(kernel).byteValue();
321
                    }
322
                } else {
323
                    outputByteRow[i] = inputByteRow[i];
324
                }
325
            }
326
        }
327
    }
328

    
329
    private class ShortRowProcessor extends AbstractRowProcessor {
330

    
331
        public ShortRowProcessor(int band) {
332
            super(band);
333
        }
334

    
335
        @Override
336
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
337
            Number[] inputShortRow = (Number[])inputRow;
338
            byte[] outputByteRow = (byte[])outputRow;
339
            for (int i = 0; i < inputShortRow.length; i++) {
340
                //Solo se procesan aquellas columnas en las que se pueden crear kernels, el resto se copian
341
                if ((i - HALF_SIDE_WINDOW >= 0) && (i + HALF_SIDE_WINDOW < inputShortRow.length)) {
342
                    double[][] k = new double[SIDE_WINDOW][SIDE_WINDOW];
343
                    int r=0;
344
                    int c=0;
345
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
346
                        short[] row = (short[]) iterator.next();
347
                        for (int column = Math.max(i - HALF_SIDE_WINDOW, 0); column <= Math.min(i + HALF_SIDE_WINDOW,
348
                            inputShortRow.length-1); column++) {
349
                            double value = ((Short) row[c]).shortValue();
350
                            if (noData.isDefined() && noData.getValue().equals(value)) {
351
                                //FIXME: Si es noData, deber?a no influir en el resultado
352
                                // ?Que valor poner en el kernel?
353
                                k[r][c] = 0;
354
                            } else {
355
                                k[r][c] = value;
356
                            }
357
                        }
358
                    }
359
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
360
                    outputByteRow[i] = processValue(kernel).byteValue();
361
                } else {
362
                    outputByteRow[i] = ((Short)inputShortRow[i]).byteValue();
363
                }
364
            }
365
        }
366

    
367
    }
368

    
369
    private class UShortRowProcessor extends AbstractRowProcessor {
370

    
371
        public UShortRowProcessor(int band) {
372
            super(band);
373
        }
374

    
375
        @Override
376
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
377

    
378
            Number[] inputShortRow = (Number[]) inputRow;
379
            byte[] outputByteRow = (byte[]) outputRow;
380
            for (int i = 0; i < inputShortRow.length; i++) {
381
                // Solo se procesan aquellas columnas en las que se pueden crear
382
                // kernels, el resto se copian
383
                if ((i - HALF_SIDE_WINDOW >= 0) && (i + HALF_SIDE_WINDOW < inputShortRow.length)) {
384
                    double[][] k = new double[SIDE_WINDOW][SIDE_WINDOW];
385
                    int r = 0;
386
                    int c = 0;
387
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
388
                        short[] row = (short[]) iterator.next();
389
                        for (int column = Math.max(i - HALF_SIDE_WINDOW, 0); column <= Math.min(i + HALF_SIDE_WINDOW,
390
                            inputShortRow.length-1); column++) {
391
                            double value = 0xFFFF & ((Short) row[c]).shortValue();
392
                            if (noData.isDefined() && noData.getValue().equals(value)) {
393
                                // FIXME: Si es noData, deber?a no influir en el
394
                                // resultado
395
                                // ?Que valor poner en el kernel?
396
                                k[r][c] = 0;
397
                            } else {
398
                                k[r][c] = value;
399
                            }
400
                        }
401
                    }
402
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
403
                    outputByteRow[i] = processValue(kernel).byteValue();
404
                } else {
405
                    outputByteRow[i] = ((Short) inputShortRow[i]).byteValue();
406
                }
407
            }
408
        }
409
    }
410

    
411
    private class IntRowProcessor extends AbstractRowProcessor {
412

    
413

    
414
        public IntRowProcessor(int band) {
415
            super(band);
416
        }
417

    
418
        @Override
419
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
420
            Number[] inputIntRow = (Number[])inputRow;
421
            byte[] outputByteRow = (byte[]) outputRow;
422
            for (int i = 0; i < inputIntRow.length; i++) {
423
                // Solo se procesan aquellas columnas en las que se pueden crear
424
                // kernels, el resto se copian
425
                if ((i - HALF_SIDE_WINDOW >= 0) && (i + HALF_SIDE_WINDOW < inputIntRow.length)) {
426
                    double[][] k = new double[SIDE_WINDOW][SIDE_WINDOW];
427
                    int r = 0;
428
                    int c = 0;
429
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
430
                        int[] row = (int[]) iterator.next();
431
                        for (int column = Math.max(i - HALF_SIDE_WINDOW, 0); column <= Math.min(i + HALF_SIDE_WINDOW,
432
                            inputIntRow.length-1); column++) {
433
                            double value = ((Integer) row[c]).intValue();
434
                            if (noData.isDefined() && noData.getValue().equals(value)) {
435
                                // FIXME: Si es noData, deber?a no influir en el
436
                                // resultado
437
                                // ?Que valor poner en el kernel?
438
                                k[r][c] = 0;
439
                            } else {
440
                                k[r][c] = value;
441
                            }
442
                        }
443
                    }
444
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
445
                    outputByteRow[i] = processValue(kernel).byteValue();
446
                } else {
447
                    outputByteRow[i] = ((Integer) inputIntRow[i]).byteValue();
448
                }
449
            }
450
        }
451
    }
452
    private class FloatRowProcessor extends AbstractRowProcessor {
453

    
454

    
455
        public FloatRowProcessor(int band) {
456
            super(band);
457
        }
458

    
459
        @Override
460
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
461
            Number[] inputFloatRow = (Number[])inputRow;
462
            byte[] outputByteRow = (byte[]) outputRow;
463
            for (int i = 0; i < inputFloatRow.length; i++) {
464
                // Solo se procesan aquellas columnas en las que se pueden crear
465
                // kernels, el resto se copian
466
                if ((i - HALF_SIDE_WINDOW >= 0) && (i + HALF_SIDE_WINDOW < inputFloatRow.length)) {
467
                    double[][] k = new double[SIDE_WINDOW][SIDE_WINDOW];
468
                    int r = 0;
469
                    int c = 0;
470
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
471
                        float[] row = (float[]) iterator.next();
472
                        for (int column = Math.max(i - HALF_SIDE_WINDOW, 0); column <= Math.min(i + HALF_SIDE_WINDOW,
473
                            inputFloatRow.length-1); column++) {
474
                            Float value = ((Float) row[c]).floatValue();
475
                            if (noData.isDefined() && noData.getValue().equals(value)) {
476
                                // FIXME: Si es noData, deber?a no influir en el
477
                                // resultado
478
                                // ?Que valor poner en el kernel?
479
                                k[r][c] = 0;
480
                            } else {
481
                                k[r][c] = value;
482
                            }
483
                        }
484
                    }
485
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
486
                    outputByteRow[i] = processValue(kernel).byteValue();
487
                } else {
488
                    outputByteRow[i] = ((Float) inputFloatRow[i]).byteValue();
489
                }
490
            }
491
        }
492
    }
493
    private class DoubleRowProcessor extends AbstractRowProcessor {
494

    
495

    
496
        public DoubleRowProcessor(int band) {
497
            super(band);
498
        }
499

    
500
        @Override
501
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
502
            Number[] inputDoubleRow = (Number[])inputRow;
503
            byte[] outputByteRow = (byte[]) outputRow;
504
            for (int i = 0; i < inputDoubleRow.length; i++) {
505
                // Solo se procesan aquellas columnas en las que se pueden crear
506
                // kernels, el resto se copian
507
                if ((i - HALF_SIDE_WINDOW >= 0) && (i + HALF_SIDE_WINDOW < inputDoubleRow.length)) {
508
                    double[][] k = new double[SIDE_WINDOW][SIDE_WINDOW];
509
                    int r = 0;
510
                    int c = 0;
511
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
512
                        double[] row = (double[]) iterator.next();
513
                        for (int column = Math.max(i - HALF_SIDE_WINDOW, 0); column <= Math.min(i + HALF_SIDE_WINDOW,
514
                            inputDoubleRow.length-1); column++) {
515
                            Double value = ((Double) row[c]).doubleValue();
516
                            if (noData.isDefined() && noData.getValue().equals(value)) {
517
                                // FIXME: Si es noData, deber?a no influir en el resultado
518
                                // ?Que valor poner en el kernel?
519
                                k[r][c] = 0;
520
                            } else {
521
                                k[r][c] = value;
522
                            }
523
                        }
524
                    }
525
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
526
                    outputByteRow[i] = processValue(kernel).byteValue();
527
                } else {
528
                    outputByteRow[i] = ((Double) inputDoubleRow[i]).byteValue();
529
                }
530
            }
531
        }
532
    }
533
}