pyvips
pyvips copied to clipboard
how to resize gif image and write to file or buffer
I have a image like below:
I want to scale it and write to buffer or file, how to do it?
Hello @2h4dl, use thumbnail
, it has extra logic for resizing animated images.
thumb = pyvips.Image.thumbnail("brush.gif", 128, n=-1)
thumb.write_to_file("x.gif")
To make:
Thanks your answer @jcupitt. Still have a question.
I'm trying to read image from internet without knowing its real format, maybe gif image but named a jpg, how to read and scale or crop it?
I used pyvips.Image.new_from_buffer
and pyvips.Image.thumbnail_image
to read a gif, then saved it with write_to_file
, a still image is given not a animated image.
Add arg n=-1
to new_from_buffer
is working with gif image, but when reading other format image, like jpg, will raise an error: pyvips.error.Error: VipsForeignLoadJpegBuffer does not support argument n
.
Don't use thumbnail_image
-- it's much slower than thumbnail
.
You can open an image with new_from_file
and then check vips-loader
to get the file type:
>>> import pyvips
>>> x = pyvips.Image.new_from_file("k2.jpg")
>>> x.get("vips-loader")
'jpegload'
Hi @jcupitt , it's all right to read an image from local disk with new_from_file
.
How to read a internet image. Here is my code to read image from interent data.
import requests
import pyvips
def image_format(vips_loader):
vips_loader = vips_loader.split("_")[0]
if vips_loader == "jpegload":
return "jpeg"
elif vips_loader == "pngload":
return "png"
elif vips_loader == "gifload":
return "gif"
else:
return "unknown"
image_url = "http://www.example.com/example.jpg"
resp = requests.get(image_url)
try:
web_image = pyvips.Image.new_from_buffer(resp.content, options="", n=-1) # for gif image
except:
web_image = pyvips.Image.new_from_buffer(resp.content, options="") # for jpg, png
vips_loader = web_image.get("vips-loader")
image_fmt = image_format(vips_loader)
if image_fmt == "unknown":
pass
elif image_fmt == "gif":
# resize op
scaled_image = pyvips.Image.thumbnail_image(web_image, width=w, height=h)
# crop op
# TODO
# unknowning how to do it
else:
# resize op
scaled_image = pyvips.Image.resize(web_image, scale=0.5)
# crop
cropped_image = Image.extract_area(scaled_image , x, y, w, h)
It looks ugly and hard to read. Any suggestion will be appreciated.
new_from_buffer
is very quick -- you can do it just to get the image type.
#!/usr/bin/python3
# test with:
# https://upload.wikimedia.org/wikipedia/en/b/b4/Sceicbia.jpg
# https://upload.wikimedia.org/wikipedia/commons/6/69/F4-motion.gif
import sys
import requests
import pyvips
def loader_from_buffer(buf):
image = pyvips.Image.new_from_buffer(buf, "")
return image.get("vips-loader").split("_")[0]
image_url = sys.argv[1]
target_width = 128
buf = requests.get(image_url).content
loader = loader_from_buffer(buf)
options = ""
if loader == "gifload" or loader == "webpload":
# an animated format -- make a thumbnail of all frames
options = "n=-1"
thumb = pyvips.Image.thumbnail_buffer(buf, target_width, option_string=options)
thumb.write_to_file(sys.argv[2])
I need to crop specific area, extract_area
is preferred, so the manual way is appropriate, also resize op.
Plz tell me how to get every frame and reassemble as a new animation with pyvips, I still have no idea.
There's some demo code here:
https://github.com/libvips/libvips/issues/1167#issuecomment-440598247
You'd need to run that after thumbnail
.
Hello @2h4dl, use
thumbnail
, it has extra logic for resizing animated images.thumb = pyvips.Image.thumbnail("brush.gif", 128, n=-1) thumb.write_to_file("x.gif")
To make:
Current, I use Python 3.8, but throw error
Traceback (most recent call last):
File "resize_gif.py", line 7, in <module>
thumb = pyvips.Image.thumbnail("brush.gif", 128, n=-1)
File "/Users/xzh/.local/share/virtualenvs/vips-image-yHrOgoj2/lib/python3.8/site-packages/pyvips/vimage.py", line 135, in call_function
return pyvips.Operation.call(name, *args, **kwargs)
File "/Users/xzh/.local/share/virtualenvs/vips-image-yHrOgoj2/lib/python3.8/site-packages/pyvips/voperation.py", line 265, in call
details = intro.details[name]
KeyError: 'n'
Hello @zhaohuxing,
Your libvips is probably too old. Do you know what version you are using?
Hello @zhaohuxing,
Your libvips is probably too old. Do you know what version you are using?
Thanks, but I use version 8.9.2
vips-8.9.2-Tue Apr 21 09:26:11 UTC 2020
Ooops, sorry, it should be:
x = pyvips.Image.thumbnail("3198.gif[n=-1]", 128)
Ooops, sorry, it should be:
x = pyvips.Image.thumbnail("3198.gif[n=-1]", 128)
Thanks,
BTW, what does n=-1
mean? I call thumbnail("3198.gif", 128) without n=-1
, produced gif picture is static.
Sorry, I should not post untested code.
Here's a complete working program to thumbnail URLs, including animated images:
#!/usr/bin/python3
# test with:
# https://upload.wikimedia.org/wikipedia/en/b/b4/Sceicbia.jpg
# https://upload.wikimedia.org/wikipedia/commons/6/69/F4-motion.gif
import sys
import requests
import pyvips
def loader_from_buffer(buf):
image = pyvips.Image.new_from_buffer(buf, "")
return image.get("vips-loader").split("_")[0]
image_url = sys.argv[1]
target_width = 128
buf = requests.get(image_url).content
loader = loader_from_buffer(buf)
options = ""
if loader == "gifload" or loader == "webpload":
# an animated format -- make a thumbnail of all frames
options = "n=-1"
thumb = pyvips.Image.thumbnail_buffer(buf, target_width, option_string=options)
thumb.write_to_file(sys.argv[2])
When you load an animated image, pyvips will just give you the first frame. You can select the number of frames to load with n=
(1 meaning load 1 frame) and the first frame to load with page=
(default 0, the first frame).
n=-1
means load all frames.
@jcupitt If I did not call thumbnail function, use magick save the picture of gif format, how does resize this picture? In the background, I use bimg library, but it isn't supported gif; so I not known how to modify bimg for supported gif.
I would use thumbnail
. It has the extra logic for resizing animated images.
@jcupitt Can you provide a complete c program?
I had this example, it might help:
/* Compile with:
*
* gcc -g -Wall buffer.c `pkg-config vips --cflags --libs`
*/
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
int
main( int argc, char **argv )
{
char *buffer_in;
size_t length_in;
GError *error = NULL;
VipsImage *image, *x;
char *buffer_out;
size_t length_out;
if( VIPS_INIT( argv[0] ) )
vips_error_exit( NULL );
/* Load source image into memory. This image will be in some format,
* like JPEG.
*/
if( !g_file_get_contents( argv[1], &buffer_in, &length_in, &error ) ) {
fprintf( stderr, "unable to read file %s\n%s\n",
argv[1], error->message );
g_error_free( error );
exit( 1 );
}
/* Make a vips image from the memory buffer. This image will
* decompress from buffer_in as required.
*/
if( !(image = vips_image_new_from_buffer( buffer_in, length_in, NULL,
"access", VIPS_ACCESS_SEQUENTIAL,
NULL )) )
vips_error_exit( NULL );
/* Perform some operation on the image.
*/
if( vips_invert( image, &x, NULL ) )
vips_error_exit( NULL );
/* x now holds a reference to image, so we can drop image and update
* our pointer to point to the head of the pipeline.
*
* buffer_in will still be in use -- we can't free that until the
* whole pipeline completes.
*/
g_object_unref( image );
image = x;
/* Create an output memory buffer. Again, this buffer will contain an
* image packed into some format, it will not be an array of pixel
* values.
*/
if( vips_image_write_to_buffer( image,
".jpg", (void **) &buffer_out, &length_out, NULL ) )
vips_error_exit( NULL );
if( !g_file_set_contents( argv[2], buffer_out, length_out, &error ) ) {
fprintf( stderr, "unable to write file %s\n%s\n",
argv[1], error->message );
g_error_free( error );
exit( 1 );
}
g_object_unref( image );
g_free( buffer_in );
g_free( buffer_out );
return( 0 );
}
@jcupitt Thanks
When you load an animated image, pyvips will just give you the first frame. You can select the number of frames to load with
n=
(1 meaning load 1 frame) and the first frame to load withpage=
(default 0, the first frame).
n=-1
means load all frames.
Hi @jcupitt,
I use thumbnail
to resize picture of the GIF format from c, vips_thumbnail("new.gif[n=-1]", out, 200)
. If I use vips_thumbnail_buffer
, how to set n=-1
?
There's an argument called option_string
you can use to pass extra flags to the loader. In C:
vips_thumbnail_buffer( data, length, &image, 200, "option_string", "n=-1", NULL )
Now I look at the docs I see we'd forgotten to add a note about this extra argument. I've fixed it, thanks!
It works for me. Perhaps there's something wrong with your GIF? You need to check the return code from thumbnail_buffer
.
Test program:
/* compile with
*
* gcc -g -Wall zhaohuxing.c `pkg-config vips --cflags --libs`
*/
#include <vips/vips.h>
int
main(int argc, char *argv[])
{
char *contents;
size_t length;
VipsImage *image;
if (VIPS_INIT (argv[0]))
vips_error_exit (NULL);
if (!g_file_get_contents (argv[1], &contents, &length, NULL))
vips_error_exit (NULL);
if (vips_thumbnail_buffer (contents, length, &image, 100,
"option_string", "n=-1",
NULL))
vips_error_exit (NULL);
if (vips_image_write_to_file (image, argv[2], NULL))
return -1;
g_free (contents);
return 0;
}
I see:
$ ./a.out ~/pics/dancing-banana.gif x.gif
$ vipsheader ~/pics/dancing-banana.gif x.gif
temp-0: 121x128 uchar, 4 bands, srgb, gifload
temp-0: 95x100 uchar, 4 bands, srgb, gifload
Generating:
Hi, @jcupitt
I use vips_thumbnail_buffer with the image of the GIF format, but How to write the processed picture to buffer from c?
I tried to use vips_image_write_to_buffer
and vips_magicksave_buffer
, both failed.
You need to give vips_magicksave_buffer()
the format to encode in, eg.:
if (vips_magicksave_buffer (image,
&output_contents, &output_length,
"format", "GIF",
NULL))
vips_error_exit (NULL);
You need to give
vips_magicksave_buffer()
the format to encode in, eg.:if (vips_magicksave_buffer (image, &output_contents, &output_length, "format", "GIF", NULL)) vips_error_exit (NULL);
I truly appreciate your timely help
Hi @jcupitt, use vips_image_new_from_buffer
init image in gif format, option_str set n=-1
?
Yes, like the example above (if I understand what you mean).
Thanks @jcupitt