Шреддер анимированных GIF на PHP

Илья777

Новичок
Здравствуйте, Уважаемые!
Задача довольно специфическая. Делаю карту для браузерной игры, состоящую из квадратиков со сторонами 100x100 пикселов.
В случаях, если изображение статическое - прекрасно работает скрипт,использующий GD:

PHP:
 $i = 0;
    //- Путь к файлу карты
    $im = imagecreatefromjpeg("125.jpeg");
    for ($x=0;$x<58;$x++) // количество клеток вертикаль
    for ($y=0;$y<58;$y++){ // количество клеток  горизонталь
    //- Тут и ниже измени путь куда сохранять будешь, если файла не будет существовать - будет создание нового)
    if (!file_exists($_SERVER["DOCUMENT_ROOT"]."/map2/day2/".($x)."_".($y).".jpg"))
    {
    $id = imagecreatetruecolor(100,100);
    imagecopy($id,$im,0,0,100*$x,$y*100,100,100);
    Imagejpeg($id,$_SERVER["DOCUMENT_ROOT"]."/images/map2/day2/".($x)."_".($y).".jpg",100);
    }
    $i++;
    }
    //- тест
    echo $i;

Но, в моём случае, мне необходимо сделать то-же самое - но с большой анимированной картинкой.
Попробовал реализовать сходный функционал с помощью Imagick:
PHP:
<?php

    set_time_limit(0);
    ini_set("memory_limit", "24G");
    $i = 0;
    //- Путь к файлу карты
    $imagick = new Imagick('source.gif');
    for ($x=0;$x<28;$x++) // количество клеток вертикаль
    for ($y=0;$y<51;$y++){ // количество клеток  горизонталь
    //- Тут и ниже измени путь куда сохранять будешь, если файла не будет существовать - будет создание нового)
    // Разбить gif анимацию на изображение
    $imagick = $imagick->coalesceImages();
    if (!file_exists("".($x)."_".($y).".gif"))
    {
    // Обрезка изображений одно за другим                
    do {
         // Первые две пары-это ширина и высота урожая, а третья и четвертая-начальные точки
         // Например, если последние два параметра равны 0,0, это означает, что обрезка начнется с верхнего левого угла
         $imagick->cropImage(100,100,$x,$y);
         // После обрезки на изображении остается некоторое пустое пространство, так как холст изображения остался того же размера
         // Эта функция изменяет размер холста изображения и таким образом устраняет пустое пространство
         $imagick->setImagePage(100,100,$x,$y);
                       
    } while ($imagick->nextImage());

    // Собираем анимацию заново            
    $imagick = $imagick->deconstructImages();  
    $imagick->writeImage("gif:".($x)."_".($y).".gif");
    }
    $i++;
    }
    //- тест
    echo $i;
   
?>
Получается полная ахинея....


Уважаемые, подскажите, пожалуйста, что я сделал не так?
 

WMix

герр M:)ller
Партнер клуба
PHP:
// пустая карта
$map = new Imagick();
$map->setFormat('gif');

// гифка
$gif = new Imagick('giphy.gif');
$frames = $gif->coalesceImages();

foreach ($frames as $frame) {
    
    // каждый фрейм уменьшаем до размера 50x50
    $frame->scaleImage(50, 50, 0, 0);
    $frame->thumbnailImage(50, 50);
    $frame->setImagePage(50, 50, 0, 0);
    
    // создаем новый итоговый фрейм
    $mapFrame = new Imagick();
    $mapFrame->newImage(500, 250, new ImagickPixel('black'));
    
    // распределяем фреймы гифки на новый итоговый фрейм
    for($y = 0; $y<5; $y++){
        for($x = 0; $x<10; $x++){
            $mapFrame->compositeImage($frame, Imagick::COMPOSITE_DEFAULT, $x*50, $y*50);
        }
    }
    // задержка
    $mapFrame->setImageDelay($frame->getImageDelay());
    
    // складывем в карту
    $map->addImage($mapFrame);
    $map->nextImage();
}
$map->writeImages('map.gif', true);
 

Илья777

Новичок
Исходный файл - анимированный gif 5120x2880 пикселов.
Нужно его разбить на квадратики со сторонами 100 px и генерацией названий вида '.$nomerstroki.'_'.$nomerstolbca.'.gif
Потом в самой игре карта собирается средствами Js.

