如何为pgm纯ascii格式(P2)的python镜像库编写过滤器。这里的问题是基本的PIL滤波器假定每个像素的字节数恒定。
我的目标是使用Image.open()打开Feep.pgm。请参阅http://netpbm.sourceforge.net/doc/pgm.html或以下。
替代解决方案是,我找到PIL和所有主要图形程序支持的其他有据可查的ascii灰度格式。有什么建议么?
feep.pgm:
P2
# feep.pgm
24 7
15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
编辑:感谢您的回答,它可以工作... 但我需要一个使用Image.open()的解决方案。那里的大多数python程序都使用PIL进行图形处理(谷歌:python图像打开)。因此,我需要能够将过滤器注册到PIL。然后,我可以使用任何使用PIL的软件。我现在认为主要是scipy,pylab等相关程序。
编辑好吧,我想我现在明白了。以下是包装pgm2pil.py:
import Image
import numpy
def pgm2pil(filename):
try:
inFile = open(filename)
header = None
size = None
maxGray = None
data = []
for line in inFile:
stripped = line.strip()
if stripped[0] == '#':
continue
elif header == None:
if stripped != 'P2': return None
header = stripped
elif size == None:
size = map(int, stripped.split())
elif maxGray == None:
maxGray = int(stripped)
else:
for item in stripped.split():
data.append(int(item.strip()))
data = numpy.reshape(data, (size[1],size[0]))/float(maxGray)*255
return numpy.flipud(data)
except:
pass
return None
def imageOpenWrapper(fname):
pgm = pgm2pil(fname)
if pgm is not None:
return Image.fromarray(pgm)
return origImageOpen(fname)
origImageOpen = Image.open
Image.open = imageOpenWrapper
misha的答案略有升级。必须保存Image.open以防止永无止境的循环。如果pgm2pil返回None,则包装器将调用pgm2pil,而pgm2pil将返回None,从而调用pgm2pil ...
下面是测试函数(feep_false.pgm是格式错误的pgm,例如“P2”->“FOO”,而lena.pgm只是和图像文件):
import pgm2pil
import pylab
try:
pylab.imread('feep_false.pgm')
except IOError:
pass
else:
raise ValueError("feep_false should fail")
pylab.subplot(2,1,1)
a = pylab.imread('feep.pgm')
pylab.imshow(a)
pylab.subplot(2,1,2)
b = pylab.imread('lena.png')
pylab.imshow(b)
pylab.show()
最佳答案
我目前处理此问题的方法是通过numpy:
numpy
数组中。您不需要使用numpy
,但是我发现它比常规的Python 2D数组PIL.Image
PIL.Image.fromarray
对象如果您坚持使用
PIL.Image.open
,则可以编写一个包装程序,尝试首先加载PGM文件(通过查看 header )。如果是PGM,请使用上述步骤加载图像,否则只需将责任移交给PIL.Image.open
即可。这是一些我用来将 PBM 图像转换为numpy数组的代码。
import re
import numpy
def pbm2numpy(filename):
"""
Read a PBM into a numpy array. Only supports ASCII PBM for now.
"""
fin = None
debug = True
try:
fin = open(filename, 'r')
while True:
header = fin.readline().strip()
if header.startswith('#'):
continue
elif header == 'P1':
break
elif header == 'P4':
assert False, 'Raw PBM reading not implemented yet'
else:
#
# Unexpected header.
#
if debug:
print 'Bad mode:', header
return None
rows, cols = 0, 0
while True:
header = fin.readline().strip()
if header.startswith('#'):
continue
match = re.match('^(\d+) (\d+)$', header)
if match == None:
if debug:
print 'Bad size:', repr(header)
return None
cols, rows = match.groups()
break
rows = int(rows)
cols = int(cols)
assert (rows, cols) != (0, 0)
if debug:
print 'Rows: %d, cols: %d' % (rows, cols)
#
# Initialise a 2D numpy array
#
result = numpy.zeros((rows, cols), numpy.int8)
pxs = []
#
# Read to EOF.
#
while True:
line = fin.readline().strip()
if line == '':
break
for c in line:
if c == ' ':
continue
pxs.append(int(c))
if len(pxs) != rows*cols:
if debug:
print 'Insufficient image data:', len(pxs)
return None
for r in range(rows):
for c in range(cols):
#
# Index into the numpy array and set the pixel value.
#
result[r, c] = pxs[r*cols + c]
return result
finally:
if fin != None:
fin.close()
fin = None
return None
您将不得不对其进行略微修改以适合您的目的,即:
编辑
这是我处理 wrapper 的方法:
def pgm2pil(fname):
#
# This method returns a PIL.Image. Use pbm2numpy function above as a
# guide. If it can't load the image, it returns None.
#
pass
def wrapper(fname):
pgm = pgm2pil(fname)
if pgm is not None:
return pgm
return PIL.Image.open(fname)
#
# This is the line that "adds" the wrapper
#
PIL.Image.open = wrapper
我没有写
pgm2pil
,因为它与pgm2numpy
非常相似。唯一的区别是它将结果存储在PIL.Image
中,而不是numpy
数组中。我也没有测试包装器代码(对不起,目前时间太短了),但这是一种相当普遍的方法,因此我希望它能正常工作。现在,听起来好像您希望使用PIL进行图像加载的其他应用程序能够处理PGM。使用上面的方法是可能的,但是您需要确保在第一次调用
PIL.Image.open
之前,在之前添加了上面的包装器代码。您可以通过将包装器源代码添加到PIL源代码中(如果您可以访问)来确保实现此目的。