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 / convolution / ConvolutionOperation.java @ 43864

History | View | Annotate | Download (25.1 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.convolution;
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 ConvolutionOperation extends AbstractSpecifiedBandsOperation{
49

    
50

    
51
    static final public String AVERAGE_OPERATOR_STRING= "AVERAGE";
52
    static final public String LOW_PASS_OPERATOR_STRING= "LOW_PASS";
53
    static final public String HIGH_PASS_OPERATOR_STRING= "HIGH_PASS";
54
    static final public String GAUSS_OPERATOR_STRING= "GAUSS";
55
    static final public String CUSTOM_OPERATOR_STRING= "CUSTOM";
56

    
57
    static public String OPERATOR_PARAM = "operator";
58
    static public String CUSTOM_KERNEL_PARAM = "custom_kernel";
59
    static public String SHARPNESS_PARAM = "sharpness";
60
    static public String SIDE_WINDOW_PARAM = "side_window";
61

    
62
    private String operator;
63
    private int sideWindow;
64
    private int halfSideWindow;
65
    private double sharpness;
66
    private RowProcessor[] rowProcessors;
67
    private Kernel kernelOperator;
68

    
69
    // Kernels:------------------------------------------
70
    static final double        gauss3x3[][]    = {{1,4,1},{4,12,4},{1,4,1}};
71
    static final double        gauss5x5[][]    = {{1,2,3,2,1},{2,7,11,7,2},{3,11,17,11,3},{2,7,11,7,2},{1,2,3,2,1}};
72
    static final double        gauss7x7[][]    = {{1,1,2,2,2,1,1},{1,2,2,4,2,2,1},{2,2,4,8,4,2,2},{2,4,8,16,8,4,2},{2,2,4,8,4,2,2},{1,2,2,4,2,2,1},{1,1,2,2,2,1,1}};
73
    static final double        gauss7x7aux[][] = {{0,0,0.0003,0.0006,0.0003,0,0},{0,0.0011,0.0079,0.0153,0.0079,0.0011,0},{0.0003,0.0079,0.0563,0.1082,0.0563,0.0079,0.0003},{0.0006,0.0153,0.1082,0.2079,0.1082,0.0153,0.0006},{0.0003,0.0079,0.0563,0.1082,0.0563,0.0079,0.0003},{0,0.0011,0.0079,0.0153,0.0079,0.0011,0},{0,0,0.0003,0.0006,0.0003,0,0}};
74
    static final double        lowpass3x3[][]  = {{0,1,0},{1,6,1},{0,1,0}};
75
    static final double        lowpass5x5[][]  = {{1,1,1,1,1},{1,4,4,4,1},{1,4,12,4,1},{1,4,4,4,1},{1,1,1,1,1}};
76
    static final double        media3x3[][]    = {{1,1,1},{1,1,1},{1,1,1}};
77
    static final double        media5x5[][]    = {{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1},{1,1,1,1,1}};
78
    static final double        media7x7[][]    = {{1,1,1,1,1,1,1},{1,1,1,1,1,1,1},{1,1,1,1,1,1,1},{1,1,1,1,1,1,1},{1,1,1,1,1,1,1},{1,1,1,1,1,1,1},{1,1,1,1,1,1,1}};
79
    static final double        highpass3x3[][] = {{-1,-1,-1},{-1,9,-1},{-1,-1,-1}};
80

    
81
    /**
82
     * @param factory
83
     *
84
     */
85
    public ConvolutionOperation(OperationFactory factory) {
86
        super(factory);
87
    }
88

    
89
    @Override
90
    public void preProcess() throws BufferOperationException {
91
        super.preProcess();
92
        BufferManager manager = BufferLocator.getBufferManager();
93

    
94
        sideWindow = (int) this.getParameter(SIDE_WINDOW_PARAM, 3);
95
        halfSideWindow = (int)(sideWindow/2);
96
        sharpness = (double) this.getParameter(SHARPNESS_PARAM, 0);
97
        operator = (String) this.getParameter(OPERATOR_PARAM, AVERAGE_OPERATOR_STRING);
98

    
99
        switch (operator) {
100
        case AVERAGE_OPERATOR_STRING:
101
            switch (sideWindow) {
102
            case 3:
103
                kernelOperator = manager.createKernel(media3x3,9);
104
                break;
105
            case 5:
106
                kernelOperator = manager.createKernel(media5x5,25);
107
                break;
108
            case 7:
109
                kernelOperator = manager.createKernel(media7x7,49);
110
                break;
111
            default:
112
                throw new IllegalArgumentException("Kernel side isn't valid for the selected operator.");
113
            }
114
            break;
115
        case LOW_PASS_OPERATOR_STRING:
116
            switch (sideWindow) {
117
            case 3:
118
                kernelOperator = manager.createKernel(lowpass3x3,10);
119
                break;
120
            case 5:
121
                kernelOperator = manager.createKernel(lowpass5x5);
122
                break;
123
            default:
124
                throw new IllegalArgumentException("Kernel side isn't valid for the selected operator.");
125
            }
126
            break;
127
        case HIGH_PASS_OPERATOR_STRING:
128
            switch (sideWindow) {
129
            case 3:
130
                double[][] h = new double[3][3];
131
                for (int i = 0; i < h.length; i++) {
132
                    for (int j = 0; j < h[i].length; j++) {
133
                        h[i][j] = highpass3x3[i][j];
134
                    }
135
                }
136
                h[1][1] = 29 - (sharpness * 20.9) / 100;
137

    
138
                kernelOperator = manager.createKernel(h);
139
                break;
140
            default:
141
                throw new IllegalArgumentException("Kernel side isn't valid for the selected operator.");
142
            }
143
            break;
144
        case GAUSS_OPERATOR_STRING:
145
            switch (sideWindow) {
146
            case 3:
147
                kernelOperator = manager.createKernel(gauss3x3,34);
148
                break;
149
            case 5:
150
                kernelOperator = manager.createKernel(gauss5x5,121);
151
                break;
152
            case 7:
153
                kernelOperator = manager.createKernel(gauss7x7aux);
154
                break;
155
            default:
156
                throw new IllegalArgumentException("Kernel side isn't valid for the selected operator.");
157
            }
158
            break;
159
        case CUSTOM_OPERATOR_STRING:
160
            kernelOperator = (Kernel) this.getParameter(CUSTOM_KERNEL_PARAM, null);
161
            if(kernelOperator==null) {
162
                throw new IllegalArgumentException("A Kernel is needed for the selected operator.");
163
            };
164
            if( kernelOperator.getSide()!=sideWindow){
165
                throw new IllegalArgumentException("Kernel side incorrect.");
166
            };
167
            break;
168
        }
169

    
170
        int bands = this.getInputBuffer().getBandCount();
171
        NoData[] noData = this.getInputBuffer().getBandNoData();
172
        if (mustCopyUnprocessedBands()) {
173
            try {
174
                this.setOutputBuffer(
175
                    manager.createBuffer(
176
                            this.getInputBuffer().getRows(), 
177
                            this.getInputBuffer().getColumns(), 
178
                            this.getInputBuffer().getBandTypes(),
179
                            this.getInputBuffer().getBandNoData(), 
180
                            this.getInputBuffer().getProjection(), 
181
                            this.getInputBuffer().getEnvelope()));
182
            } catch (LocatorException | BufferException | CreateEnvelopeException e) {
183
                throw new ProcessingOperationException(e);
184
            }
185
        } else {
186
            try {
187
                this.setOutputBuffer(
188
                    manager.createBuffer(
189
                            this.getInputBuffer().getRows(), 
190
                            this.getInputBuffer().getColumns(), 
191
                            this.getProcessableBandTypesAsArray(),
192
                            this.getProcessableBandNoDatasAsArray(), 
193
                            this.getInputBuffer().getProjection(), 
194
                            this.getInputBuffer().getEnvelope()));
195
            } catch (LocatorException | BufferException | CreateEnvelopeException e) {
196
                throw new ProcessingOperationException(e);
197
            }
198
        }
199

    
200
        rowProcessors = new RowProcessor[bands];
201
        for (int band = 0; band < noData.length; band++) {
202
            if (isProcessableBand(band)) {
203
                int bandType = this.getInputBuffer().getBand(band).getDataType();
204
                switch (bandType) {
205
                case BufferManager.TYPE_BYTE:
206
                    rowProcessors[band] = new ByteRowProcessor(band);
207
                    break;
208
                case BufferManager.TYPE_USHORT:
209
                    rowProcessors[band] = new UShortRowProcessor(band);
210
                    break;
211
                case BufferManager.TYPE_SHORT:
212
                    rowProcessors[band] = new ShortRowProcessor(band);
213
                    break;
214
                case BufferManager.TYPE_INT:
215
                    rowProcessors[band] = new IntRowProcessor(band);
216
                    break;
217
                case BufferManager.TYPE_FLOAT:
218
                    rowProcessors[band] = new FloatRowProcessor(band);
219
                    break;
220
                case BufferManager.TYPE_DOUBLE:
221
                    rowProcessors[band] = new DoubleRowProcessor(band);
222
                    break;
223
                default:
224
                    throw new IllegalArgumentException("Unknow type of band '" + band + "'");
225
                }
226
            }
227
        }
228
        }
229

    
230
    @Override
231
    public void process() throws ProcessingOperationException {
232
        super.process();
233
        for (int band=0; band<this.getInputBuffer().getBandCount(); band++){
234
            if (getBandsToProcess().contains(band)) {
235
                Band bufferBand = this.getInputBuffer().getBand(band);
236
                Band outputBufferBand = this.getOutputBuffer().getBand(band);
237

    
238
                for (int row = 0; row < this.getInputBuffer().getRows(); row++) {
239
                    Object rowBuffer = bufferBand.createRowBuffer();
240
                    bufferBand.fetchRow(row, rowBuffer);
241
                    List<Object> bundle = new ArrayList<>();
242
                    //FIXME: Solo se procesan aquellas filas en las que se pueden crear kernels, el resto ?qu? hacer? (ver abajo)
243
                    if ((row - halfSideWindow >= 0) && (row + halfSideWindow < this.getInputBuffer().getRows())) {
244
                        for (int r = Math.max(row - halfSideWindow, 0); r <= Math.min(row + halfSideWindow,
245
                            this.getInputBuffer().getRows()-1); r++) {
246
                            Object bundleRow = bufferBand.createRowBuffer();
247
                            bufferBand.fetchRow(r, bundleRow);
248
                            bundle.add(bundleRow);
249
                        }
250

    
251
                        Object outputRowBuffer = outputBufferBand.createRowBuffer();
252
                        // outputBufferBand.fetchRow(row, outputRowBuffer);
253

    
254
                        rowProcessors[band].processRow(rowBuffer, bundle, outputRowBuffer);
255

    
256
                        outputBufferBand.putRow(row, outputRowBuffer);
257
                    } else {
258
                        // FIXME: Si son de tipo BYTE, las copio, si no, no hacemos nada
259
                        if(this.getInputBuffer().getBandTypes()[band]==BufferManager.TYPE_BYTE){
260
                            outputBufferBand.putRow(row, rowBuffer);
261
                        }
262
                    }
263
                }
264
            } else {
265
                if(mustCopyUnprocessedBands()){
266
                    try {
267
                        this.getOutputBuffer().getBand(band).copyFrom(this.getInputBuffer().getBand(band));
268
                    } catch (BandException e) {
269
                        throw new ProcessingOperationException(e);
270
                    }
271
                }
272
            }
273
        }
274
    }
275

    
276
    @Override
277
    public void postProcess()  throws BufferOperationException {
278
        super.postProcess();
279
    }
280

    
281
    interface RowProcessor {
282
        void processRow(Object inputRow, List<Object> bundleRow, Object outputRow);
283
        Number processValue(Kernel kernel);
284
    };
285

    
286
    private abstract class AbstractRowProcessor implements RowProcessor {
287
        int band;
288
        NoData noData;
289

    
290
        public AbstractRowProcessor(int band) {
291
            this.band = band;
292
            noData = getInputBuffer().getBand(band).getNoData();
293
        }
294

    
295
        @Override
296
        public Number processValue(Kernel kernel) {
297

    
298
            double resultConvolution = kernelOperator.convolution(kernel);
299
            if (resultConvolution > 255) {
300
                resultConvolution = 255;
301
            } else if (resultConvolution < 0) {
302
                resultConvolution = 0;
303
            }
304
          return (byte)(resultConvolution);
305
        }
306
    }
307

    
308
    private class ByteRowProcessor extends AbstractRowProcessor {
309

    
310

    
311
        public ByteRowProcessor(int band) {
312
            super(band);
313
        }
314

    
315
        @Override
316
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
317
            byte[] inputByteRow = (byte[])inputRow;
318
            byte[] outputByteRow = (byte[])outputRow;
319
            for (int i = 0; i < inputByteRow.length; i++) {
320
                // FIXME: Solo se procesan aquellas columnas en las que se pueden crear kernels, el resto ?se copian?
321
                if ((i - halfSideWindow >= 0) && (i + halfSideWindow < inputByteRow.length)) {
322
                    double[][] k = new double[sideWindow][sideWindow];
323
                    int r=0;
324
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
325
                        int c=0;
326
                        byte[] row = (byte[]) iterator.next();
327
                        for (int column = Math.max(i - halfSideWindow, 0); column <= Math.min(i + halfSideWindow,
328
                            inputByteRow.length-1); column++) {
329
                            byte value = ((Byte) row[column]);
330
                            if (noData.isDefined() && noData.getValue().equals(value)) {
331
                                //FIXME: Si es noData, deber?a no influir en el resultado
332
                                // ?Que valor poner en el kernel?
333
                                k[r][c] = 0;
334
                            } else {
335
                                k[r][c] = (double)(0xFF & value);
336
                            }
337
                            c++;
338
                        }
339
                        r++;
340
                    }
341
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
342
                    if(noData.isDefined() && noData.getValue().equals(inputByteRow[i])){
343
                        outputByteRow[i] = inputByteRow[i];
344
                    } else {
345
                        outputByteRow[i] = processValue(kernel).byteValue();
346
                    }
347
                } else {
348
                    outputByteRow[i] = inputByteRow[i];
349
                }
350
            }
351
        }
352
    }
