С Python's PIL, как установить DPI перед загрузкой изображения?


Я пытался использовать PIL, чтобы открыть (Illustrator).eps-файл, внесите некоторые изменения и сохраните его. Я хочу установить документ на 300 точек на дюйм, а цветовой режим на cmyk Перед открытием, созданием или интерпретацией объекта.

Сначала я попробовал то же самое с PythonMagick, и это сработало так:

import PythonMagick
# That's NOT what I want
img72 = PythonMagick.Image()
img_file = 'epstest.eps'
img.read(img_file)
img_dens = img72.density()
print 'W: %d, H: %d' % (img.size().width(), img.size().height())
# W: 403, H: 2475 <-- See here
print 'Density Width: %r' % img_dens.width() # 72
print 'Density Height: %r' % img_dens.height() # 72

# THAT is what I want
img300 = PythonMagick.Image()
img_file = 'epstest.eps'
img300.density('300')      # set density / resolution
img300.read(img_file)      # opens with defined density
img_dens = img300.density()
print 'W: %d, H: %d' % (img.size().width(), img.size().height())
# W: 1679, H: 10312 <-- See here!
print 'Density Width: %r' % img_dens.width() # 300
print 'Density Height: %r' % img_dens.height() # 300

Проблема с PythonMagick: преобразование цветовых режимов не работает, поэтому я попробовал то же самое с PIL, который я бы предпочел:

from PIL import Image

img = Image.open('epstest.eps')

Я знаю, что можно установить dpi в то время как экономия.

Вещи, которые не работают:

img = Image() # TypeError: 'module' object is not callable
img = Image.new() # TypeError: new() takes at least 2 arguments (0 given)
# .new() would create a different object anyway..
img = Image.open('epstest.eps', dpi = 300)
img = Image.open('epstest.eps', dpi = (300, 300) )
# After opening an Image
img.load(dpi=(300,300))

Относительно входного сигнала : My .eps-файл-если он интерпретируется с 72dpi (по умолчанию PIL), он заканчивается 403x2475 px, с 300dpi он должен быть 1677x10311 px. Кроме того, В.EPS-файл не содержит предварительный просмотр изображения, ни какой работы с растровыми данными. Только 2 цвета (черный и белый), простые векторы. Было бы полезно составить каталог огромного количества цветоделенных объектов .eps-файлы.

Относительно вывода : будет png.

Решение:

Большое спасибо Пауло - это его решение с очень небольшими изменениями:

from PIL import Image
from PIL import EpsImagePlugin
import math
filename = 'epstest.eps'
def open_eps(filename, dpi=300.0):
    img = Image.open(filename)
    original = [float(d) for d in img.size]
    # scale = width / original[0] # calculated wrong height
    scale = dpi/72.0            # this fixed it
    if dpi is not 0:
        img.load(scale = math.ceil(scale))
    if scale != 1:
        img.thumbnail([round(scale * d) for d in original], Image.ANTIALIAS)
    return img

img = open_eps(filename, dpi=300.0)
img.save('pil_test.png', dpi=(300.0, 300.0))
1 4

1 ответ:

AFAIK, хотя он может содержать встроенные растровые изображения и эскиз предварительного просмотра, EPS-это векторный формат. Имеет смысл установить DPI только в том случае, если вы генерируете выходные данные в растровом формате.

Вы правы - я пытаюсь создать растровое изображение из eps. Но открытие (разбор?) - .eps-файл с определенным разрешением определяет фактический размер пикселя (задается определенный размер документа). PythonMagick делает это правильно, но я хотел бы использовать PIL, если это возможно. – OP

Это происходит потому, что драйвер EPS в PythonMagick преобразует EPS в растровое представление на входе (помните, что IM, базовая библиотека, является "процессором растровых изображений") - в то время как в PIL драйвер EPS также может записывать изображения EPS.

Смотрите "слово о форматах векторных изображений " в ImageMagick:

