001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009
010package imagingbook.common.ij;
011
012import ij.IJ;
013import ij.process.ColorProcessor;
014import ij.process.ImageProcessor;
015import ij.process.LUT;
016
017import java.awt.Color;
018import java.awt.image.ColorModel;
019import java.awt.image.IndexColorModel;
020
021
022/**
023 * This enum type wraps a selection of ImageJ's lookup tables (originally defined in class
024 * {@link ij.plugin.LutLoader}).
025 *
026 * @author WB
027 * @version 2022/09/15 converted to enum type
028 * @see LUT
029 */
030public enum NamedLookupTable {
031        
032        Fire(fire()),
033        Grays(grays()),
034        Ice(ice()),
035        RedGreen(redgreen()),
036        Rgb332(rgb332()),
037        Spectrum(spectrum());
038        
039        // ------------------------------------------------------
040
041        private final LUT lut;
042        
043        private NamedLookupTable(LUT lut) {
044                this.lut = lut;
045        }
046        
047        /**
048         * Returns the actual lookup table for this named item.
049         * 
050         * @return the associated {@link LUT} instance
051         */
052        public LUT getLUT() {
053                return this.lut;
054        }
055        
056        // ------------------------------------------------------
057        
058        /**
059         * Create a new lookup-table from three RGB arrays of length 256.
060         * 
061         * @param r Red component values.
062         * @param g Green component values.
063         * @param b Blue component values.
064         * @return A new instance of type {@link ij.process.LUT}.
065         */
066        public static LUT create(byte[] r, byte[] g, byte[] b) {
067                if (r.length != 256 || g.length != 256 || b.length != 256) {
068                        throw new IllegalArgumentException("component arrays must be of length 256");
069                }
070                return new LUT(r, g, b);
071        }
072        
073        // ------------------------------------------------------
074
075
076        /**
077         * ImageJ's 'fire' LUT, as originally defined in class {@link ij.plugin.LutLoader}.
078         * @return A new instance of type {@link ij.process.LUT}.
079         */
080        private static LUT fire() {
081                int[] r = { 0, 0, 1, 25, 49, 73, 98, 122, 146, 162, 173, 184, 195, 207, 217, 229, 240, 252,
082                                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
083                int[] g = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 35, 57, 79, 101, 117, 133, 147, 161,
084                                175, 190, 205, 219, 234, 248, 255, 255, 255, 255 };
085                int[] b = { 0, 61, 96, 130, 165, 192, 220, 227, 210, 181, 151, 122, 93, 64, 35, 5, 0, 0, 0, 0,
086                                0, 0, 0, 0, 0, 0, 0, 35, 98, 160, 223, 255 };
087
088                return create(interpolateTo256(r), interpolateTo256(g), interpolateTo256(b));
089        }
090
091
092        /**
093         * ImageJ's 'grays' LUT, as originally defined in class {@link ij.plugin.LutLoader}.
094         * @return A new instance of type {@link ij.process.LUT}.
095         */
096        private static LUT grays() {
097                byte[] r = new byte[256];
098                byte[] g = new byte[256];
099                byte[] b = new byte[256];
100                for (int i = 0; i < r.length; i++) {
101                        r[i] = (byte) i;
102                        g[i] = (byte) i;
103                        b[i] = (byte) i;
104                }
105                return create(r, g, b);
106        }
107
108        /**
109         * ImageJ's 'ice' LUT, as originally defined in class {@link ij.plugin.LutLoader}.
110         * @return A new instance of type {@link ij.process.LUT}.
111         */
112        private static LUT ice() {
113                int[] r = { 0, 0, 0, 0, 0, 0, 19, 29, 50, 48, 79, 112, 134, 158, 186, 201, 217, 229, 242, 250, 250, 250, 250,
114                                251, 250, 250, 250, 250, 251, 251, 243, 230 };
115                int[] g = { 156, 165, 176, 184, 190, 196, 193, 184, 171, 162, 146, 125, 107, 93, 81, 87, 92, 97, 95, 93, 93, 90,
116                                85, 69, 64, 54, 47, 35, 19, 0, 4, 0 };
117                int[] b = { 140, 147, 158, 166, 170, 176, 209, 220, 234, 225, 236, 246, 250, 251, 250, 250, 245, 230, 230, 222,
118                                202, 180, 163, 142, 123, 114, 106, 94, 84, 64, 26, 27 };
119
120                return new LUT(interpolateTo256(r), interpolateTo256(g), interpolateTo256(b));
121        }
122
123        /**
124         * ImageJ's 'spectrum' LUT, as originally defined in class {@link ij.plugin.LutLoader}.
125         * @return A new instance of type {@link ij.process.LUT}.
126         */
127        private static LUT spectrum() {
128                byte[] r = new byte[256];
129                byte[] g = new byte[256];
130                byte[] b = new byte[256];
131                for (int i = 0; i < r.length; i++) {
132                        Color c = Color.getHSBColor(i/255f, 1.0f, 1.0f);
133                        r[i] = (byte) c.getRed();
134                        g[i] = (byte) c.getGreen();
135                        b[i] = (byte) c.getBlue();
136                }
137                return new LUT(r, g, b);
138        }
139
140        /**
141         * ImageJ's 'rgb332' LUT, as originally defined in class {@link ij.plugin.LutLoader}.
142         * @return A new instance of type {@link ij.process.LUT}.
143         */
144        private static LUT rgb332() {
145                byte[] r = new byte[256];
146                byte[] g = new byte[256];
147                byte[] b = new byte[256];
148                for (int i = 0; i < r.length; i++) {
149                        r[i] = (byte) (i & 0xE0);
150                        g[i] = (byte) ((i << 3) & 0xE0);
151                        b[i] = (byte) ((i << 6) & 0xC0);
152                }
153                return new LUT(r, g, b);
154        }
155        
156        /**
157         * ImageJ's 'redgreen' LUT, as originally defined in class {@link ij.plugin.LutLoader}.
158         * @return A new instance of type {@link ij.process.LUT}.
159         */
160        private static LUT redgreen() {
161                byte[] r = new byte[256];
162                byte[] g = new byte[256];
163                byte[] b = new byte[256];
164                for (int i = 0; i < 128; i++) {
165                        r[i] = (byte) (i * 2);
166                        g[i] = (byte) 0;
167                        b[i] = (byte) 0;
168                }
169                for (int i = 128; i < 256; i++) {
170                        r[i] = (byte) 0;
171                        g[i] = (byte) (i * 2);
172                        b[i] = (byte) 0;
173                }
174
175                return new LUT(r, g, b);
176        }
177
178        // --------------------------------------------------------------
179
180        private static byte[] interpolateTo256(int[] samples) {
181                final int nColors = 256;
182                int nSamples = samples.length;
183                byte[] component = new byte[nColors];
184                double scale = (double) nSamples / nColors;
185                for (int i = 0; i < component.length; i++) {
186                        int i1 = (int) Math.floor(i * scale);
187                        int i2 = i1 + 1;
188                        if (i2 >= nSamples)
189                                i2 = nSamples - 1;
190                        double frac = i * scale - i1;   // frac is in [0,1]
191                        int val = (int) Math.round((1.0 - frac) * samples[i1] + frac * samples[i2]);
192                        if (val < 0)
193                                val = 0;
194                        else if (val > 255)
195                                val = 255;
196                        component[i] = (byte) val;
197                        //component[i] = (byte) ((1.0 - frac) * (samples[i1] & 0xFF) + frac * (samples[i2] & 0xFF));
198                }
199                return component;
200        }
201        
202        // ----------------------------------------------------------------------
203
204        /**
205         * Lists the contents of the lookup-table currently associated with the specified image.
206         *
207         * @param ip the image.
208         */
209        public static void listCurrentLut(ImageProcessor ip) {
210                ColorModel cm = ip.getCurrentColorModel();
211                IndexColorModel icm = (IndexColorModel) cm;
212                int mapSize = icm.getMapSize();
213                byte[] reds = new byte[mapSize];
214                byte[] grns = new byte[mapSize];
215                byte[] blus = new byte[mapSize];
216                icm.getReds(reds);
217                icm.getGreens(grns);
218                icm.getBlues(blus);
219                for (int i = 0; i < mapSize; i++) {
220                        IJ.log(String.format("%3d: %3d %3d %3d", i, reds[i] & 0xFF, grns[i] & 0xFF, blus[i] & 0xFF));
221                }
222        }
223
224        /**
225         * Modifies the lookup table to display a bright image with gray values in the range minGray ... 255. Does nothing
226         * if ip is of type {@link ColorProcessor}.
227         *
228         * @param ip the target image.
229         * @param minGray minimum gray value.
230         */
231        public static void brightLut(ImageProcessor ip, int minGray) {
232                if (minGray < 0 || minGray >= 255)
233                        return;
234                ColorModel cm = ip.getColorModel();
235                if (!(cm instanceof IndexColorModel))
236                        return;
237                IndexColorModel icm = (IndexColorModel) cm;
238                int mapSize = icm.getMapSize();
239                byte[] reds = new byte[mapSize];
240                byte[] grns = new byte[mapSize];
241                byte[] blus = new byte[mapSize];
242                float scale = (255 - minGray) / 255f;
243                for (int i = 0; i < mapSize; i++) {
244                        byte g = (byte) (Math.round(minGray + scale * i) & 0xFF);
245                        reds[i] = g;
246                        grns[i] = g;
247                        blus[i] = g;
248                }
249                ip.setColorModel(new IndexColorModel(8, mapSize, reds, grns, blus));
250        }
251
252}