High resolution with get_screenshot_as_png

Is there a way to adjust the PNG resolution in bokeh.io.get_screenshot_as_png?
It doesn’t look like I can with bokeh directly… but maybe there’s a workaround?

https://docs.bokeh.org/en/latest/docs/user_guide/export.html
https://docs.bokeh.org/en/latest/docs/reference/io.html#bokeh.io.export.get_screenshot_as_png

Thanks!

The function you linked accepts width and height

Yes, but that’s not the same as resolution? SVG export is much higher resolution.

It is? width and height allow you to specify the pixel resolution of the output image exactly to your needs. You can ask for 300x300 pixel output, or 3000x3000 pixel output. I’m not sure what you are asking for, if not that. The SVG output has no intrinsic resolution at all, it is vector format and is scaled independently by whatever is displaying it .

I suppose you could get what you need in the end, but images have a size and a resolution.

It’s not a huge deal, but it’s valuable to those generating figures for presentations or papers. The PNG resolution exported is only 72dpi.

DPI is a function of the pixel resolution (size) and the actual physical dimensions that you render that image of that size into. The DPI field in the image is mostly irrelevant, since nothing at all can stop you from rendering a 300x300 pixel, “300 DPI” image in ten inches of screen space, making that DPI value completely false.

It sounds like what you are are asking for is an API that takes a DPI and a target image size in physical units, and automatically generates an image of an appropriate pixel size based on those given physical dimensions? There is no API in Bokeh that will do that. I don’t think there is any value in that, until or unless there is also a way to specify things like line widths in DPI-aware units, but that would be a non-trivial task.

If you are just asking for a way to override the reported DPI in the image, then that’s probably a simple thing to do and I don’t see why it couldn’t be done as long as the documentation was extremely clear that setting the value is you stipulating a DPI.

Thanks Bryan. My software lets users export figures to png and svg, and I’m just trying to find an intuitive way to match their dimensions with PNG having a reasonably high resolution suitable for presentations or journals, for the same dimensions.

But looking at this again, maybe width and height as you said translates to resolution and the bokeh figure dimensions are image dimensions? I’ll do some testing. My image size, in the screen shot appears to match the bokeh figure dimensions. Maybe that will stay locked if I change default width and height values?

FWIW I still think we may be operating on different definitions of “resolution”. When I hear or say “resolution” I mean exactly and specifically “pixel resolution”, which is the number of pixels in an image, and nothing more. For rectangular images with a width W in pixels and height H in pixels this is often expressed more conveniently as “an image with resolution W*H”.

Sorry about the confusion, yeah, I’m thinking from a graphic designer’s perspective (photoshop, pixelmator). Like I said, not a huge deal. I think the only nuisance is that the default size when you drop the image into Powerpoint/Keynote/etc. will be very large if you want a high resolution (DPI). I’m not sure how these applications take the DPI into consideration when rescaling, so it’s not clear to me if I need to do some post-processing after the Bokeh export yet.

It looks like the export_png function in Bokeh overrides the figure dimensions. The operating system still see a 72DPI regardless.

Sounds like I should look into an image manipulation library.

One last question… do you happen to know where the 72DPI comes from? Is that somewhere in Bokeh? Perhaps it’s somewhere in selenium or phantomjs.

export_png is a thin wrapper around get_screenshot_as_png so they will behave largely identically.

As for the 72 I don’t actually know, my first assumption is that it is simply a default value from PIL’s Image type.

I’m guessing phantomjs. If I find an eloquent solution, I’ll be sure to post. Thanks again Bryan.

I’m only familiar python at the moment, but I’ll peruse this code.

Actually, I think I’ll just use this: https://stackoverflow.com/a/9174789

from PIL import Image

im = Image.open("test.png")
im.save("test-600.png", dpi=(600,600))

@dan.cutright FYI as of Bokeh 2.0 PhantomJS is no longer used (Chrome or FF headless are used direclty)

Does that PIL code above actually change anything other than the reported DPI value?

I’m not sure yet, I’m going to write a wrapper for get bokeh.io.get_screenshot_as_png and post it later today.

If I understand correctly, the height and width you provide to bokeh.io.export_png is not in pixels but rather dimensions with the assumed 72DPI (I’m still not sure where that is assumed yet, but I may just work-around that).

It’s definitely pixels, and the number of pixels simply is what it is. If you ask Bokeh for 300x300 output, you will get an image with 300x300 pixels. In general, the only effect the DPI value in the file has is to tell other software (that cares to look), how much physical space those pixels should take up. But anything and anyone is free to ignore the DPI value. And more importantly, you can set the DPI value in the image file to anything you want. It is stipulated, arbitrarily.

Edit: Maybe this will help:

There are only three quantities:

  • number of pixels
  • density of pixels/inch
  • number of inches

These are related by:

(# pixels) * DPI = (# inches)

Bokeh fixes the (# pixels). Full stop. It’s up to you to decide if you want to fix the output size when you display the image (thereby mathematically determining the DPI—whatever is reported in the file is made irrelevant). Or if you are using software that will respect the reported DPI in the file, and adjust the rendered output size accordingly (taking the size out of your hands). Either way, in general you can only ever control two of those things, and Bokeh also demands that (# pixels) be one of them.

Again, it sounds like you want a different API that let’s you specify DPI and space, and computes the image pixel size automatically. You have to compute that manually for now. What is tricky is that things like line width are specified in pixels which mean that can’t take the DPI into account. If your line width is “1” then it is 1 pixel whether the output image is 300 pixels wide or 30000 pixels wide. That’s not great. You would currently need to take that into account manually and bump line width yourself on large output sizes if you want that. I would say a DPI+space API option would be much more useful and valuable only if things like line width can be made to consider DPI.

Thanks Bryan. I did some poking around, and it looks like the DPI has nothing to do with image quality (at least in Pixelmator, Keynote, and macOS Preview).

I think the onus is on my end. Maybe provide my user’s with an option to apply scaling factor that affects the figure dimensions as well as the glyphs proportionally.