Java 如何将BMP图像转换为8位灰度

Java 如何将BMP图像转换为8位灰度,java,image,bmp,grayscale,Java,Image,Bmp,Grayscale,我必须做一个程序,读取24位BMP图像(没有ImageIO或任何外部库),并使其成为8位灰度BMP图像。。。我读到我必须更改图像的标题,使其为8位,然后。所以我读到BitCount字节位于头的29和30处,并尝试更改它们 首先我读取我的文件并生成如下的字节向量 FileInputStream image= new FileInputStream(path); byte[] bytesImage = new byte[image.available()]; image.read(bytesImag

我必须做一个程序,读取24位BMP图像(没有ImageIO或任何外部库),并使其成为8位灰度BMP图像。。。我读到我必须更改图像的标题,使其为8位,然后。所以我读到BitCount字节位于头的29和30处,并尝试更改它们

首先我读取我的文件并生成如下的字节向量

FileInputStream image= new FileInputStream(path);
byte[] bytesImage = new byte[image.available()];
image.read(bytesImage);
image.close();
然后我得到图像头并将其复制到一个新的向量

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);
然后我更改标题信息字节,使其成为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.
好的,现在问题来了,出于某种原因,如果我尝试使用不同的头来编写vecGrey,我就不能用vecGrey来编写头文件,如下所示:

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无法读取图像头

因此,我认为程序无法正确读取标题,但我不知道为什么!如果我将BitCount值更改为16,它仍然可以工作,但是更改为1、4或8时,它不会工作,并且会出现相同的错误。。。我没有上传我的洞代码,因为它是西班牙语的,但如果需要,我可以在这里翻译和编辑

谢谢


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

将BMP从24位更改为8位时,必须更改标题中的其他内容,首先更改图像大小(字节3-6),因为您处理的是8位图像,每个像素有一个字节,因此,新尺寸应为

headerSize{通常为54}+(NumberOfColor*4){这是用于颜色表/托盘,我建议将其设置为256}+宽度*高度{实际像素数}

接下来,您必须指出像素数据的偏移位置,即颜色表/托盘之后,该值位于字节11-14中,新值应为:

headerSize+NumberOfColor*4

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

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

[0,0,0,0][1,1,1,0][2,2,2,0][3,3,0]。。。[255255,0]

请注意,256是计算图像新大小所需的NumberOfColor,因为它是颜色托盘中的“条目”数

接下来,您需要在表格/托盘之后写入新的像素数据。因为你得到了一个24位的图像,你可以提取像素矩阵并获得每个像素的RGB值,只要记住你有一个字节数组,它的值从-128到127,你需要确保你得到的是int值,所以如果任何通道的强度小于0,那么加上256以获得int值,然后,您可以应用一个公式,该公式为您提供灰度:

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

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

完成后,只需在文件末尾添加两个保留的0,就应该有一个全新的8位灰度图像,即24位图像

希望这有帮助

资料来源:您提供的资料和:

将BMP从24位更改为8位时,您必须更改标题中的其他一些内容,首先图像的大小会发生变化(字节3-6),因为您处理的是8位图像,因此每个像素有一个字节,因此新的大小应为

headerSize{通常为54}+(NumberOfColor*4){这是用于颜色表/托盘,我建议将其设置为256}+宽度*高度{实际像素数}

接下来,您必须指出像素数据的偏移位置,即颜色表/托盘之后,该值位于字节11-14中,新值应为:

headerSize+NumberOfColor*4

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

接下来,您需要在heeader旁边添加颜色表/调色板。我之所以推荐256色,是因为颜色托盘是这样写的:[B,G,R,0],其中BGR是蓝色,绿色和蓝色