Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I save a JPG image in 4:2:0 colorspace with Imagick?

Tags:

php

imagick

Google Pagespeed can convert RGB images to the YUV 4:2:0 colourspace. I would like to do the same in PHP using imagick.

Briefly, on why 4:2:0 is desired:

"This filter reduces the color sampling of jpeg images to 4:2:0. Human vision is much more sensitive to changes in brightness than changes in hue or saturation, and 4:2:0 color sampling allows us to keep brightness data while reducing the amount of hue and saturation data by 3/4. This can significantly reduce the image size while having only a small effect on perception."

I have tried to change the colorspace with imagemagick to YUV but it is not working at all well! The white backdrop goes to green and the rest of the colours are wrong, plus inverted luminance.

The following code is useless, but it shows a few of the things I have tried.

With the Google format created by mod_pagespeed the resultant images look fine in a browser. That is what I am hoping to achieve, images that are for the web.

    $image_info = getimagesize($source_file);
    $im = new Imagick();
    $im->readImage($source_file);
    $profiles = $im->getImageProfiles('*', false);
    $has_icc_profile = (array_search('icc', $profiles) !== false);
    if ($has_icc_profile === false) {
        $icc_srgb = file_get_contents(Mage::getBaseDir('var') . DS . 'metodo' . DS . 'demo_data' . DS . 'AdobeRGB1998.icc');
        $im->profileImage('icc', $icc_srgb);
        unset($icc_srgb);
    }
    //$im->setImageColorspace(1);
    $im->setInterlaceScheme(Imagick::INTERLACE_PLANE);
    $im->setImageCompressionQuality(85);
    //$im->stripImage(); 
    $im->setImageColorspace(11);
    $im->thumbnailImage($this->_imageSrcWidth, $this->_imageSrcHeight);
    //$im->negateImage(false, Imagick::CHANNEL_ALL);
    $im->stripImage();
    $im->writeImage($fileName);
like image 866
Henry's Cat Avatar asked Dec 08 '22 05:12

Henry's Cat


2 Answers

I was intrigued by the lack of documentation on the Imagick::setSamplingFactors method (see my comment), so I tried to figure it out.

Using the Imagick::identifyImage method it became clear that the Imagick library doesn't use the 4:2:0 notation for chroma subsampling, but something like '1x1,1x1,1x1'. At http://www.ftgimp.com/help/C/filters/jpeg.html (20180608: no longer available, archived here) it's made clear that '1x1,1x1,1x1' translates to 4:4:4, and '2x2,1x1,1x1' to 4:2:0. As the Imagick::setSamplingFactors method requires an array as its argument I tried the following, which worked successfully:

$img = new Imagick($source_file)
$img->setSamplingFactors(array('2x2', '1x1', '1x1'));
$im->writeImage($fileName);
like image 158
Plenka Avatar answered Dec 11 '22 09:12

Plenka


Maybe not technically an answer, but too awkward to format as a comment, and it may help find the answer.

You can do this sort of thing that may provide some insight as to how to use parameters and what they seem to do:

convert input.jpg -colorspace YUV out.jpg
convert input.jpg -colorspace YUV -sampling-factor 4:2:2 out442.jpg
convert input.jpg -colorspace YUV -sampling-factor 4:2:0 out420.jpg

then you can see the resulting filesizes for a variety of settings. It does seem that the output filesizes correlate pretty well with the sampling, and that IM's default sampling is 4:4:4 since that matches the size when I don't provide any -sampling-factor:

ls -l out*
-rw-r--r--  1 mark  staff  105563 26 Nov 10:23 out.jpg
-rw-r--r--  1 mark  staff   77230 26 Nov 10:23 out410.jpg
-rw-r--r--  1 mark  staff   81755 26 Nov 10:24 out411.jpg
-rw-r--r--  1 mark  staff   82835 26 Nov 10:23 out420.jpg
-rw-r--r--  1 mark  staff  105563 26 Nov 10:25 out444.jpg

This link may help.

like image 30
Mark Setchell Avatar answered Dec 11 '22 09:12

Mark Setchell