【问题标题】:Manipulating pixels in binary bitmap image with DataBufferByte使用 DataBufferByte 操作二进制位图图像中的像素
【发布时间】:2013-09-28 23:22:23
【问题描述】:

我无法访问 BufferedImage 中的单个像素。我的图像是二进制的,只有黑色或白色。这意味着图像的每个字节包含 8 个像素(每像素 1 位)。

为了确保正确索引图像,我编写了一个快速测试将所有像素设置为 1(黑色):

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;

public class ImageTest {
    public static void main(String... args) throws Exception {
        File input = new File("stripes.bmp");
        final BufferedImage image = ImageIO.read(input);
        System.out.println(image);

        byte[] byte_buffer = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

        final int width = image.getWidth(), height = image.getHeight();

        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {
                int byte_index = (i + j * width) / 8;
                int bit_index = (i + j * width) % 8;
                byte_buffer[byte_index] |= 1 << bit_index;
            }
        }

        ImageIO.write(image, "bmp", new File("stripes_out.bmp"));
    }
}

输入图像,stripes.bmp 看起来像:

输出是:

我希望图像显示为全黑,但底部有几行未修改。显然,我没有到达字节缓冲区的末尾。

进一步调查,数组中似乎有一些额外的字节。

width = 810, height = 723
-> width * height / 8 = 73203
byte_buffer.length = 73746

不幸的是,这 543 个额外的字节不仅仅是在开头,因为跳过前 543 个字节会在图像的开头留下几行未修改。

我错过了什么?如何正确索引各个像素?

【问题讨论】:

  • 图片尺寸是多少?你试过通过光栅吗?可能是应用了填充以保持宽度为 4 的倍数。

标签: java image


【解决方案1】:

字节缓冲区数组中的每个图像行都是字节对齐的。这意味着每行的“真实”位宽必须是 8 的倍数。在您的图像中,每行因此四舍五入到 816 位。我假设每行中的最后 6 位被丢弃。

    816 * 723 / 8 = 73746

所以只需将你的宽度四舍五入到最接近的 8 倍数,然后在你的内部循环中使用它:

    final int width = image.getWidth(),
              height = image.getHeight(),
              rowBitWidth = ((width + 7) / 8) * 8;

    for (int j = 0; j < height; j++) {
        for (int i = 0; i < width; i++) {
            int byte_index = (i + j * rowBitWidth) / 8;
            int bit_index = (i + j * rowBitWidth) % 8;
            byte_buffer[byte_index] |= 1 << bit_index;
        }
    }

【讨论】:

  • 谢谢!有记录的地方吗?我似乎在任何地方都找不到它。
  • 不幸的是,我也无法在任何地方找到它的文档。我只是将字节数组转储到 System.out(如 0s 和 1s),寻找模式中的中断,并注意到每 800 位有额外的位。但是,我只是查看了文档并注意到 BufferedImage 类有一个 setRGB 方法来设置单个像素。为什么不使用它并传入0 为黑色和0xFFFFFFFF 为白色?
  • 不错的侦探工作。避免使用 getRGB 和 setRGB 方法的原因是使用 DataBuffer 明显更快(在我的测试中,至少快 10 倍)。
  • 我读了更多。 Raster 文档说“SampleModel 描述了 Raster 的样本如何存储在 DataBuffer 的原始数组元素中。”所以你应该从Raster 得到SampleModel 并用它来解释DataBuffer。然而,实际确定数据存储格式的是SampleModel 子类。看起来所有的子类都有一个getScanlineStride 方法,它指示图像中一行有多少字节数组元素。因此,您需要弄清楚您正在使用什么示例模型并将其转换为该模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 2019-08-07
  • 2014-07-06
  • 2023-03-09
相关资源
最近更新 更多