353

    
354
    private class ShortRowProcessor extends AbstractRowProcessor {
355

    
356
        public ShortRowProcessor(int band) {
357
            super(band);
358
        }
359

    
360
        @Override
361
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
362
            Number[] inputShortRow = (Number[])inputRow;
363
            byte[] outputByteRow = (byte[])outputRow;
364
            for (int i = 0; i < inputShortRow.length; i++) {
365
                //Solo se procesan aquellas columnas en las que se pueden crear kernels, el resto se copian
366
                if ((i - halfSideWindow >= 0) && (i + halfSideWindow < inputShortRow.length)) {
367
                    double[][] k = new double[sideWindow][sideWindow];
368
                    int r=0;
369
                    int c=0;
370
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
371
                        short[] row = (short[]) iterator.next();
372
                        for (int column = Math.max(i - halfSideWindow, 0); column <= Math.min(i + halfSideWindow,
373
                            inputShortRow.length-1); column++) {
374
                            double value = ((Short) row[c]);
375
                            if (noData.isDefined() && noData.getValue().equals(value)) {
376
                                //FIXME: Si es noData, deber?a no influir en el resultado
377
                                // ?Que valor poner en el kernel?
378
                                k[r][c] = 0;
379
                            } else {
380
                                k[r][c] = value;
381
                            }
382
                        }
383
                    }
384
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
385
                    outputByteRow[i] = processValue(kernel).byteValue();
386
                } else {
387
                    outputByteRow[i] = ((Short)inputShortRow[i]).byteValue();
388
                }
389
            }
