Laravel controller using Intervention Image for image resize/crop is outputting pixelated images

1k Views Asked by At

I've made this class for Laravel that leverages Intervention Image Cache to process and cache images uploaded by the user. This class is called via a GET endpoint that passes things like image URI, target width/height, and crop w/h where applicable.

The images are resized and cropped to the right dimensions; however, the images seem to be slightly pixelated, as if they were potentially resized to a smaller dimension and then slightly blown back up. I'm trying to figure out if I'm doing resize/crop operations in the wrong order and if that may be causing the issue. It seems to be happening regardless of whether there are crop parameters passed or not. I've tried with JPG as the output format as well with no success. Any thoughts?

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Intervention\Image\Exception\NotReadableException;
use Intervention\Image\Facades\Image;

/**
 * Class ImageCacheController
 *
 * Handles resizing, cropping and caching of modified images.
 */
class ImageCacheController extends Controller
{
    /**
     * Handles the image caching process.
     */
    public function __invoke(Request $request, $width, $height, $crop_x, $crop_y, string $uri): mixed
    {
        // Set dimensions to null if they are 'null' string, and cast them to integers
        $width = $this->convertNullString($width);
        $height = $this->convertNullString($height);
        $crop_x = $this->convertNullString($crop_x);
        $crop_y = $this->convertNullString($crop_y);

        // Get the image source
        $src = storage_path('app') . '/public/uploads/' . $uri;
        if (!file_exists($src)) {
            // If the image doesn't exist, return a 404
            return response('', 404);
        }

        try {

            $cachedImage = Image::cache(function ($image) use ($src, $width, $height, $crop_x, $crop_y, $uri) {
                $rawImage = Image::make($src);
                $originalWidth = $rawImage->getWidth();
                $originalHeight = $rawImage->getHeight();

                // Calculate the ratio of the original image
                $ratio = $originalWidth / $originalHeight;

                // If the height is null, set it to 0
                $cropHeight = $height === null ? 0 : $height;
                $cropWidth = $width;

                // If the cropHeight is less than 1, set it to the width divided by the ratio
                if ($cropHeight < 1) {
                    $cropHeight = (int)floor($width / $ratio);
                    $height = null;
                }

                $image = $image->make($src);

                $image = $image->resize($cropWidth === null ? null : $width, $height, function ($constraint) {
                    $constraint->aspectRatio();
                });

                // If the crop_x and crop_y are set, crop the image
                if (isset($crop_x, $crop_y) && $cropHeight !== null) {
                    $image = $image->fit($width, $cropHeight)->trim();
                }

                // Encode the image to webp
                return $image->encode('webp', 95);
            }, 7776000, true);

            return $cachedImage->response();

        } catch (NotReadableException $e) {
            // If there was an error reading the image, return a 404
            return response($e->getMessage(), 404);
        }
    }

    /**
     * Converts the string 'null' to a real null value.
     *
     * @param mixed $value
     * @return mixed|null
     */
    private function convertNullString(mixed $value): ?int
    {
        return $value === 'null' ? null : (int)$value;
    }
}
1

There are 1 best solutions below

1
Dipesh Sukhia On

If you are using Intervention Image to resize/crop images in Laravel and the output images are pixelated, it is likely because you are not setting the image quality correctly.

When you resize or crop an image, the quality of the image can be affected. To ensure that the output image is of high quality, you need to set the quality parameter when saving the image. Here is an example:

use Intervention\Image\Facades\Image;

public function resizeImage()
{
    $image = Image::make(public_path('images/image.jpg'));
    $image->resize(200, null, function ($constraint) {
        $constraint->aspectRatio();
    });
    $image->save(public_path('images/resized-image.jpg'), 80);
}

In the example above, we have set the quality parameter to 80 when saving the resized image. This ensures that the output image is of high quality and not pixelated.

You can also set the quality parameter when cropping an image. Here is an example:

use Intervention\Image\Facades\Image;

public function cropImage()
{
    $image = Image::make(public_path('images/image.jpg'));
    $image->crop(200, 200, 100, 100);
    $image->save(public_path('images/cropped-image.jpg'), 80);
}

In this example, we have set the quality parameter to 80 when saving the cropped image.

By setting the quality parameter correctly, you can ensure that the output images are of high quality and not pixelated. You can experiment with different quality values to find the best setting for your images.