Crop and Resize Images with ImageMagick

    Sandeep Panda
    Share

    If your website allows users to upload photos, image cropping/resizing functionality certainly comes in handy. But users might not have access to image manipulation tools like Photoshop, so by providing a cropping/resizing feature you can allow users to upload photos from any device (e.g. tablets or phones) without them having to worry about the the final size. Furthermore, you can create different versions of the same image and also allow users to crop specific portions of uploaded picture.

    In this article I’ll show you how to create an image cropping tool with the help of the ImageMagick PHP extension. This tutorial assumes that you already have the extension installed, so if not then be sure to read the manual.

    Getting Familiar

    The ImageMagick extension performs image processing using the ImageMagick library. ImageMagick provides a lot of API methods through which you can manipulate an image. It offers a simple object-oriented interface to use the API; you just need to create an instance of the Imagick class and then call the appropriate methods to start manipulating the images.

    Since we’re going to create an image cropper, we’ll mostly be using the two methods: cropImage() and thumbnailimage().

    cropImage

    The cropImage() method accepts four arguments. The first two arguments indicate the height and width of the cropped region, and the last two indicate the X and Y coordinates of the top-left corner of the cropped area.

    For example:

    <?php
    $inFile = "test.jpg";
    $outFile = "test-cropped.jpg";
    $image = new Imagick($inFile);
    $image->cropImage(400,400, 30,10);
    $image->writeImage($outFile);

    We create an Imagick object first, passing to its constructor the filename of our image. Then we call cropImage() with appropriate arguments. In this case the code will produce a cropped image of size 400×400px starting at 30px from the top and 10px in from the left of the original image. Finally, writeImage() saves the result back to disk for us.

    thumbnailImage

    The thumbnailImage() method simply accepts the height and width of the resized image and can be used as follows:

    <?php
    $inFile = "test.jpg";
    $outFile = "test-thumbnail.jpg";
    $image = new Imagick($inFile);
    $image->thumbnailImage(200, 200);
    $image->writeImage($outFile);

    The above code produces a 200×200px version of image. If either the width or height argument to thumbnailImage() is set as 0, the aspect ratio is maintained.

    We can also pass a third argument known as bestfit; If this is set true, the image will be resized in such a way that the new dimensions can be contained within the height and width specified. For example, a call to thumbnailImage(400, 400, true) on a 1200×768px image will produce a 400×200px version. The new dimensions are less than or equal to the specified dimensions.

    Now that you’re familiar with the two methods, we’re good to go.

    Upload and Cropping

    Of course we’ll need to create an HTML form with which users can upload photos:

    <form action="upload.php" method="post" enctype="multipart/form-data">
     <label for="file">Image:</label>
     <input type="file" name="file" id="file"><br>
     <input type="submit" name="submit" value="Upload and Crop">
    </form>

    As soon as the user hits the upload button a POST request will be sent to a script which will handle the file upload process and show the uploaded image to the user for cropping. Remember, when uploading files you need to set the form’s enctype attribute to “multipart/form-data”.

    An upload script handles the image upload and resizing the image if required.

    <?php
    if (isset($_FILES["file"])) {
        $tmpFile = $_FILES["file"]["tmp_name"];
        $fileName = ... // determine secure name for uploaded file
    
        list($width, $height) = getimagesize($tmpFile);
        // check if the file is really an image
        if ($width == null && $height == null) {
            header("Location: index.php");
            return;
        }
        // resize if necessary
        if ($width >= 400 && $height >= 400) {
            $image = new Imagick($tmpFile);
            $image->thumbnailImage(400, 400);
            $image->writeImage($fileName);
        }
        else {
            move_uploaded_file($tmpFile, $fileName);
        }
    }

    You should be very careful while accepting files from users as someone can upload a malicious file to your server if you don’t create a secure upload system. For more information, read the article File Uploads with PHP, paying special attention to the “Security Considerations” section.

    The getimagesize() function returns a null height and width if the file is not an image, so if the code detects the file is not an image, it simply redirects the user elsewhere. Additionally, you can also check the image type and size of the file using getimagesize(), but in this case we just check if the file is an image based on the null values.

    Since the script already knows the width and height of the uploaded image at this point, it can determine whether the image should be resized down. If dimensions are more than 400×400px, it’s resized to create a 400×400px thumbnail by calling thumbnailImage(). As I explained earlier, the method takes two parameters: width and height. If you want to maintain the image’s aspect ratio, it’s recommended to pass 0 as one of the arguments.

    Finally, the resized image is saved by calling writeImage(), or moved with move_uploaded_file() if it was already a suitable size.

    The saved image is outputted to the browser so that the user gets a chance to crop it. To allow users to select specific portion of the image, I use a jQuery plugin called ImageAreaSelect. To use the plugin, the jQuery library needs to be available, as well as the plugin’s CSS and JavaScript files.

    <script type="text/javascript" src="scripts/jquery.min.js"></script>
    <script type="text/javascript" src="scripts/jquery.imgareaselect.pack.js"></script>
    <link rel="stylesheet" type="text/css" href="css/imgareaselect-default.css">

    To initialize the plugin you can use the following code:

    selection = $('#photo').imgAreaSelect({
        handles: true,
        instance: true
    });

    #photo is the id of the image shown to the user. By setting the handles property true you can show resize handles on the selection area, and instance true gets an instance and save it to the variable selection. It’s through selection that you can easily retrieve the selection area later when the user finalizes his cropping.

    getSelection() gives you everything you need. For example, getSelection().width gives you the width of the selection area. Similarly, you can use getSelection().x1 and getSelection().y1 to find the coordinates of the top left-corner of the selection area.

    Putting this all together in a page, you’ll most likely register an onclick callback to a link or button, which the user can click to finalize his selection. When the button is clicked, the selection area width, height, and top-left corner coordinates is retrieved.

    $("#crop").click(function(){
        var s = selection.getSelection();
        var width = s.width;
        var height = s.height;
        var x = s.x1;
        var y = s.y1;
        ...
    });

    An Ajax request is sent to a cropping script, passing these values as request parameters since they are needed to crop the image through ImageMagick. Additionally, you’ll want to send the image name since the script will need to know the name of the image that it will be cropping.

    var request = $.ajax({
        url: "crop.php",
        type: "GET",
        data: {
            x: x,
            y: y,
            height: height,
            width: width,
            image: $("#photo").attr("src")
        }
    });

    When the request finishes, the image is reloaded so that the new cropped version will be shown in place of the old image.

    request.done(function(msg) {
        $("#photo").attr("src", msg);
        selection.cancelSelection();
    });

    When cropping is done, the request returns the name of the cropped image, and the new image is loaded by changing the src attribute.

    So what does the cropping script look like?

    <?php
    $file = basename($_GET["image"]);
    $cropped = "cropped_" . $file;
    $image = new Imagick($file);
    $image->cropImage($_GET["width"], $_GET["height"], $_GET["x"], $_GET["y"]);
    $image->writeImage($cropped);
    echo $cropped;

    The script first extracts the name of the image that needs to be cropped; the image name will be a full URL in the form of http://example.com/path/image.jpg, but only the image.jpg part is needed. Next, the string “cropped” is prefixed to the original name so that it will look like cropped_image.jpg. This allows the cropped image to be saved without overwriting the original.

    The cropImage() method is used to crop the original image using the four parameters related to the selection area that were sent to the script, and then the cropped image is saved to the new file. The name of the cropped image is then echoed back to the Ajax request for display in the browser.

    Conclusion

    In this article I created a simple cropping tool to show you the power and easy of use of the ImageMagick extension. You can learn more about it, and be creative and make something more useful using its powerful API. For instance, if you wanted to extend what I’ve presented here, you could give users the option to download their images in multiple sizes. Sample code to accompany this article is available on GitHub to get you started.

    Image via Fotolia

    And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Jump Start PHP.

    Comments on this article are closed. Have a question about PHP? Why not ask it on our forums?