【问题标题】:How can i change the BufferedImage type?如何更改 BufferedImage 类型?
【发布时间】:2016-06-21 20:08:14
【问题描述】:

我正在使用ImageIO 导入图像以将图像组件(alpha、red、green、blue)存储在字节数组中。稍后我需要从这个字节数组中读取,但我不知道组件的顺序(我不知道第一个元素是 alpha,第二个是红色......还是不在数组中)被存储并且是阿尔法甚至存储?

是否有默认顺序?或者有没有办法将序列更改为 ABGR 或 ARGB 等所需的序列。

获取数组中组件的当前代码。

    BufferedImage temp;

    try{
        temp = ImageIO.read(new File(path));
        ImageArray = ((DataBufferByte)temp.getRaster().getDataBuffer()).getData();
    }catch(IOException ex){
        ex.printStackTrace();
    }

java 文档根本没有帮助我。

【问题讨论】:

  • 请向我们展示您的代码如何将图像组件存储在字节数组中。
  • @VGR 代码已上传。现在我不知道组件是如何排列数组的。
  • 你的标题与你的问题无关。
  • @EJP 抱歉,我认为您没有理解问题,简而言之,问题是如果我没有默认序列,那么我如何将序列更改为所需的序列。很抱歉造成混乱。

标签: java image graphics bufferedimage


【解决方案1】:

java中的BufferedImage对象有两个相关方法:

int getRGB(int x, int y)

void setRGB(int x, int y, int rgb)

在这两种情况下,无论图片是如何设置的,像素通道始终遵循相同的方案。 int rgb 是一个四字节整数。

最左边的字节是 alpha,然后是红色,然后是蓝色,然后是绿色。也就是说,argb。因为一个字节可以保存一个从 0 到 255 的小整数。每个字节有 256 个不同的个体强度。实际位如下所示:

aaaaaaaarrrrrrrrggggggggbbbbbbbb

要提取您想要访问的颜色或 alpha,请使用位运算符。

例如,如果我只想要红色,我可以使用位移运算符将位向右移动 16 位,然后使用十六进制值来提取该通道:

int rgb = image.getRGB(x, y);
int a = (rgb >> 24) & 0x000000ff;
int r = (rgb >> 16) & 0x000000ff;
int g = (rgb >>  8) & 0x000000ff;
int b =       (rgb) & 0x000000ff;

让我们看看结果如何。假设我们已经有了颜色:

rgb -> the binary 00101011 10101010 11111111 00000000

然后让它向右移动 16 位:

00000000 00000000 00101011 10101010

请注意,现在 alpha 仍然存在……我们不想要它。因此,我们将通过对它们进行运算来清除除红色位之外的所有内容:

    00000000 00000000 00101011 10101010
and 00000000 00000000 00000000 11111111
  = 00000000 00000000 00000000 10101010

这只是颜色 10101010 的红色分量。

您也可以使用相同的思路反过来设置所有三个位通道:

int rgb = (a << 24) | (r << 16) | (g << 8) | b;

...假设每个通道已经在 0-255 之间。

我一直记得如何使用助记符来使用位运算符 “设置或清除和”。这意味着将位设置为 1 使用“或”。要将位清除为 0,请使用“和”。 ">" 将位向左或向右移动由箭头右侧的操作数指定的次数。

修正答案(代码添加到问题后)

尽管出于性能和清晰度的原因不鼓励使用,但可以将栅格作为各种数组直接访问。在这些情况下,字节顺序遵循图像的类型和颜色模型。这是一个这样的例子:

BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR);
// If we swap the type, what we put in our for loop will vary greatly
DataBufferByte dbb = ((DataBufferByte)img.getRaster().getDataBuffer());
byte[] bytes = dbb.getData();
for (int i = 0; i < bytes.length; i++) {
  // modify byte array here with bytes[i];
}
try {
  SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, 100, 100, 3, 100*3, new int[]{2, 1, 0});
  DataBuffer dataBuffer = new DataBufferByte(bytes, bytes.length);
  Raster raster = Raster.createRaster(sampleModel, dataBuffer, null);
  img.setData(raster);
} catch stuff......

如果您要将TYPE_3BYTE_BGR 更改为TYPE_3BYTE_RGB,则在上面的示例中,每三个字节 (3n) 将变为红色而不是蓝色;此外,每三个加两个 (3n+2) 字节将是蓝色而不是红色。这实际上只是交换红色和蓝色字节。

如果 TYPE_3BYTE...BYTE 更改为 INT,则数组将是字节数组而不是整数。

【讨论】:

  • 这意味着Array[0] is alpha, Array[1] is red, Array[2] is green, Array[3] is blue。对吗?
  • 如果我没记错的话,DataBufferByte 中的每个索引都只是一个字节。所以是的,每个像素应该有 4 个。我现在正在启动一个示例项目来确认,我会尽快回复您。
  • 因此,一旦您达到修改实际光栅中字节的级别,这就是您正在做的事情,通道的大小和顺序完全取决于您的组件模型。这可以通过使用缓冲图像中的 $img.getColorModel().getComponentSize()$ 调用来获得。如果您通过其构造函数创建缓冲图像,则格式是通过使用 BufferedImage.TYPE....
  • 但是对于纹理映射,我认为在运行程序之前获取所有纹理的数据比 getRGB 更好,我错了吗?
  • 感谢您的回答。我不太关注优化,因为它只是一个爱好项目,所以快速和肮脏对我来说很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
相关资源
最近更新 更多