سؤال
ما هي الطريقة الفعالة والواضحة لقراءة صور 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);
ويبدو مثل هذا:
المحلول
نصائح أخرى
لست على دراية كبيرة بتنسيق 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