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

Roc*_*bot 4 python image-processing python-imaging-library pythonmagick

我试图使用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
Run Code Online (Sandbox Code Playgroud)

PythonMagick的问题:转换颜色模式不起作用,所以我尝试使用PIL,我更喜欢它:

from PIL import Image

img = Image.open('epstest.eps')
Run Code Online (Sandbox Code Playgroud)

我知道可以在保存时设置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))
Run Code Online (Sandbox Code Playgroud)

关于输入:我的.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))
Run Code Online (Sandbox Code Playgroud)

Pau*_*ine 5

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

你是对的 - 我试图从eps生成一个位图图片.但是打开(解析?)具有特定分辨率的.eps文件确定了实际的像素大小(给定一定的文档大小).PythonMagick做得对,但我想尽可能使用PIL. - OP

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

请参阅ImageMagick中的" 关于矢量图像格式的一个词 ":

为什么这很重要?因为IM是一个"光栅图像处理器",虽然它可以读取或写入以其中一种矢量格式存储的图像,但它通过将图像转换为内部光栅图像和从内部光栅图像转换图像来实现.因此,如果您尝试将图像从矢量格式转换为另一种矢量格式,IM将基本上以当前定义的分辨率或密度光栅化此图像,这有望(但不太可能)适合您打算使用它的输出设备上.换句话说,IM的任何输出都不会是真正的矢量格式.虽然它可以将其内部栅格格式转换为矢量格式文件,但结果只是光栅格式图像周围的表面矢量图像包装.除非为输出设备正确定义光栅图像(在正确的分辨率下),否则结果不会特别好.不幸的是,IM的新用途对此一无所知.他们将IM视为可以将PDF转换为Postscript的转换器,在预期的输出设备上生成具有"块状"混叠效果,"褪色"颜色或模糊图像的图像,这些图像看起来并不好看.这对我想说的内容有用... 避免使用ImageMagick将"矢量图像"转换为"矢量图像"转换 EG:在以下格式之间进行转换: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)
Run Code Online (Sandbox Code Playgroud)

[更新2]

与我最初的猜测相反,PIL也会对图像进行光栅化处理.当我保存为EPS时,它只是围绕位图创建了一个包装器.根据OP,他的环境默认分辨率似乎是72 ppi.

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

im.load(scale=300.0/72.0)
Run Code Online (Sandbox Code Playgroud)

可能最好只指定所需的宽度而不是分辨率 - 例如,如果你想要宽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)
Run Code Online (Sandbox Code Playgroud)

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

[编辑3]

Paolo,方式很好,但看起来规模只接受普通整数... 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)
Run Code Online (Sandbox Code Playgroud)

不确定我是否应该使用math.round而不是int你有了主意.