سؤال

ما هي الطريقة الفعالة والواضحة لقراءة صور PGM ذات 16 بت في Python باستخدام numpy؟

لا يمكنني استخدام PIL لتحميل صور PGM ذات 16 بت بسبب خطأ PIL.يمكنني القراءة في الرأس بالرمز التالي:

dt = np.dtype([('type', 'a2'),
               ('space_0', 'a1', ),
               ('x', 'a3', ),
               ('space_1', 'a1', ),
               ('y', 'a3', ),
               ('space_2', 'a1', ),
               ('maxval', 'a5')])
header = np.fromfile( 'img.pgm', dtype=dt )
print header

هذا يطبع البيانات الصحيحة: ('P5', ' ', '640', ' ', '480', ' ', '65535') لكن لدي شعور بأن هذه ليست الطريقة الأفضل على الإطلاق.وبعد ذلك، أواجه مشكلة في كيفية معرفة كيفية قراءة البيانات التالية لـ x بواسطة y (في هذه الحالة 640x480) بمقدار 16 بت مع إزاحة size(header).

يحرر:تمت إضافة الصورة

كود MATLAB لقراءة الصورة وعرضها هو:

I = imread('foo.pgm'); 
imagesc(I);

ويبدو مثل هذا:

enter image description here

هل كانت مفيدة؟

المحلول

Genacodicetagpre

نصائح أخرى

لست على دراية كبيرة بتنسيق PGM، ولكن بصفة عامة ستستخدمه فقط numpy.fromfile. fromfile سيبدأ من أي موضع يوجد فيه مؤشر الملف الذي تمرر إليه، لذلك يمكنك ببساطة البحث (أو القراءة) حتى نهاية الرأس، ثم استخدم fromfile لقراءة الباقي في.

سوف تحتاج إلى استخدام infile.readline() بدلاً من 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))

في ملاحظة جانبية، يبدو أن ملف "foo.pgm" الذي أشرت إليه في تعليقك يحدد عددًا خاطئًا من الصفوف في الرأس.

إذا كنت ستقرأ الكثير من الملفات التي من المحتمل أن تحتوي على هذه المشكلة، فيمكنك فقط ملء المصفوفة بالأصفار أو اقتطاعها، مثل هذا.

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

في الواقع، "السلسلة" التي تأتي بعد الرأس هي ملف ثنائي في ملفك.لقد قمت بحل ذلك أدناه (وجدت ما يلي: ndarray: [2047 2047 2047 ..., 540 539 539]) ولكن هناك مشكلة أخرى:الملف ليس طويلا بما فيه الكفاية؛يحسب 289872 رقمًا فقط بدلاً من 640*480...

أنا آسف جدًا على المبالغة التي قمت بها من خلال إنشاء فصل دراسي لذلك ...

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

يحرر:فكرة رائعة من جو كينغتون لملء الصورة بالأصفار!

من هنا أعي أنه يمكن فصل معلومات الرأس عن طريق مسافتين ، أو حرفعوائد أو غيرها.إذا كانت مسافاتك مفصولة بمسافات (أبلغني إذا كان الأمر بخلاف ذلك) ، فيمكنك القيام بما يلي: Genacodicetagpre

أصبحت بياناتك الآن مصفوفة بتنسيق int16!

لنفترض أنك لا تزال مهتمًا بمعلومات الرأس ، يمكنك القيام بما يلي: Genacodicetagpre

بحيث يمكنك التحقق من بيانات الصورة مقابل خطوط القراءة: Genacodicetagpre

تحرير : نظرًا لأن بيانات الصورة ثنائية ، يجب فتح الملف كـ "rb" وقراءته من بعد معلومات الرأس: Genacodicetagpre

بفضل إجابة @ joe-kington للمساعدة في اكتشاف ذلك.الحل يتبع.

هناك القليل من العمل الإضافي لعدم ترميز طول الرأس المعروف (17 بايت في هذه الحالة) ، ولكن لتحديدها من العنوان.ينص معيار PGM على أن العنوان ينتهي عادةً بسطر جديد ولكن يمكن أن ينتهي بأي مسافة بيضاء.أعتقد أن هذا الكود سينكسر على PGM الذي يستخدم مسافة بيضاء غير سطر جديد لمحدد نهاية الرأس.سيتم تحديد حجم الرأس في هذه الحالة من خلال حجم المتغيرات التي تحمل العرض والارتفاع والحجم الأقصى ، بالإضافة إلى وحدتي بايت لـ "P5" ، بالإضافة إلى 4 بايت من المسافات البيضاء.

الحالات الأخرى التي قد ينكسر فيها هذا الأمر هي إذا كان العرض أو الارتفاع أكبر من int (صورة كبيرة جدًا).أو إذا كان PGM 8 بت بدلاً من 16 بت (والذي يمكن تحديده من maxval ، والعرض المحتمل ، والارتفاع ، والملف). Genacodicetagpre

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top