Как конвертировать анимированный GIF в анимированный WEBP?


я использую GD для управления изображениями в PHP, на основе этот код

<?php
/**
Función para detectar si una imagen es animación
*/
function is_ani($filename) {  
    if(!($fh = @fopen($filename, 'rb')))
        return false;
    $count = 0;
    while(!feof($fh) && $count < 2) {
        $chunk = fread($fh, 1024 * 100);
        $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
   }

    fclose($fh);
    return $count > 1;
}
?>

я намерен разделить фреймы внутри GIF (я могу даже использовать эту библиотеку) и сохранить их в формате WEBP.

как я могу собрать/создать анимированное изображение в формате WEBP с помощью GD в PHP?

--отредактировано--

версия PHP:

PHP 7.2.4 (built: Apr 25 2018 12:05:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

Gd_info:

#php -r 'print_r(gd_info());'
Array
(
    [GD Version] => bundled (2.1.0 compatible)
    [FreeType Support] =>
    [GIF Read Support] => 1
    [GIF Create Support] => 1
    [JPEG Support] => 1
    [PNG Support] => 1
    [WBMP Support] => 1
    [XPM Support] =>
    [XBM Support] => 1
    [WebP Support] => 1
    [BMP Support] => 1
    [JIS-mapped Japanese Font Support] =>
)

Эта система работает на Debian 8.

--отредактировано 2--

Используя в качестве основы проект alo-malbarez и класс для извлечения кадров из GIF's Я сделал проект с более чистым и понятным классом экстрактора для людей, которым нужны определенные фрагменты GIF. В этом проекте вместе оба класса, чтобы сделать конвертер GIF в WEBP, у него все еще есть ошибки, но я работаю над их исправлением.

 11
Author: Solrac, 2018-04-25

2 answers

Будет отсутствовать тема того, как вы извлекаете кадры из gif, но для этого есть ссылка в конце

Вот PdC о том, как собрать анимированный webp в php, используя только GD (с поддержкой webp)

Часть 1: кодирование каждого кадра

1) frame преобразуется в webp с GD и захватывается в переменную с ob_start

2) мы отбрасываем заголовок fileformat из этого потока и сохраняем frameData (начинается с " VP8 " и идет до конца) в array (buffer) рядом с info ширина, высота и продолжительность кадра

3) возвращается к 1, пока больше нет фреймов

Примечание: чтобы изменить качество сжатия изменить строку

imagewebp($image);

Поставив, например,

imagewebp($image, null, 75);

null предназначен для того, чтобы вы не записывали файл

Часть 2: Собрать анимированный webp в соответствии со спецификацией https://developers.google.com/speed/webp/docs/riff_container

An animated image with EXIF metadata may look as follows:

RIFF / WEBP

+- VP8X (descriptions of features used)
+- ANIM (global animation parameters)
+- ANMF (frame1 parameters + data)
+- ANMF (frame2 parameters + data)
+- ANMF (frame3 parameters + data)
+- ANMF (frame4 parameters + data)
+- EXIF (metadata) <- opcional

0) open stream или собрать в var

1) заголовок fileformat (RIFF+filesize+WEBP)

1. а) вычислить filesize (uint32) filesize - это общее количество байтов файла -8 заголовка или общее количество байтов всех chunks + 4

2) chunk header VP8X

2. а) вычислить chunksize (uint32) = 10

2. b) поместите биты альфа и анимации в 1

2. c) кодировать ширину и высоту canvas в uint24 (-1)

3) chunk header ANIM

3. а) вычислить chunksize (uint32) = 6

3. b) цвет фона: BGRA (0,0,0,0)

3. c) loop count (uint16) 0=infinityandbeyond

4) для каждого кадра, сохраненного в массиве (буфер), часть 1.2

4. а) chunk header ANMF

4. b) вычислить chunksize (uint32) = 16 + всего байтов в frameData

4. c) происхождение х, у кадра (uint24) (/2)

4. d) ширина и высота кадра (uint24) (-1)

4. e) продолжительность миллисекунд (uint24)

4. f) reserved (6 бит) + alpha blending (1 бит) + отбросить frame (1 бит)

4. g) данные кадра, сохраненные в буфере, часть 1.2

5) save to disk или close stream

#!/usr/bin/env php
<?php

function toUint32($n){
  $ar = unpack("C*", pack("L", $n));
  return $ar;
}
function toUint24($n){
  $ar = unpack("C*", pack("L", $n));
  array_pop($ar);
  return $ar;
}
function toUint16($n){
  $ar = unpack("C*", pack("S", $n));
  return $ar;
}

function bytesToString($bytes){
  return implode(array_map("chr", $bytes));
}

function binaryToBytes($bits){
  $octets = explode(' ', $bits);
  return array_map("bindec", $octets);
}

$oWidth=120;
$oHeight=20;

$frameArray = [];

