Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Size of gif increases after processing with Vips::Image.thumbnail #274

Unanswered
chloe-meister asked this question in Q&A
Discussion options

Hi there!

I'm using Vips::Image.thumbnail to resize gif images and I noticed that for some gifs, the size after resizing is much bigger than before.

My code:

input_path = input_file.path << "[n=-1]"
image = Vips::Image.thumbnail(input_path, size, size: :down)
image.magicksave(temp_file.path, optimize_gif_frames: true, optimize_gif_transparency: true)

As an example, the gif attached is 1.9MB, and after processing by the code above it's 4.88MB.
giphy

I've tried changing the quality in magicksave:

image.magicksave(temp_file.path, optimize_gif_frames: true, optimize_gif_transparency: true, quality: x)

with x = 0, 1, 50 and 100 but it didn't make any difference.

I wonder what I could be missing?

Many thanks!
Chloe

You must be logged in to vote

Replies: 13 comments

Comment options

Hello @chloe-meister,

I think that's just the way it is. magicksave exposes some of imagemagick's GIF write controls, but not all, unfortunately.

GIF optimisation is a very manual process. If your source GIF has been carefully tuned, you'll need to do the same amount of tuning to get a small thumbnail from it.

You must be logged in to vote
0 replies
Comment options

I'll have a quick look inside the GIF, it's an interesting test image.

You must be logged in to vote
0 replies
Comment options

I understand. Thanks for the quick answer and for looking at the gif. Hopefully you can find something interesting out of it 🙂

You must be logged in to vote
0 replies
Comment options

Actually, it seems to be working OK for me. I tried:

$ vips copy wave.gif[n=-1] x.gif[optimize-gif-frames,optimize-gif-transparency]
$ ls -l wave.gif x.gif 
-rw-rw-r-- 1 john john 1916652 Jul 22 13:44 wave.gif
-rw-rw-r-- 1 john john 1919527 Jul 22 17:43 x.gif

So the size stays low. However, if I shrink I see:

$ vipsthumbnail wave.gif[n=-1] -o x.gif[optimize-gif-frames,optimize-gif-transparency] --size 400
$ ls -l x.gif
-rw-rw-r-- 1 john john 5106476 Jul 22 17:47 x.gif

What's happening is that the shrink operation also slightly sharpens. Your original GIF has perfect flat edges that compress well, but the raised edges on the shrunk version need more bytes to represent.

I don't think there's much that can be done about this, unfortunately.

You must be logged in to vote
0 replies
Comment options

I also tried just loading and saving the image:

image = Vips::Image.new_from_file(input_file.path, n: -1)
image.magicksave(temp_file.path, optimize_gif_frames: true, optimize_gif_transparency: true)

and indeed the size stays low. As you pointed out, the issue is with shrinking. The increase in file size makes sense given that it's sharpening as well.

Thank you very much for the clarifications 🙂

You must be logged in to vote
0 replies
Comment options

Just wondering. Is it possible to make a thumbnail with another kernel? Such as "nearest" that won't mix colors.

You must be logged in to vote
0 replies
Comment options

Yes, thumbnail used to have a kernel, option, but we removed it as it was too confusing. People kept picking the wrong one and getting bad results :(

You can do the thumbnailing yourself since it's pretty easy with GIF. Something like:

#!/usr/bin/ruby
require 'vips'
image = Vips::Image.new_from_file ARGV[0], access: :sequential, n: -1
target_size = ARGV[2].to_i
# image.height will be for all frames -- we want the height of one page. If
# page-height is missing, default to image height
page_height = if image.get_typeof("page-height") != 0 
 image.get("page-height")
 else
 image.height
 end
# we now know the number of pages in the animation
n_loaded_pages = image.height / page_height
# resize to fit a inside target_size by target_size box
scale = [target_size.to_f / image.width, target_size.to_f / page_height].min
# adjust the scale so that we hit the image.height exactly, or we'll hae frames
# that vary in size
target_height = (page_height * scale).round
scale = (target_height.to_f * n_loaded_pages) / image.height
# normally you'd need to premultiply around resize, but :nearest does not mix
# pixels, so there's no need
image = image.resize scale, kernel: :nearest
# we need to set the new page_height
image = image.copy
image.set "page-height", target_height
image.write_to_file ARGV[1], 
 optimize_gif_frames: true, optimize_gif_transparency: true

Running:

$ ./thumb_nearest.rb ~/pics/wave.gif x.gif 400

ie. a 20% shrink makes:

x

1.34mb, so it's smaller. Nearest will give nasty artifacts with large reductions, of course, eg.:

$ ./thumb_nearest.rb ~/pics/wave.gif x.gif 64

You can see there are odd "sparkle" effects:

x

You must be logged in to vote
0 replies
Comment options

Thank you so much, works a treat!

You must be logged in to vote
0 replies
Comment options

Hmmm, I don't understand the reason of sparkles. It's kind of divided on even and odd frames. Maybe it's related to this particular gif or encoder does something different on even/odd phases.

In theory shrinking 4x should be the same as shrinking twice by 2x.

You must be logged in to vote
0 replies
Comment options

A 8x nearest-neighbour shrink is just taking every 8th pixel, so you get horrible aliasing. You could use a triangle filter instead, but they look very soft, and the mushy edges would be hard to compress.

You must be logged in to vote
0 replies
Comment options

If that can help, when I tried thumbnailing as above with large reductions I didn't get the sparkles but the last frames have got many white parts in them, as shown with the file attached.
avatar_image

My implementation is really close to @jcupitt 's above:

 def resize_gif(vips_image, size)
 # GH issue: https://github.com/libvips/ruby-vips/issues/244
 # This will get nasty with large reductions
 target_size = size.to_i
 page_height = vips_image.get("page-height") || vips_image.height
 n_frames = vips_image.height / page_height
 scale = [target_size.to_f / vips_image.width, target_size.to_f / page_height].min
 target_height = (page_height * scale).round
 scale = (target_height.to_f * n_frames) / vips_image.height
 # normally you'd need to premultiply around resize, but :nearest does not mix
 # pixels, so there's no need
 new_image = vips_image.resize(scale, kernel: :nearest)
 # we need to set the new page_height
 new_image.set("page-height", target_height)
 new_image
 end

Called by:

image = Vips::Image.new_from_file(input_file.path, n: -1, access: :sequential)
image = resize_gif(image, size)
image.write_to_file(temp_file.path, optimize_gif_frames: true, optimize_gif_transparency: true)
You must be logged in to vote
0 replies
Comment options

I was using 8.10 and it has some fixes to the GIF loader.

You'll need to add a copy before the set or you'll see errors in large programs. You must have a unique reference to an image before modifying the metadata.

You must be logged in to vote
0 replies
Comment options

Thanks for clarifying, I wondered why the copy indeed. I'll add it back 🙂

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet
Converted from issue

This discussion was converted from issue #244 on January 31, 2021 13:10.

AltStyle によって変換されたページ (->オリジナル) /