Imagick Resize, Center, and Sparse Fill problems - php

My end goal here is to resize the input image to 100px width, 125px height. Some of the input images are a different Aspect Ratio, so I wish for them to be in a 100x125 container with the background sparse filled from their edge color.
Ok, so this works for the basic resize:
$image = new Imagick($imgFile);
$image->resizeImage(100,0, Imagick::FILTER_LANCZOS, 1, false);
$image->writeImage("$Dir/$game.png");
header("Content-type: ".$image->getImageFormat());
echo $image;
$image->clear();
$image->destroy();
However I've been searching for hours, and I cannot find a simple "This is how you center an image in a canvas" bit for PHP's Imagick library. Everything is for the actual ImageMagick convert application, which is not really what I'm after. I've tried compositing the resized image into an empty newImage with the set width and height, but it just seems to overwrite the dimensions regardless of the composite type, setting the Gravity to center and then the extent to 100x125 has no effect ( It always sits at 0,0, and trying to set the y offset to ((125-imageheight)/2) resulted in an offset that was way more than it should have been )
Edit:
$imageOutput = new Imagick();
$image = new Imagick($imgFile);
$image->resizeImage(100,0, Imagick::FILTER_LANCZOS, 1, false);
$imageOutput->newImage(100, 125, new ImagickPixel('black'));
$imageOutput->compositeImage($image, Imagick::COMPOSITE_ADD, 0, ((125 - $image->getImageHeight()))/2 );
$imageOutput->setImageFormat('png');
$imageOutput->writeImage("$Dir/$game.png");
header("Content-type: ".$imageOutput->getImageFormat());
echo $imageOutput;
$image->clear();
$image->destroy();
So I got my centering working, gravity apparently has no effect on actual images.
I have absolutely no idea where I would even begin to try and recreate a command line edge-in sparse fill in PHP with the library.

I ended up using a combination of Imagick and shell calls to convert itself, I'll eventually rewrite it to use entirely shell calls. I also changed my dimensions, here's the code:
$imageOutput = new Imagick(); // This will hold the resized image
$image = new Imagick($imgFile); // Open image file
$image->resizeImage(120,0, Imagick::FILTER_LANCZOS, 1, false); // Resize it width-wise
$imageOutput->newImage(120, 150, "none"); // Make the container with transparency
$imageOutput->compositeImage($image, Imagick::COMPOSITE_ADD, 0, ((150 - $image->getImageHeight())/2) ); // Center the resized image inside of the container
$imageOutput->setImageFormat('png'); // Set the format to maintain transparency
$imageOutput->writeImage("$Dir/$game.temp.png"); // Write it to disk
$image->clear(); //cleanup -v
$image->destroy();
$imageOutput->clear();
$imageOutput->destroy();
//Now the real fun
$edge = shell_exec("convert $Dir/$game.temp.png -channel A -morphology EdgeIn Diamond $Dir/$game.temp.edge.png"); // Get the edges of the box, create an image from just that
$shepards = shell_exec("convert $Dir/$game.temp.edge.png txt:- | sed '1d; / 0) /d; s/:.* /,/;'"); // get the pixel coordinates
$final = shell_exec("convert $Dir/$game.temp.edge.png -alpha off -sparse-color shepards '$shepards' png:- | convert png:- $Dir/$game.temp.png -quality 90 -composite $Dir/$game.jpg"); // Sparse fill the entire container using the edge of the other image as shepards , then composite that on top of this new image
unlink("$Dir/$game.temp.png"); // cleanup temp files
unlink("$Dir/$game.temp.edge.png");
set_header_and_serve("$Dir/$game.jpg"); // serve the newly created file

Related

How to crop image with dimensions (without quality loss) in PHP?