function getFrameData($image, $msec){
  $w = imagesx($image);
  $h = imagesy($image);

  ob_start();
  imagewebp($image);
  if (ob_get_length() % 2 == 1) :
    echo "\0";
  endif;
  $image_data = ob_get_contents();
  ob_end_clean();
  $frameData = substr($image_data, strpos($image_data, "VP8 "));
  return Array(
    "frameData" => $frameData,
    "duration" => bytesToString(toUint24($msec)),
    "width" => bytesToString(toUint24($w - 1)),
    "height" => bytesToString(toUint24($h -1 )),
  );
}

// Create a blank image and add some text
$im = imagecreatetruecolor($oWidth, $oHeight);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5,  'WebP with PHP', $text_color);

$frameArray[] = getFrameData($im, 70);
imagedestroy($im);

// Create a blank image and add some text
$im = imagecreatetruecolor($oWidth, $oHeight);
$text_color = imagecolorallocate($im, 14, 233, 91);
imagestring($im, 1, 5, 5,  'WebP with PHP', $text_color);

$frameArray[] = getFrameData($im, 70);
imagedestroy($im);

// create new WEBP
$fileWEBP = "";
$fileHeader = "";
$fileContents = "";

// Chunk HEADER VP8X
$fileContents .="VP8X";
$headChunkSize = bytesToString(toUint32(10));
// bit flags Rsv|I|L|E|X|A|R|                   Reserved
$oVP8XflagsBin = "00010010 00000000 00000000 00000000";
$oVP8Xflags = bytesToString(binaryToBytes($oVP8XflagsBin));
$oCanvasSize = bytesToString(toUint24($oWidth-1)).bytesToString(toUint24($oHeight-1));
$fileContents .= $headChunkSize. $oVP8Xflags. $oCanvasSize;

// Chunk HEADER ANIM
$fileContents .="ANIM";
$animChunkSize = bytesToString(toUint32(6));
// loop count 16bits, 0 = infinito
// bytesToString(toUint16(0));
$oLoopCount = str_repeat(chr(0), 2);
// 32bits BGRA, Blue Green Red Alpha (0,0,0,0)
$oBackGround = str_repeat(chr(0), 4);
$fileContents .= $animChunkSize . $oBackGround . $oLoopCount;

foreach ($frameArray as $frame) :
  // Chunk HEADER ANMF
  $fileContents .="ANMF";
  $frameDataChunkSize = bytesToString(toUint32(strlen($frame['frameData'])+16));
  // frame origin X Y
  // bytesToString(toUint24(originX)) . bytesToString(toUint24(originY))
  // (0,0)
  $fOrigin = str_repeat(chr(0), 6);
  // frame size (uint24) width-1 , (uint24) height-1
  $fSize = $frame['width'].$frame['height'];
  // frame duration in miliseconds (uint24)
  $fDuration = $frame['duration'];
  // frame options bits
  // reserved (6 bits) + alpha blending (1 bit) + descartar frame (1 bit)
  $fFlagsBin = "00000010";
  $fFlags = bytesToString(binaryToBytes($fFlagsBin));
  // chunk payload
  $fileContents .= $frameDataChunkSize.$fOrigin.$fSize.$fDuration.$fFlags.$frame['frameData'];
endforeach;

// calculate Size and build file header
$fileSize = bytesToString(toUint32(strlen($fileContents)+4));
$fileHeader = "RIFF".$fileSize."WEBP";
$fileWEBP = $fileHeader.$fileContents;

file_put_contents('animated.webp',$fileWEBP);

Ссылка РЕПО на случай, если вы хотите расширить его или сделать вилку с частью извлечения gif frames

Https://github.com/aloMalbarez/php-GD-Animated-webp

Чтобы извлечь кадры из gif, вы можете использовать это класс

Https://github.com/jacoka/GIFDecoder

Волна foreach ( $gifDecoder -> GIFGetFrames ( ) as $frame )

Edit я поместил ссылку на класс GIFDecoder оригинального автора

 1
Author: alo Malbarez, 2018-05-07 00:47:48

На самом деле мало информации, мне было трудно найти это:

Https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php

Примеры также есть в официальной документации PHP:

Http://php.net/manual/es/function.imagewebp.php http://php.net/manual/es/function.imagecreatefromgif.php

Но я суммирую первую ссылку (которая интересна) для вашего случая в в частности:

<?php
    $im = imagecreatefromgif($filepath);
    imagewebp($im, $resizedFilepath);
    imagedestroy($im);
?>

Затем у вас есть возможность использовать команду типа оболочки с двоичным файлом типа: https://developers.google.com/speed/webp/docs/gif2webp

Хотя я предупреждаю вас, что формат плохо поддерживается, и я не знаю, насколько он надежен, как я исследовал, я бы сказал, что он находится в очень зеленой фазе развития.

 0
Author: track3r, 2018-05-05 11:44:22