Domanda

Ho immagini PGM a 16 bit che sto cercando di leggere in Python. Sembra (?) Come PIL non supporta questo formato?

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

Mostra all'incirca l'immagine, ma non è giusto. Ci sono bande scure in tutto e si dice che IMG abbia mode=L. Penso che questo sia legato a una domanda iniziale di cui ho avuto File TIFF a 16 bit. 16 bit è così raro che PIL non lo supporta? Qualche consiglio su come posso leggere file PGM a 16 bit in Python, usando PIL o un'altra libreria standard o codice coltivato in casa?

È stato utile?

Soluzione

Quanto segue dipende solo da numpy Per caricare l'immagine, che può essere PGM RAW/PPM RAW a 8 bit. Mostro anche un paio di modi diversi per visualizzare l'immagine. Quello che usa PIL (import Image) richiede che i dati vengano prima convertiti in 8 bit.

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

Note di utilizzo

  • Alla fine ho capito "come decide di endianness"-sta effettivamente immagazzinando l'immagine in memoria come Big-Endian (piuttosto che nativo). Questo schema potrebbe rallentare qualsiasi elaborazione delle immagini non banali, sebbene altri problemi di prestazioni con Python possano relegare questa preoccupazione a una banalità (vedi sotto).

  • Ho fatto una domanda relativa alla preoccupazione di Endianness qui. Mi sono anche imbattuto in una confusione interessante legata all'endianness con questo perché stavo testando preelaborando l'immagine con pnmdepth 65535 Il che non è buono (di per sé) per testare l'endianness poiché i byte bassi e alti potrebbero finire per essere lo stesso (non me ne sono accorto subito perché print(array) output decimale). Avrei dovuto anche applicare pnmgamma per salvarmi un po 'di confusione.

  • Perché Python è così lento, numpy cerca di essere subdolointelligente su come applica determinate operazioni (vedi trasmissione). La prima regola empirica per l'efficienza con numpy è Lascia che Numpy gestisca l'iterazione per te (o metti in altro modo Non scrivere il tuo for Loop). La cosa divertente nel codice sopra è che segue solo parzialmente questa regola quando si fa "Esempio di elaborazione delle immagini", e quindi le prestazioni di quella linea hanno una dipendenza estrema dai parametri a cui sono stati dati reshape.

  • Il prossimo grande numpy Mistero di endianness: perché lo fa newbyteorder() sembra restituire un array, quando è documentato per restituire a dtype. Questo è rilevante se si desidera convertire in endian nativo dst.pixels=dst.pixels.byteswap(True).newbyteorder().

  • Suggerimenti sul porting a Python 3: Input binaria con un'intestazione di testo ASCII, letta da stdin

Altri suggerimenti

Hai bisogno di una modalità di "L;16"; Tuttavia sembra che PIL abbia una modalità di "L" hardcoded in file.c Quando si carica un PGM. Dovresti Scrivi il tuo decodificatore Se vuoi essere in grado di leggere un PGM a 16 bit.

Tuttavia, il supporto delle immagini a 16 bit sembra ancora traballante:

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

Penso che Pil sia capace lettura Immagini con 16 bit, ma in realtà memorizzare e manipolarle sono ancora sperimentali.

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

Vedi, ha appena interpretato il 0xCAFE valore come 0xFE, che non è esattamente corretto.

Ecco un generico Pnm/Pam lettore basato su Numpy e una funzione non documentata in 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)

Certo scrivere Questo formato di immagine generalmente non richiede l'assistenza di una biblioteca ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top