Select PHP's Image resizing algorithm - php

The function imagecopyresampled is useful to generate a thumbnail or resize images, while keeping aspect ratio:
$fn = $_FILES['data']['tmp_name'];
$size = getimagesize($fn);
$width = $size[0];
$height = $size[1];
$ratio = $width / $height;
if ($ratio > 1 && $size[0] > 500) { $width = 500; $height = 500 / $ratio; }
else { if ($ratio <= 1 && $size[1] > 500) { $width = 500 * $ratio; $height = 500; }}
$src = imagecreatefromstring(file_get_contents($fn));
$dst = imagecreatetruecolor($width, $height);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
imagedestroy($src);
imagejpeg($dst, 'test.jpg');
imagedestroy($dst);
How can I select the resizing algorithm used by PHP?
Note: as stated in this question, setting imagesetinterpolation($dst, IMG_BILINEAR_FIXED); or such things doesn't seem to work.
According to tests I did (in another language), "bilinear resizing" sometimes gives better result than bicubic, and sometimes it's the contrary (depends if it's downsizing or upsizing).
(source: dpchallenge.com)

An alternative is the imagescale() function, that allows specifying the interpolation algorithm as a parameter:
imagescale($image, $new_width, $new_height, $algorithm);
According to the documentation $algorithm can be:
One of IMG_NEAREST_NEIGHBOUR, IMG_BILINEAR_FIXED, IMG_BICUBIC, IMG_BICUBIC_FIXED or anything else (will use two pass).
A test in PHP 7.0.15 (comparing file hash) shows that BICUBIC and BICUBIC_FIXED result in a different image, while BILINEAR_FIXED and NEAREST_NEIGHBOUR result in the same image.

imagecopyresampled is based/part of LibGD , by looking at the source code of LibGD, you can see clearly its implementation , also the documentation is not ambiguous about the used algorithm as it's stated that :
If the source and destination area differ in size, the area will be resized using bilinear interpolation for truecolor images, and nearest-neighbor interpolation for palette images.
So how can you select the resizing algorithm used by PHP?
If you insist/must use LibGD functions, you can't (unless you recompile PHP with a LibGD fork you code just for this matter).
However if you're free to use another image manipulation library, you can simply use one that use a different algorithm for resizing, for example Imagick seems to offer a wide range of interpolations but since the documentation is quite mute about it here is the constants needed to use the Imagick::setImageInterpolateMethod(int $) method :
const INTERPOLATE_UNDEFINED = 0;
const INTERPOLATE_AVERAGE = 1;
const INTERPOLATE_BICUBIC = 2;
const INTERPOLATE_BILINEAR = 3;
const INTERPOLATE_FILTER = 4;
const INTERPOLATE_INTEGER = 5;
const INTERPOLATE_MESH = 6;
const INTERPOLATE_NEARESTNEIGHBOR = 7;
const INTERPOLATE_SPLINE = 8;

Well you could download the PHP Source , add your filter function and compile php.
here you can find the filters https://github.com/php/php-src/blob/master/ext/gd/libgd/gd_interpolation.c#L481
here is the switch case where you have to apply the method https://github.com/php/php-src/blob/master/ext/gd/libgd/gd_interpolation.c#L2530
here you can define constants https://github.com/php/php-src/blob/master/ext/gd/libgd/gd.h#L137
happy hacking :D

Why do you not use a library? I think that if you use a php library it will be more easy. Try this one. Hope it will help you.

Related

Crop and resize theory

