floriankarsten / kirby3-vipsthumbnail

Kirby3 vipsthumbnail thumbnail driver
MIT License
11 stars 0 forks source link

Add Extra Options to Vips? #2

Open shoesforindustry opened 4 years ago

shoesforindustry commented 4 years ago

Hi Florian, I have been playing some more with your awesome vips driver and adding in some extra options...

    ‘optimize_coding’=> true
    ‘optimize_scans’ => true
    ‘trellis_quant’ => true
    ‘quant_table’  => 3 #same setting used by mozjpeg I believe
    ‘overshoot_deringing’ => true

  1. If you take a 1993 × 2491px jpg 1.2mb original colour photo with the options below you get a file of size of 231 KB - pretty good!

    ‘interlace’     => true
    ‘strip’         => true
    ‘quality’       => 50
  2. But with the options below you get a file of 182 KB, a handy saving, at the expense of some processing time.

    ‘interlace’     => true
    ‘strip’         => true
    ‘quality’       => 50
    ‘optimize_coding’=> true
    ‘optimize_scans’ => true
    ‘trellis_quant’ => true
    ‘quant_table’  => 3 #same setting used by mozjpeg I believe
    ‘overshoot_deringing’ => true
  3. Using mozjpeg on the same file at the same quality you get a file of 179 kb

  4. A webp at Q50 is quite a bit less at 148kb but it maybe perceptually a bit different on photos - but maybe OK?

(Of course if you go the jpg route only (no webp) then there is only one set of images in multiple sizes… which may or may not be important.)

At least for photos, these extra options look like a good compromise between image quality and image size.

*It would of course be nice to use mozjpeg with vips rather than the default (jpeg or jpeg-turbo?). But the install option for mozjpeg with vips on homebrew has been removed… and I’m not sure I want to build vips from scratch with mozjpeg…I may do it if the lock down continues much longer...

So Florian what are your thoughts on these extra options?

OSX so all installed via brew

iskrisis commented 4 years ago

Hi! Sorry i am not really sure what you are asking me to do. Do you want to change default settings to something else? Are there options that don't work if you put them in the config file? Thanks!

shoesforindustry commented 4 years ago

Hi Florian, sorry to be unclear. I was just saying that these would be good options to add to your plugin as I have done on my local copy.

iskrisis commented 4 years ago

Hi, i've tried to address it here. https://github.com/floriankarsten/kirby3-vipsthumbnail/blob/dev/Vipsthumbnail.php

Could you try if that works with your options? There is now array $allowed options that allows adding one word output options i am not sure if some other options need parameters. We need o gather all the required options to work.

iskrisis commented 4 years ago

@shoesforindustry ?

shoesforindustry commented 4 years ago

Very sorry Florian, I've been away from the Internet, I shall have a look tomorrow and report back. Thank you for your efforts.

shoesforindustry commented 4 years ago

OK, I have managed to have a quick look this morning and whilst the driver still works it did not as compress as much as my version. I changed my Vipsthumbnail.php in a slightly different way to you and add it below as well as my config which shows the options. I'm sorry but I am going to have intermittent internet access, so I may not respond right away, I hope this is OK ;-)

<?php

namespace Floriankarsten;

use Exception;

/**
 * TBA
 *
 */
class Vipsthumbnail
{
    public $options = [];
    public $src;
    public $dst;

    public function __construct(string $src, string $dst, array $options = [])
    {
        $this->src = $src;
        $this->dst = $dst;
  // option merging: plugin defaults > config options > options from image    
    $this->options = array_merge($this->defaults(), option('thumbs'), array_filter($options));

        if ($this->options['log'] === true) {
            // $this->logMessage(json_encode($this->defaults()));
            // $this->logMessage(json_encode(option('thumbs')));
            // $this->logMessage(json_encode(array_filter($options)));
            $this->logMessage(json_encode($this->options));
        }
    }

    protected function defaults(): array
    {
        return [
            'bin'           => 'vipsthumbnail',
            'interlace'     => false,
            'autoOrient'    => true,
            'crop'          => false,
            'height'        => null,
            'strip'         => true,
            'quality'       => 90,
            'width'         => null,
            'optimize_coding'=> false,
            'optimize_scans' => false,
            'trellis_quant' => false,
            'quant_table'  => null,
            'overshoot_deringing' => false,
            'log'           => false
        ];
    }

