War es hilfreich?

Lösung

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

Andere Tipps

Ich bin mit dem PGM-Format nicht besonders vertraut, aber im Allgemeinen würden Sie nur numpy.fromfile verwenden.fromfile beginnt an der Position, an der sich der Dateizeiger befindet, an den Sie ihn übergeben. Sie können also einfach bis zum Ende des Headers suchen (oder lesen) und dann den Rest mit fromfile einlesen.

Sie müssen infile.readline() anstelle von next(infile) verwenden.

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 einer Randnotiz wird in der Datei "foo.pgm", auf die Sie in Ihrem Kommentar verwiesen haben, die falsche Anzahl von Zeilen in der Kopfzeile angegeben.

Wenn Sie viele Dateien einlesen möchten, bei denen dieses Problem möglicherweise auftritt, können Sie das Array einfach mit Nullen auffüllen oder wie folgt abschneiden.

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 der Tat ist der 'String' nach dem Header eine Binärdatei in Ihrer Datei.Ich habe das unten gelöst (folgendes gefunden: ndarray: [2047 2047 2047 ..., 540 539 539]), aber es gibt ein anderes Problem: Die Datei ist nicht lang genug;zählt nur 289872 Zahlen anstelle von 640 * 480 ...

Es tut mir schrecklich leid für meine Übertreibung, wenn ich eine Klasse dafür mache ...

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

edit: tolle Idee von Joe Kington, das Bild mit Nullen zu füllen!

von hier Ich verstehe, dass die Header-Informationen durch Leerzeichen oder Wagen getrennt werden könnenkehrt zurück oder andere.Wenn Ihre durch Leerzeichen getrennt ist (informieren Sie mich, falls nicht anders angegeben), können Sie Folgendes tun:

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

Ihre Daten sind jetzt ein Array im int16-Format!

Angenommen, Sie interessieren sich immer noch für die Header-Informationen. Sie können Folgendes tun:

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

damit Sie die Bilddaten mit den gelesenen Zeilen vergleichen können:

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

Bearbeiten : Wenn die Bilddaten binär sind, muss die Datei als 'rb' geöffnet und nach den Header-Informationen gelesen werden:

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

Vielen Dank an die Antwort von @ joe-kington, die Ihnen dabei geholfen hat, dies herauszufinden.Die Lösung folgt.

Es ist ein wenig zusätzliche Arbeit erforderlich, um die bekannte Header-Länge (17 Byte in) nicht fest zu codieren in diesem Fall), aber um es aus dem Header zu bestimmen.Der PGM-Standard besagt, dass der Header normalerweise mit einer neuen Zeile endet, aber mit einem beliebigen Leerzeichen enden kann.Ich denke, dieser Code wird in einem PGM unterbrochen, das Leerzeichen ohne Zeilenumbruch für das Delimeter am Ende des Headers verwendet.Die Headergröße wird in diesem Fall durch die Größe der Variablen bestimmt, die Breite, Höhe und maximale Größe enthalten, plus zwei Bytes für 'P5' plus 4 Bytes Leerzeichen.

Andere Fälle, in denen dies auftreten kann, sind, wenn die Breite oder Höhe größer als ein int ist (sehr großes Bild).Oder wenn das PGM 8-Bit statt 16-Bit ist (was aus maxval und möglicher Breite, Höhe und Dateigröße bestimmt werden kann).

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top