390
        }
391

    
392
    }
393

    
394
    private class UShortRowProcessor extends AbstractRowProcessor {
395

    
396
        public UShortRowProcessor(int band) {
397
            super(band);
398
        }
399

    
400
        @Override
401
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
402

    
403
            Number[] inputShortRow = (Number[]) inputRow;
404
            byte[] outputByteRow = (byte[]) outputRow;
405
            for (int i = 0; i < inputShortRow.length; i++) {
406
                // Solo se procesan aquellas columnas en las que se pueden crear
407
                // kernels, el resto se copian
408
                if ((i - halfSideWindow >= 0) && (i + halfSideWindow < inputShortRow.length)) {
409
                    double[][] k = new double[sideWindow][sideWindow];
410
                    int r = 0;
411
                    int c = 0;
412
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
413
                        short[] row = (short[]) iterator.next();
414
                        for (int column = Math.max(i - halfSideWindow, 0); column <= Math.min(i + halfSideWindow,
415
                            inputShortRow.length-1); column++) {
416
                            double value = 0xFFFF & ((Short) row[c]);
417
                            if (noData.isDefined() && noData.getValue().equals(value)) {
418
                                // FIXME: Si es noData, deber?a no influir en el
419
                                // resultado
420
                                // ?Que valor poner en el kernel?
421
                                k[r][c] = 0;
422
                            } else {
423
                                k[r][c] = value;
424
                            }
425
                        }
426
                    }
427
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
428
                    outputByteRow[i] = processValue(kernel).byteValue();
429
                } else {
430
                    outputByteRow[i] = ((Short) inputShortRow[i]).byteValue();
431
                }
432
            }
