Question

J'ai des images PGM 16 bits que j'essaie de lire en Python.Il semble (?) Que PIL ne supporte pas ce format?

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

Affiche à peu près l'image, mais ce n'est pas correct.Il y a des bandes sombres partout et img aurait un mode=L.Je pense que cela est lié à une première question que j'avais sur les fichiers TIFF 16 bits .Le 16 bits est-il si rare que PIL ne le supporte tout simplement pas?Un conseil sur la façon dont je peux lire des fichiers PGM 16 bits en Python, en utilisant PIL ou une autre bibliothèque standard, ou du code local?

Était-ce utile?

La solution

Ce qui suit ne dépend que de numpy pour charger l'image, qui peut être 8 bits ou PGM / PPM brut 16 bits. Je montre également quelques façons différentes de visualiser l'image. Celui qui utilise PIL (import Image) nécessite que les données soient d'abord converties en 8 bits.

#!/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()

Notes d'utilisation

  • J'ai finalement compris "comment il décide de l'endianness" - il stocke en fait l'image en mémoire comme big-endian (plutôt que native). Ce schéma peut ralentir tout traitement d'image non trivial - bien que d'autres problèmes de performances avec Python puissent reléguer cette préoccupation à une trivialité (voir ci-dessous).

  • J'ai posé une question relative au problème d'endianité ici . J'ai également rencontré une confusion intéressante liée à l'endianness avec cela parce que je testais en prétraitant l'image avec pnmdepth 65535 qui n'est pas bon (en soi) pour tester l'endianness puisque les octets bas et haut pourraient finir par être les mêmes (je n'ai pas remarquez tout de suite car print(array) génère des décimales). J'aurais dû également appliquer pnmgamma pour éviter toute confusion.

  • Parce que Python est si lent, numpy essaie d'être sournois intelligent sur la façon dont il applique certaines opérations (voir diffusion ). La première règle d'or pour l'efficacité avec numpy est de laisser numpy gérer l'itération pour vous (ou en d'autres termes don n'écrivez pas vos propres boucles for ). La chose amusante dans le code ci-dessus est qu'il ne suit que partiellement cette règle lors du "traitement d'image d'exemple", et par conséquent, les performances de cette ligne ont une dépendance extrême sur les paramètres qui ont été donnés à reshape.

  • Le prochain grand mystère d'endianness numpy: Pourquoi newbyteorder() semble-t-il renvoie un tableau , lorsqu'il est documenté pour renvoyer un dtype. Ceci est pertinent si vous souhaitez convertir en endian natif avec dst.pixels=dst.pixels.byteswap(True).newbyteorder().

  • Conseils sur le portage vers Python 3: entrée binaire avec un en-tête de texte ASCII, lu depuis stdin

Autres conseils

Vous avez besoin d'un mode de "L;16";cependant, il semble que PIL ait un mode de "L" codé en dur dans File.c lors du chargement d'une PGM.Vous devrez écrire votre propre décodeur si vous voulez pouvoirpour lire une sortie PGM 16 bits.

Cependant, la prise en charge des images 16 bits semble toujours floconneuse:

>>> 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

Je pense que PIL est capable de lire des images avec 16 bits, mais en fait, les stocker et les manipuler est encore expérimental.

>>> 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)]

Vous voyez, il a simplement interprété la valeur 0xCAFE comme 0xFE, ce qui n'est pas exactement correct.

Voici un PNM / Lecteur PAM basé sur NumPy et une fonction non documentée dans 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)

Bien sûr, écrire ce format d'image ne nécessite généralement pas l'assistance d'une bibliothèque ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top