    001/*-
    002 *******************************************************************************
    003 * Copyright (c) 2022 Diamond Light Source Ltd.
    004 * All rights reserved. This program and the accompanying materials
    005 * are made available under the terms of the Eclipse Public License v1.0
    006 * which accompanies this distribution, and is available at
    007 * http://www.eclipse.org/legal/epl-v10.html
    008 *
    009 * Contributors:
    010 *    Peter Chang - initial API and implementation and/or initial documentation
    011 *******************************************************************************/
    012
    013package org.eclipse.january.dataset;
    014
    015import java.util.Arrays;
    016
    017import org.slf4j.Logger;
    018import org.slf4j.LoggerFactory;
    019
    020/**
    021 * Class to hold colour datasets as red, green, blue tuples of byte integers. Note that
    022 * the values are treated as unsigned integers
    023 * @since 2.3
    024 */
    025public class RGBByteDataset extends CompoundByteDataset implements Cloneable {
    026        // pin UID to base class
    027        private static final long serialVersionUID = Dataset.serialVersionUID;
    028
    029        private static final Logger logger = LoggerFactory.getLogger(RGBByteDataset.class);
    030
    031        private static final int ISIZE = 3; // number of elements per item
    032
    033        /**
    034         * Create a null dataset
    035         */
    036        public RGBByteDataset() {
    037                super(ISIZE);
    038        }
    039
    040        /**
    041         * @param shape output shape
    042         */
    043        public RGBByteDataset(final int... shape) {
    044                super(ISIZE, shape);
    045        }
    046
    047        /**
    048         * @param data interleaved RGB values
    049         * @param shape output shape
    050         */
    051        public RGBByteDataset(final byte[] data, final int... shape) {
    052                super(ISIZE, data, shape);
    053        }
    054
    055        /**
    056         * Copy a dataset
    057         * @param dataset to clone
    058         */
    059        public RGBByteDataset(final RGBByteDataset dataset) {
    060                super(dataset);
    061        }
    062
    063        @Override
    064        public RGBByteDataset clone() {
    065                return new RGBByteDataset(this);
    066        }
    067
    068        /**
    069         * Create a dataset using given data (red, green and blue parts are given separately)
    070         * @param redData data for red
    071         * @param greenData data for green
    072         * @param blueData data for blue
    073         * @param shape (can be null to create 1D dataset)
    074         */
    075        public RGBByteDataset(final int[] redData, final int[] greenData, final int[] blueData, int... shape) {
    076                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
    077                dsize = dsize > blueData.length ? blueData.length : dsize;
    078                if (shape == null || shape.length == 0) {
    079                        shape = new int[] {dsize};
    080                }
    081                isize = ISIZE;
    082                size = ShapeUtils.calcSize(shape);
    083                if (size != dsize) {
    084                        logger.error("Shape is not compatible with size of data array");
    085                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
    086                }
    087                this.shape = shape.clone();
    088
    089                try {
    090                        odata = data = createArray(size);
    091                } catch (Throwable t) {
    092                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
    093                        throw new IllegalArgumentException(t);
    094                }
    095
    096                for (int i = 0, n = 0; i < size; i++) {
    097                        data[n++] = (byte) redData[i];
    098                        data[n++] = (byte) greenData[i];
    099                        data[n++] = (byte) blueData[i];
    100                }
    101        }
    102
    103        /**
    104         * Create a dataset using given data (red, green and blue parts are given separately)
    105         * @param redData data for red
    106         * @param greenData data for green
    107         * @param blueData data for blue
    108         * @param shape (can be null to create 1D dataset)
    109         */
    110        public RGBByteDataset(final short[] redData, final short[] greenData, final short[] blueData, int... shape) {
    111                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
    112                dsize = dsize > blueData.length ? blueData.length : dsize;
    113                if (shape == null || shape.length == 0) {
    114                        shape = new int[] {dsize};
    115                }
    116                isize = ISIZE;
    117                size = ShapeUtils.calcSize(shape);
    118                if (size != dsize) {
    119                        logger.error("Shape is not compatible with size of data array");
    120                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
    121                }
    122                this.shape = shape.clone();
    123
    124                try {
    125                        odata = data = createArray(size);
    126                } catch (Throwable t) {
    127                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
    128                        throw new IllegalArgumentException(t);
    129                }
    130
    131                for (int i = 0, n = 0; i < size; i++) {
    132                        data[n++] = (byte) redData[i];
    133                        data[n++] = (byte) greenData[i];
    134                        data[n++] = (byte) blueData[i];
    135                }
    136        }
    137
    138        /**
    139         * Create a dataset using given data (red, green and blue parts are given separately)
    140         * @param redData data for red
    141         * @param greenData data for green
    142         * @param blueData data for blue
    143         * @param shape (can be null to create 1D dataset)
    144         */
    145        public RGBByteDataset(final byte[] redData, final byte[] greenData, final byte[] blueData, int... shape) {
    146                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
    147                dsize = dsize > blueData.length ? blueData.length : dsize;
    148                if (shape == null || shape.length == 0) {
    149                        shape = new int[] {dsize};
    150                }
    151                isize = ISIZE;
    152                size = ShapeUtils.calcSize(shape);
    153                if (size != dsize) {
    154                        logger.error("Shape is not compatible with size of data array");
    155                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
    156                }
    157                this.shape = shape.clone();
    158
    159                try {
    160                        odata = data = createArray(size);
    161                } catch (Throwable t) {
    162                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
    163                        throw new IllegalArgumentException(t);
    164                }
    165
    166                for (int i = 0, n = 0; i < size; i++) {
    167                        data[n++] = redData[i];
    168                        data[n++] = greenData[i];
    169                        data[n++] = blueData[i];
    170                }
    171        }
    172
    173        private final static int MIN_VALUE = 0;
    174        private final static int MAX_VALUE = 255;
    175
    176        /**
    177         * Create a dataset using given colour data (colour components are given separately)
    178         * @param red dataset
    179         * @param green dataset
    180         * @param blue dataset
    181         */
    182        public RGBByteDataset(final Dataset red, final Dataset green, final Dataset blue) {
    183                super(ISIZE, red.getShapeRef());
    184                red.checkCompatibility(green);
    185                red.checkCompatibility(blue);
    186
    187                if (red.max().doubleValue() > MAX_VALUE || red.min().doubleValue() < MIN_VALUE ||
    188                                green.max().doubleValue() > MAX_VALUE || green.min().doubleValue() < MIN_VALUE ||
    189                                blue.max().doubleValue() > MAX_VALUE || blue.min().doubleValue() < MIN_VALUE) {
    190                        logger.warn("Some values are out of range and will be truncated");
    191                }
    192
    193                IndexIterator riter = red.getIterator();
    194                IndexIterator giter = green.getIterator();
    195                IndexIterator biter = blue.getIterator();
    196
    197                for (int i = 0; riter.hasNext() && giter.hasNext() && biter.hasNext();) {
    198                        data[i++] = (byte) red.getElementLongAbs(riter.index);
    199                        data[i++] = (byte) green.getElementLongAbs(giter.index);
    200                        data[i++] = (byte) blue.getElementLongAbs(biter.index);
    201                }
    202        }
    203
    204        /**
    205         * Create a dataset using given grey data
    206         * @param grey dataset
    207         */
    208        public RGBByteDataset(final Dataset grey) {
    209                super(ISIZE, grey.getShapeRef());
    210
    211                IndexIterator giter = grey.getIterator();
    212
    213                for (int i = 0; giter.hasNext();) {
    214                        final byte g = (byte) grey.getElementLongAbs(giter.index);
    215                        data[i++] = g;
    216                        data[i++] = g;
    217                        data[i++] = g;
    218                }
    219        }
    220
    221        /**
    222         * Create a dataset using given compound data
    223         * @param colour dataset with colour data
    224         */
    225        public RGBByteDataset(final CompoundDataset colour) {
    226                super(ISIZE, colour.getShapeRef());
    227
    228                if (colour.getElementsPerItem() != 3) {
    229                        throw new IllegalArgumentException("Compound dataset must have three elements per item");
    230                }
    231
    232                final IndexIterator it = colour.getIterator();
    233                for (int i = 0; it.hasNext();) {
    234                        data[i++] = (byte) colour.getElementLongAbs(it.index);
    235                        data[i++] = (byte) colour.getElementLongAbs(it.index + 1);
    236                        data[i++] = (byte) colour.getElementLongAbs(it.index + 2);
    237                }
    238        }
    239
    240        /**
    241         * Create a RGB dataset from an object which could be a Java list, array (of arrays...) or Number. Ragged
    242         * sequences or arrays are padded with zeros. The item size is the last dimension of the corresponding
    243         * elemental dataset
    244         *
    245         * @param obj object
    246         * @return dataset with contents given by input
    247         */
    248        public static RGBByteDataset createFromObject(final Object obj) {
    249                CompoundByteDataset result = (CompoundByteDataset) DatasetUtils.createCompoundDataset(ByteDataset.createFromObject(obj), ISIZE);
    250                return new RGBByteDataset(result.data, result.shape);
    251        }
    252
    253        /**
    254         * Create a RGB dataset from a compound dataset (no normalisation performed)
    255         * @param a dataset
    256         * @return RGB dataset (grey if input dataset has less than 3 elements per item)
    257         */
    258        public static RGBByteDataset createFromCompoundDataset(final CompoundDataset a) {
    259                if (a instanceof RGBByteDataset)
    260                        return (RGBByteDataset) a;
    261                final int is = a.getElementsPerItem();
    262                if (is < 3) {
    263                        return new RGBByteDataset((Dataset) a);
    264                }
    265
    266                if (a instanceof CompoundByteDataset && is == 3) {
    267                        return new RGBByteDataset((byte[]) a.getBuffer(), a.getShapeRef());
    268                }
    269
    270                final RGBByteDataset rgb = new RGBByteDataset(a.getShapeRef());
    271                final IndexIterator it = a.getIterator();
    272
    273                int n = 0;
    274                while (it.hasNext()) {
    275                        rgb.data[n++] = (byte) a.getElementLongAbs(it.index);
    276                        rgb.data[n++] = (byte) a.getElementLongAbs(it.index + 1);
    277                        rgb.data[n++] = (byte) a.getElementLongAbs(it.index + 2);
    278                }
    279
    280                return rgb;
    281        }
    282
    283        /**
    284         * Create a RGB dataset from hue, saturation and value dataset
    285         * @param hue (in degrees from -360 to 360)
    286         * @param saturation (from 0 to 1), can be null to denote 1
    287         * @param value (from 0 to 1)
    288         * @return RGB dataset
    289         */
    290        public static RGBByteDataset createFromHSV(final Dataset hue, final Dataset saturation, final Dataset value) {
    291                if ((saturation != null && !hue.isCompatibleWith(saturation)) || !hue.isCompatibleWith(value)) {
    292                        throw new IllegalArgumentException("Hue, saturation and value datasets must have the same shape");
    293                }
    294
    295                RGBByteDataset result = new RGBByteDataset(hue.getShapeRef());
    296                IndexIterator it = result.getIterator(true);
    297                int[] pos = it.getPos();
    298                byte[] rgb = new byte[3];
    299
    300                if (saturation == null) {
    301                        while (it.hasNext()) {
    302                                convertHSVToRGB(hue.getDouble(pos), 1, value.getDouble(pos), rgb);
    303                                result.setAbs(it.index, rgb);
    304                        }
    305                } else {
    306                        while (it.hasNext()) {
    307                                convertHSVToRGB(hue.getDouble(pos), saturation.getDouble(pos), value.getDouble(pos), rgb);
    308                                result.setAbs(it.index, rgb);
    309                        }
    310                }
    311
    312                return result;
    313        }
    314
    315        /**
    316         * Create a RGB dataset from hue, saturation and lightness dataset
    317         * @param hue (in degrees from -360 to 360)
    318         * @param saturation (from 0 to 1), can be null to denote 1
    319         * @param lightness (from 0 to 1)
    320         * @return RGB dataset
    321         */
    322        public static RGBByteDataset createFromHSL(final Dataset hue, final Dataset saturation, final Dataset lightness) {
    323                if ((saturation != null && !hue.isCompatibleWith(saturation)) || !hue.isCompatibleWith(lightness)) {
    324                        throw new IllegalArgumentException("Hue, saturation and lightness datasets must have the same shape");
    325                }
    326
    327                RGBByteDataset result = new RGBByteDataset(hue.getShapeRef());
    328                IndexIterator it = result.getIterator(true);
    329                int[] pos = it.getPos();
    330                byte[] rgb = new byte[3];
    331
    332                if (saturation == null) {
    333                        while (it.hasNext()) {
    334                                convertHSLToRGB(hue.getDouble(pos), 1, lightness.getDouble(pos), rgb);
    335                                result.setAbs(it.index, rgb);
    336                        }
    337                } else {
    338                        while (it.hasNext()) {
    339                                convertHSLToRGB(hue.getDouble(pos), saturation.getDouble(pos), lightness.getDouble(pos), rgb);
    340                                result.setAbs(it.index, rgb);
    341                        }
    342                }
    343
    344                return result;
    345        }
    346
    347        private static void convertHSVToRGB(double h, double s, double v, byte[] rgb) {
    348                double m = 255 * v;
    349                double chroma = s * m;
    350                m -= chroma;
    351                double hprime = h / 60.;
    352                if (hprime < 0) {
    353                        hprime += 6;
    354                }
    355                byte sx = (byte) (chroma * (1 - Math.abs((hprime % 2) - 1)) + m);
    356                byte sc = (byte) (chroma + m);
    357                byte sm = (byte) m;
    358
    359                if (hprime < 1) {
    360                        rgb[0] = sc;
    361                        rgb[1] = sx;
    362                        rgb[2] = sm;
    363                } else if (hprime < 2) {
    364                        rgb[0] = sx;
    365                        rgb[1] = sc;
    366                        rgb[2] = sm;
    367                } else if (hprime < 3) {
    368                        rgb[0] = sm;
    369                        rgb[1] = sc;
    370                        rgb[2] = sx;
    371                } else if (hprime < 4) {
    372                        rgb[0] = sm;
    373                        rgb[1] = sx;
    374                        rgb[2] = sc;
    375                } else if (hprime < 5) {
    376                        rgb[0] = sx;
    377                        rgb[1] = sm;
    378                        rgb[2] = sc;
    379                } else if (hprime < 6) {
    380                        rgb[0] = sc;
    381                        rgb[1] = sm;
    382                        rgb[2] = sx;
    383                } else { // if hue is outside domain
    384                        rgb[0] = sm;
    385                        rgb[1] = sm;
    386                        rgb[2] = sm;
    387                }
    388        }
    389
    390        private static void convertHSLToRGB(double h, double s, double l, byte[] rgb) {
    391                double m = l;
    392                double chroma = s * (1 - Math.abs(2 * m - 1));
    393                m -= chroma * 0.5;
    394                m *= 255;
    395                chroma *= 255;
    396                double hprime = h / 60.;
    397                if (hprime < 0) {
    398                        hprime += 6;
    399                }
    400                byte sx = (byte) (chroma * (1 - Math.abs((hprime % 2) - 1)) + m);
    401                byte sc = (byte) (chroma + m);
    402                byte sm = (byte) m;
    403
    404                if (hprime < 1) {
    405                        rgb[0] = sc;
    406                        rgb[1] = sx;
    407                        rgb[2] = sm;
    408                } else if (hprime < 2) {
    409                        rgb[0] = sx;
    410                        rgb[1] = sc;
    411                        rgb[2] = sm;
    412                } else if (hprime < 3) {
    413                        rgb[0] = sm;
    414                        rgb[1] = sc;
    415                        rgb[2] = sx;
    416                } else if (hprime < 4) {
    417                        rgb[0] = sm;
    418                        rgb[1] = sx;
    419                        rgb[2] = sc;
    420                } else if (hprime < 5) {
    421                        rgb[0] = sx;
    422                        rgb[1] = sm;
    423                        rgb[2] = sc;
    424                } else if (hprime < 6) {
    425                        rgb[0] = sc;
    426                        rgb[1] = sm;
    427                        rgb[2] = sx;
    428                } else { // if hue is outside domain
    429                        rgb[0] = sm;
    430                        rgb[1] = sm;
    431                        rgb[2] = sm;
    432                }
    433        }
    434
    435        @Override
    436        public RGBByteDataset getSlice(SliceIterator siter) {
    437                CompoundByteDataset base = super.getSlice(siter);
    438
    439                RGBByteDataset slice = new RGBByteDataset();
    440                copyToView(base, slice, false, false);
    441                slice.setData();
    442                return slice;
    443        }
    444
    445        @Override
    446        public RGBByteDataset getView(boolean deepCopyMetadata) {
    447                RGBByteDataset view = new RGBByteDataset();
    448                copyToView(this, view, true, deepCopyMetadata);
    449                view.setData();
    450                return view;
    451        }
    452
    453        /**
    454         * @return red value in the first position
    455         */
    456        public byte getRed() {
    457                return data[getFirst1DIndex()];
    458        }
    459
    460        /**
    461         * @param i position in first dimension
    462         * @return red value in given position
    463         */
    464        public byte getRed(final int i) {
    465                return data[get1DIndex(i)];
    466        }
    467
    468        /**
    469         * @param i position in first dimension
    470         * @param j position in second dimension
    471         * @return red value in given position
    472         */
    473        public byte getRed(final int i, final int j) {
    474                return data[get1DIndex(i, j)];
    475        }
    476
    477        /**
    478         * @param pos position
    479         * @return red value in given position
    480         */
    481        public byte getRed(final int... pos) {
    482                return data[get1DIndex(pos)];
    483        }
    484
    485        /**
    486         * @return green value in the first position
    487         */
    488        public byte getGreen() {
    489                return data[getFirst1DIndex() + 1];
    490        }
    491
    492        /**
    493         * @param i position in first dimension
    494         * @return green value in given position
    495         */
    496        public byte getGreen(final int i) {
    497                return data[get1DIndex(i) + 1];
    498        }
    499
    500        /**
    501         * @param i position in first dimension
    502         * @param j position in second dimension
    503         * @return green value in given position
    504         */
    505        public byte getGreen(final int i, final int j) {
    506                return data[get1DIndex(i, j) + 1];
    507        }
    508
    509        /**
    510         * @param pos position
    511         * @return green value in given position
    512         */
    513        public byte getGreen(final int... pos) {
    514                return data[get1DIndex(pos) + 1];
    515        }
    516
    517        /**
    518         * @return blue value in the first position
    519         */
    520        public byte getBlue() {
    521                return data[getFirst1DIndex() + 2];
    522        }
    523
    524        /**
    525         * @param i position in first dimension
    526         * @return blue value in given position
    527         */
    528        public byte getBlue(final int i) {
    529                return data[get1DIndex(i) + 2];
    530        }
    531
    532        /**
    533         * @param i position in first dimension
    534         * @param j position in second dimension
    535         * @return blue value in given position
    536         */
    537        public byte getBlue(final int i, final int j) {
    538                return data[get1DIndex(i, j) + 2];
    539        }
    540
    541        /**
    542         * @param pos position
    543         * @return blue value in given position
    544         */
    545        public byte getBlue(final int... pos) {
    546                return data[get1DIndex(pos) + 2];
    547        }
    548
    549        /**
    550         * Get a red value from given absolute index as a byte - note this index does not
    551         * take in account the item size so be careful when using with multi-element items
    552         *
    553         * @param n absolute index
    554         * @return red value
    555         */
    556        public byte getRedAbs(int n) {
    557                return data[n*isize];
    558        }
    559
    560        /**
    561         * Get a green value from given absolute index as a byte - note this index does not
    562         * take in account the item size so be careful when using with multi-element items
    563         *
    564         * @param n absolute index
    565         * @return green value
    566         */
    567        public byte getGreenAbs(int n) {
    568                return data[n*isize + 1];
    569        }
    570
    571        /**
    572         * Get a blue value from given absolute index as a byte - note this index does not
    573         * take in account the item size so be careful when using with multi-element items
    574         *
    575         * @param n absolute index
    576         * @return blue value
    577         */
    578        public byte getBlueAbs(int n) {
    579                return data[n*isize + 2];
    580        }
    581
    582
    583        // weights from NTSC formula aka ITU-R BT.601 for mapping RGB to luma
    584        private static final double Wr = 0.299, Wg = 0.587, Wb = 0.114;
    585
    586        /**
    587         * Convert colour dataset to a grey-scale one using the NTSC formula, aka ITU-R BT.601, for RGB to luma mapping
    588         * @param <T> dataset sub-interface
    589         * @param clazz dataset sub-interface
    590         * @return a grey-scale dataset of given type
    591         */
    592        public <T extends Dataset> T createGreyDataset(final Class<T> clazz) {
    593                return createGreyDataset(clazz, Wr, Wg, Wb);
    594        }
    595
    596        /**
    597         * Convert colour dataset to a grey-scale one using given RGB to luma mapping
    598         * @param <T> dataset sub-interface
    599         * @param clazz dataset sub-interface
    600         * @param red weight
    601         * @param green weight
    602         * @param blue weight
    603         * @return a grey-scale dataset of given class
    604         */
    605        public <T extends Dataset> T createGreyDataset(final Class<T> clazz, final double red, final double green, final double blue) {
    606                final T grey = DatasetFactory.zeros(clazz, shape);
    607                final IndexIterator it = getIterator();
    608
    609                int i = 0;
    610                while (it.hasNext()) {
    611                        grey.setObjectAbs(i++, red*Byte.toUnsignedInt(data[it.index]) + green*Byte.toUnsignedInt(data[it.index + 1]) + blue*Byte.toUnsignedInt(data[it.index + 2]));
    612                }
    613                return grey;
    614        }
    615
    616        /**
    617         * Convert colour dataset to a grey-scale one using the NTSC formula, aka ITU-R BT.601, for RGB to luma mapping
    618         * @param dtype dataset type
    619         * @return a grey-scale dataset of given class
    620         * @deprecated Use {@link RGBByteDataset#createGreyDataset(Class)}
    621         */
    622        @Deprecated
    623        public Dataset createGreyDataset(final int dtype) {
    624                return createGreyDataset(Wr, Wg, Wb, dtype);
    625        }
    626
    627        /**
    628         * Convert colour dataset to a grey-scale one using given RGB to luma mapping
    629         * @param red weight
    630         * @param green weight
    631         * @param blue weight
    632         * @param dtype dataset type
    633         * @return a grey-scale dataset of given type
    634         * @deprecated Use {@link RGBByteDataset#createGreyDataset(Class, double, double, double)}
    635         */
    636        @Deprecated
    637        public Dataset createGreyDataset(final double red, final double green, final double blue, final int dtype) {
    638                return createGreyDataset(DTypeUtils.getInterface(dtype), red, green, blue);
    639        }
    640
    641        /**
    642         * Extract red colour channel
    643         * @param <T> dataset sub-interface
    644         * @param clazz dataset sub-interface
    645         * @return a dataset of given class
    646         */
    647        public <T extends Dataset> T createRedDataset(final Class<T> clazz) {
    648                return createColourChannelDataset(0, clazz, "red");
    649        }
    650
    651        /**
    652         * Extract green colour channel
    653         * @param <T> dataset sub-interface
    654         * @param clazz dataset sub-interface
    655         * @return a dataset of given class
    656         */
    657        public <T extends Dataset> T createGreenDataset(final Class<T> clazz) {
    658                return createColourChannelDataset(1, clazz, "green");
    659        }
    660
    661        /**
    662         * Extract blue colour channel
    663         * @param <T> dataset sub-interface
    664         * @param clazz dataset sub-interface
    665         * @return a dataset of given class
    666         */
    667        public <T extends Dataset> T createBlueDataset(final Class<T> clazz) {
    668                return createColourChannelDataset(2, clazz, "blue");
    669        }
    670
    671        /**
    672         * Extract red colour channel
    673         * @param dtype dataset type
    674         * @return a dataset of given type
    675         * @deprecated Use {@link #createRedDataset}
    676         */
    677        @Deprecated
    678        public Dataset createRedDataset(final int dtype) {
    679                return createColourChannelDataset(0, DTypeUtils.getInterface(dtype), "red");
    680        }
    681
    682        /**
    683         * Extract green colour channel
    684         * @param dtype dataset type
    685         * @return a dataset of given type
    686         * @deprecated Use {@link #createGreenDataset}
    687         */
    688        @Deprecated
    689        public Dataset createGreenDataset(final int dtype) {
    690                return createColourChannelDataset(1, DTypeUtils.getInterface(dtype), "green");
    691        }
    692
    693        /**
    694         * Extract blue colour channel
    695         * @param dtype dataset type
    696         * @return a dataset of given type
    697         * @deprecated Use {@link #createBlueDataset}
    698         */
    699        @Deprecated
    700        public Dataset createBlueDataset(final int dtype) {
    701                return createColourChannelDataset(2, DTypeUtils.getInterface(dtype), "blue");
    702        }
    703
    704        private <T extends Dataset> T createColourChannelDataset(int channelOffset, Class<T> clazz, String cName) {
    705                final T channel = DatasetFactory.zeros(clazz, shape);
    706
    707                final StringBuilder cname = name == null ? new StringBuilder() : new StringBuilder(name);
    708                if (cname.length() > 0) {
    709                        cname.append('.');
    710                }
    711                cname.append(cName);
    712                channel.setName(cname.toString());
    713
    714                final IndexIterator it = getIterator();
    715
    716                int i = 0;
    717                while (it.hasNext()) {
    718                        channel.setObjectAbs(i++, Byte.toUnsignedInt(data[it.index + channelOffset]));
    719                }
    720
    721                return channel;
    722        }
    723
    724
    725        /**
    726         * @return red view
    727         */
    728        public ByteDataset getRedView() {
    729                return getColourChannelView(0, "red");
    730        }
    731
    732        /**
    733         * @return green view
    734         */
    735        public ByteDataset getGreenView() {
    736                return getColourChannelView(1, "green");
    737        }
    738
    739        /**
    740         * @return blue view
    741         */
    742        public ByteDataset getBlueView() {
    743                return getColourChannelView(2, "blue");
    744        }
    745
    746        private ByteDataset getColourChannelView(final int channelOffset, final String cName) {
    747                ByteDataset view = getElements(channelOffset);
    748                view.setName(cName);
    749                return view;
    750        }
    751
    752        @Override
    753        public double getElementDoubleAbs(final int index) {
    754                return Byte.toUnsignedInt(data[index]);
    755        }
    756
    757        @Override
    758        public long getElementLongAbs(final int index) {
    759                return Byte.toUnsignedInt(data[index]);
    760        }
    761
    762        @Override
    763        public Number max(boolean... ignored) {
    764                int max = 0;
    765                final IndexIterator it = getIterator();
    766
    767                while (it.hasNext()) {
    768                        for (int i = 0; i < ISIZE; i++) {
    769                                int value = Byte.toUnsignedInt(data[it.index + i]);
    770                                if (value > max)
    771                                        max = value;
    772                        }
    773                }
    774                return max;
    775        }
    776
    777        @Override
    778        public Number min(boolean... ignored) {
    779                int min = MAX_VALUE;
    780                final IndexIterator it = getIterator();
    781
    782                while (it.hasNext()) {
    783                        for (int i = 0; i < ISIZE; i++) {
    784                                int value = Byte.toUnsignedInt(data[it.index + i]);
    785                                if (value < min)
    786                                        min = value;
    787                        }
    788                }
    789                return min;
    790        }
    791}