    function logMessage($log_msg)
    {
        // dumblog from stack overflow please no judging
        if (!empty($this->options['logdir'])) {
            $log_filename = $this->options['logdir'];
        } else {
            $log_filename = __DIR__ . "/logs";
        }

        if (!file_exists($log_filename)) {
            // create directory/folder uploads.
            mkdir($log_filename, 0777, true);
        }
        $log_file_data = $log_filename . '/vipthumbnaillog_' . date('d-M-Y') . '.log';
        file_put_contents($log_file_data, $log_msg . "\n", FILE_APPEND);
    }

    protected function autoOrient()
    {
        if ($this->options['autoOrient'] === true) {
            return '--rotate';
        }
    }

    protected function convert(): string
    {
        return sprintf($this->options['bin'] . ' %s', $this->src);
    }

    protected function interlace()
    {
        if ($this->options['interlace']) {
            return 'interlace';
        }
    }

  protected function optimize_coding()
    {
        if ($this->options['optimize_coding']=== true) {
            return 'optimize-coding';
        }
  }
  protected function trellis_quant()
    {
        if ($this->options['trellis_quant']=== true) {
            return 'trellis-quant';
        }
  }

  protected function optimize_scans()
    {
        if ($this->options['optimize_scans']=== true) {
            return 'optimize-scans';
        }
  }

  protected function quant_table()
    {
        if ($this->options['quant_table']) {
      return 'quant_table=' . $this->options['quant_table'];
        }
  }

  protected function overshoot_deringing()
    {
        if ($this->options['overshoot_deringing']) {
        return 'overshoot-deringing';
        }
  }

    public function process(): string
    {

        $command = [];
        $outputOptions = [];

        $outputOptions[] = $this->strip();
        $outputOptions[] = $this->optimize_coding();
        $outputOptions[] = $this->optimize_scans();
        $outputOptions[] = $this->trellis_quant();
        $outputOptions[] = $this->quant_table();
        $outputOptions[] = $this->interlace();
        $outputOptions[] = $this->quality();
        $outputOptions[] = $this->overshoot_deringing();

        $outputOptions = implode(',', array_filter($outputOptions));

        $command[] = $this->convert();
        $command[] = $this->autoOrient();
        $command[] = $this->resize();

v$command[] = $this->save($outputOptions);
        // remove all null values and join the parts
        $command = implode(' ', array_filter($command));

        if ($this->options['log'] === true) {
            $this->logMessage($command);
        }

        // try to execute the command
        exec($command, $output, $return);

        // log broken commands
        if ($return !== 0) {
            throw new Exception('The Vips convert command could not be executed: ' . $command);
        }

        return $this->dst;
    }

    protected function save($outputOptions): string
    {
        return sprintf('-o %s[%s]', $this->dst, $outputOptions);
    }

    protected function quality(): string
    {

        return 'Q=' . $this->options['quality'];
    }

    protected function resize(): string
    {
        // normalize crop
        // here it should be possible to take $this->options['crop'] "center" etc to make crops by direction
        // dirty weak check $this->options['crop'] == true if its either true or string "center" etc
        if (is_string($this->options['crop'])) {
            $this->options['crop'] = true;
        }

        // simple resize
        if ($this->options['crop'] === false) {
            return sprintf('--size %sx%s', $this->options['width'], $this->options['height']);
        }

        if ($this->options['crop'] === true && $this->options['height'] === 0) {
            // assume crop to square like ->crop(100)
            return sprintf('--size %sx%s --smartcrop attention', $this->options['width'], $this->options['width']);
        }

        if ($this->options['crop'] === true) {
            // assume crop with exact sizes
            return sprintf('--size %sx%s --smartcrop attention', $this->options['width'], $this->options['width']);
        }
    }

    protected function strip()
    {
        if ($this->options['strip']) {
            return 'strip';
        }
    }
}

And in my config.php I have this, I've listed the full set of options:

'thumbs' => [
  'driver' => 'vipsthumbnail',
  'bin' => '/usr/local/bin/vipsthumbnail',
  'interlace' => true,
  'autoOrient' => true,
  'crop' => false,
  'height' => null,
  'strip' => true,
  'quality' => 90,
  'width' => null,
  'optimize_coding' => true,
  'optimize_scans' => true,
  'trellis_quant' => true,
  'quant_table' => 3,
  'overshoot_deringing' => true,
  'log' => true
],