Сжатие PNG Sonata Media с альфа-каналом

SonataMediaBundle увеличивает размер изображения PNG со следующими настройками:

video_image:
    providers:
        - sonata.media.provider.image
    formats:
        medium: { width: 1306, quality: 100 }

Исходный размер изображения составляет 246 КБ (с той же шириной и высотой), а размер «измененного» изображения составляет 3 МБ. Это происходит из-за того, что quality: 100 устанавливает png_compression_level => 0.

Если я установлю quality: 0, размер PNG почти в порядке (и изображение выглядит очень хорошо), но сжатие JPG делает изображение похожим на импрессионизм.

Поэтому я решил это с помощью пользовательского изменения размера изображений PNG.

Но при установке png_compression_level => 9 размер сжатого изображения все равно не идеален, 664Кб.

Преобразование изображения PNG в PNG8 решило эту проблему, и размер стал очень приличным - 233 КБ (то есть даже меньше, чем исходное изображение), но у меня возникли некоторые проблемы с альфа-каналом.

Большинство изображений с прозрачностью сжаты нормально, но некоторые из них были повреждены:

исходное изображение (оно белое на прозрачном фоне)

белая рамка

сжатое изображение

такой же, но меньше

Это мой пользовательский ресайзер (код немного уродлив, потому что это всего лишь черновик):

<?php

namespace AppBundle\Resizer;

use Gaufrette\File;
use Imagine\Gd\Image;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Resizer\SimpleResizer;

class PngCustomResizer extends SimpleResizer
{
    public function resize(MediaInterface $media, File $in, File $out, $format, array $settings)
    {
        /** @var Image $image */
        $image = $this->adapter->load($in->getContent());

        $thumbnail = $image->thumbnail($this->getBox($media, $settings), $this->mode);

        $resource = $thumbnail->getGdResource();

        $width = $thumbnail->getSize()->getWidth();
        $height = $thumbnail->getSize()->getHeight();

        // convert to png8 with alpha
        $img = imagecreatetruecolor($width, $height);
        $bga = imagecolorallocatealpha($img, 0, 0, 0, 127);
        imagecolortransparent($img, $bga);
        imagefill($img, 0, 0, $bga);
        imagecopy($img, $resource, 0, 0, 0, 0, $width, $height);
        imagetruecolortopalette($img, false, 255);
        imagealphablending($img, false);
        imagesavealpha($img, true);

        $optimizedImage = new Image($img, $image->palette(), $image->metadata());

        // set quality 0 to set png compression = 9
        $content = $optimizedImage->get($format, ['quality' => 0]);

        $out->setContent($content, $this->metadata->get($media, $out->getName()));
    }
}

Что-то не так с моим кодом, или, может быть, я должен использовать другой способ?

Все, что я хочу, это изменить размер файлов JPG и PNG без увеличения размера и без заметной потери качества.

ОБНОВЛЕНИЕ

  1. imagetruecolortopalette сбрасывает альфа-значения на 0 или 127, без значений 2-126. Таким образом, края изображения теряют свою гладкость. Я пытаюсь исправить это, устанавливая старое альфа-значение для каждого пикселя, но пока не добился успеха.

  2. артефакты на вышеупомянутом изображении проявляются только при уменьшении размера (в данном конфиге "большой палец")

    formats:
        wide: { width: 1306, quality: 95}
        mobile: { width: 640, quality: 95}
        thumb: { height: 50 , quality: 95}
    

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


person Pavel Alazankin    schedule 17.01.2018    source источник
comment
Прозрачность скрывает это, но кажется, что в этих пикселях все-таки есть какие-то данные. Если вы очистите все каналы с помощью полностью прозрачной альфы, эти артефакты пониженной дискретизации должны исчезнуть. Вы (вероятно) могли бы настроить свой код для этого или использовать внешний инструмент.   -  person Jongware    schedule 17.01.2018
comment
не могли бы вы уточнить, что вы имеете в виду здесь, если вы закрываете все каналы полностью прозрачной альфой? не уверен, что хорошо понимаю   -  person Pavel Alazankin    schedule 17.01.2018
comment
См. это обсуждение на форуме ImageMagick: imagemagick.org/discourse-server/ viewtopic.php?t=13746 и этот (также ImageMagick) обзор: imagemagick. org/Usage/formats/#png_compress. Оттуда: изображение PNG сохранит цвет полностью прозрачных пикселей. Это даже при том, что вы не можете видеть, что прозрачность имеет цвет, и PNG сохраняет эти данные.   -  person Jongware    schedule 17.01.2018


Ответы (1)


Я пробовал разные способы с функциями GD, но не получил удовлетворительного результата.

Поэтому лучший способ, который я нашел, - использовать Imagick вместо GD:

services.yml:

    sonata.media.resizer.custom:
        class: AppBundle\Resizer\CustomResizer
        arguments: [@sonata.media.adapter.image.imagick, 'outbound', @sonata.media.metadata.proxy]

AppBundle\Resizer\CustomResizer:

<?php

namespace AppBundle\Resizer;

use Sonata\MediaBundle\Resizer\SimpleResizer;

class PngCustomResizer extends SimpleResizer
{
}

config.yml:

sonata_media:
    providers:
        image:
            resizer: sonata.media.resizer.custom
person Pavel Alazankin    schedule 23.01.2018