I need to crop the image with PHP by using the dimensions.
And save it into the local with JPEG format.
Dimensions that i receive is,
{"left":82.5,"top":48.875,"width":660,"height":371.25}
I need to crop from Original size of the image.
Ex. image is 1200x800, then the result image dimension from the actual size, not resizing or any. Because the quality should be same.
How could i use these params to crop the image ?
Is it possible ?
Use the built-in imagick class:
$image = realpath("/path/to/your/image.extension");
$cropped = realpath("/path/to/your/output/image.png");
$imObj = new Imagick();
$imObj->cropImage($width, $height, $offset_x, $offset_y);
$imObj->setImageFormat("png"); // this is unnesesary, you can force an image format with the extension of the output filename.
$imObj->writeImage($cropped);
As for lossless output, use an image format with lossless encoding. PNG is perfect for the job, since it was designed for network transfer (hence the "Adam-7" interlacing).
Check this related question about lossless image formats on graphic design stack:
What are lossless image formats?
You can use imageCopyResampled function which was designed pretty much exactly for this.
$image = imagecreatefromjpeg($imageFileURL);
/***
* resize values (imported)
***/
$left = 82;
$top = 49;
$width = 660;
$height = 371;
/***
* Create destination image
***/
$newImage = imagecreatetruecolor($width,$height);
$saveToFile = "destintion filespace of image file.jpg"
if(imagecopyresampled($newImage, $image, //dest/source images
0, 0, // dest coordinates
$left, $top, // source coordinates
$width, $height, // size of area to paste to
$width, $height // size of area to copy from
)){
imagejpeg($newImage,$saveToFile,100); //zero compression saved to file
print "image resized ok!!";
}
The new fileimage will be the size specified with $width,$height and will be offset from the original image by the values given in $left and $top. From your question this looks like what you want. This will not resize or change the compression of the image (until you save the file and then possibly set these details yourself).

How to clean background captcha image using imagick

I try to clean background captcha image using php imagick method and i used convert method as an alternative on console but i got same result. But it's not really worked.
Then i will use tesseract ocr for getting captcha code. There is a code for tesseract:
tesseract -psm 7 captcha.png output
Captcha image:
captcha
There is my code:
//Example Tutorial::backgroundMasking
function backgroundMasking()
{
//Load the image
$imagick = new \Imagick(realpath('captchaa.png'));
$backgroundColor = "rgb(162, 234, 160)";
$fuzzFactor = 0.1;
// Create a copy of the image, and paint all the pixels that
// are the background color to be transparent
$outlineImagick = clone $imagick;
$outlineImagick->transparentPaintImage(
$backgroundColor, 0, $fuzzFactor * \Imagick::getQuantum(), false
);
// Copy the input image
$mask = clone $imagick;
// Deactivate the alpha channel if the image has one, as later in the process
// we want the mask alpha to be copied from the colour channel to the src
// alpha channel. If the mask image has an alpha channel, it would be copied
// from that instead of from the colour channel.
$mask->setImageAlphaChannel(\Imagick::ALPHACHANNEL_DEACTIVATE);
//Convert to gray scale to make life simpler
$mask->transformImageColorSpace(\Imagick::COLORSPACE_GRAY);
// DstOut does a "cookie-cutter" it leaves the shape remaining after the
// outlineImagick image, is cut out of the mask.
$mask->compositeImage(
$outlineImagick,
\Imagick::COMPOSITE_DSTOUT,
0, 0
);
// The mask is now black where the objects are in the image and white
// where the background is.
// Negate the image, to have white where the objects are and black for
// the background
$mask->negateImage(false);
$fillPixelHoles = false;
if ($fillPixelHoles == true) {
// If your image has pixel sized holes in it, you will want to fill them
// in. This will however also make any acute corners in the image not be
// transparent.
// Fill holes - any black pixel that is surrounded by white will become
// white
$mask->blurimage(2, 1);
$mask->whiteThresholdImage("rgb(10, 10, 10)");
// Thinning - because the previous step made the outline thicker, we
// attempt to make it thinner by an equivalent amount.
$mask->blurimage(2, 1);
$mask->blackThresholdImage("rgb(255, 255, 255)");
}
//Soften the edge of the mask to prevent jaggies on the outline.
$mask->blurimage(2, 2);
// We want the mask to go from full opaque to fully transparent quite quickly to
// avoid having too many semi-transparent pixels. sigmoidalContrastImage does this
// for us. Values to use were determined empirically.
$contrast = 15;
$midpoint = 0.7 * \Imagick::getQuantum();
$mask->sigmoidalContrastImage(true, $contrast, $midpoint);
// Copy the mask into the opacity channel of the original image.
// You are probably done here if you just want the background removed.
$imagick->compositeimage(
$mask,
\Imagick::COMPOSITE_COPYOPACITY,
0, 0
);
// To show that the background has been removed (which is difficult to see
// against a plain white webpage) we paste the image over a checkboard
// so that the edges can be seen.
// Create the check canvas
$canvas = new \Imagick();
$canvas->newPseudoImage(
$imagick->getImageWidth(),
$imagick->getImageHeight(),
"pattern:checkerboard"
);
// Copy the image with the background removed over it.
$canvas->compositeimage($imagick, \Imagick::COMPOSITE_OVER, 0, 0);
$max = $imagick->getQuantumRange();
$max = $max["quantumRangeLong"];
$canvas->thresholdimage(0.77 * $max);
$canvas->negateImage(false, \Imagick::CHANNEL_ALL);
//Output the final image
$canvas->setImageFormat('png');
header("Content-Type: image/png");
echo $canvas->getImageBlob();
}
Result captcha image: enter image description here
what can i do for getting better result?

