An easy way to do this is with a third-party image processing library like PIL / Pillow . The code is simple enough so you can understand it just minutes from the examples in Image in documents ...
But if you are not allowed to do this, let's see how to do it manually.
Firstly, BMP is not a text file format, it is a binary format. This means you must read it in binary mode. And you cannot read it “line by line” because it does not have lines to read. Since the bytes object is not modified, you probably want to copy it to a bytearray to work. So:
with open('spam.bmp', 'rb') as f: data = bytearray(f.read())
Then you need to analyze the BMP file format. I assume that the main point of the exercise is to figure out how to do it yourself, so I will give you a link to a Wikipedia article that describes it better than Microsoft docs, and you can go from there.
The struct module in the standard library will be very useful for interpreting headers; it is much easier to read a 32-bit small number with struct.unpack_from('<L', data, offset) than by reading data[offset] , data[offset+1] , etc. and recombining them into a 32-bit number.
I assume that you can ignore all options for BMP compression, otherwise this would be too complicated a destination. In fact, you can simply assume that all the headers will indicate the most common option and only the code for this. But you can ask your teacher about it.
Now that you have found part of the “pixel array” in BMP, and you understand how to interpret it from the DIB header, you can just set the pixels to white depending on what you want by setting the values in the corresponding bytearray indices. For example, this 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, as soon as you change your red pixels to white, you can save them with:
with open('eggs.bmp', 'wb') as f: f.write(data)