elixir-image/image

`:quality` option for `Image.write` values for PNG

Closed this issue · 5 comments

The docs for Image.write state that quality can be a number between 1 and 100. For PNG, however, passing a quality of 100 produces the following error:

GLib-GObject-CRITICAL **: 10:43:13.875: value "10" of type 'gint' is invalid or out of range for property 'compression' of type 'gint'

I imagine this is because PNG compression is between 1 and 9. I'm happy to submit a PR but I'm not entirely sure how you would want to handle this. Should there be validation is the output is a PNG or just call it out? I wasn't quite sure but is it the :effort in the png_write_option()?

Steps to reproduce:

"some-image.png"
|> Image.open!()
|> Image.write!("some-resaved-image.png", quality: 100)

I'm using the latest version of Image on macos 13.1.

Thanks for the issue. Its tricky to find a common lexicon for image write options that can operate across different image types given the many different options in play.

As you point out, for .png files, :quality was a proxy for :compression (not :effort which is a separate parameter). :compression needs to be in the range 1..9 and I wasn't conforming the:quality parameter - which is in the range 1..100 for all image types - into the range 1..9 correctly for .png images.

On reflection I think :quality for .png files is better expressed as the :Q (quantisation level) parameter to Vix.Vips.Operation.pngsave/2 so I've pushed a PR that does that. And added the parameter :compression for .png files.

Net result:

  • :quality for .png files is now a proxy for :Q. Like all image types, :quality is in the range 1..100.
  • :compression is added as a valid parameter for .png files.

Feedback is very welcome, and if you've the time a test run running from the main branch on GitHub to validate (or refute) would be great.

I'm now using the main branch from github and it certainly works for my use-case! I'm actually not concerned about being able to output top-quality PNGs, it's that my image generator's input accepts a :quality arg and can specify multiple output formats for one image. Testing that is how I cam across this. In reality, only JPGs would ever have a quality of 100 before they are converted to PDFs for printing. That is to say is that I have no business use-cases to test PNG quality. However, in the little test I did do, PNGs output with quality 1 and quality 100 are identical.

iex> image = Image.open!("whale.jpg")
iex> Image.write!(image, "quality-whale.png", quality: 100)
iex> Image.write!(image, "discount-whale.png", quality: 1)
iex> File.read!("quality-whale.png") == File.read!("discount-whale.png")
true

I tried other numbers than just 1.

On further research its clear that :Q for a png file is only effective if palletising the image. Which is why it has no effect in your example. I've changed the docs to say that :quality has no effect for .png images but the parameter remains valid for the reason you outline: to have consistent parameters for all formats where possible.

I think this resolves the issue, albeit not with total satisfaction. If you agree I'll close the issue.

I agree that it solves the issue albeit not with total general satisfaction; however, it more than solves it for my immediate needs, so by all means close this issue!

I realize that it's not totally true that I don't care about PNG quality as I need transparent fallback images for webp. While I know the default PNG quality is fine, I can certainly play around with it when testing. I had been completely off of image processing after my initial spike while I did several other spikes. I'm on the implementation step for images so hopefully I'll learn a thing or two to help out more.

it's a tricky situation. I think it's helpful to have a consistent set of "save" options that work predictably across different image types. The each type has its own considerations. I'd welcome any ideas and suggestions on how to make this better.

This library I'm treating as very "user requirement driven" so I look forward to your feedback on where there are gaps or issues that can filled or improved.