Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create binary PBM/PGM/PPM

Tags:

python

pbmplus

I'm trying to understand how to create binary PBM/PGM/PPM files. As I know there are two types for each format: plain and raw. For example, structure of black PBM 5x5 looks like this:

P1
# This is a comment
5 5
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

So as you see it is simple: white is 0, black is 1. However, PBM has raw version, which looks like this:

'P4\n# This is a comment\n5 5\n\xf8\xf8\xf8\xf8\xf8'

How can I do it? Description of PBM format says:

A raster of Height rows, in order from top to bottom. Each row is Width bits, packed 8 to a byte, with don't care bits to fill out the last byte in the row. Each bit represents a pixel: 1 is black, 0 is white. The order of the pixels is left to right. The order of their storage within each file byte is most significant bit to least significant bit. The order of the file bytes is from the beginning of the file toward the end of the file. A row of an image is horizontal. A column is vertical. The pixels in the image are square and contiguous.

I don't understand what I need to do; I suspect that I may need to use struct or array.array, but I'm not sure. I need your help; could you give an example in Python how to create such file?

>>> size = (5, 5)
>>> array = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
>>> create_pbm(size, array)
'P4\n5 5\n\xf8\xf8\xf8\xf8\xf8'

I need a good speed, because I need to process much larger images (e.g. 2000x5000). But the problem is that I need to use pure Python, without ctypes and libraries. Could you help me, please, and give a small example how to create binary PBM files?

It will be even more amazing, if you can tell me about binary PGM and PPM processing.

Thanks!

like image 969
ghostmansd Avatar asked Sep 11 '12 17:09

ghostmansd


1 Answers

I'm sure there's lots of room for improvement here, (in terms of efficiency) but does this work correctly?

import struct
def create_pbm(size,lst):
    out = ['P4\n'+' '.join(map(str,size))+'\n'] #header
    for j in xrange(0,len(lst),size[1]):
        #single row of data
        row = lst[j:j+size[1]]
        #padded string which can be turned into a number with `int`
        s = ''.join(map(str,row))+'0000000'
        #Turn the string into a number and pack it (into unsigned int) using struct. 
        vals = [struct.pack('B',int(s[i*8:(i+1)*8],2)) for i in xrange(size[0]//8+1) ]
        out.append(''.join(vals))
    return ''.join(out)

a = [1]*25 #flat black image.
print repr(create_pbm((5,5),a))

EDIT

As for reading, this seems to work:

def read_pbm(fname):
    with open(fname) as f:
        data = [x for x in f if not x.startswith('#')] #remove comments
    p_whatever = data.pop(0)  #P4 ... don't know if that's important...
    dimensions = map(int,data.pop(0).split())
    arr = []
    col_number = 0
    for c in data.pop(0):
        integer = struct.unpack('B',c)[0]
        col_number += 8
        bits = map(int,bin(integer)[2:])
        arr.extend(bits[:min(8,dimensions[0]-col_number)])
        if(col_number > dimensions[0]):
            col_number = 0 

    return (dimensions, arr)

Do these file formats need to be square? that seems unlikely. It's quite possible that I got rows/columns mixed up in the dimensions portion. Feel free to check that ;-).

like image 163
mgilson Avatar answered Oct 01 '22 16:10

mgilson