Calculate nearest possible dimension value PHP (keeping ratio)

I am trying to implement API for image resizing. It is created not exactly for image processing, this is only one part/feature of API.
What I want to implement.
I have url for retrieving image from server it looks like
mywebpage.com/api/product/42/image
This url will return URL to full image of product with id 42.
Everything is ok.
We can specify desired size with GET parameters like this
mywebpage.com/api/product/42/image?width=200&height=300
It also looks fine
But my question if following.
As we can have different images on server with different dimension and aspect ratio, I need to keep this ratio while resizing.
For example I need image to fit 200x300 container but I have 1024x576 (16:9) image on the server. I need to resize this image but keep initial aspect ratio(16:9) but to fit desired container.
How can I efficiently calculate new image size to return depending on incoming desired dimension and current image aspect ratio.
I want to thank everyone in advance for any help or advises.
Here is a script I used to make similar thing. Quite old , so may be not up to date.
<?php
if( isset($_GET["width"]) && is_numeric($_GET["width"]))
$target_width = intval($_GET["width"]);
else
$target_width= 200;//default value
if( isset($_GET["height"]) && is_numeric($_GET["height"]))
$target_height = intval($_GET["width"]);
else
$target_height= 300;//default value
if( isset($_GET["id"]) && is_numeric($_GET["id"]))//prevent any unwanted filesystem access
$original_image_path = "img/products/$id.jpg";
else
$original_image_path = "placeholder.png"
//http://php.net/manual/fr/function.getimagesize.php
$image_size = getimagesize($original_image_path);
//get the ratio of the original image
$image_ratio= $image_size[1]/ $image_size[0];
$original_image = imagecreatefromjpeg($original_image_path);
$new_image = imagecreatetruecolor($target_width, $image_ratio * $target_width);
//paints the image in white
//http://php.net/manual/en/function.imagefill.php
//http://php.net/manual/en/function.imagecolorallocatealpha.php
imagefill( $new_image, 0, 0, imagecolorallocatealpha($new_image, 255,255,255,127) );
imagesavealpha($new_image, TRUE);
/*
Copies the original to the new, preserving the ratio.
The original image fills all the width of the new,
and is placed on the top of the new.
http://php.net/manual/en/function.imagecopyresized.php
*/
imagecopyresized(
$new_image,$original_image,
0,0,
0,0,
$target_width,$image_ratio * $target_width,
$image_size[0],$image_size[1]
);
//image is returned in response to the request
header ("Content-type: image/png");
imagepng( $new_image );
?>
Well if you need to always fit a container of 200x300 (or what ever is passed through the URL), you may not because able to simply resize it because as you are aware it will affect the images aspect ratio.
If this is the case what you can do is resize the image to the closest size then crop the remainder of the image.
I assume you will be using imagemagick for this. Have you checked out the documentation? The cropThumbnailImage method does what I just explained.
Example usage:
/* Read the image */
$im = new imagick( "test.png" );
/* create the thumbnail */
$im->cropThumbnailImage( 80, 80 );
/* Write to a file */
$im->writeImage( "th_80x80_test.png" );
http://php.net/manual/en/imagick.cropthumbnailimage.php

