I wish to make part (or in fact, several parts) of an image transparent using IMagick, such that I can use it as a mask over a different image. I can't figure out any way of doing this in an simple fashion.
So say my starting image is represented as below, where X is any color:
XXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXXX
Then I want to be able to make certain rectangular regions transparent (so it ends up a bit like a punch-card):
XXXXXXXXXXXXX
X XXXXXXXXXX
X XXXX XXXX
XXXXXXX XXXX
XXXXXXXXXXXXX
Does anyone know of a good way of doing this? Thanks.
Figured it out.
//Open your image and get its dimensions
$image = new Imagick('image.png');
$height = $image->getImageHeight();
$width = $image->getImageWidth();
//Create a new transparent image of the same size
$mask = new Imagick();
$mask->newImage($width, $height, new ImagickPixel('none'));
$mask->setImageFormat('png');
//Draw onto the new image the areas you want to be transparent in the original
$draw = new ImagickDraw();
$draw->setFillColor('black');
$draw->rectangle( 10,10,100,100 );
$mask->drawImage( $draw );
//Composite the images using Imagick::COMPOSITE_DSTOUT
$image->compositeImage($mask, Imagick::COMPOSITE_DSTOUT, 0, 0, Imagick::CHANNEL_ALPHA);
Related
I am writing a script that takes an arrow image and rotates it by a set number of degrees. Using the code below, when the angle is a multiple of 90 the image rotates and displays as expected.
The source image looks like this (74 x 74):
Images after rotating by 90:
Images after rotating by any other number (not a multiple of 90) eg 45:
As can be seen in the image, the tip of the arrow has been cropped out of the image. Could anyone please tell me why this is happening? Again, multiples of 90 are fine, it's just any other number where the unusual cropping occurs.
$props = ['w' => 74, 'h' => 74];
$angle = 360 - $_GET['angle'];
$final_img = imagecreatetruecolor($props['w'], $props['h']);
imagesavealpha($final_img, true);
$transColor = imagecolorallocatealpha($final_img, 0, 0, 0, 127);
imagefill($final_img, 0, 0, $transColor);
$rotate = imagecreatefrompng('arrow.png');
$src = imagerotate($rotate, $angle, $transColor); //rotated my image
$src_x = ImageSX($src); //find out new x width
$src_y = ImageSY($src); //find out new y height
$src_widthx = $src_x/2 - $props['w']/2; // divide each by 2 and then subtract desired end width from wider rotated width
$src_heighty = $src_y/2 - $props['h']/2; // and again for height
imagecopy($final_img, $src, 0, 0, $src_widthx, $src_heighty, $props['w'], $props['h']);
header('Content-Type: image/png');
imagepng($final_img);
When you rotate a square of nXm pixels by lets say 45 degrees you will get the diagonals(which are bigger than n or m and equal sqrt(n^2+m^2)) of the image be the new rotated image width and height.
The function crops the rotated image using the original dimensions of the image, namely n and m.
A way to fix the problem would be by crating a bigger blank image with the appropriate size, sqrt(width_original_image^2+height_original_image^2), and than copy the original image to the new image using imagecopy. After that you can use imagerotate on the new image
I installed and used the ImageMagick PHP library and the rotations show uncropped, no matter the degree of rotation.
I'm trying to convert an pdf to a jpeg, trim the whitespaces around the content and resize it to 300x600
In PHP using ImageMagick 6.7.7-10
here's my code:
$im = new \Imagick();
$im->setBackgroundColor("white");
$im->readimage($url);
$im->setImageFormat("jpeg");
$im->trimImage(0);
$im->resizeImage(300, 600, Imagick::FILTER_LANCZOS, 0.9);
$im->writeImage($tmpFilePath);
$im->clear();
$im->destroy();
here is the PDF
http://cs1.fuman.de/file.php/1AOrL6-PzT71Z-dk0000-CsjquC
and here the resulting JPG
http://cs1.fuman.de/file.php/1AOrL0-kWAl8P-ml0000-xAhOiw
Does anyone know, what's going wrong here?
Thanks in advance
JD
I don't know why the background color is being ignored. I think it's to do with JPEG not having a concept of a 'background' colour, and so when the image is converted from having alpha to not having alpha, bad things happen.
I suggest using this code:
$imagick = new \Imagick();
// Make the image be large when read from PDF so have decent quality later
$imagick->setResolution(92, 92);
// only use the first page of the PDF
$imagick->readimage("./orig.pdf[0]");
// Make a white background imge
$canvas = new Imagick();
$canvas->newPseudoImage(
$imagick->getImageWidth(),
$imagick->getImageHeight(),
'canvas:white'
);
// Need to use png format to keep alpha channge
$imagick->setImageFormat('png');
// Composite our image, into the white background
$canvas->compositeImage($imagick, \Imagick::COMPOSITE_ATOP, 0, 0);
$canvas->resizeImage(300, 600, Imagick::FILTER_LANCZOS, 1);
$canvas->setImageFormat('png');
$canvas->writeImage("output.png");
I am using imagecopy to crop a PNG image to a user specification.
Currently, if the crop area is bigger than the image, any "extra space" becomes black, but I would like it to be white.
Have searched around a bunch, I have discovered that you can use imagefill or imagefilledrectangle to make the background white, however if this is done before imagecopy, then it has no effect and if it is done after imagecopy, it also makes any black parts of the image white.
My code currently looks like this and suffers from black parts of the original image being turned white as well as the extra space:
// Input and output files
$infile = "[Image]";
$outfile = "[Output path for image]"
// Make the image
$orig =imagecreatefromjpeg($infile);
$width = imagesx($orig);
$height = imagesy($orig);
$new = imagecreatetruecolor($width, $height);
// Crop the image
imagecopy($new, $orig, 0, 0, -100, 100, $width, $height);
// Try and make the extra space white
$white = imagecolorallocate($new, 255,255,255);
imagefill($new, 0, 0, $white);
// Save the file
imagepng($new, $outfile);
How can I make that extra space white without affecting the original image? I have no control over what image users might upload, so I can't really pick a transparent color as that color might be part of their original image.
EDIT: This scenario arises when a user chooses a crop size outside of the original image dimensions, something that I do want to be a valid option. The crop is to force a square image, but if the user uploads, say, a landscape rectangle and wants all of their image in the final crop, then the crop will be outside of the image on the top and bottom (which is where I want it to be white instead of black)
This happens because you are supplying 'invalid' values to imagecopy() (that is, the crop coordinates are outside the bounds of the source image). GD simply fills in the out of bounds area with black pixels. It would be lovely if it instead used transparent (or any colour) pixels but unfortunately that's not an option.
I don't completely understand what you are trying to do (your source doesn't seem to match your stated goal) but a possible solution involves restricting the crop to the bounds of the image:
$src = imagecreatefromjpeg('JPEG FILE'); // 100x100 image in my test.
$src_w = imagesx($src);
$src_h = imagesy($src);
$user_crop = [
'x' => -50,
'y' => -50,
'width' => 150,
'height' => 150
];
if ($user_crop['x'] < 0) {
$user_crop['x'] = 0;
}
if ($user_crop['y'] < 0) {
$user_crop['y'] = 0;
}
if ($user_crop['x'] + $user_crop['width'] > $src_w) {
$user_crop['width'] = $src_w - $user_crop['x'];
}
if ($user_crop['y'] + $user_crop['height'] > $src_h) {
$user_crop['height'] = $src_h - $user_crop['y'];
}
$dest = imagecreatetruecolor($src_w, $src_h);
imagefill($dest, 0, 0, 0x00ffffff); // opaque white.
imagecopy(
$dest,
$src,
$user_crop['x'],
$user_crop['y'],
$user_crop['x'],
$user_crop['y'],
$user_crop['width'],
$user_crop['height']
);
header('Content-type: image/png;');
imagepng($dest);
imagedestroy($src);
imagedestroy($dest);
exit;
Note that I've made a few assumptions in this code about placement of the cropped image.
I have a simple watermark script which works well, but it seems adobe sRGB images lose color quality.
Running a watermark command via shell/imagemagick works great - no color quality lost.
Using imagick, however, dulls the color.
Here is the series of commands I use:
$image = new Imagick();
$image->readImage($this->source_path);
$watermark = new Imagick();
$watermark->readImage($this->watermark_path);
// how big are the images?
$iWidth = $image->getImageWidth();
$iHeight = $image->getImageHeight();
$wWidth = $watermark->getImageWidth();
$wHeight = $watermark->getImageHeight();
// calculate the position
$x = ( $iWidth - $wWidth ) / 2;
$y = ( $iHeight - $wHeight ) / 2;
//we have to make the transparency go to white, or it will become an awefull black color in jpeg version
$white = new Imagick();
$white->newImage($image->getImageWidth(), $image->getImageHeight(), "white");
if ($image->getImageColorspace() == Imagick::COLORSPACE_SRGB) {
$watermark->setColorspace(imagick::COLORSPACE_RGB);
$white->setColorspace(imagick::COLORSPACE_RGB);
}
$white->compositeimage($image, Imagick::COMPOSITE_OVER, 0, 0);
//now apply watermark
$white->compositeImage($watermark, imagick::COMPOSITE_OVER, $x, $y);
//save
$white->writeImage($this->destination);
//save memory
$image->destroy();
$white->destroy();
I made a half-educated assumption that if I convert the other two elements (the white background, and the png overlay) to sRGB, nothing would be lost. I did that with this segment here:
if ($image->getImageColorspace() == Imagick::COLORSPACE_SRGB) {
$watermark->setColorspace(imagick::COLORSPACE_RGB);
$white->setColorspace(imagick::COLORSPACE_RGB);
}
...Still no shrimp Lieutenant Dan...
Is there any possible way around this issue? Ideally I'd like to use the shell commands, but I'd like to perfect the imagick version for those who do not have shell access in their environments.
I found the solution here: http://www.php.net/manual/ru/imagick.compositeimage.php (was like a scavenger hunt!)
The solution as stated in above link:
You might need to set the colorspace the same when composing two
images over each other
<?php
//Creating two Imagick object
$first = new Imagick('first.jpg');
$second = new Imagick('second.jpg');
// Set the colorspace to the same value
$first->setImageColorspace($second->getImageColorspace() );
//Second image is put on top of the first
$first->compositeImage($second, $second->getImageCompose(), 5, 5);
//new image is saved as final.jpg
$first->writeImage('final.jpg');
?>
So I have a user image and a user specified point of interest based off of that image.
I have retrieved that point of interest from a XML file and put them into variables so I have 2 points.
x= 246
y= 73
My question: How can I crop a 45 by 53 thumbnail image with the above coordinates being the center-point of the thumbnail? I don't want the image to scale at all, just crop.
With GD it should work this way:
// Open source image
$srcImg = imagecreatefromjpeg ( $filename );
// Create new image for the cropped version
$destImg = imagecreate ( 45, 53 );
// Calculate the upper left of the image-part we want to crop
$startX = x - 45 / 2;
$startY = y - 53 / 2;
// Copy image part into the new image
imagecopy ( $destImg, $srcImg , 0, 0, $startX, $startY, 45, 53 );
// Write the new image with quality 90
imagejpeg($destImg, 'newfile.jpg', 90);
You might want to check for rounded coordinates as your image might blur if you don't. You should check if your cropped image coordinates are well within your original image if the user lets say chooses a corner as poi.