È stato utile?

Soluzione

import re
import numpy

def read_pgm(filename, byteorder='>'):
    """Return image data from a raw PGM file as numpy array.

    Format specification: http://netpbm.sourceforge.net/doc/pgm.html

    """
    with open(filename, 'rb') as f:
        buffer = f.read()
    try:
        header, width, height, maxval = re.search(
            b"(^P5\s(?:\s*#.*[\r\n])*"
            b"(\d+)\s(?:\s*#.*[\r\n])*"
            b"(\d+)\s(?:\s*#.*[\r\n])*"
            b"(\d+)\s(?:\s*#.*[\r\n]\s)*)", buffer).groups()
    except AttributeError:
        raise ValueError("Not a raw PGM file: '%s'" % filename)
    return numpy.frombuffer(buffer,
                            dtype='u1' if int(maxval) < 256 else byteorder+'u2',
                            count=int(width)*int(height),
                            offset=len(header)
                            ).reshape((int(height), int(width)))


if __name__ == "__main__":
    from matplotlib import pyplot
    image = read_pgm("foo.pgm", byteorder='<')
    pyplot.imshow(image, pyplot.cm.gray)
    pyplot.show()

Altri suggerimenti

Non ho molta familiarità con il formato PGM, ma in generale useresti solo numpy.fromfile.fromfile inizierà in qualunque posizione si trovi il puntatore del file a cui gli passi, quindi puoi semplicemente cercare (o leggere) alla fine dell'intestazione, quindi utilizzare fromfile per leggere il resto.

Dovrai utilizzare infile.readline() invece di next(infile).

import numpy as np

with open('foo.pgm', 'r') as infile:
    header = infile.readline()
    width, height, maxval = [int(item) for item in header.split()[1:]]
    image = np.fromfile(infile, dtype=np.uint16).reshape((height, width))

In una nota a margine, il file "foo.pgm" a cui hai puntato nel tuo commento sembra specificare il numero sbagliato di righe nell'intestazione.

Se stai leggendo molti file che potenzialmente hanno questo problema, puoi semplicemente riempire l'array con zeri o troncarlo, in questo modo.

import numpy as np

with open('foo.pgm', 'r') as infile:
    header = next(infile)
    width, height, maxval = [int(item) for item in header.split()[1:]]
    image = np.fromfile(infile, dtype=np.uint16)
    if image.size < width * height:
        pad = np.zeros(width * height - image.size, dtype=np.uint16)
        image = np.hstack([image, pad])
    if image.size > width * height:
        image = image[:width * height]
    image = image.reshape((height, width))

In effetti, la "stringa" dopo l'intestazione è un binario nel tuo file.L'ho risolto di seguito (trovato quanto segue: ndarray: [2047 2047 2047 ..., 540 539 539]) ma c'è un altro problema: il file non è abbastanza lungo;conta solo 289872 numeri invece di 640 * 480 ...

Sono terribilmente dispiaciuto per la mia esagerazione facendo un corso per questo ...

import numpy as np
import Image

class PGM(object):
    def __init__(self, filepath):

        with open(filepath) as f:

            # suppose all header info in first line:
            info = f.readline().split()
            self.type = info[0]
            self.width, self.height, self.maxval = [int(v) for v in info[1:]]
            size = self.width * self.height

            lines = f.readlines()
            dt = [np.int8, np.int16][self.maxval > 255]
            try:
                # this will work if lines are integers separated by e.g. spaces
                self.data = np.array([l.split() for l in lines], dtype=dt).T
            except ValueError:
                # data is binary
                data = np.fromstring(lines[0], dtype=dt)
                if data.size < size:
                    # this is the case for the 'db.tt/phaR587 (foo.pgm)'
                    #raise ValueError('data binary string probably uncomplete')
                    data = np.hstack((data, np.zeros(size-data.size)))
                self.data = data[:size].reshape((self.width, self.height))

            assert (self.width, self.height) == self.data.shape
            assert self.maxval >= self.data.max()

        self._img = None

    def get_img(self):
        if self._img is None:
            # only executed once
            size = (self.width, self.height)
            mode = 'L'
            data = self.data
            self.img = Image.frombuffer(mode, size, data)

        return self.img

    Image = property(get_img)

mypgm = PGM('foo.pgm')

mypgm.Image

modifica: ottima idea di Joe Kington per riempire l'immagine di zeri!

da qui Capisco che le informazioni di intestazione possono essere separate da spazi, a caporitorni o altri.Se il tuo è separato da spazi (informami se diversamente) puoi fare:

with open('img.pgm') as f:
    lines = f.readlines()
    data = np.array([line.split() for line in lines[1:]], dtype=np.int16).T

i tuoi dati ora sono un array in formato int16!

Supponi di essere ancora interessato alle informazioni di intestazione, puoi fare:

class Header(object):
    def __init__(self, type, width, height, maxval):
        self.type = type
        self.width = int(width)
        self.height = int(height)
        self.maxval = int(maxval)

h = Header(*lines[0].split()[:4])

in modo da poter confrontare i dati dell'immagine rispetto alle righe lette:

assert (h.width, h.height) == data.shape    
assert h.maxval >= data.max()

Modifica : con i dati dell'immagine binari , il file deve essere aperto come "rb" e letto dopo le informazioni di intestazione:

import numpy as np

def as_array(filepath):
    f = open(filepath, 'r')
    w, h = size = tuple(int(v) for v in next(f).split()[1:3])
    data_size = w * h * 2

    f.seek(0, 2)
    filesize = f.tell()
    f.close()
    i_header_end = filesize - (data_size)

    f = open(filepath, 'rb')
    f.seek(i_header_end)
    buffer = f.read()
    f.close()

    # convert binary data to an array of the right shape
    data = np.frombuffer(buffer, dtype=np.uint16).reshape((w, h))

    return data

a = as_array('foo.pgm')

Grazie alla risposta di @ joe-kington per aver aiutato a capirlo.La soluzione segue.

C'è un po 'di lavoro extra per non codificare in modo rigido la lunghezza nota dell'intestazione (17 byte in formato questo caso), ma per determinarlo dall'intestazione.Lo standard PGM dice che l'intestazione di solito termina con una nuova riga ma può terminare con qualsiasi spazio bianco.Penso che questo codice si interromperà su un PGM che utilizza spazi vuoti non di nuova riga per il delimetro di fine intestazione.La dimensione dell'intestazione in questo caso sarebbe determinata dalla dimensione delle variabili che contengono larghezza, altezza e maxsize, più due byte per "P5", più 4 byte di spazio.

Altri casi in cui questo potrebbe non funzionare sono se la larghezza o l'altezza sono maggiori di un int (immagine molto grande).O se il PGM è a 8 bit anziché a 16 bit (che può essere determinato da maxval e possibile larghezza, altezza e dimensione del file).

#!/usr/bin/python
import numpy as np
import matplotlib.pyplot as plt

file='foo.pgm'
infile = open(file,'r')
header = next(infile)
width, height, maxval = [int(item) for item in header.split()[1:]]
infile.seek(len(header))
image = np.fromfile(infile, dtype=np.uint16).reshape((height, width))
print width, height, maxval
plt.figimage(image)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top