433
        }
434
    }
435

    
436
    private class IntRowProcessor extends AbstractRowProcessor {
437

    
438

    
439
        public IntRowProcessor(int band) {
440
            super(band);
441
        }
442

    
443
        @Override
444
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
445
            Number[] inputIntRow = (Number[])inputRow;
446
            byte[] outputByteRow = (byte[]) outputRow;
447
            for (int i = 0; i < inputIntRow.length; i++) {
448
                // Solo se procesan aquellas columnas en las que se pueden crear
449
                // kernels, el resto se copian
450
                if ((i - halfSideWindow >= 0) && (i + halfSideWindow < inputIntRow.length)) {
451
                    double[][] k = new double[sideWindow][sideWindow];
452
                    int r = 0;
453
                    int c = 0;
454
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
455
                        int[] row = (int[]) iterator.next();
456
                        for (int column = Math.max(i - halfSideWindow, 0); column <= Math.min(i + halfSideWindow,
457
                            inputIntRow.length-1); column++) {
458
                            double value = ((Integer) row[c]);
459
                            if (noData.isDefined() && noData.getValue().equals(value)) {
460
                                // FIXME: Si es noData, deber?a no influir en el
461
                                // resultado
462
                                // ?Que valor poner en el kernel?
463
                                k[r][c] = 0;
464
                            } else {
465
                                k[r][c] = value;
466
                            }
467
                        }
468
                    }
469
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
470
                    outputByteRow[i] = processValue(kernel).byteValue();
471
                } else {
472
                    outputByteRow[i] = ((Integer) inputIntRow[i]).byteValue();
473
                }
474
            }
