I want to convert images held as strings in variables as fast as possible to WebP format while shrinking larger images but do not enlarge smaller images.
The base system is a Debian 9.9 with PHP 7.3. I've tried to measure speed for the following techniques: imagejpeg
, imagewebp
, using cwep
and php-vips
. I used the following code:
$jpeg = function() use ($image) {
$old_image = @imagecreatefromstring($image);
$old_width = (int)@imagesx($old_image);
$old_height = (int)@imagesy($old_image);
$new_width = 1920;
$new_width = min($old_width, $new_width);
$ratio = $new_width / $old_width;
$new_height = $old_height * $ratio;
$new_image = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $new_width, $new_height, $old_width, $old_height);
ob_start();
imagejpeg($new_image, NULL, 75);
$image = ob_get_clean();
};
$webp = function() use ($image) {
$old_image = @imagecreatefromstring($image);
$old_width = (int)@imagesx($old_image);
$old_height = (int)@imagesy($old_image);
$new_width = 1920;
$new_width = min($old_width, $new_width);
$ratio = $new_width / $old_width;
$new_height = $old_height * $ratio;
$new_image = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $new_width, $new_height, $old_width, $old_height);
ob_start();
imagewebp($new_image, NULL, 75);
$image = ob_get_clean();
};
$convert = function(string $image, int $width, int $height) {
$cmd = sprintf('cwebp -m 0 -q 75 -resize %d %d -o - -- -', $width, $height);
$fd = [
0 => [ 'pipe', 'r' ], // stdin is a pipe that the child will read from
1 => [ 'pipe', 'w' ], // stdout is a pipe that the child will write to
2 => [ 'pipe', 'w' ], // stderr is a pipe that the child will write to
];
$process = proc_open($cmd, $fd, $pipes, NULL, NULL);
if (is_resource($process)) {
fwrite($pipes[0], $image);
fclose($pipes[0]);
$webp = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$result = proc_close($process);
if ($result === 0 && strlen($webp)) {
return $webp;
}
}
return FALSE;
};
$cwebp = function() use ($image, $convert) {
$old_image = @imagecreatefromstring($image);
$old_width = (int)@imagesx($old_image);
$old_height = (int)@imagesy($old_image);
$new_width = 1920;
$new_width = min($old_width, $new_width);
$ratio = $new_width / $old_width;
$new_height = $old_height * $ratio;
$image = $convert($image, $new_width, $new_height);
};
$vips = function() use ($image) {
$image = Vips\Image::newFromBuffer($image);
$old_width = (int)$image->get('width');
$old_height = (int)$image->get('height');
$new_width = 1920;
$new_width = min($old_width, $new_width);
$ratio = $new_width / $old_width;
// $new_height = $old_height * $ratio;
$image = $image->resize($ratio);
$image = $image->writeToBuffer('.webp[Q=75]');
};
I've called $jpeg()
, $webp()
, $cwebp()
and $vips()
ten times in a loop and runtime in seconds is:
JPEG: 0.65100622177124
WEBP: 1.4864070415497
CWEBP: 0.52562999725342
VIPS: 1.1211001873016
So calling cwebp
CLI tool seems to be the fastest way, that is surprising. I've read many times that vips
is a extremly fast tool (mostly faster than imagemagick
), so I'd like to focus on vips
.
Can anyone help me to optimize $vips()
for better performance? Maybe there are some options for writeToBuffer()
or resize()
which are not known to me. It's really important that all operations do work in memory only without reading files from disk or storing files on disk.