使用Python的PIL,如何在加载图像之前设置DPI?

我试图使用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默认值),它最终得到403×2475像素,300dpi应该是1677×10311像素.此外,.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))
最佳答案
AFAIK虽然可以包含嵌入式位图和预览缩略图,但EPS是一种基于矢量的格式.如果以位图格式生成输出,则设置DPI才有意义.

You are right – I am trying to generate a bitmap picture from the eps. But opening (parsing?) an .eps-file with a certain resolution determines the actual pixel-size (given a certain document size). PythonMagick does this right but i would like to use PIL if possible. – OP

这是因为PythonMagick中的EPS驱动程序将EPS转换为输入上的位图表示(记住IM,底层库是’光栅图像处理器’) – 而在PIL中,EPS驱动程序也可以写入EPS图像.

请参阅ImageMagick中的“A word about Vector Image formats”:

Why is this important? Because IM is a ‘raster image processor’, and while it can read or write images stored in one of the vector formats it does so by converting the image to and from a internal raster image.
Consequently if you are trying to convert a image from a vector format, to another vector format, IM will essentially rasterize this image at the currently defined resolution or density which will hopefully (but unlikely) be suitable for the output device you intend to use it on.
In other words, any output from IM will never be a true vector format. While it can convert its internal raster format into a vector format file, the result is only a superficial vector image wrapper around an image in raster format. And unless the raster image is defined properly (at the right resolution) for the output device, the result will not be particularly good.
Unfortunately new uses to IM do not know anything about this. They see IM as a converter that can convert say PDF to Postscript, producing images with ‘blocky’ aliasing effects, ‘washed out’ colors, or blurry images that just do not look good at all, on the intended output device.
Which brings use to what I am trying to say…
Avoid using ImageMagick for ‘Vector Image’ to ‘Vector Image’ conversions
EG: converting between formats like: PDF, PS, SVG
In other words, use the right tool for the right job. And for this situation, ImageMagick is not the right tool.

另见关于EPS on PIL的说明:

PIL identifies EPS files containing image data, and can read files that contain embedded raster images (ImageData descriptors). If Ghostscript is available, other EPS files can be read as well. The EPS driver can also write EPS images.

[更新1]

PIL文档中缺少Pillow docs中的信息:

If Ghostscript is available, you can call the load() method with the following parameter to affect how Ghostscript renders the EPS

scale

Affects the scale of the resultant rasterized image. If the EPS suggests that the image be rendered at 100px x 100px, setting this parameter to 2 will make the Ghostscript render a 200px x 200px image instead. The relative position of the bounding box is maintained:

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

所以最后的答案是:尽管在加载EPS文件时没有内置参数来指定ppi中所需的分辨率,但您可以使用scale参数以您想要的任何分辨率加载它.如果你足够关心,我想Pillow维护者会很高兴为此获得PR.

[编辑3]

Paolo, the way is good, but it looks like scale is only accepting plain integers… 4,166666667 (300.0/72.0) is rounded to 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,但你明白了.

转载注明原文:使用Python的PIL,如何在加载图像之前设置DPI? - 代码日志