-
Notifications
You must be signed in to change notification settings - Fork 62
Size of gif increases after processing with Vips::Image.thumbnail #274
-
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
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 13 comments
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
I'll have a quick look inside the GIF, it's an interesting test image.
Beta Was this translation helpful? Give feedback.
All reactions
-
I understand. Thanks for the quick answer and for looking at the gif. Hopefully you can find something interesting out of it 🙂
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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 🙂
Beta Was this translation helpful? Give feedback.
All reactions
-
Just wondering. Is it possible to make a thumbnail with another kernel? Such as "nearest" that won't mix colors.
Beta Was this translation helpful? Give feedback.
All reactions
-
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:
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:
Beta Was this translation helpful? Give feedback.
All reactions
-
🎉 1
-
Thank you so much, works a treat!
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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)
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
Thanks for clarifying, I wondered why the copy
indeed. I'll add it back 🙂
Beta Was this translation helpful? Give feedback.