Statistics
| Revision:

svn-gvsig-desktop / branches / org.gvsig.desktop-2018a / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.mapcontext / org.gvsig.fmap.mapcontext.impl / src / main / java / org / gvsig / raster / lib / legend / impl / operations / equalization / EqualizationOperation.java @ 43803

History | View | Annotate | Download (19.9 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.legend.impl.operations.equalization;
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.NoData;
34
import org.gvsig.raster.lib.buffer.api.exceptions.BandException;
35
import org.gvsig.raster.lib.buffer.api.exceptions.BufferException;
36
import org.gvsig.raster.lib.buffer.api.exceptions.BufferOperationException;
37
import org.gvsig.raster.lib.buffer.api.operations.OperationFactory;
38
import org.gvsig.raster.lib.buffer.api.statistics.HistogramBand;
39
import org.gvsig.raster.lib.buffer.api.statistics.Statistics;
40
import org.gvsig.raster.lib.buffer.spi.exceptions.ProcessingOperationException;
41
import org.gvsig.raster.lib.buffer.spi.operations.AbstractOperation;
42
import org.gvsig.raster.lib.legend.api.RasterLegendLocator;
43
import org.gvsig.raster.lib.legend.api.RasterLegendManager;
44
import org.gvsig.raster.lib.legend.api.colorinterpretation.ColorInterpretation;
45
import org.gvsig.raster.lib.legend.spi.AbstractColoredOperation;
46
import org.gvsig.tools.locator.LocatorException;
47

    
48

    
49
/**
50
 * @author fdiaz
51
 *
52
 */
53
public class EqualizationOperation extends AbstractColoredOperation{
54

    
55
    static public String STATISTICS_PARAM = "statistics";
56

    
57

    
58
    private Statistics statistics;
59
    private List<Integer> bandsToProcess;
60
    private RowProcessor[] rowProcessors;
61
    private HistogramBand[] histogramBands;
62
    private Integer dataType;
63
    private long[][] lahe;
64
    private long[][] laheNegative;
65
    private int nElements;
66

    
67

    
68

    
69
    /**
70
     * @param factory
71
     *
72
     */
73
    public EqualizationOperation(OperationFactory factory) {
74
        this.factory = factory;
75
    }
76

    
77
    @Override
78
    public void preProcess() throws BufferOperationException {
79
        super.preProcess();
80
        BufferManager manager = BufferLocator.getBufferManager();
81
        RasterLegendManager legendManager = RasterLegendLocator.getRasterLegendManager();
82

    
83
        if(this.parameters.getDynClass().getDynField(STATISTICS_PARAM)!=null) {
84
            statistics = (Statistics) this.parameters.getDynValue(STATISTICS_PARAM);
85
        }
86
        if (statistics==null) {
87
            statistics = this.buffer.getStatistics(null);
88
        };
89
        histogramBands = statistics.getHistogram();
90

    
91
        int bands = this.buffer.getBandCount();
92
        NoData[] noData = this.buffer.getBandNoData();
93

    
94

    
95
        if (copyUnprocessedBands) {
96
            this.parameters.setDynValue(OUTPUT_COLOR_INTERPRETATION_PARAM, colorInterpretation);
97
            noData = this.buffer.getBandNoData();
98
            try {
99
                this.outputBuffer =
100
                    manager.createBuffer(this.buffer.getRows(), this.buffer.getColumns(), this.buffer.getBandTypes(),
101
                        this.buffer.getBandNoData(), this.buffer.getProjection(), this.buffer.getEnvelope());
102
            } catch (LocatorException | BufferException | CreateEnvelopeException e) {
103
                throw new ProcessingOperationException(e);
104
            }
105
        } else {
106
            List<String> colorInterpretations = new ArrayList<String>();
107
            List<NoData> noDatas = new ArrayList<NoData>();
108
            List<Integer> types = new ArrayList<Integer>();
109
            for (int band = 0; band < bands; band++) {
110
                if (isProcessableBand(band)) {
111
                    colorInterpretations.add(colorInterpretation.get(band));
112
                    noDatas.add(this.buffer.getBandNoData()[band]);
113
                    types.add(this.buffer.getBandTypes()[band]);
114
                }
115
            }
116
            if (colorInterpretation.hasAlphaBand()) {
117
                colorInterpretations.add(ColorInterpretation.ALPHA_BAND);
118
            }
119
            outputColorInterpretation = legendManager.createColorInterpretation(colorInterpretations.toArray(new String[0]));
120
            this.parameters.setDynValue(OUTPUT_COLOR_INTERPRETATION_PARAM, outputColorInterpretation);
121
            int[] typesInt = new int[types.size()];
122
            for (Iterator iterator = types.iterator(); iterator.hasNext();) {
123
                int i = 0;
124
                Integer type = (Integer) iterator.next();
125
                typesInt[i] = type.intValue();
126
            }
127
            try {
128
                this.outputBuffer =
129
                    manager.createBuffer(this.buffer.getRows(), this.buffer.getColumns(), typesInt,
130
                        noDatas.toArray(new NoData[0]), this.buffer.getProjection(), this.buffer.getEnvelope());
131
            } catch (LocatorException | BufferException | CreateEnvelopeException e) {
132
                throw new ProcessingOperationException(e);
133
            }
134
        }
135

    
136
        rowProcessors = new RowProcessor[bands];
137
        dataType = null;
138
        if(getBandsToProcess().isEmpty()){
139
            throw new ProcessingOperationException("There is no RGB band.", null);
140
        }
141
        for (Iterator<Integer> iterator = getBandsToProcess().iterator(); iterator.hasNext();) {
142
            Integer band = (Integer) iterator.next();
143
            int bandType = this.buffer.getBand(band).getDataType();
144
            if(dataType!=null) {
145
                if(dataType!=bandType){
146
                    throw new IllegalArgumentException("All bands must be of same data type.");
147
                }
148
            } else {
149
                dataType=bandType;
150
            }
151
            switch (bandType) {
152
            case BufferManager.TYPE_BYTE:
153
                rowProcessors[band] = new ByteRowProcessor(band);
154
                break;
155
            case BufferManager.TYPE_USHORT:
156
                rowProcessors[band] = new UShortRowProcessor(band);
157
                break;
158
            case BufferManager.TYPE_SHORT:
159
                rowProcessors[band] = new ShortRowProcessor(band);
160
                break;
161
            case BufferManager.TYPE_INT:
162
                rowProcessors[band] = new IntRowProcessor(band);
163
                break;
164
            case BufferManager.TYPE_FLOAT: //Can't process float band
165
                rowProcessors[band] = null; //new FloatRowProcessor(band);
166
                break;
167
            case BufferManager.TYPE_DOUBLE: //Can't process double band
168
                rowProcessors[band] = null; //new DoubleRowProcessor(band);
169
                break;
170
            default:
171
                throw new IllegalArgumentException("Can't process type of band '" + band + "'");
172
            }
173
        }
174

    
175
        double[][] accumNormalize = convertTableToNormalizeAccumulate(getHistogramTable());
176
        double[][] accumNormalizeNeg = convertTableToNormalizeAccumulate(getNegativeHistogramTable());
177

    
178
        int maxValue = 255;
179
        switch (dataType) {
180
        case BufferManager.TYPE_BYTE:
181
            maxValue = 255;
182
            break;
183
        case BufferManager.TYPE_SHORT:
184
            maxValue = Short.MAX_VALUE;
185
            break;
186
        case BufferManager.TYPE_USHORT:
187
            maxValue = Math.abs(Short.MIN_VALUE) + Short.MAX_VALUE;
188
            break;
189
        case BufferManager.TYPE_INT:
190
            maxValue = Integer.MAX_VALUE;
191
            break;
192
        default:
193
            throw new IllegalArgumentException("Can't process type '" + dataType + "'");
194
        }
195

    
196
        lahe = lahe(accumNormalize, maxValue);
197
        laheNegative = lahe(accumNormalizeNeg, maxValue);
198
        nElements = (laheNegative[0].length - 1); //FIXME: ???
199

    
200
    }
201

    
202

    
203
    /**
204
     * Obtiene la tabla completa del histograma
205
     * @return long[][]
206
     */
207
    private long[][] getHistogramTable() {
208
        int size;
209
        int rangeMin;
210
        int rangeMax;
211
        switch (dataType) {
212
        case BufferManager.TYPE_BYTE:
213
            rangeMin = 0;
214
            rangeMax = 255;
215
            size = 256;
216
            break;
217
        case BufferManager.TYPE_SHORT:
218
            rangeMin = Short.MIN_VALUE;
219
            rangeMax = Short.MAX_VALUE;
220
            size = rangeMax-rangeMin+1;
221
            break;
222
        case BufferManager.TYPE_USHORT:
223
            rangeMin = 0;
224
            rangeMax = Math.abs(Short.MIN_VALUE) + Short.MAX_VALUE;
225
            size = rangeMax-rangeMin+1;
226
            break;
227
        case BufferManager.TYPE_INT:
228
            rangeMin = Integer.MIN_VALUE;
229
            rangeMax = Integer.MAX_VALUE;
230
            size = rangeMax-rangeMin+1;
231
            break;
232
        default:
233
            throw new IllegalArgumentException("Can't process type '" + dataType + "'");
234
        }
235
        long[][] table = new long[getBandsToProcess().size()][size];
236
        for (int i = 0; i < table.length; i++) {
237
            System.arraycopy(histogramBands[getBandsToProcess().get(i)].getFrequencies(), 0, table[i], 0, size);
238
        }
239
        return table;
240
    }
241

    
242
    /**
243
     * Obtiene el histograma de la imagen negativa.
244
     * @return long[][]
245
     */
246
    private long[][] getNegativeHistogramTable() {
247
        long[][] table = getHistogramTable();
248
        long[][] tableNeg = new long[table.length][table[0].length];
249
        for (int i = 0; i < tableNeg.length; i++){
250
            for (int j = 0; j < tableNeg[i].length; j++){
251
                tableNeg[i][table[i].length - j - 1] = table[i][j];
252
            }
253
        }
254
        return tableNeg;
255
    }
256

    
257
    /**
258
     * Obtiene la tabla de valores normalizada. Divide todos los elementos por el n?mero de
259
     * pixeles total.
260
     * @return tabla de valores normalizada
261
     */
262
    private static double[][] convertToNormalizeHistogram(long[][] tableToConvert) {
263
        long[] nValues = new long[tableToConvert.length];
264
        for (int i = 0; i < tableToConvert.length; i++) {
265
            for (int j = 0; j < tableToConvert[i].length; j++) {
266
                nValues[i] += tableToConvert[i][j];
267
            }
268
        }
269

    
270
        double[][] res = new double[tableToConvert.length][tableToConvert[0].length];
271
        for (int i = 0; i < tableToConvert.length; i++) {
272
            for (int j = 0; j < tableToConvert[i].length; j++) {
273
                res[i][j] = (double)((double)tableToConvert[i][j] / (double)nValues[i]);
274
            }
275
        }
276

    
277
        return res;
278
    }
279

    
280
    /**
281
     * Obtiene la tabla de valores normalizada y acumulada. Divide todos los elementos por el n?mero de
282
     * pixeles total.
283
     * @return tabla de valores normalizada
284
     */
285
    private static double[][] convertTableToNormalizeAccumulate(long[][] tableToConvert) {
286
        double[][] res = convertToNormalizeHistogram(tableToConvert);
287
        for (int i = 0; i < tableToConvert.length; i++) {
288
            for (int j = 0; j < tableToConvert[i].length; j++) {
289
                if(j > 0) {
290
                    res[i][j] += res[i][j - 1];
291
                }
292
            }
293
        }
294
        return res;
295
    }
296

    
297

    
298

    
299

    
300

    
301

    
302
    @Override
303
    public void process() throws ProcessingOperationException {
304
        super.process();
305
        for (int band=0; band<this.buffer.getBandCount(); band++){
306
            if (getBandsToProcess().contains(band)) {
307
                Band bufferBand = this.buffer.getBand(band);
308
                Band outputBufferBand = this.outputBuffer.getBand(band);
309

    
310
                for (int row = 0; row < this.buffer.getRows(); row++) {
311
                    Object rowBuffer = bufferBand.createRowBuffer();
312
                    bufferBand.fetchRow(row, rowBuffer);
313

    
314
                    Object outputRowBuffer = outputBufferBand.createRowBuffer();
315
                    outputBufferBand.fetchRow(row, outputRowBuffer);
316

    
317
                    rowProcessors[band].processRow(rowBuffer, outputRowBuffer);
318

    
319
                    outputBufferBand.putRow(row, outputRowBuffer);
320
                }
321
            } else {
322
                try {
323
                    this.outputBuffer.getBand(band).copyFrom(this.buffer.getBand(band));
324
                } catch (BandException e) {
325
                    throw new ProcessingOperationException(e);
326
                }
327
            }
328
        }
329
    }
330

    
331
    @Override
332
    public void postProcess() throws BufferOperationException {
333
        super.postProcess();
334
    }
335

    
336
    /**
337
     * M?todo lahe para la ecualizaci?n. Cada posici?n del array resultante tendr? el valor de salida para
338
     * un valor de entrada dado.
339
     * @param accumNorm Histograma acumulado
340
     * @param value Valor m?ximo
341
     * @return
342
     */
343
    private long[][] lahe(double[][] accumNorm, int value) {
344
        long[][] res = new long[accumNorm.length][accumNorm[0].length];
345
        for (int i = 0; i < res.length; i++){
346
              for (int j = 0; j < res[i].length; j++){
347
                  res[i][j] = Math.round(accumNorm[i][j] * value);
348
              }
349
        }
350
        return res;
351
    }
352

    
353
    /**
354
     * @param band
355
     * @return
356
     */
357
    private List<Integer> getBandsToProcess() {
358
        if(bandsToProcess == null){
359
            int bands = this.buffer.getBandCount();
360
            bandsToProcess = new ArrayList<Integer>();
361

    
362
            for (int band = 0; band < bands; band++) {
363
                if (isProcessableBand(band)) {
364
                    bandsToProcess.add(band);
365
                }
366
            }
367
        }
368
        return bandsToProcess;
369
    }
370

    
371
    /**
372
     * @param band
373
     * @return
374
     */
375
    private boolean isProcessableBand(int band) {
376
        return isRGBBand(band) && (this.buffer.getBandTypes()[band] == BufferManager.TYPE_BYTE
377
            || this.buffer.getBandTypes()[band] == BufferManager.TYPE_SHORT
378
            || this.buffer.getBandTypes()[band] == BufferManager.TYPE_USHORT
379
            || this.buffer.getBandTypes()[band] == BufferManager.TYPE_INT
380
        );
381
    }
382

    
383
    private boolean isRGBBand(int band) {
384
        String bandColorInterpretation = colorInterpretation.get(band);
385
        return (bandColorInterpretation.equals(ColorInterpretation.RED_BAND) ||
386
            bandColorInterpretation.equals(ColorInterpretation.GREEN_BAND) ||
387
            bandColorInterpretation.equals(ColorInterpretation.BLUE_BAND));
388
    }
389

    
390

    
391
    interface RowProcessor {
392
        void processRow(Object inputRow, Object outputRow);
393
        Number processValue(Object value);
394
    };
395

    
396
    private abstract class AbstractRowProcessor implements RowProcessor {
397
        protected int band;
398
        protected HistogramBand histogram;
399
        protected NoData noData;
400

    
401
        public AbstractRowProcessor(int band) {
402
            this.band = band;
403
            this.histogram = statistics.getHistogram()[this.band];
404
            this.noData = buffer.getBand(this.band).getNoData();
405
        }
406
    }
407

    
408
    private class ByteRowProcessor extends AbstractRowProcessor {
409

    
410

    
411
        public ByteRowProcessor(int band) {
412
            super(band);
413
        }
414

    
415
        @Override
416
        public void processRow(Object inputRow, Object outputRow) {
417
            byte[] inputByteRow = (byte[])inputRow;
418
            byte[] outputByteRow = (byte[])outputRow;
419

    
420
            for (int i = 0; i < inputByteRow.length; i++) {
421
                outputByteRow[i] = processValue(inputByteRow[i]).byteValue();
422
            }
423
        }
424

    
425
        @Override
426
        public Number processValue(Object value) {
427
            if(noData.isDefined() && noData.getValue().equals(value)){
428
                return (byte)value;
429
            }
430

    
431
            //M?todo lahe
432
            int intValue = 0xFF & ((Byte) value).byteValue();
433
            int equalizationPositive = (int)lahe[getBandsToProcess().indexOf(new Integer(band))][intValue % (int)histogram.getNumValues()];
434
            int equalizationNegative = (int)laheNegative[getBandsToProcess().indexOf(new Integer(band))][nElements - (intValue % (int)histogram.getNumValues())];
435

    
436
            int resValue = ((nElements - equalizationNegative) + equalizationPositive) / 2;
437
            return (byte)(resValue & 0x000000ff);
438
        }
439
    }
440

    
441
    private class ShortRowProcessor extends AbstractRowProcessor {
442

    
443
        public ShortRowProcessor(int band) {
444
            super(band);
445
        }
446

    
447
        @Override
448
        public void processRow(Object inputRow, Object outputRow) {
449
            short[] inputByteRow = (short[])inputRow;
450
            short[] outputByteRow = (short[])outputRow;
451
            for (int i = 0; i < inputByteRow.length; i++) {
452
                outputByteRow[i] = processValue(inputByteRow[i]).shortValue();
453
            }
454
        }
455

    
456
        @Override
457
        public Number processValue(Object value) {
458
            if(noData.isDefined() && noData.getValue().equals(value)){
459
                return (byte)0;
460
            }
461

    
462
            //M?todo lahe
463
            int intValue = ((Short) value).byteValue();
464
            int equalizationPositive = (int)lahe[getBandsToProcess().indexOf(new Integer(band))][intValue % (int)histogram.getNumValues()];
465
            int equalizationNegative = (int)laheNegative[getBandsToProcess().indexOf(new Integer(band))][nElements - (intValue % (int)histogram.getNumValues())];
466

    
467
            int resValue = ((nElements - equalizationNegative) + equalizationPositive) / 2;
468
            return (short)resValue;
469
        }
470

    
471
    }
472

    
473
    private class UShortRowProcessor extends AbstractRowProcessor {
474

    
475
        public UShortRowProcessor(int band) {
476
            super(band);
477
        }
478

    
479
        @Override
480
        public void processRow(Object inputRow, Object outputRow) {
481
            short[] inputByteRow = (short[])inputRow;
482
            short[] outputByteRow = (short[])outputRow;
483
            for (int i = 0; i < inputByteRow.length; i++) {
484
                outputByteRow[i] = processValue(inputByteRow[i]).shortValue();
485
            }
486
        }
487

    
488
        @Override
489
        public Number processValue(Object value) {
490
            if(noData.isDefined() && noData.getValue().equals(value)){
491
                return (byte)0;
492
            }
493

    
494
            //M?todo lahe
495
            int intValue = 0xFFFF & ((Short) value).byteValue();
496
            int equalizationPositive = (int)lahe[getBandsToProcess().indexOf(new Integer(band))][intValue % (int)histogram.getNumValues()];
497
            int equalizationNegative = (int)laheNegative[getBandsToProcess().indexOf(new Integer(band))][nElements - (intValue % (int)histogram.getNumValues())];
498

    
499
            int resValue = ((nElements - equalizationNegative) + equalizationPositive) / 2;
500
            return (short)resValue;
501
        }
502

    
503
    }
504

    
505
    private class IntRowProcessor extends AbstractRowProcessor {
506

    
507
        public IntRowProcessor(int band) {
508
            super(band);
509
        }
510

    
511
        @Override
512
        public void processRow(Object inputRow, Object outputRow) {
513
            int[] inputByteRow = (int[])inputRow;
514
            int[] outputByteRow = (int[])outputRow;
515
            for (int i = 0; i < inputByteRow.length; i++) {
516
                outputByteRow[i] = processValue(inputByteRow[i]).intValue();
517
            }
518
        }
519

    
520
        @Override
521
        public Number processValue(Object value) {
522
            if(noData.isDefined() && noData.getValue().equals(value)){
523
                return (byte)0;
524
            }
525

    
526
            Double dValue = ((Number) value).doubleValue();
527

    
528
            //M?todo lahe
529
            int intValue = ((Integer) value).intValue();
530
            int equalizationPositive = (int)lahe[getBandsToProcess().indexOf(new Integer(band))][intValue % (int)histogram.getNumValues()];
531
            int equalizationNegative = (int)laheNegative[getBandsToProcess().indexOf(new Integer(band))][nElements - (intValue % (int)histogram.getNumValues())];
532

    
533
            int resValue = ((nElements - equalizationNegative) + equalizationPositive) / 2;
534
            return (int)resValue;
535
        }
536
    }
537
}