Python и 16-битный PGM - PullRequest
       39

Python и 16-битный PGM

5 голосов
/ 09 сентября 2011

У меня есть 16-битные изображения PGM, которые я пытаюсь прочитать на Python.Похоже (?), Что PIL не поддерживает этот формат?

import Image
im = Image.open('test.pgm')
im.show()

Примерно показывает изображение, но это не так.Повсюду темные полосы, и сообщается, что у img mode=L.Я думаю, что это связано с ранним вопросом о 16-битных файлах TIFF .Является ли 16-битным редким, что PIL просто не поддерживает его?Любой совет, как я могу читать 16-битные файлы PGM в Python, используя PIL или другую стандартную библиотеку, или собственный код?

Ответы [ 3 ]

4 голосов
/ 09 сентября 2011

Вам нужен режим "L;16"; однако, похоже, что PIL имеет режим "L", жестко запрограммированный в File.c при загрузке PGM. Вам нужно написать свой собственный декодер , если вы хотите читать 16-битный PGM.

Тем не менее, поддержка 16-битных изображений все еще кажется нестабильной:

>>> im = Image.fromstring('I;16', (16, 16), '\xCA\xFE' * 256, 'raw', 'I;16') 
>>> im.getcolors()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/dist-packages/PIL/Image.py", line 866, in getcolors
    return self.im.getcolors(maxcolors)
ValueError: image has wrong mode

Я думаю, что PIL способен читать изображения с 16 битами, но на самом деле их хранение и манипулирование ими все еще являются экспериментальными.

>>> im = Image.fromstring('L', (16, 16), '\xCA\xFE' * 256, 'raw', 'L;16') 
>>> im
<Image.Image image mode=L size=16x16 at 0x27B4440>
>>> im.getcolors()
[(256, 254)]

Видите, оно просто интерпретировало значение 0xCAFE как 0xFE, что не совсем правильно.

1 голос
/ 19 июля 2015

Следующее зависит только от numpy для загрузки изображения, которое может быть 8-битным или 16-битным необработанным PGM / PPM.Я также показываю несколько разных способов просмотра изображения.Тот, который использует PIL (import Image), требует, чтобы данные сначала были преобразованы в 8-битные.

#!/usr/bin/python2 -u

from __future__ import print_function
import sys, numpy

def read_pnm_from_stream( fd ):
   pnm = type('pnm',(object,),{}) ## create an empty container
   pnm.header = fd.readline()
   pnm.magic = pnm.header.split()[0]
   pnm.maxsample = 1 if ( pnm.magic == 'P4' ) else 0
   while ( len(pnm.header.split()) < 3+(1,0)[pnm.maxsample] ): s = fd.readline() ; pnm.header += s if ( len(s) and s[0] != '#' ) else ''
   pnm.width, pnm.height = [int(item) for item in pnm.header.split()[1:3]]
   pnm.samples = 3 if ( pnm.magic == 'P6' ) else 1
   if ( pnm.maxsample == 0 ): pnm.maxsample = int(pnm.header.split()[3])
   pnm.pixels = numpy.fromfile( fd, count=pnm.width*pnm.height*pnm.samples, dtype='u1' if pnm.maxsample < 256 else '>u2' )
   pnm.pixels = pnm.pixels.reshape(pnm.height,pnm.width) if pnm.samples==1 else pnm.pixels.reshape(pnm.height,pnm.width,pnm.samples)
   return pnm

if __name__ == '__main__':

## read image
 # src = read_pnm_from_stream( open(filename) )
   src = read_pnm_from_stream( sys.stdin )
 # print("src.header="+src.header.strip(), file=sys.stderr )
 # print("src.pixels="+repr(src.pixels), file=sys.stderr )

## write image
   dst=src
   dst.pixels = numpy.array([ dst.maxsample-i for i in src.pixels ],dtype=dst.pixels.dtype) ## example image processing
 # print("dst shape: "+str(dst.pixels.shape), file=sys.stderr )
   sys.stdout.write(("P5" if dst.samples==1 else "P6")+"\n"+str(dst.width)+" "+str(dst.height)+"\n"+str(dst.maxsample)+"\n");
   dst.pixels.tofile( sys.stdout ) ## seems to work, I'm not sure how it decides about endianness

## view using Image
   import Image
   viewable = dst.pixels if dst.pixels.dtype == numpy.dtype('u1') else numpy.array([ x>>8 for x in dst.pixels],dtype='u1')
   Image.fromarray(viewable).show()

## view using scipy
   import scipy.misc
   scipy.misc.toimage(dst.pixels).show()

Замечания по использованию

  • I в конце концовВыяснили, «как он решает проблему порядка байтов» - он на самом деле хранит изображение в памяти как big-endian (а не как нативный).Эта схема может замедлить любую нетривиальную обработку изображений - хотя другие проблемы с производительностью Python могут свести эту проблему к тривиальности (см. Ниже).

  • Я задал вопрос, связанный спроблема порядка байтов здесь .Я также столкнулся с некоторой интересной путаницей, связанной с порядком байтов, с этим, потому что я тестировал, предварительно обрабатывая изображение с pnmdepth 65535, что не очень хорошо (само по себе) для тестирования байтов, так как младшие и старшие байты могут в итоге быть одинаковыми (я несразу не заметить, потому что print(array) выводит десятичную).Я должен был также применить pnmgamma, чтобы избежать путаницы.

  • Поскольку Python такой медленный, numpy пытается быть подлый умным в том, как он применяетсяопределенные операции (см. вещание ).Первое практическое правило для эффективности с numpy - это , пусть numy обрабатывает итерацию для вас (или, иначе говоря, не пишите свои for циклы ).Самое смешное в приведенном выше коде заключается в том, что при выполнении «примера обработки изображения» он только частично следует этому правилу, и поэтому производительность этой строки сильно зависит от параметров, которые были заданы для reshape.

  • Следующая большая загадка numpy endianness: почему newbyteorder(), кажется, возвращает массив , когда задокументировано , чтобы вернуть dtype.Это актуально, если вы хотите конвертировать в native endian с помощью dst.pixels=dst.pixels.byteswap(True).newbyteorder().

  • Советы по переносу на Python 3: двоичный ввод с текстовым заголовком ASCII, читаемый из stdin

1 голос
/ 03 февраля 2013

Вот общий PNM / PAM считыватель на основе NumPy и недокументированная функция в PyPNG .

def read_pnm( filename, endian='>' ):
   fd = open(filename,'rb')
   format, width, height, samples, maxval = png.read_pnm_header( fd )
   pixels = numpy.fromfile( fd, dtype='u1' if maxval < 256 else endian+'u2' )
   return pixels.reshape(height,width,samples)

Конечно написание этот формат изображения обычно не требует помощи библиотеки ...

...