ImageMagick caption in PHP not filling size vertical dimension - php

I have cobbled together the following code, which very nearly works:
<?php
$img = new Imagick("quote_blank.jpg");
$txt = new Imagick();
$txt->setBackgroundColor("transparent");
$txt->newPseudoImage(380,250, "Caption:".htmlspecialchars($_GET['quote']) );
$txt->colorizeImage('#468847',1);
$img->compositeImage($txt, imagick::COMPOSITE_OVER, 10, 80);
$draw = new ImagickDraw();
$draw->setFillColor('#468847');
$draw->setGravity(Imagick::GRAVITY_SOUTHEAST);
$draw->setFontSize(25);
$draw->setFontStyle(3);
$img->annotateImage($draw, 5,5,0, htmlspecialchars($_GET['attrib']) );
$img->setImageFormat('jpg');
header('Content-Type: image/jpeg');
echo $img;
?>
(please note that quote_blank.jpg is a 400x400 image background over which the text is rendered and resides in the same directory as the php file).
The issue is that the caption only fills the 380x250 PseudoImage with a very small number of short words. Anything of any length results in just the top half (or less) of the box having any text in it (aside from the attribution annotation).
It seems like the PseudoImage is working correctly but that ImageMagick's algorithm for calculating the font size is only designed to fill the width, not the height. I have no idea how it decides what line length to go for (which would presumably in turn dictate the font size and therefore number of lines and vertical coverage of the caption box).
So I guess my question is this: Is there any way of changing how it does it's calculations in order to fill as much of the caption box as possible, horizontal AND vertical?
Sample of just a few words, showing the caption can go full-height:
Sample of a more typical length of text, showing it doesn't fill the box vertically

I tested your code with ImageMagick 6.8.9-8 and got the following output, which is better than what you're getting. If you're using an older version, try updating ImageMagick.

Vinicius Pinto had the right answer right off the bat. But updating wasn't so easy on a shared server- I have not figured out how to get Imagick to use the updated version. So I had to rewrite my code to access ImageMagick via the commandline, which I wanted to share. Code doesn't show up well as far as I can tell on a comment, so sorry for cheating the answer feature a little.
$location='/home/user/local/bin/convert';
$command='convert -background none -size 380x250 -fill "#468847" caption:"'.htmlspecialchars($_GET['quote']).'" quote_blank.jpg +swap -gravity southeast -geometry +10+80 -composite convert -fill "#468847" -gravity southeast -pointsize 25 -annotate 0x20+5+5 "'.htmlspecialchars($_GET['attrib']).'" anno_label.jpg';
exec ($location . ' ' .$command);
header('Content-Type: image/jpeg');
readfile('anno_label.jpg');
unlink('anno_label.jpg');

Related

Error Level Analysis with Imagick PHP - result without color

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.

PHP : Convert dynamic uploaded image to specific color(dynamic color)

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:

PHP Imagick (ImageMagick) RGB > CMYK with Flat Black

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.

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

ImageMagick: how to draw two strings with different size over image?

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());
?>

Categories