Создать двоичный PBM/PGM/PPM
Я пытаюсь понять, как создавать двоичные файлы PBM/PGM/PPM. Как я знаю, есть два типа для каждого формата: обычный и сырой. Например, структура черного PBM 5x5 выглядит так:
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
Итак, как вы видите, все просто: белый - 0, черный - 1. Однако у PBM есть необработанная версия, которая выглядит следующим образом:
'P4\n# This is a comment\n5 5\n\xf8\xf8\xf8\xf8\xf8'
Как мне это сделать? Описание формата PBM гласит:
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.
Я не понимаю, что мне нужно делать; Я подозреваю, что мне может понадобиться struct
или же array.array
, но я не уверен. Мне нужна ваша помощь; Не могли бы вы привести пример в Python, как создать такой файл?
>>> 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'
Мне нужна хорошая скорость, потому что мне нужно обрабатывать намного большие изображения (например, 2000x5000). Но проблема в том, что мне нужно использовать чистый Python, без ctypes
и библиотеки. Не могли бы вы помочь мне, и приведите небольшой пример, как создавать двоичные файлы PBM?
Будет еще удивительнее, если вы расскажете мне о бинарной обработке PGM и PPM.
Спасибо!
1 ответ
Я уверен, что здесь есть много возможностей для улучшения (с точки зрения эффективности), но работает ли это правильно?
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))
РЕДАКТИРОВАТЬ
Что касается чтения, это похоже на работу:
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)
Эти форматы файлов должны быть квадратными? это кажется маловероятным. Вполне возможно, что я перепутал строки / столбцы в части размеров. Не стесняйтесь проверить это;-).