475
        }
476
    }
477
    private class FloatRowProcessor extends AbstractRowProcessor {
478

    
479

    
480
        public FloatRowProcessor(int band) {
481
            super(band);
482
        }
483

    
484
        @Override
485
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
486
            Number[] inputFloatRow = (Number[])inputRow;
487
            byte[] outputByteRow = (byte[]) outputRow;
488
            for (int i = 0; i < inputFloatRow.length; i++) {
489
                // Solo se procesan aquellas columnas en las que se pueden crear
490
                // kernels, el resto se copian
491
                if ((i - halfSideWindow >= 0) && (i + halfSideWindow < inputFloatRow.length)) {
492
                    double[][] k = new double[sideWindow][sideWindow];
493
                    int r = 0;
494
                    int c = 0;
495
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
496
                        float[] row = (float[]) iterator.next();
497
                        for (int column = Math.max(i - halfSideWindow, 0); column <= Math.min(i + halfSideWindow,
498
                            inputFloatRow.length-1); column++) {
499
                            Float value = ((Float) row[c]);
500
                            if (noData.isDefined() && noData.getValue().equals(value)) {
501
                                // FIXME: Si es noData, deber?a no influir en el
502
                                // resultado
503
                                // ?Que valor poner en el kernel?
504
                                k[r][c] = 0;
505
                            } else {
506
                                k[r][c] = value;
507
                            }
508
                        }
509
                    }
510
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
511
                    outputByteRow[i] = processValue(kernel).byteValue();
512
                } else {
513
                    outputByteRow[i] = ((Float) inputFloatRow[i]).byteValue();
514
                }
515
            }
516
        }
517
    }
518
    private class DoubleRowProcessor extends AbstractRowProcessor {
519

    
520

    
521
        public DoubleRowProcessor(int band) {
522
            super(band);
523
        }
524

    
525
        @Override
526
        public void processRow(Object inputRow, List<Object> bundleRow, Object outputRow) {
527
            Number[] inputDoubleRow = (Number[])inputRow;
528
            byte[] outputByteRow = (byte[]) outputRow;
529
            for (int i = 0; i < inputDoubleRow.length; i++) {
530
                // Solo se procesan aquellas columnas en las que se pueden crear
531
                // kernels, el resto se copian
532
                if ((i - halfSideWindow >= 0) && (i + halfSideWindow < inputDoubleRow.length)) {
533
                    double[][] k = new double[sideWindow][sideWindow];
534
                    int r = 0;
535
                    int c = 0;
536
                    for (Iterator<Object> iterator = bundleRow.iterator(); iterator.hasNext();) {
537
                        double[] row = (double[]) iterator.next();
538
                        for (int column = Math.max(i - halfSideWindow, 0); column <= Math.min(i + halfSideWindow,
539
                            inputDoubleRow.length-1); column++) {
540
                            Double value = ((Double) row[c]);
541
                            if (noData.isDefined() && noData.getValue().equals(value)) {
542
                                // FIXME: Si es noData, deber?a no influir en el resultado
543
                                // ?Que valor poner en el kernel?
544
                                k[r][c] = 0;
545
                            } else {
546
                                k[r][c] = value;
547
                            }
548
                        }
549
                    }
550
                    Kernel kernel = BufferLocator.getBufferManager().createKernel(k);
551
                    outputByteRow[i] = processValue(kernel).byteValue();
552
                } else {
553
                    outputByteRow[i] = ((Double) inputDoubleRow[i]).byteValue();
554
                }
555
            }
556
        }
557
    }
558
}