-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathbmp.py
72 lines (57 loc) · 2.81 KB
/
bmp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"""A module for dealing with BMP bitmpa image files."""
def write_grayscale(filename, pixels):
"""Creates and writes a grayscale BMP file.
Args:
filename: The name of the BMP file to be created.
pixels: A rectangular image stored as a sequence of rows.
Each row must be an iterable series of integers in the range 0-255
Raises:
OSError: If the file couldn't be written.
"""
height = len(pixels)
width = len(pixels[0])
with open(filename, 'wb') as bmp:
#BMP header
bmp.write(b'BM')
size_bookmark = bmp.tell() #the next four bytes hold the filesize as a 32-bit
bmp.write(b'\x00\x00\x00\x00') #little-endian integer. Zero placeholder for now.
bmp.write(b'\x00\x00') #Unused 16-bit integer - should be zero
bmp.write(b'\x00\x00') #Unused 16-bit integer - should be zero
pixel_offset_bookmark = bmp.tell() #The next four bytes hold the integer offset
bmp.write(b'\x00\x00\x00\x00') #to the pixel data. Zero placeholder for now.
# Image Header
bmp.write(b'\x28\x00\x00\x00') # Image header size in bytes - 40 decimal
bmp.write(_int32_to_bytes(width)) # Image width in pixels
bmp.write(_int32_to_bytes(height))# Image height in pixels
bmp.write(b'\x01\x00') # Number of image planes
bmp.write(b'\x08\x00') # Bits per pixel 8 for grayscale
bmp.write(b'\x00\x00\x00\x00') # No compression
bmp.write(b'\x00\x00\x00\x00') # Zero for uncompressed images
bmp.write(b'\x00\x00\x00\x00') # Unused pixels per meter
bmp.write(b'\x00\x00\x00\x00') # Unused pixels per meter
bmp.write(b'\x00\x00\x00\x00') # Use whole color table
bmp.write(b'\x00\x00\x00\x00') # All colors are important
# Color palette - a linear grayscale
for c in range(256):
bmp.write(bytes((c, c, c, 0))) # Blue, Green, Red, Zero
# Pixel data
pixel_data_bookmark = bmp.tell()
for row in reversed(pixels): # BMP files are bottom to top
row_data = bytes(row)
bmp.write(row_data)
padding = b'\x00' * (4 - (len(row) % 4)) #Pad row to multiple of four bytes
bmp.write(padding)
# End of file
eof_bookmark = bmp.tell()
# Fill in file size placeholder
bmp.seek(size_bookmark)
bmp.write(_int32_to_bytes(eof_bookmark))
# Fill in pixel offset placeholder
bmp.seek(pixel_offset_bookmark)
bmp.write(_int32_to_bytes(pixel_data_bookmark))
def _int32_to_bytes(i):
"""Convert an integer to four bytes in little-endian format."""
return bytes((i & 0xff,
i >> 8 & 0xff,
i >> 16 & 0xff,
i >> 24 & 0xff))