I need some guidance on the theory behind cropping and resizing images around a focal point, with a bounding box.
In my situation, I have a variety of different image size requirements (100x100, 500x200, 1200x50 for example) at different definitions (1x, 2x, 3x etc.).
The definitions effectively turn 50x50 cropped image into a 100x100 cropped image, thus providing a 2x definition for higher screen definition devices.
I'm provided with a user's uploaded image with an x,y focal point, and a bounding box with two x,y coordinates (topLeft[x,y], bottomRight[x,y]).
What is the theory being turning my user provided image into a variety of images at different sizes and resolutions? Research has led me to find one or the other, but not all of my requirements together.
In my specific environment, I'm using PHP, Laravel and the image Intervention library, although this is somewhat irrelevant due to the nature of this question.
Here's an image class I wrote some while back which uses the GD lib.
https://github.com/delboy1978uk/image/blob/master/src/Image.php
I didn't write code for resizing and cropping based on a focal point, but I do have a resizeAndCrop() method that works on the premise that the focal point is in the centre:
public function resizeAndCrop($width,$height)
{
$target_ratio = $width / $height;
$actual_ratio = $this->getWidth() / $this->getHeight();
if($target_ratio == $actual_ratio){
// Scale to size
$this->resize($width,$height);
} elseif($target_ratio > $actual_ratio) {
// Resize to width, crop extra height
$this->resizeToWidth($width);
$this->crop($width,$height,true);
} else {
// Resize to height, crop additional width
$this->resizeToHeight($height);
$this->crop($width,$height,true);
}
}
here's the crop() method, which you can set a focal point to left, center or right:
/**
* #param $width
* #param $height
* #param string $trim
*/
public function crop($width,$height, $trim = 'center')
{
$offset_x = 0;
$offset_y = 0;
$current_width = $this->getWidth();
$current_height = $this->getHeight();
if($trim != 'left')
{
if($current_width > $width) {
$diff = $current_width - $width;
$offset_x = ($trim == 'center') ? $diff / 2 : $diff; //full diff for trim right
}
if($current_height > $height) {
$diff = $current_height - $height;
$offset_y = ($trim = 'center') ? $diff / 2 : $diff;
}
}
$new_image = imagecreatetruecolor($width,$height);
imagecopyresampled($new_image,$this->_image,0,0,$offset_x,$offset_y,$width,$height,$width,$height);
$this->_image = $new_image;
}
I won't bother explaining imagecopyresampled() since you were just looking for theory behind the cropping, but the docs are here http://php.net/manual/en/function.imagecopyresampled.php
Bear in mind, resizing images using PHP's GD library is memory intensive, depending on the size of the image. I like to use imagemagick, which PHP has a wrapper class called Imagick, worth looking at if you run into trouble.
I hope this helps you, good luck!

Name of image and way to generate it

I frequently see the following kind of images on many websites (on StackOverflow for example).
What is this kind of image called and how do I generate it? (preferably in PHP)
Duplicate question, but here is more to read about: http://en.wikipedia.org/wiki/Identicon
Using GD library and this source http://sourceforge.net/projects/identicons/ for PHP
Quote from wiki, for those who are lazy to open the link:
An Identicon is a visual representation of a hash value, usually of an IP address, that serves to identify a user of a computer system as a form of avatar while protecting the users' privacy. The original Identicon was a 9-block graphic, and the representation has been extended to other graphic forms by third parties.
You can a try this code something similar to your above query.
[Not a direct answer though]
<?php
$Width = 64;
$Height = 64;
$Image = imagecreate($Width, $Height);
for($Row = 1; $Row <= $Height; $Row++) {
for($Column = 1; $Column <= $Width; $Column++) {
$Red = mt_rand(0,255);
$Green = mt_rand(0,255);
$Blue = mt_rand(0,255);
$Colour = imagecolorallocate ($Image, $Red , $Green, $Blue);
}
}
header('Content-type: image/png');
imagepng($Image);
?>
Generates a random color each and every time like this.
Just google 'gravatar'.... (as an alternative to what u wanted)

What's the best (fastest for performance) code in PHP to resize very large images (3000x6000px)?

I found many examples on how to resize an image however I'm curious about what's the best (fastest) code in PHP to do it and for very large images (over 1000px).
I wrote this simple example... anyone knows a better implementation?
<?php
$filename = 'myimage.jpg';
$image = imagecreatefromjpeg($filename);
$scale = 50; // resize the image to 50% of its original width and height
$width = imagesx($image);
$width_scaled = $width * $scale/100;
$height = imagesy($image);
$height_scaled = $height * $scale/100;
$image_scaled = imagecreatetruecolor($width_scaled, $height_scaled);
imagecopyresampled($image_scaled, $image, 0, 0, 0, 0, $width_scaled, $height_scaled, $width, $height);
?>
I'd use imagecopyresized. I think it's faster because you skip one step. However, the actual resizing is probably the most time consuming, so you won't gain much unless you find a separate library/plugin that is faster.

Setting the grayscale value of a pixel in php

