为什么 raster.setPixels() 返回灰度图像

Why raster.setPixels() is returning a grayscale image

我在这里尝试用最快的方法将 3 个矩阵(R、G 和 B)保存到 BufferedImage 中。

我在 StackExchange 上找到了这个方法,但它对我不起作用,因为它是以灰度颜色保存的图像。

如果我做错了什么,或者如果有比 bufferimage.setRGB() 更快的方法,请帮助我。谢谢!

public static BufferedImage array_rasterToBuffer(int[][] imgR,
         int[][]imgG, int[][] imgB) {

    final int width = imgR[0].length;
    final int height = imgR.length;
    int numBandas = 3;
    int[] pixels = new int[width*height*numBandas];
    int cont=0;
    System.out.println("max: "+width*height*3);
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            for (int band = 0; band < numBandas; band++) {
                pixels[(((i*width)+j)*numBandas +band)] =Math.abs((  (imgR[i][j] & 0xff) >> 16 | (imgG[i][j] & 0xff) >> 8 | (imgB[i][j] & 0xff)));
                cont+=1;
            }
        }
    }


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

    WritableRaster rast = (WritableRaster) bufferImg.getData(); 
    rast.setPixels(0, 0, width, height, pixels);
    bufferImg.setData(rast);

    return bufferImg;
}

我觉得你变灰是因为表情

Math.abs((  (imgR[i][j] & 0xff) >> 16 | (imgG[i][j] & 0xff) >> 8 | (imgB[i][j] & 0xff)));

不依赖于band,所以你的rgb值都是一样的。

表达式看起来很狡猾,因为在将 rgb 值打包成单个 int.

时,您通常使用左移运算符 <<

我不确定,因为我不熟悉你正在使用的类,但我猜这样的东西可能有用

for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
        pixels[(((i*width)+j)*numBandas)]     = imgR[i][j] & 0xFF;
        pixels[(((i*width)+j)*numBandas + 1)] = imgG[i][j] & 0xFF;
        pixels[(((i*width)+j)*numBandas + 2)] = imgB[i][j] & 0xFF;
    }
}

如果你想要一个更快的方法,你需要从BufferedImage中得到"live"WritableRaster,并在图像的"native"格式中设置像素,这对于 TYPE_INT_RGB 是 "pixel packed"。这将为您节省多个(至少两个)数组副本和一些数据转换。它还将为您节省 2/3 的用于转换的内存,因为我们每个像素只需要一个数组组件。

下面的方法应该会快很多:

public static BufferedImage array_rasterToBuffer(int[][] imgR, int[][] imgG, int[][] imgB) {
    final int width = imgR[0].length;
    final int height = imgR.length;

    // The bands are "packed" for TYPE_INT_RGB Raster, 
    // so we need only one array component per pixel
    int[] pixels = new int[width * height];

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            // "Pack" RGB values to native TYPE_INT_RGB format
            // (NOTE: Do not use Math.abs on these values, and without alpha there won't be negative values)
            pixels[((y * width) + x)] = ((imgR[y][x] & 0xff) << 16 | (imgG[y][x] & 0xff) << 8 | (imgB[y][x] & 0xff));
        }
    }

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

    // NOTE: getRaster rather than getData for "live" view
    WritableRaster rast = bufferImg.getRaster();

    // NOTE: setDataElements rather than setPixels to avoid conversion
    // This requires pixels to be in "native" packed RGB format (as above)
    rast.setDataElements(0, 0, width, height, pixels);

    // No need for setData as we were already working on the live data
    // thus saving at least two expensive array copies

    return bufferImg;
}

// Test method, displaying red/green/blue stripes
public static void main(String[] args) {
    int[][] fooR = new int[99][99];
    int[][] fooG = new int[99][99];
    int[][] fooB = new int[99][99];

    for (int i = 0; i < 33; i++) {
        Arrays.fill(fooR[i], 0xff);
        Arrays.fill(fooG[i + 33], 0xff);
        Arrays.fill(fooB[i + 66], 0xff);
    }

    BufferedImage image = array_rasterToBuffer(fooR, fooG, fooB);

    showIt(image);
}

// For demonstration only
private static void showIt(final BufferedImage image) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            JFrame frame = new JFrame("JPEGTest");
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            JScrollPane scroll = new JScrollPane(new JLabel(new ImageIcon(image)));
            scroll.setBorder(BorderFactory.createEmptyBorder());
            frame.add(scroll);

            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    });
}

如果您不需要 "managed"(可能的显示硬件加速)图像,可以进一步优化它。诀窍是直接创建图像 "around" 您的 pixels 数组,从而在 setDataElements 中节省一次数组分配和数组复制。缺点是在某些情况下,图像在屏幕上的绘制速度会稍慢一些。不过,这主要是游戏或流畅动画的问题。

用以下代码替换从 BufferedImage bufferImg = new BufferedImage...return 语句的行:

DataBufferInt buffer = new DataBufferInt(pixels, pixels.length);

int[] bandMasks = {0xFF0000, 0xFF00, 0xFF}; // RGB (no alpha)
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, width, bandMasks, null);

ColorModel cm = new DirectColorModel(32,
        0x00ff0000,       // Red
        0x0000ff00,       // Green
        0x000000ff,       // Blue
        0x00000000        // No Alpha
);
BufferedImage bufferImg = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);

PS:请注意,我还更改了 x/y 循环内的移位,从右移位到左移位。可能只是一个小错字。 :-)