Numpy和16位PGM
在Python中,如何用numpy高效且清晰地读取16位的PGM图像?
我不能使用PIL来加载16位的PGM图像,因为PIL有个bug。我可以用以下代码读取图像的头部信息:
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')
。不过我觉得这可能不是最好的方法。而且,我还不知道如何读取接下来的数据,这些数据是640x480的16位图像,读取时需要跳过size(header)
的部分。
编辑:已添加图像
用MATLAB读取和显示图像的代码是:
I = imread('foo.pgm');
imagesc(I);
图像看起来是这样的:
5 个回答
2
从这里我了解到,文件的头部信息可以用空格、换行符或其他方式分隔。如果你的文件是用空格分开的(如果不是请告诉我),你可以这样做:
with open('img.pgm') as f:
lines = f.readlines()
data = np.array([line.split() for line in lines[1:]], dtype=np.int16).T
这样,你的数据现在就变成了一个int16格式的数组!
假设你还想查看头部信息,你可以这样做:
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])
这样你就可以把读取的行和图像数据进行对比:
assert (h.width, h.height) == data.shape
assert h.maxval >= data.max()
补充: 由于图像数据是二进制格式,文件需要以'rb'模式打开,并在读取头部信息后再读取数据:
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')
4
我对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))
27
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()
当然可以!请把你想要翻译的内容发给我,我会帮你用简单易懂的语言解释清楚。