-
Notifications
You must be signed in to change notification settings - Fork 109
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
Convert PNG to AVIF/WebP on upload #371
Comments
I think it's definitely a good idea 💯 there would be two options:
My vote goes for png->lossy because the images of websites aren't intended to be 1:1 with the original so we can take the advantage of the compression techniques like chroma subsampling that decimates the image filesize. Another suggestion is to use the png as the source image and not to create the subsizes in png. EDIT: the bug of the webp missing transparency is related to versions before gd 2.1.1 (6 years ago, php 5.6)... I forgot to mention about, but i guess actually isn't a real issue since it's deprecated. here more info |
@felixarntz Any news about this, I loved the plugin, but most of my images are .png and that would be very good, I did an online conversion test and saw that the images would be half the size! Thanks |
Any update on this yet? |
Any plans on this feature? First post in this thread is dated Jun 13, 2022... |
@ficod, yeah, I decided to convert them manually with Imagick: <?php
namespace App\Media;
/**
* Set image quality based on mime type.
*
* @param int $quality The image quality level.
* @param string $mime_type The mime type of the image.
*
* @return int The modified image quality level.
*/
function set_image_quality($quality, $mime_type)
{
// Check if the mime type is 'image/webp'
if ('image/webp' === $mime_type) {
// Set a higher quality level for 'image/webp'
return 86;
}
// Return the original quality level for other mime types
return $quality;
}
// Add a filter to set image quality
add_filter('wp_editor_set_quality', __NAMESPACE__ . '\\set_image_quality', 10, 2);
///
/**
* Scale down and optimize uploading image.
*
* @param array $file The uploading file information.
* @return array The modified file information.
*/
function resize_preupload_image($file)
{
$allowed_types = array('image/png', 'image/bmp', 'image/jpeg', 'image/jpg', 'image/webp');
// Check mime type
// Check if Imagick extension is available
if (!in_array($file['type'], $allowed_types) || !class_exists('Imagick', false) || !class_exists('ImagickPixel', false)) {
return $file;
}
// Check size limit
$limit = 24000;
$size = $file['size'] / 1024;
if ($size > $limit) {
$file['error'] = "Image files must be smaller than {$limit}kb";
return $file;
}
// Get the temporary image path
$filepath = $file['tmp_name'];
// Get the file's dimensions
list($image_width, $image_height) = @getimagesize($filepath);
// Calculate the new dimensions for scaling
$max_width = <YOUR_MAX_WIDTH_NUMBER>;
$max_height = intval($image_height * ($max_width / $image_width));
// Create a new Imagick object from the uploaded image
$image = new \Imagick($filepath);
// Get original image size
$original_size = strlen($image->getImageBlob());
// Check if image already has webp format (do not change quality)
if ('image/webp' === $file['type']) {
$image->setImageCompressionQuality(100);
} else {
// Set webp format (support transparency)
$image->setImageFormat('webp');
// Set image compression quality
$image->setImageCompressionQuality(86);
}
// Scale down the image to the new dimensions
$image->resizeImage(min($image_width, $max_width), min($image_height, $max_height), \Imagick::FILTER_CATROM, 1);
// Get resized (webp converted) image size
$resized_size = strlen($image->getImageBlob());
// Check if the resized image has a larger file size than the original
if ($resized_size >= $original_size) {
// Original image has smaller or equal size, so keep it
$image->clear();
return $file;
}
// Save the resized image, overwrite the original file
$image->writeImage($filepath);
$file['size'] = $resized_size;
$image->clear();
// Return result
return $file;
}
add_action('wp_handle_upload_prefilter', __NAMESPACE__ . '\\resize_preupload_image', 10, 1); |
Now that we have landed AVIF support in core, the current "WebP Uploads" image plugin could be expanded to include support for AVIF. This would also be good opportunity to add some more flexibility around input formats we handle, or progressive encoding (also new in 6.5).
@predaytor - Here is are some alternate approaches: To filter the mime types that the add_filter( 'webp_uploads_upload_image_mime_transforms', function( $transforms ) {
$transforms['image/png'] = array( 'image/png', 'image/webp' );
return $transforms;
} ); To leverage the core filter directly: add_filter( 'image_editor_output_format', function( $mappings ) {
$mappings['image/png'] = 'image/webp';
return $mappings;
} ); |
@adamsilverstein @predaytor Thank you all for your replies. What I don't understand is why this plugin can't receive an official update to solve the issue. I ended up preferring another plugin: WebP Express |
AVIF is a good alternative for JPEG files So, this plugin being to be useless since the AVIF is now supported by the core and since this plugin do not support convert PNG files to WebP. We just need a new plugin to convert automaticly WEBP, JPEG and PNG files into AVIF |
Sorry but this is wrong! avif image format fully support alpha channel (the transparency) Addy Osmani has written a nice book on the argument, recommended |
Now that the Modern Images Formats plugin will support AVIF format (output), thanks to @adamsilverstein on #1151. Adam make this one, for his plugin, what do you think about that setting page ? Leave the choice for users to convert a JPEG input into AVIF, PNG input into WebP, and WebP input into AVIF (for example) that can be a great evolution for our Modern Images Formats plugin and it will be more consistent with the new plugin name 😉 |
Two support topics have raised this the past couple days: |
Please update the topic to add PNG to Avif image format conversion |
This is the only reason why i don't use this plugin. No .png support :( |
Let's try to land wider support for uploaded images, including PNGs. The biggest question I have is about the best UI to provide. For my plugin (pictured above) I went with giving users full control of the output format they want for each input format, but that me a bit over the top for this plugin? Maybe we can use a simplified approach where you choose an output format, then you choose from a checkbox list of input formats, choosing the ones you want to transform? Or maybe just automatically transform any uploaded images when we can reasonably do so? |
@adamsilverstein Yeah, I do think that would be over-the-top to provide such granularity. With considering decisions not options, should JPEG and PNG always be converted to AVIF? Or would there be scenarios where one should rather be converted to WebP? Like, as I understand, only lossless AVIF supports transparency. So would perhaps all JPEGs be converted to AVIF as well as all non-transparent PNGs, but then leave transparent PNGs to converted to WebP (lossless or lossy)? For sites that want to have fine-tuned control, I'd think that we'd perhaps offer a filter instead of a UI for that. |
that makes sense, possibly a filter against whatever we decide are the defaults. existing core filters the plugin itself uses could already be used to fine tune the final behavior.
interesting point, for transparent images, WebP lossy is probably the best choice. I like the general direction of mapping to what we think is the best available output type for the input type. I will work on a PR with that approach and we can discuss further. |
Afaik (but I can be wrong) lossy avifs also support the alpha channel (the transparency). you can give a try with squoosh and converting a transparent image to avif (for me has worked) As to which is "preferred" I prepared this which converts random images and measures their PSNR / SSIM ( spoiler: in the end avif always wins) the only doubts about whether to use avif or not is the time it takes to convert images which is hundreds of times longer than converting a jpeg, this in some cases (e.g. image regeneration by woocommerce) leads to unexpected results |
I was wondering if you could address this request when it launches with WordPress 6.6? This request has been raised and pending for 2 years and still has not been resolved. |
Interesting. I was just going off of what I found in search. For example via shortpixel.com:
I guess that's just wrong.
That's a great point. I've seen approaches where the WebP is generated first since it is fast, with the AVIF generation then sent to a background process since it can take a long time. |
Imagick says it uses 5 as the default value - although I didn't see that default in the code. I created a simple PR to change the default for WordPress Imagick AVIF handling to 7 - WordPress/wordpress-develop#7068 |
@erikyo - I created this mini plugin to test processing time by mime type using WordPress - https://gist.github.com/adamsilverstein/b4e91c9ab1e6f546ec98e3dcc53afb7d Times were too fast in my local to me meaningful to measure. I need to test it in a slower environment to measure actual performance, or modify to run the process repeatedly. |
@jzern Below the tests using different speeds and quality settings for the AVIF format. It seems you are right about that, the "heic:speed" option greatly reduces time without interfering with quality too much. avif / testing quality + heic:speed (updated with some line charts at the end of the file) @adamsilverstein thanks again, here are the results: 2k image jpg 2000x2400{
"profiling_data": [
{
"format": "image\/jpeg",
"size": {
"width": 300,
"height": 300
},
"filesize": 78452,
"time": 0.027272939682006836
},
{
"format": "image\/jpeg",
"size": {
"width": 600,
"height": 600
},
"filesize": 78452,
"time": 0.0017361640930175781
},
{
"format": "image\/jpeg",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 78452,
"time": 0.0017290115356445312
},
{
"format": "image\/jpeg",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 78452,
"time": 0.001683950424194336
},
{
"format": "image\/webp",
"size": {
"width": 300,
"height": 300
},
"filesize": 79732,
"time": 0.03746390342712402
},
{
"format": "image\/webp",
"size": {
"width": 600,
"height": 600
},
"filesize": 79732,
"time": 0.012015819549560547
},
{
"format": "image\/webp",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 79732,
"time": 0.01200103759765625
},
{
"format": "image\/webp",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 79732,
"time": 0.011939048767089844
},
{
"format": "image\/avif",
"size": {
"width": 300,
"height": 300
},
"filesize": 12328,
"time": 0.21320605278015137
},
{
"format": "image\/avif",
"size": {
"width": 600,
"height": 600
},
"filesize": 12328,
"time": 0.1872880458831787
},
{
"format": "image\/avif",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 12328,
"time": 0.18783307075500488
},
{
"format": "image\/avif",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 12328,
"time": 0.19110894203186035
}
],
"total_time": {
"image\/jpeg": {
"start": 1721671552.943534,
"end": 1721671552.975962,
"total": 0.03242802619934082
},
"image\/webp": {
"start": 1721671553.005592,
"end": 1721671553.079016,
"total": 0.07342386245727539
},
"image\/avif": {
"start": 1721671553.108473,
"end": 1721671553.887918,
"total": 0.7794449329376221
}
}
}
png image 3600x2700 {
"profiling_data": [
{
"format": "image\/jpeg",
"size": {
"width": 300,
"height": 300
},
"filesize": 15270,
"time": 0.030642032623291016
},
{
"format": "image\/jpeg",
"size": {
"width": 600,
"height": 600
},
"filesize": 15270,
"time": 0.0013499259948730469
},
{
"format": "image\/jpeg",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 15270,
"time": 0.0013699531555175781
},
{
"format": "image\/jpeg",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 15270,
"time": 0.0014090538024902344
},
{
"format": "image\/webp",
"size": {
"width": 300,
"height": 300
},
"filesize": 13284,
"time": 0.033232927322387695
},
{
"format": "image\/webp",
"size": {
"width": 600,
"height": 600
},
"filesize": 13284,
"time": 0.010429859161376953
},
{
"format": "image\/webp",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 13284,
"time": 0.010452032089233398
},
{
"format": "image\/webp",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 13284,
"time": 0.010426044464111328
},
{
"format": "image\/avif",
"size": {
"width": 300,
"height": 300
},
"filesize": 5811,
"time": 0.16109609603881836
},
{
"format": "image\/avif",
"size": {
"width": 600,
"height": 600
},
"filesize": 5811,
"time": 0.1379549503326416
},
{
"format": "image\/avif",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 5811,
"time": 0.13739490509033203
},
{
"format": "image\/avif",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 5811,
"time": 0.13708209991455078
}
],
"total_time": {
"image\/jpeg": {
"start": 1721672632.182837,
"end": 1721672632.217613,
"total": 0.03477597236633301
},
"image\/webp": {
"start": 1721672632.550024,
"end": 1721672632.614569,
"total": 0.0645449161529541
},
"image\/avif": {
"start": 1721672632.946851,
"end": 1721672633.520387,
"total": 0.5735359191894531
}
}
}
very large png image 4300x2850 {
"profiling_data": [
{
"format": "image\/jpeg",
"size": {
"width": 300,
"height": 300
},
"filesize": 10764,
"time": 0.023726940155029297
},
{
"format": "image\/jpeg",
"size": {
"width": 600,
"height": 600
},
"filesize": 10764,
"time": 0.001238107681274414
},
{
"format": "image\/jpeg",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 10764,
"time": 0.0012209415435791016
},
{
"format": "image\/jpeg",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 10764,
"time": 0.0011870861053466797
},
{
"format": "image\/webp",
"size": {
"width": 300,
"height": 300
},
"filesize": 8844,
"time": 0.03127789497375488
},
{
"format": "image\/webp",
"size": {
"width": 600,
"height": 600
},
"filesize": 8844,
"time": 0.008497953414916992
},
{
"format": "image\/webp",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 8844,
"time": 0.008488893508911133
},
{
"format": "image\/webp",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 8844,
"time": 0.008411169052124023
},
{
"format": "image\/avif",
"size": {
"width": 300,
"height": 300
},
"filesize": 3431,
"time": 0.1293962001800537
},
{
"format": "image\/avif",
"size": {
"width": 600,
"height": 600
},
"filesize": 3431,
"time": 0.1075899600982666
},
{
"format": "image\/avif",
"size": {
"width": 1200,
"height": 1200
},
"filesize": 3431,
"time": 0.10776495933532715
},
{
"format": "image\/avif",
"size": {
"width": 2400,
"height": 2400
},
"filesize": 3431,
"time": 0.10826396942138672
}
],
"total_time": {
"image\/jpeg": {
"start": 1721673332.803404,
"end": 1721673332.830782,
"total": 0.027377843856811523
},
"image\/webp": {
"start": 1721673333.215625,
"end": 1721673333.272304,
"total": 0.05667901039123535
},
"image\/avif": {
"start": 1721673333.657167,
"end": 1721673334.11019,
"total": 0.45302295684814453
}
}
}
a small consideration about this. the 300x300 images takes more time to be processed, probably due to the time required time to resize the image with the sinc-lanczos filter. this pattern is repeated for all of these formats as well My server specs:
@y-guyon Thanks so much for providing such interesting information! will try asap also with the avif codec. I have chosen the heic one since that Colab script is from 2 years ago, and at that time the heic codec was much more widespread and (if I remember correctly) was also faster I would, however, like to point out that the command from the command line is used and instead WordPress uses Imagick, which is not performing in the same way. The biggest limitation in this case would be the number of cores used to convert the image than with imagemagick it should only use 1 core for each file converted |
@erikyo thanks for sharing those results. Clearly the AVIF generation is taking much longer than the other formats - still the slowest time was well under a second, so for average use in WordPress this might be OK. I'll try to repeat the experiment on a lower end server as well as expanding the code to run a large number of repetitions.
Seems likely we should change the default for WordPress (from 5) to 7 |
Thanks for testing it out. The results look reasonable.
libavif's default is 6; either that or 7 should be a good default. |
To verify, I modified the script so that it now uses only the full-size image and tests the quality vs ‘heif:speed’ vs time It is even clearer that there is no quality loss with speed = 7. However, since the default speed for ImageMagick is 6, changing it to 7 is unlikely to affect the encoding performance significantly. |
Somewhere I saw a default of 5 mentioned for Imagick (although I didn't find the code), perhaps it is readable so I will try to check it. Still if quality isn't adversely affected, probably worth raising given the very slow encoding speed. |
https://imagemagick.org/script/defines.php
It would be good to verify this though. |
I couldn't find the default anywhere in the codebase, but I did see a 22% speed improvement when explicitly setting the speed variable to 7 (comments on the PR). I can re-test with 5 to verify this is the same speed as not setting a speed - this will confirm the default. In the meantime, I plan to resume work here on the PNG -> AVIF/WebP conversion support. |
I created a PR that adds PNG upload to AVIF (or WebP) output - #1421 |
@mukeshpanchal27 friday release? 🤠 |
The next Performance Lab release is planned for the week after WordCamp US. See https://make.wordpress.org/core/2024/09/03/performance-chat-summary-03-september-2024/ |
@chrillep You can still use a pre-release version of the plugin though. I just created a build via Your early testing and feedback would be much appreciated! |
Thank you very much, i make some tests with your file and all is working good for me :) 🚀 |
@jzern While compiling a list of contributors that helped with the upcoming WordPress 6.7 release using the project's "Prop" script, your account came up as not having linked their WordPress.org and GitHub accounts. In the WordPress project, all credit given out must be attached to a WordPress.org account. After creating a W.org account (or logging in to your preexisting one), you can link your GitHub account following these steps in the Core Handbook. If you could kindly link your .org account and just share your .org username, we can ensure you receive proper attribution! cc/@jeffpaul |
I signed up for an account, just waiting on the approval to be able to set a password. I didn't really contribute all that much here, so feel free to leave me out if you're about to finalize the release. |
@jzern All constructive activity deserves recognition and WordPress tries to practice giving props liberally. You gave several tips and suggestions that helped arrive at an eventual solution. 🙂 |
I still haven't seen an email for this. Nothing obvious in spam either. login.wordpress.org says it's still pending. |
The account is setup now. It's jzern@ and linked to my github account. |
Sorry, I'm a noob and just found my way here from google. I am looking to automatically convert images to Afif on a LEMP server. Is this code something I can use to implement this? How can I use it exactly? Thanks! |
As noted in this support topic, add the ability to automatically convert PNG images to WebP.
The text was updated successfully, but these errors were encountered: