Support inline PNGs of matplotlib plots #381

Closed
wants to merge 2 commits into
from

Projects

None yet

3 participants

@mdboom

Add '--pylab inline_png' option to render PNGs inline using the matplotlib's Agg renderer.

Some (including myself) find the lack of pixel snapping in SVG to be rather "fuzzy". This allows the user to display matplotlib's Agg-rendered PNGs rather than Qt-rendered SVGs.

Comments on the interface to this are welcome -- I just added an "inline_png" option to the --pylab commandline argument. This could be done more dynamically if desired -- it's possible to switch between the SVG and Agg backends in matplotlib at runtime.

@ellisonbg
IPython member

Any backend can generate a png in memory right? If so, then I think we don't need a dedicated backend for this. We can use the existing backend and add a new data format. Here is the rough idea:

In the show method in the inline backend:

https://github.com/ipython/ipython/blob/master/IPython/zmq/pylab/backend_inline.py

We can rename send_svg_figure to something like send_figure_data and create both png and svg data. This will probably involve writing the png analogy to figure_to_svg here:

https://github.com/ipython/ipython/blob/master/IPython/lib/pylabtools.py#L78

And then calling that. The important point is that the dict here:

https://github.com/ipython/ipython/blob/master/IPython/zmq/pylab/backend_inline.py#L76

Can accomodate multiple data types. The benefit of sending both is that frontends can then decide which representation to use. In principle, a frontend could view any or all reprsentations that are sent back. We should also add a formatter for png here as well:

https://github.com/ipython/ipython/blob/master/IPython/lib/pylabtools.py#L249

We will also have to modify the frontend to enable selection of the image type. This part of the code is pretty new, so you may run into issues.

@fperez
IPython member

Should make it configurable and not compute both all the time by default? The generation of both forms could get expensive... Michael, do you have a sense for which backend takes more time to produce output, the svg or the png one?

@ellisonbg
IPython member
@mdboom

The performance of the Agg backend really starts to win as the data gets large. It is able to "simplify" the path by removing points that would otherwise be invisible, which really speeds up the stroking operation. Markers are also much faster because it can render the marker itself only once and then just copy it. Lesser-used features like quadmeshes and gouraud shading are also much faster with the Agg backend because SVG doesn't really have the high-level primitives for those and they have to be rendered as many tiny polygons. But my main motivation for using the Agg PNGs was pixel-snapping. Since we don't know the scale of SVG a priori, matplotlib can't do pixel snapping for SVG. The SVG standard provides a very blunt tool for this in the "shape-rendering" property:

http://www.w3.org/TR/SVG/painting.html#ShapeRenderingProperty

Unfortunately, it's up to the rendering agent how specifically to handle this. Using "crispEdges" in Firefox and Chrome just turns off antialiasing, and doesn't address pixel-snapping, so rather than merely fuzzy lines, you get crisp lines but one side of the rectangle may be 2 pixels wide, while the other sides are 1. The Qt renderer IPython is using seems to ignore the shape-rendering attribute entirely (which isn't surprising, given that shape-rendering is not a part of SVG Tiny).

I would also suggest not generating both every time if we can avoid it.

Ok -- so this patch can at least show the differences, but it sounds like it's not the right approach. Let me summarize and see if I'm following you:

  1. We want a single backend for "inline" mode.

  2. There should be some way to set the format of the image on-the-fly (any suggestions as to what is consistent the rest of IPython? I'm not as experienced with IPython as I probably should be).

  3. There should also be a way to persistently set a default.

Also "would be cool" (but I digress) to bring up an interactive plot window, allow the user to pan/zoom the axes, move the legend etc., then close the window and have the plot placed inline. Would be "best of both worlds" -- the interactivity of the regular matplotlib plot window, and the nice history/serialization of the new inline image stuff.

@ellisonbg
IPython member

Thanks, this is quite helpful. It sounds like having png as the default then probably makes more sense.

  1. Yes, I think we want a single backend for inline mode.
  2. We probably want to have a configuration/runtime attribute that controls these things. Let's get the single backend/png code working first and then we can figure out where to put this attribute.
  3. Yep.

We already have this. In a regular GUI backend you can do:

display(*getfigs(1))

And you will get the plot inline for figure 1. No arguments gives you all figs and display_svg and display_png also work.

@fperez
IPython member
@ellisonbg
IPython member
@mdboom

You can do:

display(figure(1))

though your argument still stands that to display all of them, you have to remember the asterisk:

display(*getfigs())

@fperez
IPython member

OK, here's a plan:

  • let's leave the display*() api discussion for later, when the dust has settled a bit more on the capabilities of the display machinery. If we add too many top-level tools and the low-level foundation changes, it will be that much more stuff to change and update.

  • let's make the default inline be png, and for now add --pylab inline_svg as an option instead.

Michael, do you think you could make that update to your pull request?

And one thing to keep in mind: when the mode is png, the qt console should not offer the option to save html with embedded svg, since there will be no svg around to show...

@ellisonbg
IPython member

Only one change. I think we should only have 1 inline backend:

--pylab inline

The activation of svg rendering should be config=True attribute of something and handled in the normal config manner.

@minrk minrk added a commit to minrk/ipython that referenced this pull request Apr 22, 2011
@minrk minrk allow toggle of svg/png figure format in inline backend
png is now the default, and select_figure_format(fmt) is added to user_ns for switching between the two.

This should address some of the discussion in gh-381
501b44b
@ellisonbg
IPython member

We are closing this as this work is continuing here:

#451

@ellisonbg
IPython member

Closing

@ellisonbg ellisonbg closed this May 17, 2011
@fperez
IPython member

@mdboom, eventually the implementation ended up a little different from your approach, but many thanks for getting the ball going on this! Much appreciated.

@mdboom

No worries. Glad to at least proof-of-concept it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment