Сохраните изображение в оперативной памяти и работайте с ним с помощью ImageMagick

Я работаю над проектом, в котором клиент может создать динамическое изображение, объединив разные слои (изображения) в одно изображение, а затем показать это сгенерированное изображение клиенту. В настоящее время мой код выглядит примерно так

$output = uniqid().'.jpg';
exec('convert -size 600x800 xc:white '.$output);
$layers = array(//array of about hundreds of PNG images);
foreach($layers as $key => $singleLayer)
{
    $layerIs = $singleLayer['image'];
    $positionsXY = explode(',', $singleLayer['xy']);
    exec('composite -geometry  +'.$positionsXY[0].'+'.$positionsXY[1].' '.$layerIs.' '.$output.' '.$output);
}
$base64 = base64_encode(file_get_contents($output));
unlink($output);
return 'data:image/jpeg;base64,'.$base64;

В приведенном выше коде я создаю белое фоновое изображение, а затем объединяю на нем несколько изображений PNG, таких изображений больше 100. При этом также сохраняется это изображение каждый раз при объединении одного слоя, а затем выводится окончательное изображение клиенту после удаления сгенерированное изображение. Все это занимает около 15 секунд.

Итак, мой вопрос: есть ли у ImageMagick опция, которая позволяет мне сохранять изображение в оперативной памяти вместо сохранения изображения на жестком диске, что, в свою очередь, может ускорить мою обработку?

Хотя я нашел этот http://www.imagemagick.org/discourse-server/viewtopic.php?t=20295, но не мог понять, как это реализовать.


person Airy    schedule 31.05.2017    source источник
comment
Вы можете попробовать использовать функцию магии изображений readImageBlob: php.net/manual/en/imagick .readimageblob.php и используйте метод __toString магического объекта изображения для вывода изображения в переменную.   -  person Jite    schedule 31.05.2017
comment
@Jite Я не разбираюсь в ImageMagick. Если вы не возражаете, можете ли вы написать небольшой код, чтобы я мог лучше его понять. И обратите внимание, что я использую команду exec() для работы с ImageMagick, а не с процедурным языком PHP. Таким образом, использование переменной в этом может создать некоторую путаницу :(   -  person Airy    schedule 31.05.2017
comment
Я бы начал с рекомендации использовать расширение php для imagemagick. Это возможно? Проверьте свой phpinfo() и посмотрите, установлен ли он. В зависимости от ответа, я попытаюсь дать вам некоторые дополнительные объяснения.   -  person Jite    schedule 31.05.2017
comment
@Jite Да, он установлен, так как у меня есть VPS, и я его установил   -  person Airy    schedule 31.05.2017


Ответы (1)


Самый простой способ использовать ImageMagick в php — использовать расширение и класс ImageMagick. . Я бы не рекомендовал использовать для этого функцию exec.

Итак, сначала преобразуйте изображение и сохраните его в переменной.
Вы говорите, что не привыкли к переменным, но по вашему коду кажется, что вы привыкли, вы можете просто этого не знать.
Например, ваш $output является переменной. Переменная содержит изображение после выполнения вывода (в случае успеха).

Теперь я не уверен на 100%, что содержит ваш массив слоев, если это изображения или пути к изображениям, но я ожидаю, что это его пути, поэтому я буду придерживаться этого предположения.

Ваш первый вызов exec выполняет преобразование, я не думаю, что это необходимо, потому что у вас нет входных данных, все, что вам нужно, это "белое изображение" из того, что я вижу.
Чтобы сделать это, создайте новый объект изображения. с и создайте новое изображение правильного размера и цвета:

$image = new Imagick();
$image->newImage(600, 800, "white");

Затем вы перебираете слои, как обычно, но вместо exec вы используете изображение imagemagick, созданное выше:

foreach($layers as $key => $singleLayer) {
    $layerIs = $singleLayer['image'];
    $positionsXY = explode(',', $singleLayer['xy']);
    // Each loop should load the image into a new imagemagick object, but we release it when the scope is exited.
    $layer = new IMagick($layerIs);
    $image->compositeImage($layer, Imagick::COMPOSITE_DEFAULT, $positionsXY[0], $positionsXY[1]);
}

Когда цикл завершится, переменная $image будет содержать составное изображение, которое вы можете вернуть по своему усмотрению.

$image->setImageFormat('jpg');
return "data:image/jpeg;base64,{base64_encode($image)}";

Примечание. Код не тестируется и пишется непосредственно в браузере. Так что, возможно, его придется переписать!

person Jite    schedule 31.05.2017
comment
Большой! Jite, ваш код отлично работает. Но почему это не имеет никакого значения с точки зрения производительности. Для завершения обработки требуются те же 15 секунд. - person Airy; 31.05.2017
comment
Я думаю, что настоящим узким местом является загрузка сотен слоев для использования в композиции изображения. Я не уверен, насколько это возможно оптимизировать, возможно, используя изображения меньшего размера или меньше изображений, но это во многом зависит от того, как они используются. Манипуляции с изображениями, как правило, довольно тяжелая работа, так что это может занять некоторое время, как бы вы ни занимались этим. - person Jite; 31.05.2017
comment
Если вы считаете, что ответ дал ответ на вопрос, отметьте его как правильный и/или проголосуйте за него! - person Jite; 31.05.2017
comment
Хм, ты прав, Джейт. Спасибо за вашу поддержку. - person Airy; 31.05.2017
comment
Любое время. Удачи с проектом! :) - person Jite; 31.05.2017
comment
Jite, как мы можем установить качество для нашего сгенерированного изображения? Я пробовал $image-›setCompressionQuality(50); перед base64_encode(), но это не работает - person Airy; 31.05.2017
comment
Я думаю, вам нужно использовать и это, и пометить его как jpeg, например: $img->setImageCompression(Imagick::COMPRESSION_JPEG); $img->setImageCompressionQuality(50); - person Jite; 31.05.2017
comment
Отличный Джейт. Большое спасибо. Это было действительно очень полезно - person Airy; 31.05.2017