circularize an image with imagick

Trying to take a rectangular photo, crop it into a square region, and then mask it into a circular with a transparent background.
//$dims is an array with the width, height, x, y of the region in the rectangular image (whose path on disk is $tempfile)
$circle = new \Imagick();
$circle->newImage($dims['w'], $dims['h'], 'none');
$circle->setimageformat('png');
$circle->setimagematte(true);
$draw = new \ImagickDraw();
$draw->setfillcolor('#ffffff');
$draw->circle($dims['w']/2, $dims['h']/2, $dims['w']/2, $dims['w']);
$circle->drawimage($draw);
$imagick = new \Imagick();
$imagick->readImage($tempfile);
$imagick->setImageFormat( "png" );
$imagick->setimagematte(true);
$imagick->cropimage($dims['w'], $dims['h'], $dims['x'], $dims['y']);
$imagick->compositeimage($circle, \Imagick::COMPOSITE_DSTIN, 0, 0);
$imagick->writeImage($tempfile);
$imagick->destroy();
The result is the rectangular image, uncropped and without being circularized. What am I doing wrong?
Example image:
Example input for $dims = {"x":253,"y":0,"x2":438.5,"y2":185.5,"w":185.5,"h":185.5}
Rough expected output:
Image i'm getting looks roughly like the input image.
For those with an older version of Imagick (setimagematte does not exist in version lower than 6.2.9), I came up with an easy solution. The thing here is to copy opacity from the mask to the original image.
Original Image:
Mask:
Result:
The code:
$base = new Imagick('original.jpg');
$mask = new Imagick('mask.png');
$base->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
$base->writeImage('result.png');
You could use an Imagick black circle as mask but I though it wasn't perfect so I used my own.
Of course you will certainly have to resize / crop your images but that's another story.
Hope this helps.
J.
This works for me:
<?php
//$dims is an array with the width, height, x, y of the region in the rectangular image (whose path on disk is $tempfile)
$tempfile = 'VDSlU.jpg';
$outfile = 'blah.png';
$circle = new Imagick();
$circle->newImage(185.5, 185.5, 'none');
$circle->setimageformat('png');
$circle->setimagematte(true);
$draw = new ImagickDraw();
$draw->setfillcolor('#ffffff');
$draw->circle(185.5/2, 185.5/2, 185.5/2, 185.5);
$circle->drawimage($draw);
$imagick = new Imagick();
$imagick->readImage($tempfile);
$imagick->setImageFormat( "png" );
$imagick->setimagematte(true);
$imagick->cropimage(185.5, 185.5, 253, 0);
$imagick->compositeimage($circle, Imagick::COMPOSITE_DSTIN, 0, 0);
$imagick->writeImage($outfile);
$imagick->destroy();
?>
<img src="blah.png">
I always try to keep the code simple until I get it working and then add all the variables etc. That could be the problem or there could be a problem with your version of Imagick.
It's namespaced
Still do not know what it means! - I am getting a bit behind with php as I do not use it very much these days.
There's also another workaround that I suggest here :
// create an imagick object of your image
$image = new \Imagick('/absolute/path/to/your/image');
// crop square your image from its center (100px witdh/height in my example)
$image->cropThumbnailImage(100, 100);
// then round the corners (0.5x the width and height)
$image->roundCorners(50, 50);
// force the png format for transparency
$image->setImageFormat("png");
// write the new image
$image->writeImage('/absolute/path/to/your/new/image');
// done!
Many thanks to all previous answers and contributors that lead me to this code!
Feel free to test/comment my solution!
I stumbled upon this as I was searching for a similar solution for Ruby on Rails, notice that this Stackoverflow question uses vignette instead which seems to be a much simpler way to solve the problem.
I used vignette to solve my problem with rounded images in Ruby on Rails using Dragonfly.

