Convert CMYK PDF to SRGB JPG using PHP and Imagick - php

I'm trying to convert the first page of PDF documents to a JPEG using Imagick and PHP. As long as the colorspace of the PDF is SRGB, conversion succeeds and the resulting images have correct colors. However, if the PDF has a CMYK colorspace, after conversion, the image colors are off (much brighter or darker).
I'm currently using the following software:
PHP 7.4.3
ImageMagick 6.9.10-23 Q16 x86_64 20190101 (deb package)
Ghostscript 9.50 (2019-10-15)
I'm working on WSL2 on Windows 10.
My test PDF can be found here.
Because I was not happy with the resulting conversions, I firstly tried to see if it is possible to do a successful conversion using Imagick cli. After lots of trial and error, I found that the following command yielded the best result:
convert -density 300 -colorspace srgb input.pdf[0] -layers flatten -strip output.jpg
Result:
I then rewrote the command to PHP:
$input = 'input.pdf';
$output = 'output.pdf';
$image = new Imagick();
$image->setResolution(300, 300);
$image->readImage("{$input}[0]");
$image->transformImageColorspace(Imagick::COLORSPACE_SRGB);
$image = $image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
$image->setImageFormat('jpeg');
$image->stripImage();
$image->writeImage($output);
$image->destroy();
Result:
The result of the PHP code is not the same as the result of the CLI version and the original PDF. The result is the same as if I would run te following CLI command:
convert -density 300 input.pdf[0] -colorspace srgb -layers flatten -strip output.jpg
The command looks almost the same, however the transforming of the color space happens later.
My question is: what step do I miss in my PHP code to achieve the same result as the command
convert -density 300 -colorspace srgb input.pdf[0] -layers flatten -strip output.jpg
Additional information:
I also tried using color profiles to do the colorspace transformations. Instead of
$image->transformImageColorspace(Imagick::COLORSPACE_SRGB);
I used
$cmyk = file_get_contents('USWebCoatedSWOP.icc');
$rgb = file_get_contents('sRGB_v4_ICC_preference.icc');
$image->profileImage('icc', $cmyk);
$image->profileImage('icc', $rgb);
Besides these two profiles, I also tried combinations of other CMYK (CoatedFOGRA39, JapanColor2001Coated...) and SRGB (AdobeRGB1998, AppleRGB, sRGB_v4_ICC_preference_displayclass...) profiles.
However, I couldn't find a profile combination that came close to the result of the CLI output and the original PDF file.

Thanks to #fmw42, I was able to fix my issue. To fix it, set the color space using setColorSpace() before reading in the pdf.
$input = 'input.pdf';
$output = 'output.pdf';
$image = new Imagick();
$image->setResolution(300, 300);
$image->setColorSpace(Imagick::COLORSPACE_SRGB); // Add this line
$image->readImage("{$input}[0]");
// $image->transformImageColorspace(Imagick::COLORSPACE_SRGB); // You don't need this line
$image = $image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
$image->setImageFormat('jpeg');
$image->stripImage();
$image->writeImage($output);
$image->destroy();

Related

