Intervention / image

PHP Image Processing
https://image.intervention.io
MIT License
13.88k stars 1.5k forks source link

Blurry images when saved with ascending sizes coming from array inside loop #1374

Closed JVariance closed 3 months ago

JVariance commented 3 months ago

Describe the bug

I experimented with Intervention Image and observed that in my example the right order of the image sizes in the array is important to have the images saved without quality issues. I guess that the image once loaded is cached and reused in the next loop runs. If I have the sizes from low to high (300,...,2200) the images are blurry. If I have the sizes from high to low (2200,...,300) everthing's fine.

Code Example

@php
    $image_paths = ['../resources/img/ryan-schroeder-Gg7uKdHFb_c-unsplash.jpg'];
    $image_sizes = [2200, 1200, 900, 600, 300];
    // $image_sizes = [300, 600, 900, 1200, 2200];

    foreach ($image_paths as $image_path) {
        $imagePathInfo = pathinfo($image_path);
        $imageName = $imagePathInfo['filename'];
        $imageDir = $imagePathInfo['dirname'];

        foreach ($image_sizes as $size) {
            $image = Image::read($image_path);
            $image_path = "$imageDir/$imageName" . "_$size.webp";
            $aspect_ratio = $image->size()->aspectRatio();
            $height = $size / $aspect_ratio;

            $file_already_exists = file_exists($image_path);

            if (!$file_already_exists) {
                $image->scale($size);
                $image->toWebp();
                $image->save($image_path);
            }
        }
    }
@endphp

Expected behavior

I expected that the image is newly read in every single loop passage and then processed.

Images

If applicable, add problematic images or screenshots to help explain your problem.

Environment (please complete the following information):

olivervogel commented 3 months ago

I think the error is in your code. You're overwriting the $image_path variable in the second loop. So that during the second run the already scaled (down) image is read and used instead of the original path from the first loop.

Apart from that, you do not need the $image->toWebp(); call, as the subsequent $image->save(); call includes the Webp-encoding depending on the file extension of the $image_path. See documentation.

JVariance commented 3 months ago

I think the error is in your code. You're overwriting the $image_path variable in the second loop. So that during the second run the already scaled (down) image is read and used instead of the original path from the first loop.

Apart from that, you do not need the $image->toWebp(); call, as the subsequent $image->save(); call includes the Webp-encoding depending on the file extension of the $image_path. See documentation.

Oh gosh, I guess it was too late 😄

JVariance commented 3 months ago

@olivervogel could you also give me a hint why there is no difference in quality (ergo file size) when passing different quality values to toWebp()? It doesn't matter, if I use 100, 85 or 10.

olivervogel commented 3 months ago

Do you have a code example? A quick test I did results in different file sizes for different qualities.

$image = ImageManager::imagick()
    ->read('./example.jpg');

foreach ([100, 85, 10] as $quality) {
    $path = './quality_' . $quality . '.webp';
    $image->save($path, quality: $quality);
    echo "Filesize for " . $quality . " is " . filesize($path) . "\n";
}

Results in:

Filesize for 100 is 244440
Filesize for 85 is 22308
Filesize for 10 is 4736

Similar result with GD.