In my previous tutorial, we discussed basic image manipulation using the PHP GD library. In that tutorial, I gave a brief introduction to the library and showed you how to load images from a file or create them from scratch in PHP. After that, we learned how to crop, rotate, scale and flip an image using GD. I covered the imagefilter()
function to apply different filters to image resources loaded in the script. I also mentioned some useful functions in GD like imagesx()
and imagesy()
to get the width and height of the loaded image.
By the end of my last GD tutorial, you learned how to use the library to automate basic tasks like resizing all images in a directory or applying filters like grayscale on them before saving the final result. If you have never used the PHP GD library before, I would suggest that you go through that GD introductory article before reading this one.
In this tutorial, we will learn about many more useful functions in GD and how they can be used to automate more of our image manipulation tasks.
Manipulating Images Using a Convolution Matrix
Except for the pixels at the edges, every pixel in an image is surrounded by eight other pixels. Effects like blurring or edge detection are calculated for each pixel depending on that pixel’s value and the values of the surrounding pixels. In edge detection, for example, a sharp change in color implies that we have reached the edge of some object in the image. For instance, a sudden change from white to brown in the image below will signify the boundary of the cup and the table.
An easy way to specify this kind of filter is with what is called a “convolution matrix”. GD supplies the imageconvolution( $image, $matrix, $div, $offset)
function to apply a 3×3 convolution matrix to an image resource $image
.
The $matrix
parameter is an array of three arrays, each of which contains three float values—i.e. it is a 3×3 matrix. The first element of the first array is multiplied by the color value of the top-left pixel. Similarly, the second element of the first array is multiplied by the color value of the pixel directly on top of the central pixel. The final color of the pixel is obtained by adding the result of all these multiplications and then dividing it by $div
for normalization. Normalization generally keeps the final color value below 255.
As we’ve seen, the $div
parameter is used as a divisor for the result of convolution to normalize its value. The $offset
parameter, on the other hand, is used to specify an offset value for all colors. You will see how it affects the final result in the examples below.
Convolution Examples
Here is a list of some different convolution matrices that we have applied to the image of a cup on a table.
Box Blur
$box_blur = array([1, 1, 1], [1, 1, 1], [1, 1, 1]); imageconvolution($im_php, $box_blur, 9, 0);
Box blur works by just averaging each pixel with its neighbors. We set the value of the divisor to 9 because the sum of all elements in the three arrays is 9.
Sharpen
$sharpen = array([0, -1, 0], [-1, 5, -1], [0, -1, 0]); imageconvolution($im_php, $sharpen, 1, 0);
Sharpen works by exaggerating the differences between each pixel and its neighbors. This makes the edges a bit clearer. In the case of sharpen, the divisor is still 1 because the sum of all the elements in the three arrays is 1.
Emboss
$emboss = array([-2, -1, 0], [-1, 1, 1], [0, 1, 2]); imageconvolution($im_php, $emboss, 1, 0);
The emboss matrix is similar to the sharpen matrix, except that the values are negative to the upper left and positive to the lower right—that’s what creates the emboss effect. The sum of all the elements in the case of the emboss convolution matrix is 1, so we don’t have to worry about normalization or color offset.
Edge Detect
$edge_detect = array([-1, -1, -1], [-1, 8, -1], [-1, -1, -1]); imageconvolution($im_php, $edge_detect, 1, 0); imageconvolution($im_php, $edge_detect, 1, 255);
Edge detection is similar to sharpen, but the effect is even stronger. Also, the original value of the image is given no more weight than the neighbors—that means we only care about the edges, not the original solid colored areas.
With edge detection, the sum of all the array elements is 0. This means that the image we will get will mostly be black unless there is a sharp change in color, which generally occurs at the edges of objects. The mostly black image can be turned to white by setting the offset parameter to 255.
The following image shows the result of all these convolution matrices.
Image Copy Functions
PHP GD has a lot of functions to copy part of an image and then resize or merge it. When using these functions, it is important to remember that PHP considers the top-left corner of an image resource as its origin. A positive x value will take you to the right of the image, and a positive y value will take you further down.
The simplest of these functions is imagecopy( $dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h)
. It will copy the source image onto a destination image. The $dst_x
and $dst_y
parameters determine the top-left corner, where the copied image will be pasted. The $src_x
, $src_y
, $src_w
, and $src_h
parameters determine the rectangular portion of the source image, which will be copied to the destination.
You can use this function to crop images by creating an image from scratch using imagecreatetruecolor()
and copying the crop rectangle of the source image into it. You can also use it to add watermarks on images, but you have to remember that with this method, the size of the watermark cannot be changed according to the size of our images.
One solution to this problem is to use the imagecopyresized( $dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
function. It accepts all the parameters of imagecopy()
and two additional parameters to determine the size of the destination area where the source image will be copied.
The imagecopyresized()
function is not perfect, as it does not scale the image up and down very well. However, you can get better quality resizing using the imagecopyresampled()
function, which accepts all the same parameters.
Copy With Transparency
There are two more functions related to copying images which you will find very useful: imagecopymerge()
and imagecopymergegray()
.
The function imagecopymerge( $dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct)
is similar to imagecopy()
, where the additional $pct
parameter determines the transparency of the copied image. A value of 0 means no transparency, and a value of 100 means complete transparency. This will be of great help when you don’t want to completely hide the content of the main image behind your watermark.
The imagecopymergegray()
function, on the other hand, uses the last parameter to convert the source image to grayscale. If it’s set to 0, the source image will lose all its color. If it’s set to 100, the source image will remain unaffected.
Image Copy Example
The following example uses the imagecopy()
function to turn the right half of an image into its negative. We have already discussed other functions like imagefilter()
and imagescale()
used in this code snippet in the previous tutorial.
$im_php = imagecreatefromjpeg('fish-mosaic.jpg'); $im_php = imagescale($im_php, 800); $im_php_inv = imagescale($im_php, 800); $im_width = imagesx($im_php); $im_height = imagesy($im_php); imagefilter($im_php_inv, IMG_FILTER_NEGATE); imagecopy($im_php, $im_php_inv, $im_width/2, 0, $im_width/2, 0, $im_width/2, $im_height); $new_name = 'fish-mosaic-half-negate.jpg'; imagejpeg($im_php, $new_name);
Here, we create two copies of the original image, each of which has been scaled down to be 800 pixels wide. After that, we use the imagefilter()
function to create a negative of the $img_php_inv
image resource. The right half of this negative image is then copied onto the original image using the imagecopy()
function.
This was a very basic use of the imagecopy()
function. You can see the results below. You could also divide the image into smaller sections or stripes to create more interesting image effects. We will use the imagecopymergegray()
function in the code snippet below to create a lot more stripes in the original fish image.
$im_php = imagecreatefromjpeg('fish-mosaic.jpg'); $im_php = imagescale($im_php, 800); $im_php_bw = imagescale($im_php, 800); $im_width = imagesx($im_php); $im_height = imagesy($im_php); $stripes = 200; for($i = 0; $i < $stripes; $i++) { if($i%2 == 0) { imagecopymergegray($im_php, $im_php_bw, $i*$im_width/$stripes, 0, $i*$im_width/$stripes, 0, $im_width/$stripes, $im_height, 0); } else { imagecopymergegray($im_php, $im_php_bw, $i*$im_width/$stripes, 0, $i*$im_width/$stripes, 0, $im_width/$stripes, $im_height, 100); } } imagefilter($im_php, IMG_FILTER_CONTRAST, -255); imagefilter($im_php, IMG_FILTER_COLORIZE, 250, 0, 0, 100); $new_name = 'fish-mosaic-stripes.jpg'; imagejpeg($im_php, $new_name);
The above code example uses a similar strategy to the previous example, but this time we have divided the image into smaller stripes, which are turned to grayscale or kept unchanged based on the value of the variable $i
. After completing all the copy merge operations, we apply two filters on the image to make the stripes stand out.
The following image shows the final result of these two functions in conjunction with different image filters.
Embedding Watermarks or Other Information in Images
Some organizations add watermarks to their images in order to make it clear that they own the image. It also helps with brand recognition and discourages other people from blatantly copying the images. Thanks to PHP GD, watermarking images is a straightforward task.
$im_php = imagecreatefromjpeg('waterfall.jpg'); $watermark = imagecreatefrompng('watermark.png'); $im_width = imagesx($im_php); $im_height = imagesy($im_php); $watermark = imagescale($watermark, $im_width/5); $wt_width = imagesx($watermark); $wt_height = imagesy($watermark); imagecopy($im_php, $watermark, 0.95*$im_width - $wt_width, 0.95*$im_height - $wt_height, 0, 0, $wt_width, $wt_height); $new_name = 'waterfall-watermark.jpg'; imagejpeg($im_php, $new_name);
In the above code snippet, we have created two different image resources using imagecreatefromjpeg()
for the main image and imagecreatefrompng()
for the watermark respectively. We determine the width and height of the main image using the imagesx()
and imagesy()
functions.
Not all images that you want to watermark will have the same dimensions. If you don’t resize the watermark based on the dimensions of the main image, it could look weird. For example, a 200px watermark might look good on a 1000px image, but it will be too large for a 600px wide image, and it might look too small on a 2400px wide image.
Therefore, we use the imagescale()
function to always keep the watermark at one-fifth of the original image width. We then use the imagecopy()
function to place the watermark in the right location. Here is the final result of the above code snippet.
Besides watermarks, you can also add other information like the place where a photograph was taken or the time a photograph was taken.
Final Thoughts
After covering the basics of image manipulation in our previous tutorial, we learned about a few other useful functions in the GD library. The first part of the tutorial discussed how we can manipulate images in PHP using the convolution matrix. I also showed some examples of the convolution matrix operation to help you understand how PHP arrives at the color values of different pixels.
The second part of the tutorial explained how to copy and/or resize part of an image to paste it somewhere else. This is handy when we want to add something to an image like a watermark or a timestamp.
Try to use all these functions to create some interesting image effects!