简体   繁体   中英

How to convert BMP image to 8-bit greyscale

I have to make a program that reads a 24 bit BMP image(without ImageIO or any external library) and make it a 8 bit greyscale BMP image... I read that I must change the header of the image to make it a 8 bit, Source 1 and Source 2 . So I read here that the BitCount bytes are at 29 and 30 of the Header and try to change them...

First I read my file and generate the byte vector like this

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

Then I get the image header and copy it to a new vector

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);

Then I change the header info bytes to make it an 8 bit BMP like this:

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.

Okay now comes the problem, for some reason I can't write a file with the header in vecGrey if i try to write vecGrey with a diferent header as show here:

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");

I know that I must change values in the vecGrey, but this should work showing incorrect output(probably a non greyscale image or not an image at all). But when I try to read the file that I generate in the makeInterface() method I get a

javax.imageio.iioexception unable to read the image header

So I asume that the program is unable to read correctly the header, but I don't know why! If I change the BitCount value to 16 it still works, but to 1, 4 or 8 it doesn't work with the same error... I didn't upload my hole code because it's in spanish, but if needed I can translate it and edit here.

Thanks!

EDIT1: I'm only using 640x480 24-bit BMP images, so I don't need to check padding.

When changing a BMP from 24bit to 8 bit you have to change several other things in the header, first of all the size of the image changes (bytes 3-6), since you are dealing with an 8bit image there is one byte per pixel, therefore the new size should become

headerSize {Usually 54} +(numberOfColors*4) {This is for the color table/pallette, I recommend setting this at 256} +width*height {The actual amount of pixels}

Next you must indicate where is the offset for the pixel data, which is right after the color table/pallete, this value is located in the bytes 11-14 and the new value should be:

headerSize +numberOfColors*4

Next you need to modify the BITMAPINFOHEADER which starts on byte 15, bytes 15-18 should contain the size of this second header which is usually 40, if you just want to convert to grayscale you can ignore and leave some bytes unmodified until you reach byte 29 and 30 where you modify the bitCount (like you already did), then in bytes 35-38 as far as I know you have to input the new image size we have already calculated, bytes 47-50 determines the number of colors in your color palette, since you are doing grayscale I'd recommend using 256 colors and I'll explain why in a bit. Bytes 51-54 contains the number of important colors, set it at 0 to indicate every color is important.

Next you need to add the color table/palette right next to the heeader. The reason why I recommend 256 colors is because the color pallette is written like so: [B,G,R,0] where BGR are Blue, Green and Red color values in RGB format and a constant 0 at the end, with 256 colors you can make a palette that writes RGB values were R=G=B which should yield a shade of gray. So, next to the header you must add this new series of bytes in an ascending order:

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

Note that 256 is the numberOfColors which you need to calculate the new size of the image because it's the number of "entries" in the color pallette.

Next you'll want to write your new pixel data after the table/pallette. Since you were given an image in 24 bits, you can extract the pixel matrix and obtain RGB values of each pixel, just remember that you have a byte array which has values from -128 to 127, you need to make sure that you are getting the int value, so if the intensity of any channel is < 0 then add 256 to it to get the int value, then you can apply an equation which gives you an intensity of gray:

Y' = 0.299R' + 0.587G' + 0.114B' Where Y' is the intensity of gray, RGB are the intensities of Red, Green and Blue.

You can round the result of the equation and then write it as a byte to the imange, and do the same with every pixel in the original image.

When you are done, simply add the two reserved 0s at the end of the file and you should have a brand new 8bit grayscale image of a 24bit image.

Hope this helped.

sources: The one you provided and: https://en.wikipedia.org/wiki/BMP_file_format https://en.wikipedia.org/wiki/Grayscale

you should first see the hex format of both 24-bit BMP as well as gray scale BMP, then you should go step wise, -read 24-bit bmp header -read data after offset. -write header of 8-bit gray scale image -write the data into 8-bit gray scale image. note: you have to convert rgb bits into 8-bit gray scale by adding rgb bits and divide them by 3.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM