6

我试图使用 PIL 打开一个(Illustrator).eps 文件,进行一些更改并保存它。我想在打开、创建或解释对象之前将文档设置为 300 dpi,并将颜色模式设置为 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))

关于输入:我的 .eps 文件 - 如果它用 72dpi 解释(似乎是 PIL 默认值),它以 403x2475 像素结束,300dpi 它应该是 1677x10311 像素。此外,.eps 文件不包含预览位图,也不包含任何位图数据。只有 2 种颜色(黑色和白色),纯矢量。制作大量颜色分离的 .eps 文件的目录会很有用。

关于输出:将是一个png。

解决方案:

非常感谢 Paulo - 这是他的解决方案,改动非常小:

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))
4

1 回答 1

6

AFAIK,虽然它可以包含嵌入式位图和预览缩略图,但 EPS 是一种基于矢量的格式。只有在生成位图格式的输出时,设置 DPI 才有意义。

你是对的 - 我正在尝试从 eps 生成位图图片。但是打开(解析?)具有特定分辨率的 .eps 文件会确定实际的像素大小(给定特定的文档大小)。PythonMagick 做到了这一点,但如果可能的话,我想使用 PIL。– 欧普

这是因为 PythonMagick 中的 EPS 驱动程序在输入时将 EPS 转换为位图表示(请记住,底层库 IM 是一个“光栅图像处理器”)——而在 PIL 中,EPS 驱动程序也可以编写 EPS 图像。

请参阅ImageMagick 中的“关于矢量图像格式的词”:

为什么这很重要?因为 IM 是一个“光栅图像处理器”,虽然它可以读取或写入以其中一种矢量格式存储的图像,但它通过将图像与内部光栅图像转换来实现这一点。因此,如果您尝试将图像从矢量格式转换为另一种矢量格式,IM 基本上会以当前定义的分辨率或密度对该图像进行光栅化,希望(但不太可能)适合您打算使用它的输出设备上。换句话说,来自 IM 的任何输出都不会是真正的矢量格式。虽然它可以将其内部光栅格式转换为矢量格式文件,但结果只是围绕光栅格式图像的表面矢量图像包装。除非为输出设备正确定义了光栅图像(以正确的分辨率),结果不会特别好。不幸的是,IM 的新用途对此一无所知。他们将 IM 视为一种转换器,可以将 PDF 转换为 Postscript,在预期的输出设备上生成具有“块状”混叠效果、“褪色”颜色或看起来根本不好看的模糊图像。这使我想说的有用... 避免使用 ImageMagick 进行“矢量图像”到“矢量图像”的转换 例如:在 PDF、PS、SVG 等格式之间进行转换 换句话说,为正确的工作使用正确的工具。对于这种情况,ImageMagick 不是正确的工具。

另请参阅PIL 上有关 EPS的说明:

PIL 识别包含图像数据的 EPS 文件,并且可以读取包含嵌入式光栅图像(ImageData 描述符)的文件。如果 Ghostscript 可用,也可以读取其他 EPS 文件。EPS 驱动程序还可以写入 EPS 图像

[更新 1]

PIL 文档中缺少来自Pillow 文档的信息:

如果 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]

与我最初的猜测相反,PIL 还光栅化了图像。当我保存为 EPS 时,它只是围绕位图制作了一个包装器。根据 OP,默认分辨率在他的环境中似乎是 72 ppi。

如果您知道默认分辨率是 72 ppi(每英寸像素),那么计算您想要的任何密度的比例只是一个简单的比例问题 - 给出r您想要的分辨率,s就是比例:1 : s = 72 : rergo:

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)

所以最终的答案是:虽然在加载 EPS 文件时没有内置参数可以在 ppi 中指定所需的分辨率,但您可以使用 scale 参数以您想要的任何分辨率加载它。如果你足够关心,我猜 Pillow 的维护者会很高兴收到一个 PR。

[编辑 3]

Paolo,方法很好,但看起来 scale 只接受普通整数...... 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.roundint但你明白了。

于 2015-02-05T13:50:53.373 回答