How Can I Use Imagick to Change the Color Profile to SRGB and Have it Preserve Colors Like `convert {file} -profile {iccfile} {file}

I'm having an issue converting the color profile from "Display P3" to "sRGB IEC61966-2.1" in Imagick (PHP API Class), and having it preserve the colors of an image the same way it does when running from command line: convert IMG_1329.jpg -profile sRGB2014.icc testimage.jpg
<?php
header('Content-type: image/jpeg');
$sRGB_icc = file_get_contents('sRGB2014.icc');
$image = new \Imagick('IMG_1329.jpg');
$image->setImageProfile('icc', $sRGB_icc);
$image->transformImageColorspace(\Imagick::COLORSPACE_SRGB);
echo $image;
The original image can be downloaded here:
http://cdn.webplant.media/IMG_1329.jpg
The ICC Color profile used can be downloaded here:
http://cdn.webplant.media/sRGB2014.icc
Here is a screenshot of the different outputs. Notice the vibrant colors in the roof and on the trunk of the tree in the original image, and the image converted with convert.
Original vs Imagick vs Convert
How can I get Imagick to output the same quality image as when using convert IMG_1329.jpg -profile sRGB2014.icc testimage.jpg?

How to convert command imagemagick to php code?

I want use OCR. But the images can't read perfectly, so i'm converting image to delete noise background, Original Images.
then, i'm run this command :
convert -colorspace gray -modulate 120 -contrast-stretch 10%x80% -modulate 140 -gaussian-blur 1 -contrast-stretch 5%x50% +repage -negate -gaussian-blur 4 -negate -modulate 130 original.jpeg clean.jpeg
Images Result
The problem is, how to convert above command to php?
Well, i'm very confusing using imagick in php.
mycode (this what i know) :
$image = new Imagick('captcha.png');
$image->modulateImage(450, 0, 500);
$image->writeImage("output.jpg");
Result from PHP Imagick : HERE
I know, it's diffrent configuration number, but result not to far.
Any suggestions how?
==== answare (thank you fmw42)
$image = new Imagick('captcha.png');
$image->thresholdimage(0.1 * \Imagick::getQuantum(), 134217727);
$image->shaveImage(2, 1);
$image->writeImage("output.jpg");
To remove the black border and threshold your image in ImageMagick, do
Input:
convert img.png -shave 1x1 -threshold 0 result.png
Because the 8 and 7 touch, I would be surprised if OCR worked.
For Imagick, see
https://www.php.net/manual/en/imagick.thresholdimage.php
https://www.php.net/manual/en/imagick.shaveimage.php

PDF Thumbnail always generated as grayscale using Imagick

I am stuck and could really use some help on this one. I am using PHP and Imagick to generate a thumbnail which is working great. However, I noticed that CMYK PDFs are generated always as grayscale. So I tested this by taking the CMYK PDF and manually converting it to RGB with Adobe Acrobat Pro. Then I re-ran it through the following code and it produced a color image. I know about
$image->transformImageColorSpace(1);
or
$image->setImageColorSpace(1);
However this doesn't work. What is the correct way for converting a pdf to a color PNG image? I have looked at the following links with no luck:
http://php.net/manual/en/imagick.setimagecolorspace.php
Convert PDF to JPEG with PHP and ImageMagick
Any help on this one would be great.
Here is the code:
$filePath = fileSaveUserUpload("path/to/file", ""); //path changed here...
$_SESSION['FILEPATH'] = $filePath;
//-------------first makes a thumbnail of first page in image/pdf
$extension_pos = strrpos($filePath, '.'); // find position (number) of the last dot, so where the extension starts
$image = new Imagick();
$image->readImage($filePath."[0]"); //reads an image at a path(first page only in this case)
$image->transformImageColorSpace(1); //convert to RGB
$image->setbackgroundcolor('white'); //replace transparency with this color
$image->setCompression(Imagick::COMPRESSION_LOSSLESSJPEG);
$image->setCompressionQuality(150);
$image->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE); //remove transparency
$image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN); //make everything that was transparent white
$image->thumbnailImage(0,250); //max height 300 but try and preserve aspect ratio (wdithxheight)
$thumbnail = substr($filePath, 0, $extension_pos) . '_thumb.png';// . substr($filePath, $extension_pos);
$image->writeImage($thumbnail);
$image->clear();
$image->destroy();
UPDATE:
I am using the following imagick version:
ImageMagick 6.9.1-2 Q16 x86 2015-04-14
3.3.0RC2
GhostScript Version:
9.18
Here is the original PDF (changed it to a picture here):
Here is the thumbnail that it produced:
This ONLY happens with CMYK PDFs. If I take this same PDF and convert it to RGB through adobe acrobat it comes out color. I tested this and it still holds true.
greeting from 2019. was having this problem still on gs 9.26 (9.27 doesnt work at all)
set the colourspace BEFORE loading the file, transform it AFTER.
// setup imagick for colour
$Img = new Imagick();
$Img->SetResolution(200,200);
$Img->SetColorspace(Imagick::COLORSPACE_SRGB);
// now read pdf first page.
$Img->ReadImage("{$File}[0]");
$Img->TransformImageColorSpace(Imagick::COLORSPACE_SRGB);
// the rest of your stuff.
You may try changing:
$image->transformImageColorSpace(1);
to
$image->transformImageColorSpace(Imagick::COLORSPACE_RGB);.
I'm not sure what you are trying to do with the 1, but according to PHP.net, there are predefined colorspaces that can be added to transformImageColorSpace(); to have the image output with the correct colorspace.
The problem you are seeing is likely to be an old version of GhostScript that is not doing the conversion correctly. Below is the image produced when calling your code on a system that has GhostScript version 8.70 installed:
btw you almost certainly want to be using SRGB colorspace, not plain old RGB. SRGB is the correct one to use for displaying images on computer screens.
$image->transformImageColorSpace(\Imagick::COLORSPACE_SRGB);
Here is a command line to test ghostscript by itself doing the conversion:
./gs-916-linux_x86_64 \
-q -dQUIET -dSAFER -dBATCH \
-dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 \
-dAlignToPixels=0 -dGridFitTT=1 -sDEVICE=pngalpha \
-dFirstPage=1 -dLastPage=3 \
-dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r72 \
-sOutputFile=gs-%d.png tree-blackandwhite.pdf
It should convert the image to a PNG. You'd probably need to replace ./gs-916-linux_x86_64 with just gs.

How to increase quality of image through imagemagick binary convert?

I'm working in an image conversion using imageMagick binary convert. When i resize a small image into a larger image and also increase the quality of image.
Here's my sample code:
$img = 'old_image.png';
$path1= 'new_img.png';
exec("convert $img -quality 100% -density 600 -resize 2480x3508 -depth 400 $path1");
When i used this command its working fine and it convert large image with loss of quality.
When i need to increase quality by using sharpen 50% code in exec command it doesn't create a proper image and no response in exec command.
$img = 'old_image.png';
$path1= 'new_img.png';
exec("convert $img -sharpen 99% -quality 100% -density 600 -resize 2480x3508 -depth 400 $path1");
Here I'm using image magick binary convert. How to achieve this image quality. Any help would be appreciated.
Think sharpen works like blur and takes an argument of {radius}x{sigma}
What version are you using? Maybe look at some of the docs that correspond to it...
http://www.imagemagick.org/Usage/blur

Detect if image is grayscale or color using Imagick

I'm attempting to try and assign a value to an image based on its 'saturation level', to see if the image is black and white or color. I'm using Imagick, and have found what seems to be the perfect code for the command line and am trying to replicate it using the PHP library.
I think I understand the concept:
Convert image to HSL.
Extract the 'g' channel (which is the S channel in HSL).
Calculate the mean of this channel.
Command line code
convert '$image_path' -colorspace HSL -channel g -separate +channel -format '%[fx:mean]' info:
My PHP code
$imagick = new Imagick($image_path);
$imagick->setColorspace(imagick::COLORSPACE_HSL);
print_r($imagick->getImageChannelMean(imagick::CHANNEL_GREEN));
Output
My PHP code isn't outputting the same sort of values as the command line code, though. For example, a grayscale image gives 0 for the command line code, but the PHP code gives [mean] => 10845.392051182 [standardDeviation] => 7367.5888849872.
Similarly, another grayscale image gives 0 vs. [mean] => 31380.528443457 [standardDeviation] => 19703.501101904.
A colorful image gives 0.565309 vs. [mean] => 33991.552881892 [standardDeviation] => 16254.018540044.
There just doesn't seem to be any kind of pattern between the different values. Am I doing something obviously wrong?
Thanks.
Just to add, I've also tried this PHP code
$imagick = new Imagick($image_path);
$imagick->setColorspace(imagick::COLORSPACE_HSL);
$imagick->separateImageChannel(imagick::CHANNEL_GREEN);
$imagick->setFormat('%[fx:mean]');
But I get an Unable to set format error when I try and set the format. I've also tried setFormat('%[fx:mean] info:'), setFormat('%[mean]'), setFormat('%mean'), etc.
Update — FIXED!
Thanks to #danack for figuring out I needed to use transformImageColorspace() and not setColorspace(). The working code is below.
$imagick = new Imagick($image_path);
$imagick->transformImageColorspace(imagick::COLORSPACE_HSL);
$saturation_channel = $imagick->getImageChannelMean(imagick::CHANNEL_GREEN);
$saturation_level = $saturation_channel['mean']/65535;
setFormat doesn't replicate the command line option -format - the one in Imagick tries to set the image format, which should be png, jpg etc. The one in the command line is setting the format for info - the closest match for which in Imagick is calling $imagick->identifyImage(true) and parsing the results of that.
Also you're just calling the wrong function - it should be transformImageColorspace not setColorSpace. If you use that you can use the statistics from getImageChannelMean.
There are other ways to test for greyness which may be more appropriate under certain circumstances. The first is to convert the a clone of the image to grayscale, and then compare it to the original image:
$imagick = new Imagick($image_path);
$imagickGrey = clone $imagick;
$imagickGrey->setimagetype(\Imagick::IMGTYPE_GRAYSCALE);
$differenceInfo = $imagick->compareimages($imagickGrey, \Imagick::METRIC_MEANABSOLUTEERROR);
if ($differenceInfo['mean'] <= 0.0000001) {
echo "Grey enough";
}
That would probably be appropriate if you image had areas of color that were also transparent - so they theoretically have color, but not visibly.
Or if you only care about whether the image is of a grayscale format:
$imageType = $imagick->getImageType();
if ($imageType === \Imagick::IMGTYPE_GRAYSCALE ||
$imageType === Imagick::IMGTYPE_GRAYSCALEMATTE) {
//This is grayscale
}
I found a command line here:
convert image.png -colorspace HSL -channel g -separate +channel -format "%[fx:mean]" info:
It prints a number between 0 and 1, where zero means grayscale.
If your images have tint. (from scanner as example) you should do auto color for them before detecting gray scale.
You should normalizeImage(imagick::CHANNEL_ALL) for all separate channels of image. separateImageChannel()
But

Categories