Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace color with another color in BufferedImage

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.

like image 549
Jerfov2 Avatar asked Jan 10 '23 06:01

Jerfov2


2 Answers

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);
like image 189
VGR Avatar answered Jan 21 '23 20:01

VGR


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);
like image 29
Alexander Stocko Avatar answered Jan 21 '23 21:01

Alexander Stocko