Как написать фильтр изображений PIL для простого формата PGM? - PullRequest
4 голосов
/ 24 ноября 2010

Как я могу написать фильтр для библиотеки изображений Python для формата pgm plain ascii (P2).Проблема здесь в том, что базовый фильтр PIL предполагает постоянное количество байтов на пиксель.

Моя цель - открыть feep.pgm с помощью Image.open ().См. http://netpbm.sourceforge.net/doc/pgm.html или ниже.

Альтернативное решение состоит в том, что я нахожу другой хорошо документированный формат оттенков серого ascii, который поддерживается PIL и всеми основными графическими программами.Есть предложения?

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

edit: Спасибо за ответ, Работает ... , но Мне нуженрешение, которое использует Image.open ().Большинство программ на Python используют PIL для работы с графикой (google: изображение python открыто).Таким образом, мне нужно иметь возможность зарегистрировать фильтр в PIL.Затем я могу использовать любое программное обеспечение, которое использует PIL.Теперь я думаю, что в основном программы, зависящие от scipy, pylab и т. Д.

edit Хорошо, думаю, я понял это сейчас.Ниже обертка 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

Есть небольшое обновление ответа Миши.Image.open должен быть сохранен во избежание бесконечных циклов.Если pgm2pil возвращает None, обертка вызывает 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()

1 Ответ

4 голосов
/ 25 ноября 2010

То, как я сейчас с этим справляюсь, это через numpy :

  1. Считать изображение в массив 2D numpy. Вам не нужно для использования numpy, но я обнаружил, что его проще использовать, чем обычные массивы Python 2D
  2. Преобразование двумерного массива в объект PIL.Image с использованием PIL.Image.fromarray

Если вы настаиваете на использовании PIL.Image.open, вы можете написать оболочку, которая сначала пытается загрузить файл PGM (посмотрев на заголовок). Если это 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 будут работать нормально.

EDIT

Вот как я буду обращаться с оберткой:

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 (если у вас есть доступ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...