Почему это так важно? Потому что IM-это "процессор растровых изображений", и хотя он может читать или записывать изображения, хранящиеся в одном из векторных форматов, он это делает. таким образом, путем преобразования изображения во внутреннее растровое изображение и обратно. Следовательно, если вы пытаетесь преобразовать изображение из векторного формата в другой векторный формат, IM по существу растеризует это изображение с текущим разрешением или плотностью, которые, как мы надеемся (но маловероятно), подойдут для устройства вывода, на котором вы собираетесь его использовать. Другими словами, любой вывод из IM никогда не будет истинным векторным форматом. Хотя он может конвертировать свой внутренний растровый формат в файл векторного формата, результатом является только поверхностная векторная оболочка вокруг изображения в растровом формате. И если растровое изображение не определено правильно (с правильным разрешением) для устройства вывода, результат не будет особенно хорошим. К сожалению, новые пользователи IM ничего об этом не знают. Они видят IM как конвертер, который может конвертировать, скажем, PDF в Postscript, производя изображения с "блочными" эффектами сглаживания, "размытыми" цветами или размытыми изображениями, которые просто не выглядят хорошо на запланированном выходе устройство. Что приносит пользу тому, что я пытаюсь сказать... избегайте использования ImageMagick для преобразования "векторного изображения" в "векторное изображение" Например: преобразование между форматами, такими как: PDF, PS, SVG Другими словами, используйте правильный инструмент для правильной работы. И для этой ситуации ImageMagick не является правильным инструментом.

Смотрите также заметку о EPS на PIL :

PIL идентифицирует файлы EPS, содержащие данные изображения, и может считывать файлы, содержащие встроенный растр картинки (она дескрипторов). Если Ghostscript доступен, можно читать и другие файлы EPS. Драйвер EPS также может записывать изображения EPS .

[обновление 1]

Эта информация от подушка документов отсутствует в пильном документы:

Если Ghostscript доступен, можно вызвать метод load () со следующим параметром, чтобы повлиять на то, как Ghostscript отображает EPS

Масштаб

Влияет на масштаб результирующей растеризованное изображение. Если EPS предполагает, что изображение должно быть отрисовано со скоростью 100px x 100px, установка этого параметра в значение 2 приведет к тому, что Ghostscript будет отрисовывать изображение размером 200px x 200px. Относительное положение ограничивающего прямоугольника сохраняется:

im = Image.open(...)
im.size #(100,100)
im.load(scale=2)
im.size #(200,200)

[обновление 2]

Вопреки моему первоначальному предположению, пил также растеризует изображение. Когда я сохранял как EPS, он просто делал обертку вокруг растрового изображения. По мнению ОП, разрешение по умолчанию составляет 72 ppi в его окружении.

Если вы знаете, что разрешение по умолчанию составляет 72 ppi (пикселей на дюйм), вычисление масштаба для любой плотности, которую вы хотите, является вопросом простой пропорции-учитывая r как разрешение, которое вы хотите, s является масштабом: 1 : s = 72 : r ergo:

im.load(scale=300.0/72.0)

Может быть, будет лучше, если вы просто укажете желаемую ширину вместо разрешения - например, если вы хотите иметь его шириной 1677 пикселей:

def open_eps(filename, width=None):
    original_width = float(Image.open(filename).size[0])
    im = Image.open(filename)
    if width is not None:
        im.load(scale=width/original_width)
    return im

im = open_eps('testfile.eps', 1677)

Таким образом, окончательный ответ таков: хотя нет встроенного параметра, чтобы указать желаемое разрешение в ppi при загрузке файла EPS можно использовать параметр scale, чтобы загрузить его в любом разрешении, которое вы хотите. Если вы достаточно заботитесь, я думаю, что владельцы подушек были бы рады получить PR для этого.

[Edit 3]

Паоло, путь хорош, но похоже, что масштаб принимает только простые целые числа... 4,166666667 (300,0/72,0) округляется до 4.

Позор на мне для не тестирования.

def open_eps(filename, width=None):
    original = [float(d) for d in Image.open(filename).size]
    scale = width / original[0]
    im = Image.open(filename)
    if width is not None:
        im.load(scale=math.ceil(scale))
    if scale != 1:
        im.thumbnail([int(scale * d) for d in original], Image.ANTIALIAS)
    return im

im = open_eps('testfile.eps', 1677)

Не уверен, следует ли мне использовать math.round вместо int, но вы поняли идею.