libvips / php-vips

php binding for libvips
MIT License
615 stars 25 forks source link

Cannot profile a CMYK image. #217

Closed axkirillov closed 1 year ago

axkirillov commented 1 year ago

Hello. In imagemagick I can use the profile function to apply a profile to an image

$iccCmyk = \file_get_contents(__DIR__ . '/color_profiles/CoatedFOGRA27.icc');
$imagick->profileImage('icc', $iccCmyk);

I created a 2 tiff images using php-vips in the cmyk colorspace, one of them retains a profile (that comes from vips I guess?), the other one does not. Also I created 2 jpg versions of the same image (with and without profile).

$width = 800;
$height = 600;

$image = Vips\Image::black($width, $height);
$alpha = Vips\Image::newFromArray(array_fill(0, $height, array_fill(0, $width, 255)));
$image = $image->bandjoin($alpha);
$image = $image->colourspace('cmyk');
$image->tiffsave(__DIR__ . '/cmyk_with_profile.tif');

$image->remove("icc-profile-data");
$image->writeToFile(__DIR__ . '/cmyk_no_profile.jpg', ['Q' => 90]);
$image->tiffsave(__DIR__ . '/cmyk_no_profile.tif');

This works with the imagick profileImage function. However, when I try to apply a profile using

$image = $image->icc_transform(__DIR__ . '/color_profiles/CoatedFOGRA27.icc', ['embedded' => true]);

I get libvips error: icc_transform: unable to load or find any compatible input profile for both tiff images. Specifying 'input_profile' => 'cmyk' also results in the same error. It works, however, for the jpeg images

Do you have any idea, or can point me in the direction to where I am getting this wrong?

jcupitt commented 1 year ago

Hi @axkirillov,

$alpha = Vips\Image::newFromArray(array_fill(0, $height, array_fill(0, $width, 255)));
$image = $image->bandjoin($alpha);

This will be very slow --- you can just do:

$image = $image->bandjoin(255);

And the number will be expanded into an image. You can optionally use numbers instead of images in all php-vips functions. You can use arrays too, eg.:

$image = $image->bandjoin([1, 2]);

Will add two more bands, the first with the constant 1 and the second the constant 2. Though you don't need an alpha here, so you can just remove this code.

$image = $image->colourspace('cmyk');

Your image is mono + alpha, so this will convert your mono + alpha to rgb + alpha, then make a CMYKA image using the built-in libvips CMYK fallback profile. The default profile will be attached to the image in case anyone wants to recover your original RGB data.

So I would write:

#!/usr/bin/php
<?php

require __DIR__ . '/vendor/autoload.php';
use Jcupitt\Vips;

$width = 800;
$height = 600;

$image = Vips\Image::black($width, $height);
$image = $image->colourspace('cmyk');
$image->writeToFile('cmyk1.tif');

$image->remove("icc-profile-data");
$image->writeToFile('cmyk2.jpg', ['Q' => 90]);
$image->tiffsave('cmyk3.tif');

I can run this, then:

$ vips icc_transform cmyk1.tif x.jpg cmyk
$ vips icc_transform cmyk1.tif x.jpg cmyk --embedded
$ vips icc_transform cmyk2.jpg x.jpg cmyk
$ vips icc_transform cmyk3.tif x.jpg cmyk

And it all seems to work with git master libvips.

There have been some improvements around colour profile handling recently, so perhaps you're hitting a bug that's been fixed? Does removing the alpha help? What libvips version are you using?

axkirillov commented 1 year ago

I am on 8.14.2. For some reason doing this (without alpha) fails on image->remove with an empty error

        $width = 500;
        $height = 500;

        $image = Vips\Image::black($width, $height);
        $image = $image->colourspace('cmyk');
        $image->writeToFile(__DIR__ . '/cmyk_with_profile.tif');
        $image->writeToFile(__DIR__ . '/cmyk_with_profile.jpg', ['Q' => 90]);

        $image->remove("icc-profile-data");
Jcupitt\Vips\Exception: libvips error:
axkirillov commented 1 year ago

I'll try upgrading to 8.14.4

jcupitt commented 1 year ago

I tried with 8.14.2:

#!/usr/bin/php
<?php

require __DIR__ . '/vendor/autoload.php';
use Jcupitt\Vips;

Vips\Config::setLogger(new Vips\DebugLogger());

$width = 800;
$height = 600;

$image = Vips\Image::black($width, $height);
$image = $image->colourspace('cmyk');
$image->writeToFile('cmyk1.tif');

$image->remove('icc-profile-data');
$image->writeToFile('cmyk2.jpg', ['Q' => 90]);
$image->tiffsave('cmyk3.tif');

And it works for me. I see:

$ ./axkirikllov.php 
[2023-08-31T15:14:53+01:00] debug: black {"instance":null,"arguments":[800,600]}
[2023-08-31T15:14:53+01:00] debug: init {"library":"libvips.so.42"}
[2023-08-31T15:14:53+01:00] debug: trying to open {"libraryName":"libvips.so.42"}
[2023-08-31T15:14:53+01:00] debug: trying path {"path":""}
[2023-08-31T15:14:53+01:00] debug: success []
[2023-08-31T15:14:53+01:00] debug: init {"vips_init":0}
[2023-08-31T15:14:53+01:00] debug: init {"libvips version":[8,14,2]}
[2023-08-31T15:14:53+01:00] debug: init ["binding ..."]

So it's defiantly picking the correct version. Maybe you have a very old libvips somewhere on your system that's being selected at runtime?

I can transform these images too:

john@banana ~/try $ vips icc_transform cmyk1.tif x.jpg cmyk
john@banana ~/try $ vips icc_transform cmyk1.tif x.jpg cmyk --embedded
john@banana ~/try $ vips icc_transform cmyk2.jpg x.jpg cmyk
...
axkirillov commented 1 year ago

Weird. The version it shows me is [2023-08-31T14:21:56+00:00] debug: init {"libvips version":[8,14,2]}

jcupitt commented 1 year ago

Did you try the exact program I pasted above? Could it be something odd about the profile you are using?

axkirillov commented 1 year ago

I was running it with phpunit inside of a setup call and I think this was the reason for the empty error issue. I haven't figured out why exactly it happens. I think your original suggestion of removing the alpha solved the original issue, and the following one was from phpunit, which can be avoided by doing it outside of setup. The program you provided also works perfectly on my local machine that has 8.14.4, but also in my test code with 8.14.2 when accounted for the phpunit issue. Many thanks for all the help!