php imagick, how to make an area transparent

I want to make an area transparent within an Imagick object with a specific width, height and a top position.
For example I need a transparent area with 30px x 30px from the 15th px to the top but I can't find a way to do it.
$canvas1 = new Imagick();
$canvas1->newImage(30,60,'black','png');
Please help.
This may be a slightly simpler way of doing it. I recycled #AndreKR's setup code to get started:
$im = new Imagick();
$im->newImage(100,100, 'red');
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); // make sure it has an alpha channel
$box=$im->getImageRegion(30,30,15,15);
$box->setImageAlphaChannel(Imagick::ALPHACHANNEL_TRANSPARENT);
$im->compositeImage($box,Imagick::COMPOSITE_REPLACE,15,15);
While you can flood fill with transparency ink (not transparent ink) like this:
$im->floodFillPaintImage('#FF000000', 10, '#FFFFFF', 0, 0, false);
in this post, Anthony, apparently some important figure in the ImageMagick universe, says that you cannot draw with transparency.
So it seems you have to create a punch image and then use it to punch the transparent areas out in your actual image. To create the punch here I draw the rectangle opaque on a transparent brackground and then invert the whole image:
$punch = new Imagick();
$punch->newImage(100,100, 'transparent');
$drawing = new ImagickDraw();
$drawing->setFillColor(new ImagickPixel('black'));
$drawing->rectangle(15, 15, 45, 45);
$punch->drawImage($drawing);
$punch->negateImage(true, Imagick::CHANNEL_ALPHA);
Here's the actual image before the punching:
$im = new Imagick();
$im->newImage(100,100, 'red');
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); // make sure it has
// an alpha channel
Now we can copy over the alpha channel from our punch image. For a reason unknown to me the obvious way does not work:
// Copy over the alpha channel from one image to the other
// this does NOT work, the $channel parameter seems to be useless:
// $im->compositeImage($punch, Imagick::COMPOSITE_SRC, 0, 0, Imagick::CHANNEL_ALPHA);
However, these both work:
// Copy over the alpha channel from one image to the other
// $im->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
// $im->compositeImage($punch, Imagick::COMPOSITE_DSTIN, 0, 0);
(The light blue is the background of the Windows photo viewer, indicating transparent areas.)
You can set the opacity as follows
$image->setImageOpacity(0.0);
If you set it to 0.0 the image what you have crated will become transparent
for more information you can Set opacity in Imagick
if you want it for a particular area part then you need to change the approach by using GD library functions by doing some what like this
$img = imagecreatefrompng($imgPath); // load the image
list($width,$height) = getimagesize($imgPath); // get its size
$c = imagecolortransparent($img,imagecolorallocate($img,255,1,254)); // create transparent color, (255,1,254) is a color that won't likely occur in your image
$border = 10;
imagefilledrectangle($img, $border, $border, $width-$border, $height-$border, $c); // draw transparent box
imagepng($img,'after.png'); // save
I Could see a similar requirement which is posted in another forum here
Try
$canvas1->setImageOpacity(0);

Categories