Foi útil?

Solução

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

Outras dicas

Não estou muito familiarizado com o formato PGM, mas de modo geral, você apenas usaria numpy.fromfile.fromfile começará na posição em que o ponteiro do arquivo que você passar para ele estiver, então você pode simplesmente procurar (ou ler) até o final do cabeçalho e usar fromfile para ler o resto.

Você precisará usar infile.readline() em vez de 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))

Em uma observação lateral, o arquivo "foo.pgm" que você apontou em seu comentário parece especificar o número errado de linhas no cabeçalho.

Se for ler muitos arquivos que potencialmente têm esse problema, você pode apenas preencher a matriz com zeros ou truncá-la, assim.

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

Na verdade, a 'string' após o cabeçalho é um binário em seu arquivo.Resolvi isso abaixo (encontrei o seguinte: ndarray: [2047 2047 2047 ..., 540 539 539]) mas há outro problema: o arquivo não é longo o suficiente;conta apenas 289872 números em vez de 640 * 480 ...

Lamento muito o meu exagero em fazer uma aula para isso ...

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

editar: ótima ideia de Joe Kington para preencher a imagem com zeros!

de aqui , entendo que as informações do cabeçalho podem ser separadas por espaços ou por carrodevoluções ou outros.Se o seu estiver separado por espaços (me informe se o contrário), você pode fazer:

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

seus dados agora são uma matriz no formato int16!

Suponha que você ainda esteja interessado nas informações do cabeçalho, você pode fazer:

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

para que você possa comparar os dados da imagem com as linhas lidas:

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

Editar : com os dados da imagem sendo binários , o arquivo deve ser aberto como 'rb' e lido após as informações do cabeçalho:

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

Obrigado à resposta de @joe-kington por ajudar a descobrir isso.A solução segue.

Há um pouco de trabalho extra para não codificar o comprimento de cabeçalho conhecido (17 bytes em neste caso), mas para determiná-lo a partir do cabeçalho.O padrão PGM diz que o cabeçalho geralmente termina com uma nova linha, mas pode terminar com qualquer espaço em branco.Acho que esse código vai quebrar em um PGM que usa espaços em branco que não sejam de nova linha para o delimitador de fim de cabeçalho.O tamanho do cabeçalho, neste caso, seria determinado pelo tamanho das variáveis contendo largura, altura e tamanho máximo, mais dois bytes para 'P5', mais 4 bytes de espaço em branco.

Outros casos em que isso pode falhar são se a largura ou a altura forem maiores do que um inteiro (imagem muito grande).Ou se o PGM for de 8 bits em vez de 16 bits (que pode ser determinado a partir de maxval e possível largura, altura e tamanho do arquivo).

#!/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)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top