如何为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:

  • 将图像读取到2D numpy数组中。您不需要使用numpy,但是我发现它比常规的Python 2D数组
  • 更容易使用
  • 使用PIL.Image
  • 将2D numpy数组转换为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
    

    您将不得不对其进行略微修改以适合您的目的,即:
  • 处理P2(ASCII,灰度)而不是P1(ASCII,双电平)。
  • 如果您不使用numpy,请使用其他容器。普通的Python 2D数组可以正常工作。

  • 编辑

    这是我处理 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源代码中(如果您可以访问)来确保实现此目的。

    10-02 11:33
    查看更多