How to replace color with another color in BufferedImage

10,354

Solution 1

To avoid iterating through the pixels, change the underlying ColorModel. Here is an example. Below is the snippet where the author takes the original BufferedImage and applies the new color model.

 private static BufferedImage createImage() {
    int width = 200;
    int height = 200;
    // Generate the source pixels for our image
    // Lets just keep it to a simple blank image for now

    byte[] pixels = new byte[width * height];
    DataBuffer dataBuffer = new DataBufferByte(pixels, width*height, 0);
    SampleModel sampleModel = new SinglePixelPackedSampleModel(
    DataBuffer.TYPE_BYTE, width, height, new int[] {(byte)0xf});
    WritableRaster raster = Raster.createWritableRaster(
    sampleModel, dataBuffer, null);
    return new BufferedImage(createColorModel(0), raster, false, null);
}

private static ColorModel createColorModel(int n) {
    // Create a simple color model with all values mapping to
    // a single shade of gray
    // nb. this could be improved by reusing the byte arrays

    byte[] r = new byte[16];
    byte[] g = new byte[16];
    byte[] b = new byte[16];
    for (int i = 0; i < r.length; i++) {
        r[i] = (byte) n;
        g[i] = (byte) n;
        b[i] = (byte) n;
    }
    return new IndexColorModel(4, 16, r, g, b);
}

private BufferedImage image = createImage();
image = new BufferedImage(createColorModel(e.getX()), image.getRaster(), false, null);

Solution 2

While I haven't had a chance to test this thoroughly yet, using a LookupOp may well benefit from acceleration:

public class ColorMapper
extends LookupTable {

    private final int[] from;
    private final int[] to;

    public ColorMapper(Color from,
                       Color to) {
        super(0, 4);

        this.from = new int[] {
            from.getRed(),
            from.getGreen(),
            from.getBlue(),
            from.getAlpha(),
        };
        this.to = new int[] {
            to.getRed(),
            to.getGreen(),
            to.getBlue(),
            to.getAlpha(),
        };
    }

    @Override
    public int[] lookupPixel(int[] src,
                             int[] dest) {
        if (dest == null) {
            dest = new int[src.length];
        }

        int[] newColor = (Arrays.equals(src, from) ? to : src);
        System.arraycopy(newColor, 0, dest, 0, newColor.length);

        return dest;
    }
}

Using it is as easy as creating a LookupOp:

Color from = Color.decode("#ff00ff");
Color to = new Color(0, true);
BufferedImageOp lookup = new LookupOp(new ColorMapper(from, to), null);
BufferedImage convertedImage = lookup.filter(image, null);

Solution 3

You can get the pixels[] array of the buffered image like so

int[] pixels = ((DataBufferInt) newImg().getDataBuffer()).getData();

and then set your colors like so

for (int i = 0; i < width; i++) {
    for (int j = 0; j < height; j++) {
        color = pixels[y * width + x];
        if (color == target) {
            pixels[y * width + x] = preferred;
        }
        else {
            pixels[y * width + x] = color;
        }
    }
}

This is a slight speed up over using setRGB()

Share:
10,354
Jerfov2
Author by

Jerfov2

Updated on June 21, 2022

Comments

  • Jerfov2
    Jerfov2 almost 2 years

    So I have an image file that has a volcano on it. Everything else is 0xFFFF00FF (opaque magenta). I want to replace every pixel that contains that color with 0 (transparent). So far my method looks like this:

    public static BufferedImage replace(BufferedImage image, int target, int preferred) {
        int width = image.getWidth();
        int height = image.getHeight();
        BufferedImage newImage = new BufferedImage(width, height, image.getType());
        int color;
    
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                color = image.getRGB(i, j);
                if (color == target) {
                    newImage.setRGB(i, j, preferred);
                }
                else {
                    newImage.setRGB(i, j, color);
                }
            }
        }
    
        return newImage;
    }
    

    This works fine but seems VERY slow. I have seen someone do this another way, but I have no idea what was going on. If someone knows a better way to do this, I would very much like to hear it.