With Python`s PIL, how to set DPI before loading an image?

冷暖自知 提交于 2019-12-05 15:35:21

AFAIK, while it can contain embedded bitmaps and a preview thumbnail, EPS is a vector-based format. It only makes sense to set DPI if you are generating output in a bitmap format.

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

That is because the EPS driver in PythonMagick converts the EPS to a bitmap representation on input (remember IM, the underlying library, is a 'raster image processor') - while in PIL the EPS driver can also write EPS images.

See "A word about Vector Image formats" in ImageMagick:

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.

See also the note about 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.

[Update 1]

This information from Pillow docs is missing from the PIL 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)

[Update 2]

Contrary to my initial guess, PIL also rasterizes the image. When I saved as EPS it just made a wrapper around a bitmap. According to the OP the default resolution seems to be 72 ppi at his environment.

if you know the default resolution is 72 ppi (pixels per inch), calculating the scale for any density you want is a matter of simple proportion - given r as the resolution you want, s is the scale: 1 : s = 72 : r ergo:

im.load(scale=300.0/72.0)

May be it is best if you just specify the desired width instead of the resolution - for example if you want to have it 1677 pixels wide:

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)

So the final answer is: although there is no built-in parameter to specify the desired resolution in ppi while loading an EPS file, you can use the scale parameter to load it at any resolution you want. If you care enough, I guess the Pillow maintainers would be glad to receive a PR for this.

[Edit 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.

Shame on me for not testing.

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)

Not sure if I should use math.round instead of int but you got the idea.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!