New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JPG compression for inline pylab #4679
Conversation
This looks good to me. I'll just re-ask the same question that I did when the |
Would you like to drop a line in the what's new and the doc at the same time ? |
I don't know the history behind the
The Will look into docs and what's new. |
I added some lines in the what's new pr directory. I didn't find a convenient place to add extra doc and found the options page was autogenerated. Any tips to where I could put more information? |
Oh, I didn't implement the proposal above ( |
|
||
if has_pil: | ||
# If we have PIL using jpeg as inline image format can save some bytes. | ||
fmts.append('jpg') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced about conditionally including options for config - it could lead to unclear failures if someone sets the option to jpg and later removes PIL, because the error message will just say that jpg is not a valid value. It also means that we import PIL, which AFAIK is a relatively heavy library, even when it's not going to be used. I'd rather have jpg
as an option all the time, and check for PIL when jpg is specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about the weight of the import. However, I feel uncomfortable about advertising jpg and then, when it is about to be used, fail. It is an un-kept promise and a late failure. Documenting it in the help string can help, but won't prevent late failure during Notebook execution. What is the general policy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If late failure is a concern, I think it should be possible to have a _figure_format_changed
function which, if you select jpg, tries to import PIL, and warns you if it can't. But I'd rather the error message said "can't import PIL" or similar, rather than "jpg is not a valid option".
I don't think we have a general policy for this kind of thing.
I suggest using Pillow instead of PIL... it is a fork of PIL but a lot of better and it will probably replace PIL after all... |
Just a note: -1 to @damianavila's proposed additions of pillow to comments / docstrings. |
I'd mention Pillow in docstrings, but possibly as "PIL/Pillow" |
and because ?
I can live with this proposal... |
I think the PIL/Pillow import method is the same, and I'm actually using Pillow :) I can document it as PIL/Pillow, and be generic in the code by referring to it as "python_imaging". i really don't mind. |
I think that mention of Pillow will save a lot of headaches to future users who don't know about this better fork of PIL... but it would be better to wait for @minrk comments about his -1 to make some of these changes, before actually doing the changes... |
I think it's okay to mention just 'PIL' in the comments, since the import is the same, and we don't need to add clutter to every comment. You need the library, it doesn't matter how you install it. Mentioning Pillow in the what's new snippet is fine, though. |
OK, but I still would like to see a reference in the docstrings... maybe with the suggestion from @takluyver: "PIL/Pillow" |
I just think adding PIL/Pillow to every instance is useless clutter. At most one location seems like plenty. |
Yes, but I do not think the what's new was the proper location... but, at the end, it is a little detail... and not a reason to stop the flow of this PR... |
Sorry - by one location I meant in the code (not counting what's new). Mentioning it in the print_figure docstring and/or the traitlet help string is fine, but renaming variables as you proposed is a bit too far. |
@@ -338,5 +342,5 @@ def configure_inline_support(shell, backend): | |||
del shell._saved_rcParams |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't quite toggle correctly when moving from png to jpg, or jpg to anything - let's add [ f.type_printers.pop(Figure, None) for f in svg,png,jpg... ]
to the top, then only turn them on in the if/elif/elif
branch. That will be more consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must admit I don't totally understand what's going on there and I proceeded by copy/pasting. I see types are [un]registered so that formatters know what to handle and how to handle them. I don't get why SVG formatter was altered in each branch of the if/elif... If you can provide some background I'll be very grateful!
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only want one formatter registered at a time, otherwise every format will be published. This is saying "forget what we were publishing before, just publish the selected format." So, when png is selected, stop publishing svg and vice versa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only want one formatter registered at a time, otherwise every format will be published
I'll rebring this issue later, but I feel like we should also allow many formater no ?
I agree that is maybe too far to rename variables... I will be happy with
Great we have achieved a consensus, hehe... 👍 |
@@ -53,7 +59,20 @@ def _config_changed(self, name, old, new): | |||
inline backend.""" | |||
) | |||
|
|||
figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True, | |||
fmts = ['svg', 'png', 'retina'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add jpg to the formats, and then add a check for has_pil
in _figure_format_changed
, such as:
def _figure_format_changed(self, name, old, new):
if new == 'jpg' and not has_pil:
raise TraitError("Require PIL/Pillow for jpg figures")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if we're doing this, may as well delay the import of PIL until jpg
is set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I can live with having the PIL import inside this function. Will do!
Actually, let's back up for a second - why do we check for PIL anyway? matplotlib jpeg output seems to work fine without PIL. |
Nevermind - I was testing a non-agg backend. PIL is needed for jpg output with agg. Ignore me, I'm going home :). |
No problem, it is late here too ;-) |
Ok, I think the latest commit addresses most (if not all) of your comments. I tried to test it the best I could but I have a few concerns :
As always any feedback is welcome! |
I was indeed doing notebook --pylab=inline and was worried because I didn't get any errors! I tried %pylab inline which seems to work and now I do get exceptions in my cells! Thanks, and thanks for the iptest info too. |
@@ -0,0 +1,2 @@ | |||
* The InlineBackend.figure_format flag now supports JPEG output if PIL is available. | |||
* The new InlineBackend.quality flag controls the amount of compression (currently JPEG only) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InlineBackend.quality
between backticks ?
Looks good and test are passing. I upvote. |
Me too 👍 |
Yes, I think so, and it's not hard to actually do. Right now, you can: for fmt, mime in [('svg', 'image/svg+xml'), ('png', 'image/png'), ('jpg', 'image/jpeg')]:
f = ip.display_formatter.formatters[mime]
f.for_type(Figure, lambda fig: print_figure(fig, fmt) It's just a question of API, and the default behavior makes the most sense with only one format published. I think you proposed a 'formats' configurable, which would be a list rather than the current single string. But that's separate from this issue, which I agree is ready to go. |
Hello guys! I was wondering if you'd like me to rebase this. I ask because I've read that rebasing can be dangerous, but since it hasn't been merged yet I don't think it should be an issue. However, I'd like to know your opinion! Thanks |
It merge cleanly so there is no need to rebase for now. We usually rebase only when there is a merge conflict. |
OK! I'm looking into adding unit tests but I don't really get how to do this for such a function. I'd like to create a code cell which creates a pylab figure and just test that the server returns a jpg if I asked it to. I don't see well how to proceed? Should start |
There's a test |
Ah right, thanks I was looking in the kernel package, not the core package. Will further test my test and commit. |
try: | ||
from PIL import Image | ||
def test_figure_to_jpg(): | ||
# simple check for at least svg-looking output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
svg-looking? I think is jpg 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right :)
Sorry, looks like this needs a rebase. |
Oh noooo! ^^ Will do! |
… instead of testing it at module startup. The test is only run if figure_format is jpg. In help/error messages, refer to PIL/Pillow (i.e. not just PIL).
Saner acceptable range for jpeg quality parameter. Better docstrings.
Looks like this has been rebased - is it ready for merge? |
Yup. Thanks, @dbarbeau! |
JPG compression for inline pylab
JPG compression for inline pylab
This is a follow up to #4448
It adds JPG compression and quality options to render inline figures. This is useful for blogs where PNG isn't compact enough and SVG overly-verbose.