0

I'm working with a product, I can send BMP file to it but it accepts only BMP without headers, so I remove the header. However the product can send me its BMP files, and I'd like to show them on a web page so I need to recreate the header to make it work.

A solution could be to store the header in my file system or database, but I'd like to know if there's a solution to recreate the header please, with pillow for example.

For the moment I just open my BMP (24 bits, no header) file like this:

logo_file = open(django_settings.UPLOAD_DIR+'logos/1568/logo1-no-header.bmp', 'rb').read()

Maybe it's possible to convert the binary data to pixel and use pillow to create a new image, insert the pixels and save to BMP?

BlackJack
  • 4,476
  • 1
  • 20
  • 25
Alex
  • 487
  • 1
  • 6
  • 16
  • 3
    When you say without a header, do you mean without the file header, or the DIB header, too? Because, all you're left with is the raw pixels, which you can read and put in an array. But you won't have any idea of the width/height of the image without having access to that information somehow. And that assumes the number of color planes/bits per pixel is fixed. – Reti43 Jan 05 '16 at 12:13
  • I removed the first 54 bytes according to the spec http://www.fastgraph.com/help/bmp_header_format.html. But I know the size (164*68), so it should be possible to recreate the header. – Alex Jan 05 '16 at 12:24
  • And what about http://pillow.readthedocs.org/en/3.0.x/reference/Image.html#PIL.Image.frombytes – Alex Jan 05 '16 at 12:31
  • I don't know how to create the array from the data from :logo_file = open(django_settings.UPLOAD_DIR+'logos/1568/logo1-no-header.bmp', 'rb').read(). I get a bytes with open. – Alex Jan 05 '16 at 12:35
  • 1
    Exactly what are you trying to achieve? Having the raw data, you can do `image = Image.frombytes('RGB', (164, 68), logo_file)` to create an Image object. After that, you can save it to a file with `image.save()`, or extract the pixel the array with `np.array(image)`. – Reti43 Jan 05 '16 at 12:47
  • Thanks I wanted to save the image, however the colors changed, also the rotation whereas its 24 bits bmp. – Alex Jan 05 '16 at 13:08
  • The mode is probably wrong then. I assumed it'd be RGB because you said it was a 24bpp file. The rotation may be off because the rows are read from bottom to top, but you can rotate an image with `image = image.rotate(180)`. If that doesn't resolve your problem, edit your question to update it, with ideally providing an example of what you get and what you actually want to get. If you can upload a bitmap file with no header, people will also be able to experiment with it and replicate your error. – Reti43 Jan 05 '16 at 13:15

2 Answers2

0

Have you looked at construct? It supports BMP.

I would suggest taking a look at this earlier questions, not exactly the same but has examples that should help. how to create bmp file

Community
  • 1
  • 1
  • I'm not sure how this answers the actual question. He can read the raw data, so he doesn't need a parser, but without some critical information in the header, he won't be able to reconstruct the actual image for display. And he definitely doesn't have an array of pixels that he wants to save as binary data. – Reti43 Jan 05 '16 at 12:25
  • ok lol so the no-header file cannot be redisplayed, even with imagemagick? – Alex Jan 05 '16 at 12:26
0

There are two things needed to be done in order to massage the raw pixel data of a 24 bits per pixel BMP into a full image file with Pillow:

  • Flip the image upside down as BMP saves the pixel rows from bottom up,
  • and reorder the pixel components as BMP stores pixels in blue, green, red order but Pillow only supports the RGB order.

Suppose you have the 33456 bytes raw pixel data in a file called test.dat:

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
from PIL import Image


def main():
    with open('test.dat', 'rb') as data_file:
        raw_data = data_file.read()
    red, green, blue = Image.frombytes('RGB', (164, 68), raw_data).split()
    image = Image.merge('RGB', (blue, green, red))
    image = image.transpose(Image.FLIP_TOP_BOTTOM)
    image.save('result.bmp')


if __name__ == '__main__':
    main()

Here is a small program creating a sample test.dat:

#!/usr/bin/env python3
import base64
import gzip

DATA = b'''\
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'''


def main():
    with open('test.dat', 'wb') as file:
        file.write(gzip.decompress(base64.decodebytes(DATA)))


if __name__ == '__main__':
    main()
BlackJack
  • 4,476
  • 1
  • 20
  • 25
  • Thanks, I'm going to try that. Finally we decided that the product won't send back its bmp images without header. But I wish this question will serve somebody. – Alex Jan 18 '16 at 15:57
  • I've had no luck getting this approach to work unfortunately. – DaveTheMinion May 26 '19 at 00:30
  • @DavidB It works for me. I've added sample data to the answer. What problems do you run into with this approach? – BlackJack May 28 '19 at 10:48
  • @BlackJack I am not sure what is causing problems with this approach for me, but the bitmaps that I generate are all corrupt. I am trying to generate images from any file the the user passes in (not just images), so perhaps that is the problem. I worked around this issue by using the Pillow library, but I would still like to learn how to use this approach to generate images. – DaveTheMinion May 28 '19 at 12:14
  • 1
    @DavidB This question is about reconstructing an image from the binary data of a 24 bits per pixel BMP file without its header but a known size in pixels. The code can't be used as is to convert any file into a BMP image. _Any_ file can get tricky because the pixel data size must be divisible by four and you need to come up with plausible width and height information for the header. Keeping in mind that the width needs to be chosen in a way to prevent padding of pixel rows, so really all data is used for pixels and none as padding. Iteresting challenge but a completely different question. – BlackJack May 28 '19 at 12:47
  • @BlackJack Ah, I misunderstood what was trying to be achieved here. As for handling image generation from anything, the issues that you describe here are things that I tried to take in to account before just using Pillow, but I still could not get it working for some reason. Oh well. That's a discussion for a separate question. – DaveTheMinion May 28 '19 at 13:19