1

Python rookie here! So, I have a data file which stores a list of bytes, representing pixel values in an image. I know that the image is 3-by-3 pixels. Here's my code so far:

# Part 1: read the data
data = []
file = open("test.dat", "rb")
for i in range(0, 9)
    byte = file.read(1)
    data[i] = byte
file.close()

# Part2: create the image
image = PIL.Image.frombytes('L', (3, 3), data)
image.save('image.bmp')

I have a couple of questions:

In part 1, is this the best way to read a binary file and store the data in an array?

In part 2, I get the error "TypeError: must be string or read-only buffer, not list.

Any help on either of these?

Thank you!

Bonifacio2
  • 3,405
  • 6
  • 34
  • 54
Karnivaurus
  • 22,823
  • 57
  • 147
  • 247
  • 3
    Is this your actual code? `for i=1:9` doesn't look like Python to me. – Kevin Feb 25 '14 at 16:05
  • Oh sorry - I was thinking of Matlab. It's fixed now! – Karnivaurus Feb 25 '14 at 16:11
  • I found a question and answer at http://stackoverflow.com/questions/14759637/python-pil-bytes-to-image it may help show what you need. Your list build should be data.append(byte), but your part 2 error comment says that you should not build it as a list anyways. – sabbahillel Feb 25 '14 at 16:14

1 Answers1

3

Part 1

If you know that you need exactly nine bytes of data, that looks like a fine way to do it, though it would probably be cleaner/clearer to use a context manager and skip the explicit loop:

with open('test.dat', 'rb') as infile:
    data = list(infile.read(9)) # read nine bytes and convert to a list

Part 2

According to the documentation, the data you must pass to PIL.Image.frombytes is:

data – A byte buffer containing raw data for the given mode.

A list isn't a byte buffer, so you're probably wasting your time converting the input to a list. My guess is that if you pass it the byte string directly, you'll get what you're looking for. This is what I'd try:

with open('test.dat', 'rb') as infile:
    data = infile.read(9) # Don't convert the bytestring to a list
image = PIL.Image.frombytes('L', (3, 3), data) # pass in the bytestring
image.save('image.bmp')

Hopefully that helps; obviously I can't test it over here since I don't know what the content of your file is.

Of course, if you really need the bytes as a list for some other reason (doubtful--you can iterate over a string just as well as a list), you can always either convert them to a list when you need it (datalist = list(data)) or join them into a string when you make the call to PIL:

image = PIL.Image.frombytes('L', (3, 3), ''.join(datalist))

Part 3

This is sort of an aside, but it's likely to be relevant: do you know what version of PIL you're using? If you're using the actual, original Python Imaging Library, you may also be running into some of the many problems with that library--it's super buggy and unsupported since about 2009.

If you are, I highly recommend getting rid of it and grabbing the Pillow fork instead, which is the live, functional version. You don't have to change any code (it still installs a module called PIL), but the Pillow library is superior to the original PIL by leaps and bounds.

Henry Keiter
  • 16,863
  • 7
  • 51
  • 80
  • Thanks, that's a great help! – Karnivaurus Feb 25 '14 at 18:02
  • it might help someone - I had to add "from PIL import Image" for using the Image directly (for example: Image.frombytes(...) ) – ItayB Aug 06 '14 at 08:56
  • Is this approach only intended for bytestrings that include an image header? I am trying to create bitmaps from bytestrings that do not have an image header and I cannot open the resulting files. – DaveTheMinion May 26 '19 at 00:36