0

Is there any general algorithm or a provided class for mapping a grayscale value (0-255) to a value in a colour gradient? I am thinking of something of the sorts:

   public Color mapGrayscaleValue (byte value, Color[] gradientColors);

I am wanting this to provide clearer highlights in what would otherwise be a grayscale cloud coverage radar image I have been provided.

Andre M
  • 6,649
  • 7
  • 52
  • 93

2 Answers2

3

If you are using java.awt.image, you should look in to IndexColorModel. It does (I think) exactly what you're looking for.

You will need to generate the 256 RGB values for the gradient somehow and create a model around them, then you can create a BufferedImage from the grayscale raster using the new model.

Here's a hopefully straightforward example of this:

index color model ex

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
import java.net.*;

class ColorModelEx implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ColorModelEx());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame();
        JLabel label;
        try {
            label = new JLabel(new ImageIcon(createImage(
                Color.yellow, Color.red, Color.blue)));
        } catch (Exception e) {
            throw new AssertionError(e);
        }

        frame.add(label);
        frame.pack();
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    static BufferedImage createImage(Color... colors) throws Exception {
        if ((256 % (colors.length - 1)) != 0)
            throw new IllegalArgumentException();

        BufferedImage original =
            ImageIO.read(new URL("https://i.stack.imgur.com/7bI1Y.jpg"));

        final int w = original.getWidth();
        final int h = original.getHeight();

        BufferedImage gray =
            new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
        Graphics2D g2 = gray.createGraphics();
        g2.drawImage(original, 0, 0, null);

        byte[] r = new byte[256];
        byte[] g = new byte[256];
        byte[] b = new byte[256];

        final int fade = (256 / (colors.length - 1));

        // (generate all 256 RGB values by
        // fading between the colors supplied)
        for (int i = 0; i < 256; ++i) {
            Color c0 = colors[(i / fade)];
            Color c1 = colors[(i / fade) + 1];

            float amt = (i % fade) / ((float) fade);

            r[i] = getChannel(amt, c0.getRed(),   c1.getRed());
            g[i] = getChannel(amt, c0.getGreen(), c1.getGreen());
            b[i] = getChannel(amt, c0.getBlue(),  c1.getBlue());
        }

        // (remap same pixels to new model)
        return new BufferedImage(
            new IndexColorModel(8, 256, r, g, b),
            gray.getRaster(), false, null);
    }

    static byte getChannel(float amt, int ch0, int ch1) {
        return (byte)
            ((ch0 * (1 - amt)) + (ch1 * amt));
    }
}

(Image from here.)

Some other useful places:

Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
1

Having thought about it, I simply created a 255x1 BufferedImage and then used the GradientPaint class to create myself a colour lookup table. With this approach I can even load a manually created colour table. For anyone else:

// Set up the reference image that will be used as the color map

int width = 255;
int height = 1;

BufferedImage colorGradientImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

Graphics2D g2 = (Graphics2D) colorGradientImage.getGraphics();

Paint p = new LinearGradientPaint(
            0, 0, width, 0, new float[] { 0.0f, 0.7f, 0.8f },
            new Color[] { Color.YELLOW, Color.BLUE, Color.RED }
            );

g2.setPaint(p);
g2.fillRect(0, 0, width, height);

g2.dispose();

// Now process the data, using the colour map we created above for the colour values

// pixel data is matrix of values, from 0-255
int[][] pixelData = getSourceValues();

BufferedImage myImage = new BufferedImage(pixelData[0].length, pixelData.length, BufferedImage.TYPE_INT_RGB);
for (int y=0; y<pixelData.length; y++) {
   for (int x=0; x<myImage[y].length; x++) {
       myImage.setRGB(x,y, colorGradientImage.getRGB(pixelData,0));
   }
}

I could probably have converted the colorGradientImage to 255 length array, but for now this does the job.

If anyone knows of an alternative or better approach I am always interested.

Andre M
  • 6,649
  • 7
  • 52
  • 93