LINUX.ORG.RU

Бинарный PBM/PGM/PPM

 netpbm,


0

1

Добрый вечер. Сегодня потребовалось разобраться со структурой файлов PBM/PGM/PPM (собирательно — PNM). Выяснил, что существует два типа PNM: текстовые (plain) и бинарные (raw). С текстовым PBM/PGM разобрался, получить цвета как массив чисел уже могу, но не могу понять структуру бинарного PBM. Не могли бы вы подсказать, где там собака зарыта? Желательно пример на Python. Содержимое текстового (plain) PBM выглядит так:

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

Вот как выглядит содержимое двоичного (raw) PBM:

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

Вот что гласит информация из man:

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.

Если честно, не понимаю, как должен выглядеть код на Python для создания/чтения двоичного PBM. Почему-то преследует подозрение, что надо как-то задействовать array или struct, но не уверен, что это подозрение верно. Не могли бы вы подсказать, каков принцип создания/чтения этих двоичных файлов, желательно на Python?

Спасибо!



Последнее исправление: ghostmansd (всего исправлений: 1)

Если честно, не понимаю, как должен выглядеть код на Python для создания/чтения двоичного PBM.

apt-get install python-pythonmagick

«Батарейки в комплекте.»

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

К сожалению, вынужден обходиться стандартными библиотеками. Уже думал о привязках и к ImageMagick, и к GraphicsMagick, и к FreeImage, но, увы, видит око, да зуб неймет.

ghostmansd
() автор топика

struct

Правильно думаешь.

PolarFox ★★★★★
()
Ответ на: комментарий от KennyMinigun

У меня мало опыта написания программирования на python, но думаю звучать будет как-то так:

lines = [ 0xf8, 0xf8, ... ]; # уже прочитанные бинарные строки
for line in lines:
    bits = [];
    for i in range(1, 5): # вместо 5 подставить сколько надо цифер
        bits.append(line & 1);
        line = line >> 1;
    # тут мы имеем
    # bits = [ 1, 1, 1, 1, 1 ];

KennyMinigun ★★★★★
()
Ответ на: комментарий от i-rinat

magick

Батарейки

Это не батарейки, а Саяно-Шушенская ГЭС.

buddhist ★★★★★
()
Ответ на: комментарий от ghostmansd

Ну ты и лентяй

import array

f = open('tux_logo.pbm', 'rb')
f.readline() # header
f.readline() # comment
dimensions = f.readline() # sizes
width, height = [int(x) for x in dimensions.strip().split()]
for y in range(height):
    bytewidth = (width - 1)//8 + 1
    bline = array.array('B')
    bline.fromfile(f, bytewidth)
    x = 0
    ptr = 0
    oline = []
    while x < width:
        for shift in range(7,-1,-1):
            bit = (bline[ptr] >> shift) & 1
            if bit == 1:
                oline.append('*')
            else:
                oline.append(' ')
            x += 1
            if x >= width: break;
        ptr += 1
    print ''.join(oline)
i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Да я не лентяй, просто сначала не смог разобраться. К тому моменту, когда Вы ответили, уже разобрался и написал такой скрипт:

import math
from array import array

def bitspow(integer):
  '''bitspow(integer) -> int
  
  Return the next highest power of 2 for integer.'''
  if not isinstance(integer, int):
    raise(TypeError('bitspow() needs int, not %s' % \
      type(integer).__name__))
  if integer < 0:
    raise(ValueError('bitspow() accepts only positive integers'))
  integer |= integer >> 1;
  integer |= integer >> 2;
  integer |= integer >> 4;
  integer |= integer >> 8;
  integer |= integer >> 16;
  integer += 1;
  integer = math.log(integer, 2)
  integer = int(integer)
  return(integer)

def bits(integer, check=True):
  '''bits(integer) -> list
  
  Return the bits as list of integers. Bits are packed as byte, the necessary
  preceding zeros are added. The principle of the function:
    if integer < 2**8, it is transformed to 8 bits (one byte);
    if integer < 2**16, it is transformed to 16 bits (two bytes);
    if integer < 2**32, it is transformed to 32 bits (three bytes);
  and so on. This method can be used to read packed data.
  This function may work significantly faster, if check argument is False.
  That means that the function won't check type of the first argument.'''
  if not isinstance(integer, int):
    raise(TypeError('bits() needs int, not %s' % \
      type(integer).__name__))
  if integer < 0:
    raise(ValueError('bits() accepts only positive integers'))
  if integer <= 0xFF:
    size = 8
  elif integer <= 0xFFFF:
    size = 16
  elif integer <= 0xFFFFFF:
    size = 24
  elif integer <= 0xFFFFFFFF:
    size = 32
  else: # either case
    size = bitspow(integer)
  while size % 8:
    size += 1
  bits = bin(integer)[2:]
  bits = '0' *(size -len(bits)) +bits
  return(bits)

data = b'\x00\x01\x02\x03' *9000
data = [ord(byte) for byte in data]
data = b''.join([bits(byte) for byte in data])
data = array('B', data.replace(b'0', b'\x00').replace(b'1', b'\x01'))

Работает раза в полтора-два быстрее, чем Ваш, но все равно пока качеством недоволен. Нет, я, конечно, понимаю, что от Питона ожидать невероятного быстродействия глупо, но вдруг можно еще быстрее?

ghostmansd
() автор топика
Ответ на: комментарий от i-rinat

Библиотеками сторонними я бы с радостью воспользовался, но зачем ради простых действий качать привязки к библиотекам или писать их? Есть struct, есть array, мне кажется, что этого хватит.

ghostmansd
() автор топика
Ответ на: комментарий от ghostmansd

но зачем ради простых действий качать привязки к библиотекам или писать их?

Чтобы сэкономить мозг. Он один, в него всё не влезет.

но вдруг можно еще быстрее?

А надо?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну, в общем-то, да, т.к. я использую PBM для сканов, а там разрешение, например, вполне может быть 3000x5000, а то и больше.

ghostmansd
() автор топика
Ответ на: комментарий от ghostmansd

а там разрешение, например, вполне может быть 3000x5000, а то и больше.

Тогда в чём причина отказа от специализированных и оптимизированных библиотек для обработки изображений?

i-rinat ★★★★★
()
Ответ на: комментарий от ghostmansd

Мне ничего, кроме возможности получить цвет любой точки в PBM, не нужно.

Дело твоё.

Но я считаю, что в действительности тебе нужно просто решить задачу. Так что не выдумывай причин и бери imagemagick.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

ImageMagick - отличный пример мегатормозов на ровном месте. Она только для прототипов годится. А сам формат NETPBM настолько прост, что обычно его поддержку реализуют сами, не заморачиваясь внешними библиотеками.

moroz
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.