I have a jpeg image resource loaded into a variable in php. Given a grayscale value, like 6, how can I set a single, particular pixel to that grayscale value? My client has made it exceedingly clear that there is a big difference between grayscale and rgb.
Is this even doable in php with the GD library? If so, how?
Note: The script does indeed grayscale the entire image, but using an obscure algorithm. My script obtains the RGB for each pixel, and obtains the grayscale value corresponding to the algorithm. It just needs to now transform that pixel to that particular grayscale value.
You will probably find the the imagesetpixel function handy. The following is an example of how you could use it:
// $image = <GD image resource>
for($x = 0; $x < $width; x++)
{
for($y = 0; $y < $height; $y++)
{
$value = specialFunction($rgb_value);
// Depending on what the above function returns, the call below
// might have to be changed
$color = imagecolorallocate($image, $value, $value, $value);
imagesetpixel($image, $x, $y, $color);
}
}
Do you have access to the terminal or something along those lines to install Image Magick. If so, then you can could use the following
convert image.jpg -colorspace Gray image-bw.jpg
I'm not sure how to do it with the PHP GD Library sorry.

PHP Thumbnail Image Resizing with proportions

As a brief run down, I am currently making a dating type site. Users can create accounts and upload profile pictures (up to 8). In order to display these in the browse area of the website, I am looking for a way in PHP (with third party processor/scripts) to resize all images uploaded to have thumbnails that adhere to certain dimensions.
As an example, I will want "profile" images (thumbnails) to be NO larger than 120*150px. The scripting needs to resize uploaded images (regardless of whether they are portrait or landscape, and regardless of proportions) to adhere to these dimensions without getting stretched.
The width (eg. 120pixels) should always remain the same, but the height (eg. 150px) can vary in order to keep the image in proportion. If it's a landscape photo, I'm assuming the script would need to take a chunk out of the middle of the image?
The reason that all images to be resized is so that when profiles are display in a grid that all thumbnails are roughly the same size.
Any input would be greatly appreciated.
$maxwidth = 120;
$maxheight = 150;
$img = imagecreatefromjpeg($jpgimage);
//or imagecreatefrompng,imagecreatefromgif,etc. depending on user's uploaded file extension
$width = imagesx($img); //get width and height of original image
$height = imagesy($img);
//determine which side is the longest to use in calculating length of the shorter side, since the longest will be the max size for whichever side is longest.
if ($height > $width)
{
$ratio = $maxheight / $height;
$newheight = $maxheight;
$newwidth = $width * $ratio;
}
else
{
$ratio = $maxwidth / $width;
$newwidth = $maxwidth;
$newheight = $height * $ratio;
}
//create new image resource to hold the resized image
$newimg = imagecreatetruecolor($newwidth,$newheight);
$palsize = ImageColorsTotal($img); //Get palette size for original image
for ($i = 0; $i < $palsize; $i++) //Assign color palette to new image
{
$colors = ImageColorsForIndex($img, $i);
ImageColorAllocate($newimg, $colors['red'], $colors['green'], $colors['blue']);
}
//copy original image into new image at new size.
imagecopyresized($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
imagejpeg($newimg,$outputfile); //$output file is the path/filename where you wish to save the file.
//Have to figure that one out yourself using whatever rules you want. Can use imagegif() or imagepng() or whatever.
This will shrink any images down proportionally based on whichever side is longer (width or height), to the maximum size. It will also blow up any images smaller than max, which you can stop with a bit of checking on whether or not both width and height are less than their max. So, a 200x300 image will be shrunk to 100x150, and a 300x200 image will be shrunk to 120x80.
Hmm, you want the width to always be 120, so it would change a bit, and yeah, it would have to cut something out in the case of an image like 200x300, because that would shrink to 120x180 without any distortion, or it would have to shrink it farther and letterbox it, but that should get you started nicely.
Letterboxing this example would just involve figuring out what the proper x and y to start the drawing to the new image would be in the imagecopyresized() function. In the case of something like 100x150, the X would be 10, I think, so there would be 10px of blank space on each side for 120x150 in the end. Letterboxing 120x80 X would be 0 but Y would be 35, so there would be 35px of blank space above and below for 120x150.
You'd also want to make $newimg with $maxwidth,$maxheight rather than $newwidth,$newheight, but the imagecopyresized() would still use both $new values.
Since I'm bored and don't have anything else to do, these changes would do it:
if ($height > $width)
{
$ratio = $maxheight / $height;
$newheight = $maxheight;
$newwidth = $width * $ratio;
$writex = round(($maxwidth - $newwidth) / 2);
$writey = 0;
{
else
{
$ratio = $maxwidth / $width;
$newwidth = $maxwidth;
$newheight = $height * $ratio;
$writex = 0;
$writey = round(($maxheight - $newheight) / 2);
}
$newimg = imagecreatetruecolor($maxwidth,$maxheight);
//Since you probably will want to set a color for the letter box do this
//Assign a color for the letterbox to the new image,
//since this is the first call, for imagecolorallocate, it will set the background color
//in this case, black rgb(0,0,0)
imagecolorallocate($newimg,0,0,0);
//Loop Palette assignment stuff here
imagecopyresized($newimg, $img, $writex, $writey, 0, 0, $newwidth, $newheight, $width, $height);
That should work, haven't tried it yet.
GD or Imagick functions are what you need depending on your PHP configuration.
Sorry I'm a newbie I can't post both links in the same message :(
I have recently needed php resizing (thumbnail) solution and found Zebra_Image library, which is a lightweight image manipulation library written in PHP. The code is really clean and it's also easy to use. I highly recommend using this library. The example code is just enough to get you started.
Be make sure that you have enough memory limit set in php.ini file to manipulate images which have relatively big resolution (e.g 2560x1600). I had an error with big images and there was no error to print. I debugged the problem down to imagecreatefrom{gif,jpeg,png} calls in function _create_from_source in lines 1262, 1279, and 1288. The calls were silented with # so I couldn't have a change to get the error. I removed # lines and saw a PHP error that memory limit has been exceeded. The original memory limit was set to 32MB and I increased it to 64MB. Now I can manipulate 2560x1600 and I refuse to manipulate images which are bigger.
Below is the example code for controlling the image resolution.
$image_properties = getimagesize($UPLOADED_FILE_PATH);
$file_width = $image_properties[0];
$file_height = $image_properties[1];
if ($file_width > 2560 || $file_height > 1600) {
// handle your error whichever you like, I simply 'die' just to show
die('Cannot manipulate image bigger than 2560x1600');
}
(Note: I use Zebra Image version 2.2.2)
You can do this with ImageMagick:
convert susan.jpg -resize x200 susan_thumb.jpg
This runs with a shell command, so in PHP you'd use shell_exec() to run the above command. I don't think you need any PHP extensions.
A few flags can be found in the ImageMagick documentation to control the resizing operation. If I recall correctly, the x200 before the number means "scale with the same aspect ratio to 200px".
I wrote an install guide for ImageMagick (and ghostscript): How to install, test, convert, resize PDF using ImageMagick, Ghostscript, Windows Vista/7 x64 based on my bumbling around, maybe it can help you.
The other option is the GD library (detailed in dqhendricks answer). This is faster and apparently better documented, used for basic operations.
you don't need imagick. here is a link that will take you to a function that will resize any image using PHP GD to any arbitrary size. the function has options to use letterboxing or crop-to-fit methods to resize to the new aspect ratio. the function is also explained thoroughly. check it out.
http://www.spotlesswebdesign.com/blog.php?id=1
if this is what you are looking for, please select the check mark next to this answer. thanks!
Some says imagic faster, i use next
function resizeImageProportional($imagePath, $destinationPath, $width = false, $height = false, $filterType = \Imagick::FILTER_POINT, $blur = 1, $bestFit = false, $cropZoom = false)
{
if (!$width && !$height) {
trigger_error("WTF_IMAGE_RESIZE");
return false;
}
//The blur factor where > 1 is blurry, < 1 is sharp.
$imagick = new \Imagick(realpath($imagePath));
if (!$width || !$height) {
$orig_width = $imagick->getImageWidth();
$orig_height = $imagick->getImageHeight();
$proportion = $orig_height/$orig_width;
if (!$width) {
$width = $height*$proportion;
} elseif (!$height) {
$height = $width*$proportion;
}
}
if ($bestFit) {
$imagick->resizeImage($width, $height, $filterType, $blur, $bestFit);
} else {
$imagick->resizeImage($width, $height, $filterType, $blur);
}
if ($cropZoom) {
$cropWidth = $imagick->getImageWidth();
$cropHeight = $imagick->getImageHeight();
$newWidth = $cropWidth / 2;
$newHeight = $cropHeight / 2;
$imagick->cropimage(
$newWidth,
$newHeight,
($cropWidth - $newWidth) / 2,
($cropHeight - $newHeight) / 2
);
$imagick->scaleimage(
$imagick->getImageWidth() * 4,
$imagick->getImageHeight() * 4
);
}

Categories