Другими словами, я пытался переписать функционал вот этого кода - для анимированного исходного gif и на выходе анимированных-же часей карты:
PHP:
$i = 0;
    //- Путь к файлу карты
    $im = imagecreatefromjpeg("125.jpeg");
    for ($x=0;$x<58;$x++) // количество клеток вертикаль
    for ($y=0;$y<58;$y++){ // количество клеток  горизонталь
    //- Тут и ниже измени путь куда сохранять будешь, если файла не будет существовать - будет создание нового)
    if (!file_exists($_SERVER["DOCUMENT_ROOT"]."/map2/day2/".($x)."_".($y).".jpg"))
    {
    $id = imagecreatetruecolor(100,100);
    imagecopy($id,$im,0,0,100*$x,$y*100,100,100);
    Imagejpeg($id,$_SERVER["DOCUMENT_ROOT"]."/images/map2/day2/".($x)."_".($y).".jpg",100);
    }
    $i++;
    }
    //- тест
    echo $i;
GD тоже может работать с GIF, но анимация не поддерживается и на выходе только корректно порезанные части без анимации (только первый фрейм).
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
PHP:
$imagick = new Imagick('giphy.gif');
$frames = $imagick->coalesceImages();

for($y = 0; $y<16; $y++){
    for($x = 0; $x<16; $x++){
        
        $out = new Imagick();
        $out->newImage(16, 16, new ImagickPixel('black'));
        
        foreach ($frames as $frame) {
            $region = $frame->getImageRegion(16, 16, $x*16, $y*16);
            $region->setImagePage(16, 16, 0, 0);
            $out->addImage($region);
            $out->nextImage();
        }
        $out->writeImages('result/'.$x.'_'.$y.'.gif', true);
        
    }
}
 

ksnk

прохожий
С анимированными gif - не работает gd. Проблема в том, что gif - проприетарный формат, и GD брезгует с ним работать... Или в лом разработчикам... или фазы луны... Проблема решается множеством способов. например так - только ресайз, но, наверное, можно и допилить... или вызовом консольной ImageMagic (пост выше).
 

Илья777

Новичок
PHP:
<?php
    set_time_limit(0);
    ini_set("memory_limit", "24G"); 

    $imagick = new Imagick('source.gif');
    $frames = $imagick->coalesceImages();

    for($y = 0; $y<52; $y++){
        for($x = 0; $x<29; $x++){
         
            $out = new Imagick();
            $out->newImage(100, 100, new ImagickPixel('black'));
         
            foreach ($frames as $frame) {
                $region = $frame->getImageRegion(100, 100, $x*100, $y*100);
                $region->setImagePage(100, 100, 0, 0);
                $out->addImage($region);
                $out->nextImage();
            }
            $out->writeImages('result/'.$x.'_'.$y.'.gif', true);
         
        }
    }
?>
Этот вариант почти рабочий. Благодарю! :)
Как теперь избавиться от черноты в первом кадре или удалить первый кадр?
1434



UPDATE!!!
Решение найдено. Вот итоговый рабочий код!

PHP:
<?php
    set_time_limit(0);
    ini_set("memory_limit", "24G");  

    $imagick = new Imagick('source.gif');
    $frames = $imagick->coalesceImages();

    for($y = 0; $y<52; $y++){
        for($x = 0; $x<29; $x++){
           
            $out = new Imagick();
            //$out->newImage(100, 100, new ImagickPixel('black'));
           
            foreach ($frames as $frame) {
                $region = $frame->getImageRegion(100, 100, $x*100, $y*100);
                $region->setImagePage(100, 100, 0, 0);
                $out->addImage($region);
                $out->nextImage();              
            }
            $out->writeImages('result/'.$x.'_'.$y.'.gif', true);
            $out->clear();
            $out->destroy();          
        }
    }
?>
Сердечно благодарю WMix за найденное решение!
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
ну слушай, уж найди сам эту строчку, не надо просто капипастить всякую хрень без понимания
 

Илья777

Новичок
ну слушай, уж найди сам эту строчку, не надо просто капипастить всякую хрень без понимания
Уже всё нашёл и исправил. Теперь запустил в консоли файл через CLI и жду. :))) Ждать долго. Файл большой и 60 кадров (30 кадров в секунду)
Рендер локации в Unity3d. :)

Всё чудесно режется.



Ещё раз благодарю!
 
Последнее редактирование:

Илья777

Новичок
прилагаю готовый скрипт с индикацией прогресса - может кому-нибудь пригодится. запускать через консоль.

