working with .bmp files in python 3

amin picture amin · Nov 29, 2013 · Viewed 29.8k times · Source

I have a bmp file. It is just a red square. I have to write a program with functions to make it have white stripes. Things I would need to do:

  • load the bmp file.
  • read and assess the bmp file.
  • code certain areas coordinates of the file to be colored white.
  • close the file
  • display the end product file as output

i am a novice, and am having trouble reading or displaying the original bmp file, let alone edit the content inside. it is not similar to opening a txt file and "readline()". also, when i copy paste the bmp file in the pydev projects src folder in eclipse, it does not show up on eclipse, so i don't know if how the computer would recognize that the file is there. i want to read up on it before posting here, but i don't seem to get much results googling, since i am not sure exactly what i should search for.

Answer

abarnert picture abarnert · Nov 29, 2013

The easy way to do this is with a third-party image-processing library like PIL/Pillow. The code is simple enough that you could figure it out in a few minutes from the examples on the Image module docs…

But if you're not allowed to do that, let's look at how to do this manually.

First, BMP isn't a text file format, it's a binary format. That means you have to read it in binary mode. And you can't read it "line by line", because it doesn't have lines of text to read. Since a bytes object isn't mutable, you will probably want to copy it into a bytearray to work with. So:

with open('spam.bmp', 'rb') as f:
    data = bytearray(f.read())

Next, you need to parse the BMP file format. I assume the main point of the exercise is figuring out how to do that yourself, so I'll give you a link to Wikipedia's article, which describes it better than the Microsoft docs, and you can go from there.

The struct module in the standard library will be very helpful for interpreting the headers; it's much easier to read a 32-bit little-endian number with struct.unpack_from('<L', data, offset) than with by reading data[offset], data[offset+1], etc. and re-combining them into a 32-bit number.

I'm guessing you can ignore all the options for BMP compression—otherwise, this would be way too hard an assignment. In fact, you can probably just assume that all of the headers will specify the most common variant and only code for that. But you might want to ask your teacher for feedback on that.

Now, once you've found the "pixel array" portion of the BMP, and you've figured out how to interpret it from the DIB header, you can just set pixels to white at whichever positions you want by setting the values at the appropriate indexes of the bytearray. For example, it may turn out to be as simple as:

pos = pixel_array_offset + row_size * y + pixel_size * x
data[pos:pos+3] = 255, 255, 255

Finally, once you've changed your red pixels to white, you can save it with:

with open('eggs.bmp', 'wb') as f:
    f.write(data)