yesterday I posted about a problem with recreating an Error Level Analysis in PHP with Imagmagick. In this question I found a solution with the command-line interaction and tried to translate it into Imagick and PHP.
The following code was proposed:
convert barn.jpg \( +clone -quality 95 \) -compose difference -composite -auto-level -gamma 1.5 barn_ela.png
In this example, the result should highlight the manipulated parts of the image. So I implemented the following code:
$ELAImageMagick = new Imagick($targetDir . $OriginalImage);
$OriginalImageMagick = new Imagick($targetDir . $OriginalImage);
$ELAImageMagick->setImageCompression(Imagick::COMPRESSION_JPEG);
$ELAImageMagick->setImageCompressionQuality($this->getRequest()->postVar('elaQuality'));
$ELAImageMagick->compositeImage($OriginalImageMagick, Imagick::COMPOSITE_DIFFERENCE, 1,1);
$ELAImageMagick->autoLevelImage();
//Set gamma with a slider in the frontend
$ELAImageMagick->gammaImage($this->getRequest()->postVar('elaSize'));
//save ELA-image into Folder
$ELAImageMagick->writeImage($targetDir . $ELAImage);
Unfortunatly the result does not come close to the desired optic of the result:
The original image (yellow bird has been added in photoshop)
The ELA-Result
Does anyone have an idea, what step I didn't catch quite right and how to solve it? I looked into the documentation and didn't really find any alternatives to this.
Thanks in advance!
You need to first prove that your code works in Imagemagick. But -quality only works for writing a JPG output. So you need to actually save the result of the -quality operation as a JPG and then use it in a second command. Here I just pipe from one convert to another. The first convert actually creates the compressed JPG but just passes it to the second command without writing to disk.
Input:
convert birds.jpg -quality 95 JPG:- | convert birds.jpg - -compose difference -composite -auto-level -gamma 1.5 birds_ela.png
The result I get is:
This is a JPG version of the PNG output since the PNG was too large to post.
However the results from the command line do not match the resulting image you show. So the command is not going to work in Imagick either.
ADDITION:
From what I can see there is no tampering other than possibly the orange bird in the middle. Most of the image is flat or has random noise. The small bird in the middle has a bit brighter texture and so may be an insert.
ADDITION 2
For enhancing edge artifacts, add -geometry +1+1 to my command above just before -compose difference.
convert birds.jpg -quality 95 JPG:- | convert birds.jpg - -geometry +1+1 -compose difference -composite -auto-level -gamma 2 -define jpeg:extent=2000kb birds_ela2.png
Again posting a JPG version for file size issues.
This looks similar to your posted result.
I have a simple HTML form where the user can upload their image and I need to convert this uploaded image into a specific color with the help of PHP
For example, user upload some image I need to convert the entire image into a specific color (this color is dynamic)
Is there any PHPGD library that can help me to achieve this?
EDIT
For example, user is uploading this kind of Image,
then I need to convert into below type of Image,
I'm still unsure exactly what you are trying to do, but think one of the following may be close. Maybe you can try them in the Terminal until we can finally work out the correct operations then we can hopefully translate them into PHP. This is ImageMagick v7 syntax:
magick image.png -channel RGB -colorspace gray +level-colors red, result.png
Or this:
magick image.png -fill red +opaque white result2.png
You can specify the colour in hex like this for magenta:
magick image.png -channel RGB -colorspace gray -auto-level +level-colors '#ff00ff', result.png
If using v6 ImageMagick, replace magick with convert.
My PHP is pretty rusty, but something like this:
#!/usr/local/bin/php -f
<?php
// Emulating something akin to this ImageMagick command:
// magick image.png -fill red +opaque white result.png
// Open input image and get dimensions
$im = new \Imagick('image.png');
// Temporarily deactivate alpha channel
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_DEACTIVATE);
// Apply colour to non-white areas
$im->opaquePaintImage('white','red', 0, true);
// Reactivate alpha channel
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE);
// Save
$im->writeImage('result.png');
?>
I have tried to produce something similar. Please test the following code if that satisfies your requirement.
<?PHP
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
/*
#desc replaces target color of an image with the provided fill color
*/
function color_replace($img,$target,$fill,$fuzz){
$img->opaquePaintImage($target, $fill, $fuzz, false, Imagick::CHANNEL_DEFAULT);
return $img;
}
$img = new Imagick('source.png');
$img->setImageAlphaChannel(Imagick::ALPHACHANNEL_DEACTIVATE);
$fuzz = 0.44 * $img->getQuantumRange()['quantumRangeLong'];
$img=color_replace($img,'rgba(50,173,186,255)','red',$fuzz); // replace paste like color with red
$img=color_replace($img,'rgb(230,218,30)','#9c1f24',$fuzz); // replace golden like color with dark red
$img->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE);
$img->setImageFormat ("jpeg");
file_put_contents ("test_1.jpg", $img);
?>
Produced output with my program:
With this program, you will be able to change each individual color by calling the color_replace method every time you want to change color. The following image is an example of that.
You need some library like Imagick. To replace some color, you need clutImage
$image = new Imagick('test.jpg');
$clut = new Imagick();
$clut->newImage(1, 1, new ImagickPixel('rgb(255, 0, 0)'));
$image->clutImage($clut);
$image->writeImage('test_out.jpg');
I have another solution for you here, again of course with Imagick:
$im = new Imagick('path/to/start-image')
$im->transformimagecolorspace(Imagick::IMGTYPE_GRAYSCALE);
$im->writeImage('path/to/gray.jpg'));
$im->clear();
$im = new Imagick('path/to/gray.jpg'));
$im->blackThresholdImage( "#cdcdcd" );
$im->writeImage('path/to/black-white.jpg'));
$im->clear();
$im = new Imagick('path/to/black-white.jpg'));
$im->colorizeImage('rgba(209, 15, 16, 1)', 1, true);
$im->writeImage('path/to/red.jpg'));
$im->clear();
Then just delete the intermediate files gray.jpg and black-white.jpg and you will achieve the result you are looking for as here:
I'm using PHP Imagick to convert PNG images generated in PhantomJS to TIF CMYK,
for print purposes I need a flat Black (cmyk - 0,0,0,100) - the conversion generates blacks like (cmyk - 58,49,44,89).
I'm converting the images using color profile (section of my code below) -> the code is based on Convert image from RGB to CMYK with Imagick
is it possible to force a flat black with Imagick ? do you know any other tools that might help ?
thanks,
if ($has_icc_profile === false) {
$icc_rgb = file_get_contents( '/srgb_profiles' . '/sRGB.icc');
$image->profileImage('icc', $icc_rgb);
unset($icc_rgb);
}
// then we add an CMYK profile
$icc_cmyk = file_get_contents( '/cmyk_profiles'.'/JapanColor2002Newspaper.icc');
$image->profileImage('icc', $icc_cmyk);
UPDATE :
after checking online I think I'm looking for a UCR en.wikipedia.org/wiki/Under_color_removal method for ImageMagick - I found that convert old versions supported under color removal
-undercolor <undercolor factor>x<black-generation factor>
control undercolor removal and black generation on CMYK images.
This option enables you to perform undercolor removal and black generation on CMYK images-- images to be printed on a four-color printing system. You can con- trol how much cyan, magenta, and yellow to remove from your image and how much black to add to it. The standard undercolor removal is 1.0x1.0. You'll frequently get better results, though, if the percentage of black you add to your image is slightly higher than the percentage of C, M, and Y you remove from it. For example you might try 0.5x0.7. (http://www.chemie.fu-berlin.de/chemnet/use/suppl/imagemagick/www/convert.html) -
apparently the option is not supported anymore, I'm interested if anyone knows if UCR is the solution I'm looking for and if anyone knows if it's supported or if I'm supposed to use a different method to get the same result.
If you use ImageMagick's convert at the command line like this to generate a grayscale ramp, 1 pixel wide and 256 pixels tall, going from white to black and convert it to CMYK colorspace and then show it as text, you get what you want:
convert -size 1x256 'gradient:rgb(255,255,255)-rgb(0,0,0)' -colorspace cmyk txt:
# ImageMagick pixel enumeration: 1,256,65535,cmyk
0,0: (0%,0%,0%,0%) #0000000000000000 cmyk(0,0,0,0)
0,1: (0%,0%,0%,0.392157%) #0000000000000101 cmyk(0,0,0,1)
0,2: (0%,0%,0%,0.784314%) #0000000000000202 cmyk(0,0,0,2)
0,3: (0%,0%,0%,1.17647%) #0000000000000303 cmyk(0,0,0,3)
0,4: (0%,0%,0%,1.56863%) #0000000000000404 cmyk(0,0,0,4)
0,5: (0%,0%,0%,1.96078%) #0000000000000505 cmyk(0,0,0,5)
0,6: (0%,0%,0%,2.35294%) #0000000000000606 cmyk(0,0,0,6)
0,7: (0%,0%,0%,2.7451%) #0000000000000707 cmyk(0,0,0,7)
0,8: (0%,0%,0%,3.13725%) #0000000000000808 cmyk(0,0,0,8)
0,9: (0%,0%,0%,3.52941%) #0000000000000909 cmyk(0,0,0,9)
0,10: (0%,0%,0%,3.92157%) #0000000000000A0A cmyk(0,0,0,10)
...
...
0,249: (0%,0%,0%,97.6471%) #000000000000F9F9 cmyk(0,0,0,249)
0,250: (0%,0%,0%,98.0392%) #000000000000FAFA cmyk(0,0,0,250)
0,251: (0%,0%,0%,98.4314%) #000000000000FBFB cmyk(0,0,0,251)
0,252: (0%,0%,0%,98.8235%) #000000000000FCFC cmyk(0,0,0,252)
0,253: (0%,0%,0%,99.2157%) #000000000000FDFD cmyk(0,0,0,253)
0,254: (0%,0%,0%,99.6078%) #000000000000FEFE cmyk(0,0,0,254)
0,255: (0%,0%,0%,100%) #000000000000FFFF cmyk(0,0,0,255)
You must be doing something different - maybe this will help you work it out. I am guessing it is your ICC profiles but you can experiment with the above command.
If you just want to experiment with spot values, you can just have IM translate a single pixel like this:
convert -size 1x1 xc:#000000 -colorspace cmyk txt:
# ImageMagick pixel enumeration: 1,1,65535,cmyk
0,0: (0%,0%,0%,100%) #000000000000FFFF cmyk(0,0,0,255)
or maybe more simply like this:
convert -size 1x1 xc:#000000 -depth 8 -colorspace cmyk txt:
# ImageMagick pixel enumeration: 1,1,255,cmyk
0,0: (0,0,0,255) #000000FF cmyk(0,0,0,255)
Note the following though:
You must put profiles between input image and output image names on the command line.
If your image has no embedded profile, the first profile you give is applied to the input image and the second to the output image. If your input image does have a profile, the first profile you give is applied to the output image.
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
I want to draw a rectangle with two strings in it. I want the first string to be 15pt size (its a number), second to be 10pt size (the label). It's easy to draw single string with one size to the rectangle, I do it like this:
$image = new Imagick('someimage.png');
$draw = new ImagickDraw;
$draw->setGravity(Imagick::GRAVITY_CENTER);
$draw->setfont(__DIR__ . DS . 'TREBUCBD.TTF');
$draw->setfontsize(15);
$draw->annotation(0, 0, '50 points');
$image->drawImage($draw);
The idea here is to have "50" in "50 points" to be big.
I tried to do a $draw->push() to push the current settings to the stack then set the font size and annotation again but then the two strings overlap. I've been trying to do this for hours. Any help is very appreciated!
The above implementation is in PHP but probably I will manage to do it even by example that shows it with command line ImageMagick usage.
edit: I've started a bounty that I will award for a solution implemented in PHP.
You can use +append option to make two different text labels joined horizontally (-append - vertically):
convert -background grey -pointsize 15 -fill black label:abc \
-pointsize 10 -fill red label:cdefgh -gravity South +append test.png
This code actually produces two images sized exactly to fit font (since there's no explicit size specified), and then this two images are horizontally appended together:
Once again: the size of this image is calculated automatically to fit your labels. You can use Imagick::labelImage and Imagick::appendImages functions to achieve this. (To make image without background, you can specify -backround transparent, e.g. via Imagick::setBackgroundColor)
After that, resulting image with labels can be composed with anything you want.
Add enough space before "points" and after the "50" so you align them nicely:
<?php
$image = new Imagick('test.png');
$draw = new ImagickDraw;
$draw->setGravity(Imagick::GRAVITY_CENTER);
$draw->setfontsize(30);
$draw->annotation(0, 0, '50 ');
$image->drawImage($draw);
$draw = new ImagickDraw;
$draw->setGravity(Imagick::GRAVITY_CENTER);
$draw->setfontsize(15);
$draw->annotation(0, 0, ' points');
$image->drawImage($draw);
file_put_contents('test.png', $image->getImageBlob());
?>