Support inline PNGs of matplotlib plots #381

Closed
wants to merge 2 commits into
from

Conversation

Projects
None yet
3 participants
Contributor

mdboom commented Apr 14, 2011

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.

Owner

ellisonbg commented Apr 14, 2011

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.

Owner

fperez commented Apr 14, 2011

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?

Owner

ellisonbg commented Apr 14, 2011

Sure we could definitely make this configurable. It would be great to
have a sense of the cost of computing them.

On Wed, Apr 13, 2011 at 10:28 PM, fperez
reply@reply.github.com
wrote:

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?

Reply to this email directly or view it on GitHub:
#381 (comment)

Brian E. Granger
Cal Poly State University, San Luis Obispo
bgranger@calpoly.edu and ellisonbg@gmail.com

Contributor

mdboom commented Apr 14, 2011

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.

Owner

ellisonbg commented Apr 15, 2011

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.

Owner

fperez commented Apr 15, 2011

On Fri, Apr 15, 2011 at 9:34 AM, ellisonbg
reply@reply.github.com
wrote:

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.

I wonder if it would be worth adding to the interactive namespace, in
pylab mode, a convenience

displayfigs()

to shorten all the typing for this common case:

displayfigs() == display(*getfigs())

what do you think?

f

Owner

ellisonbg commented Apr 15, 2011

In some ways I like this, but it hides the main API we want people to be
come familiar with (display/display__) from the end user. Currently, people
don't know what display/display__ are, but in the future they will ask "why
does this object not work with IPython's display functions?" In that
future, we don't want users to think that displayfigs is somehow different
from the regular display. There is also the issue of the format used. We
already have display_png, display_svg, etc. Would we add displayfigs_png,
etc?

My only complaint about the current API is that for a single figure you
still have to add the * rather that just doing:

display(getfigs(1))

On Fri, Apr 15, 2011 at 11:21 AM, fperez <
reply@reply.github.com>wrote:

On Fri, Apr 15, 2011 at 9:34 AM, ellisonbg
reply@reply.github.com
wrote:

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.

I wonder if it would be worth adding to the interactive namespace, in
pylab mode, a convenience

displayfigs()

to shorten all the typing for this common case:

displayfigs() == display(*getfigs())

what do you think?

f

Reply to this email directly or view it on GitHub:
#381 (comment)

Brian E. Granger
Cal Poly State University, San Luis Obispo
bgranger@calpoly.edu and ellisonbg@gmail.com

Contributor

mdboom commented Apr 15, 2011

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

Owner

fperez commented Apr 15, 2011

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

Owner

ellisonbg commented Apr 19, 2011

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
Owner

ellisonbg commented May 17, 2011

We are closing this as this work is continuing here:

#451

Owner

ellisonbg commented May 17, 2011

Closing

ellisonbg closed this May 17, 2011

Owner

fperez commented May 18, 2011

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

Contributor

mdboom commented May 18, 2011

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