如何在BufferedImage中将一种颜色替换为另一种颜色

4

我有一张图像文件,上面有一座火山。其他地方都是0xFFFF00FF(不透明的品红色)。我想要用0(透明)替换掉每个包含该颜色的像素。目前我的方法如下:

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;
}

这个功能可以正常工作,但似乎非常缓慢。我看到有人用另一种方式实现了这个功能,但我不知道具体是怎么回事。如果有人知道更好的方法,请务必告诉我。

3个回答

6

虽然我还没有完全测试过,但使用LookupOp可能会从加速中受益:

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;
    }
}

使用它就像创建一个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);

3

为了避免遍历像素,改变底层ColorModel这里是一个例子。下面是作者获取原始BufferedImage并应用新颜色模型的代码片段。

 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);

你应该将链接中的相关代码添加到答案中。 - Magnilex
3
这个例子只是使用了一个将每种颜色映射到灰度的IndexColorModel。如果我想要使用一个颜色模型来将一种颜色替换为另一种颜色,我该怎么做? - Thayne

1
您可以这样获取缓冲图像的pixels[]数组
int[] pixels = ((DataBufferInt) newImg().getDataBuffer()).getData();

然后像这样设置您的颜色。
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;
        }
    }
}

这是相对于使用setRGB()稍微加速的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接