PHP:
<?php
    set_time_limit(0);
    ini_set("memory_limit", "24G"); 
    include ('../connect.php');

        class Colors {
        private $foreground_colors = array();
        private $background_colors = array();

        public function __construct() {
            // Set up shell colors
            $this->foreground_colors['black'] = '0;30';
            $this->foreground_colors['dark_gray'] = '1;30';
            $this->foreground_colors['blue'] = '0;34';
            $this->foreground_colors['light_blue'] = '1;34';
            $this->foreground_colors['green'] = '0;32';
            $this->foreground_colors['light_green'] = '1;32';
            $this->foreground_colors['cyan'] = '0;36';
            $this->foreground_colors['light_cyan'] = '1;36';
            $this->foreground_colors['red'] = '0;31';
            $this->foreground_colors['light_red'] = '1;31';
            $this->foreground_colors['purple'] = '0;35';
            $this->foreground_colors['light_purple'] = '1;35';
            $this->foreground_colors['brown'] = '0;33';
            $this->foreground_colors['yellow'] = '1;33';
            $this->foreground_colors['light_gray'] = '0;37';
            $this->foreground_colors['white'] = '1;37';

            $this->background_colors['black'] = '40';
            $this->background_colors['red'] = '41';
            $this->background_colors['green'] = '42';
            $this->background_colors['yellow'] = '43';
            $this->background_colors['blue'] = '44';
            $this->background_colors['magenta'] = '45';
            $this->background_colors['cyan'] = '46';
            $this->background_colors['light_gray'] = '47';
        }

        // Returns colored string
        public function getColoredString($string, $foreground_color = null, $background_color = null) {
            $colored_string = "";

            // Check if given foreground color found
            if (isset($this->foreground_colors[$foreground_color])) {
                $colored_string .= "\033[" . $this->foreground_colors[$foreground_color] . "m";
            }
            // Check if given background color found
            if (isset($this->background_colors[$background_color])) {
                $colored_string .= "\033[" . $this->background_colors[$background_color] . "m";
            }

            // Add string and end coloring
            $colored_string .=  $string . "\033[0m";

            return $colored_string;
        }

        // Returns all foreground color names
        public function getForegroundColors() {
            return array_keys($this->foreground_colors);
        }

        // Returns all background color names
        public function getBackgroundColors() {
            return array_keys($this->background_colors);
        }
    }

  
    $imagick = new Imagick('cropped.gif');
    $width_s = $imagick->getImageWidth();
    $height_s = $imagick->getImageHeight();
  
    $height = floor($height_s/100);
    $width = floor($width_s/100);
  
  
  
    $frames = $imagick->coalesceImages();
    $colors = new Colors();
    for($y = 0; $y<$height; $y++){
        for($x = 0; $x<$width; $x++){                 
            $out = new Imagick();         
            foreach ($frames as $frame) {
                $region = $frame->getImageRegion(100, 100, $x*100, $y*100);
                $region->setImagePage(100, 100, 0, 0);
                $out->addImage($region);
                $out->nextImage();             
            }
            $out->writeImages('/полный_путь_к_директории/'.$x.'_'.$y.'.gif', true); 
            popen('clear', 'w');
            $gotovosey4as = DB::run('SELECT COUNT(newid) FROM `nature`;')->fetchColumn();
            $stoproc = $height * $width;
            $procentovsey4as = number_format(($gotovosey4as/$stoproc) * 100,2);
            echo $colors->getColoredString(''.$x.'_'.$y.'.gif   Готово на:'.$procentovsey4as.'% ('.$gotovosey4as.' файл из '.$stoproc.') ', 'light_green','white');
            $out->clear();
            $out->destroy();
            DB::run('INSERT INTO `nature` (`newid`,`x`,`y`,`city`) VALUES (NULL,?,?,?);',[$x,$y,iconv('utf-8','windows-1251','Земли Авалона')]);
        }     
    }
    ?>
 

WMix

герр M:)ller
Партнер клуба
Код:
composer require league/climate
PHP:
require_once('vendor/autoload.php');

$climate = new League\CLImate\CLImate; // !!!

$imagick = new Imagick('cropped.gif');
$width_s = $imagick->getImageWidth();
$height_s = $imagick->getImageHeight();

$height = floor($height_s/100);
$width = floor($width_s/100);

$progress = $climate->progress()->total($height*$width); // !!!

$frames = $imagick->coalesceImages();
$colors = new Colors();
for($y = 0; $y<$height; $y++){
    for($x = 0; $x<$width; $x++){              
        $out = new Imagick();      
        foreach ($frames as $frame) {
            $region = $frame->getImageRegion(100, 100, $x*100, $y*100);
            $region->setImagePage(100, 100, 0, 0);
            $out->addImage($region);
            $out->nextImage();          
        }
        $out->writeImages('/полный_путь_к_директории/'.$x.'_'.$y.'.gif', true);

        $progress->current($x*$y); // !!!

        $out->clear();
        $out->destroy();
        DB::run('INSERT INTO `nature` (`newid`,`x`,`y`,`city`) VALUES (NULL,?,?,?);',[$x,$y,iconv('utf-8','windows-1251','Земли Авалона')]);
    }  
}
 
Сверху