BMP图片转8位灰度的方法

How to convert BMP image to 8-bit greyscale

我必须编写一个程序来读取 24 位 BMP 图像(没有 ImageIO 或任何外部库)并使其成为 8 位灰度 BMP 图像...我读到我必须更改 header图像使其成为 8 位,Source 1 and Source 2. So I read here BitCount 字节位于 Header 的 29 和 30 并尝试更改它们...

首先我读取我的文件并生成这样的字节向量

FileInputStream image= new FileInputStream(path);
byte[] bytesImage = new byte[image.available()];
image.read(bytesImage);
image.close();

然后我得到图像header并将它复制到一个新的矢量

int width = byteToInt(bytesImage[18], bytesImage[19], bytesImage[20], bytesImage[21]);
int height = byteToInt(bytesImage[22], bytesImage[23], bytesImage[24], bytesImage[25]);
int header = byteToInt(bytesImage[14], bytesImage[15], bytesImage[16], bytesImage[17]) + 14; // Add 14 for the header
vecGrey = Arrays.copyOf(bytesImage, bytesImage.length);

然后我更改 header 信息字节,使其成为 8 位 BMP,如下所示:

byte[] values = intToByte(8);
vecGrey[28] = values[0];    // This is the index for the BitCount byte 1
vecGrey[29] = values[1];    // and this one is the index for the second one.

好的,现在问题来了,由于某种原因,如果我尝试用不同的 header 编写 vecGrey,我无法在 vecGrey 中编写带有 header 的文件,如下所示:

FileOutputStream aGrey = new FileOutputStream(name+ "-gray.bmp");
aGrey.write(vecGrey);
aGrey.close();
// This is a method that displays the resulting image in a frame...
makeInterface(name + "-gray.bmp");

我知道我必须更改 vecGrey 中的值,但这应该可以显示不正确的输出(可能是非灰度图像或根本不是图像)。但是当我尝试读取在 makeInterface() 方法中生成的文件时,我得到一个

javax.imageio.iioexception unable to read the image header

所以我假设程序无法正确读取header,但我不知道为什么!如果我将 BitCount 值更改为 16,它仍然有效,但更改为 1、4 或 8 时,它不起作用并出现相同的错误...我没有上传我的漏洞代码,因为它是西班牙语,但如果需要我可以翻译并在此处编辑。

谢谢!

EDIT1:我只使用 640x480 24 位 BMP 图像,所以我不需要检查填充。

将 BMP 从 24 位更改为 8 位时,您必须更改 header 中的其他几项内容,首先是更改图像的大小(字节 3-6),因为您正在处理一个 8 位图像,每个像素一个字节,因此新的大小应该变成

header尺码{通常是54}+(numberOfColors*4){这是为了颜色table/pallette,我推荐设置为 256}+width*height {实际像素数}

接下来你必须指明像素数据的偏移量在哪里,它紧跟在颜色之后 table/pallete,这个值位于字节 11-14 中,新值应该是:

header尺寸 +numberOfColors*4

接下来需要修改从第15字节开始的BITMAPINFOHEADER,第15-18字节应该包含这一秒的大小header,通常是40,如果你只是想转换成灰度可以忽略并保留一些字节不变,直到到达修改 bitCount 的字节 29 和 30(就像你已经做过的那样),然后在字节 35-38 中据我所知你必须输入我们已经计算出的新图像大小,字节 47 -50 决定了调色板中的颜色数量,因为你正在做灰度,所以我建议使用 256 种颜色,稍后我会解释原因。字节 51-54 包含重要颜色的数量,将其设置为 0 表示每种颜色都很重要。

接下来您需要在页眉旁边添加颜色 table/palette。之所以推荐256色,是因为调色板是这样写的: [B,G,R,0] 其中BGR是RGB格式的蓝绿红颜色值,最后一个常量0,256色您可以制作一个调色板,其中写入 RGB 值是 R=G=B,这应该会产生灰色阴影。因此,在 header 旁边,您必须按升序添加这个新的字节系列:

[0,0,0,0] [1,1,1,0] [2,2,2,0] [3,3,3,0] ... [255,255,255,0]

请注意,256 是计算图像新尺寸所需的颜色数,因为它是调色板中 "entries" 的数量。

接下来您需要在 table/pallette 之后写入新的像素数据。由于给定的是 24 位图像,您可以提取像素矩阵并获得每个像素的 RGB 值,只要记住您有一个字节数组,其值从 -128 到 127,您需要确保得到int 值,因此,如果任何通道的强度 < 0,则向其添加 256 以获得 int 值,然后您可以应用一个等式来给出灰色强度:

Y' = 0.299R' + 0.587G' + 0.114B' 其中Y'是灰色的强度,R G B是红绿蓝的强度。

您可以将等式的结果四舍五入,然后将其作为一个字节写入图像,并对原始图像中的每个像素执行相同的操作。

完成后,只需在文件末尾添加两个保留的 0,您就会得到一张 24 位图像的全新 8 位灰度图像。

希望对您有所帮助。

来源:您提供的来源以及: https://en.wikipedia.org/wiki/BMP_file_format https://en.wikipedia.org/wiki/Grayscale

你应该首先看到24位BMP和灰度BMP的十六进制格式,然后你应该逐步进行, -读取 24 位 bmp header - 偏移后读取数据。 -写入header的8位灰度图像 -将数据写入8位灰度图像。 注意:您必须通过添加 rgb 位并将它们除以 3 将 rgb 位转换为 8 位灰度。