20

Is there a way to read in a bmp file in Python that does not involve using PIL? PIL doesn't work with version 3, which is the one I have. I tried to use the Image object from graphics.py, Image(anchorPoint, filename), but that only seems to work with gif files.

1
  • This page claims to have Python 3 compatible versions of PIL.
    – Thomas K
    Commented May 3, 2012 at 20:53

9 Answers 9

15

In Python it can simply be read as:

import os
from scipy import misc
path = 'your_file_path'
image= misc.imread(os.path.join(path,'image.bmp'), flatten= 0)

## flatten=0 if image is required as it is 
## flatten=1 to flatten the color layers into a single gray-scale layer
1
9

I realize that this is an old question, but I found it when solving this problem myself and I figured that this might help someone else in the future.

It's pretty easy actually to read a BMP file as binary data. Depending on how broad support and how many corner-cases you need to support of course.

Below is a simple parser that ONLY works for 1920x1080 24-bit BMP's (like ones saved from MS Paint). It should be easy to extend though. It spits out the pixel values as a python list like (255, 0, 0, 255, 0, 0, ...) for a red image as an example.

If you need more robust support there's information on how to properly read the header in answers to this question: How to read bmp file header in python?. Using that information you should be able to extend the simple parser below with any features you need.

There's also more information on the BMP file format over at wikipedia https://en.wikipedia.org/wiki/BMP_file_format if you need it.

def read_rows(path):
    image_file = open(path, "rb")
    # Blindly skip the BMP header.
    image_file.seek(54)

    # We need to read pixels in as rows to later swap the order
    # since BMP stores pixels starting at the bottom left.
    rows = []
    row = []
    pixel_index = 0

    while True:
        if pixel_index == 1920:
            pixel_index = 0
            rows.insert(0, row)
            if len(row) != 1920 * 3:
                raise Exception("Row length is not 1920*3 but " + str(len(row)) + " / 3.0 = " + str(len(row) / 3.0))
            row = []
        pixel_index += 1

        r_string = image_file.read(1)
        g_string = image_file.read(1)
        b_string = image_file.read(1)

        if len(r_string) == 0:
            # This is expected to happen when we've read everything.
            if len(rows) != 1080:
                print "Warning!!! Read to the end of the file at the correct sub-pixel (red) but we've not read 1080 rows!"
            break

        if len(g_string) == 0:
            print "Warning!!! Got 0 length string for green. Breaking."
            break

        if len(b_string) == 0:
            print "Warning!!! Got 0 length string for blue. Breaking."
            break

        r = ord(r_string)
        g = ord(g_string)
        b = ord(b_string)

        row.append(b)
        row.append(g)
        row.append(r)

    image_file.close()

    return rows

def repack_sub_pixels(rows):
    print "Repacking pixels..."
    sub_pixels = []
    for row in rows:
        for sub_pixel in row:
            sub_pixels.append(sub_pixel)

    diff = len(sub_pixels) - 1920 * 1080 * 3
    print "Packed", len(sub_pixels), "sub-pixels."
    if diff != 0:
        print "Error! Number of sub-pixels packed does not match 1920*1080: (" + str(len(sub_pixels)) + " - 1920 * 1080 * 3 = " + str(diff) +")."

    return sub_pixels

rows = read_rows("my image.bmp")

# This list is raw sub-pixel values. A red image is for example (255, 0, 0, 255, 0, 0, ...).
sub_pixels = repack_sub_pixels(rows)
7

Use pillow for this. After you installed it simply import it

from PIL import Image

Then you can load the BMP file

img = Image.open('path_to_file\file.bmp')

If you need the image to be a numpy array, use np.array

img = np.array(Image.open('path_to_file\file.bmp'))

The numpy array will only be 1D. Use reshape() to bring it into the right shape in case your image is RGB. For example:

np.array(Image.open('path_to_file\file.bmp')).reshape(512,512,3)
5

Use the excellent matplotlib library

import matplotlib.pyplot as plt
im = plt.imread('image.bmp')
2

I had to work on a project where I needed to read a BMP file using python, it was quite interesting, actually the best way is to have a review on the BMP file format (https://en.wikipedia.org/wiki/BMP_file_format) then reading it as binairy file, to extract the data.

You will need to use the struct python library to perform the extraction

You can use this tutorial to see how it proceeds https://youtu.be/0Kwqdkhgbfw

1

It depends what you are trying to achieve and on which platform?

Anyway using a C library to load BMP may work e.g. http://code.google.com/p/libbmp/ or http://freeimage.sourceforge.net/, and C libraries can be easily called from python e.g. using ctypes or wrapping it as a python module.

or you can compile this version of PIL https://github.com/sloonz/pil-py3k

1
  • Ctypes is viable, and I've used it and stuck with it, but it tends to produce relatively brittle solutions - more so than C itself. That's because the type checking could be better with ctypes. For a very stable API, ctypes can be quite nice (especially if you want to target not just cpython but also pypy), but cython might actually be better for many things if you're willing to stick with cpython. Commented May 3, 2012 at 21:47
1

If you're doing this in Windows, this site, should allow you to get PIL (and many other popular packages) up and running with most versions of Python: Unofficial Windows Binaries for Python Extension Packages

1

The common port of PIL to Python 3.x is called "Pillow". Also I would suggest pygame library for simple tasks. It is a library, full of features for creating games - and reading from some common image formats is among them. Works with Python 3.x as well.

0

Some time ago I needed to read BMPs from an old scientific software, which worked in some programs, but Python's PIL refused to open it.

BMP is a fairly simple format easily parsed by numpy; you may wish to adapt the following few lines.

def load_Philips30XL_BMP(fname):
    """ 
    Experimental loading of BMPs from Philips30XL microscopes (they have an atypical format which cannot be loaded by imageio)
    See https://ide.kaitai.io/ for more information on BMP header. 
    """
    with open(fname, mode='rb') as file: # first analyze the header
        fileContent = file.read()
        ofs, w, h, bpp, compr = [int.from_bytes(fileContent[s:e], byteorder='little', signed=False) for s,e in 
                ((0x0a,0x0e),(0x12,0x16),(0x16,0x1a),(0x1c,0x1e),(0x1e,0x22))]
    assert bpp == 8, f'monochrome/LUT image assumed (8 bit per pixel); {fname} has {bpp}bpp'
    assert compr == 0, 'no decompression algorithm implemented'
    return np.fromfile(fname, dtype=np.uint8)[ofs:ofs+w*h].reshape(h,w)[::-1,:] # BMP is "upside down" - flip vertically

Not the answer you're looking for? Browse other questions tagged or ask your own question.