PHP上传导致图像模糊

3
我正在使用一个可以在此处找到的裁剪工具:https://github.com/sconsult/croppic
我对其进行了修改以满足我的需求 - 抱歉代码量很大,但我不知道其中哪部分可能引起问题。
我最初实现了这个代码大约5-6个月前,它一直运行得非常好。对于每个测试图像,85%的质量正是合适的。自原始实施以来代码并未更改,但图像明显变得模糊 - 它们看起来不再漂亮。我已经测试过之前上传的相同图像进行比较,它们与最初上传和裁剪的图像不同。
我所知道的唯一可能影响此事的主要事件是升级我们的PHP版本。我认为在实施时它曾经是5.3xx或类似版本,现在我们运行的是5.6.18。什么会导致模糊?是否有任何与此相关的从5.35.6.18的重要更改? 请注意,我已尝试将$jpeg_quality = 85;更改为100。不幸的是,这只会增加文件大小。仍然出现相同的模糊情况。
<?php
$image = $_POST['imgUrl'];
// original sizes
$img_init_w = $_POST['imgInitW'];
$img_init_h = $_POST['imgInitH'];
// resized sizes
$img_w = $_POST['imgW'];
$img_h = $_POST['imgH'];
// offsets
$imgY1 = $_POST['imgY1'];
$imgX1 = $_POST['imgX1'];
// crop box
$cropW = $_POST['cropW'];
$cropH = $_POST['cropH'];
// rotation angle
$angle = $_POST['rotation'];

$id = $_POST['id'];
$dtormob = $_POST['dtormob'];

$jpeg_quality = 85;



$original_filename = "../../..".WEB_ROOT."lib/img/$id-original";


// different filenames depending on whether this is for mobile or desktop
switch ($dtormob)
{
    case 'dt':
        $cropped_lrg_filename  = "../../..".WEB_ROOT."lib/img/$id-lrg-w"; // 1440 x 568
        $cropped_sml_filename  = "../../..".WEB_ROOT."lib/img/$id-med-w"; // 767 x 302

        $lrg_resized_w = 1440;
        $lrg_resized_h = 568;

        $sml_resized_w = 767;
        $sml_resized_h = 302;
        break;

    case 'mob':
        $cropped_lrg_filename  = "../../..".WEB_ROOT."lib/img/$id-med-s"; // 767 x 607
        $cropped_sml_filename  = "../../..".WEB_ROOT."lib/img/$id-sml-s"; // 480 x 380

        $lrg_resized_w = 767;
        $lrg_resized_h = 607;

        $sml_resized_w = 480;
        $sml_resized_h = 380;
        break;
}



$what = getimagesize($image);

switch(strtolower($what['mime']))
{
    case 'image/png':
        $img_r = imagecreatefrompng($image);
        $source_image = imagecreatefrompng($image);
        $type = '.png';
        break;
    case 'image/jpeg':
        $img_r = imagecreatefromjpeg($image);
        $source_image = imagecreatefromjpeg($image);
        error_log('jpg');
        $type = '.jpeg';
        break;
    case 'image/gif':
        $img_r = imagecreatefromgif($image);
        $source_image = imagecreatefromgif($image);
        $type = '.gif';
        break;
    default: die('image type not supported');
}


// Error checking
if (!is_writable(dirname($cropped_lrg_filename))) {
    $response = array(
        'status' => 'error',
        'message' => "Can't write cropped File, directory inaccesible"
    );
}
else if ($what[0] > 2000 || $what[1] > 2000) {
    $response = array(
        'status' => 'error',
        'message' => '<strong>Image too large</strong>, please upload an image <strong>2000 x 2000px</strong> or less.<br /><em>(No smaller than '.$lrg_resized_w.' x '.$lrg_resized_h.'px)</em>'
    );
}
else if ($what[0] < $lrg_resized_w || $what[1] < $lrg_resized_h) {
    $response = array(
        'status' => 'error',
        'message' => '<strong>Image too small</strong>, please upload an image <strong>'.$lrg_resized_w.' x '.$lrg_resized_h.'px</strong> or more.<br /><em>(No bigger than 2000 x 2000px)</em>'
    );
}
else if ($what['channels'] > 3) {
    $response = array(
        'status' => 'error',
        'message' => '<strong>Image format error</strong>, this image is in CMYK format, please convert it to <strong>RGB</strong> first.'
    );
}
else
{
    $original_filename_absolute = PROTOCOL.SUB_DOMAIN.DOMAIN.str_replace('../../..', '', $original_filename).$type;
    $cropped_lrg_filename_absolute = PROTOCOL.SUB_DOMAIN.DOMAIN.str_replace('../../..', '', $cropped_lrg_filename).$type;
    $cropped_sml_filename_absolute = PROTOCOL.SUB_DOMAIN.DOMAIN.str_replace('../../..', '', $cropped_sml_filename).$type;

    // remove any images that already exist
    if (file_exists($original_filename.$type)) {
        unlink($original_filename.$type);
    }
    if (file_exists($cropped_lrg_filename.$type)) {
        unlink($cropped_lrg_filename.$type);
    }
    if (file_exists($cropped_sml_filename.$type)) {
        unlink($cropped_sml_filename.$type);
    }


    // lets make a copy of the original
    // ====================================================================================
    $original_img = imagecreatetruecolor($img_init_w, $img_init_h);
    imagecopyresampled($original_img, $source_image, 0, 0, 0, 0, $img_init_w, $img_init_h, $img_init_w, $img_init_h);
    imagejpeg($original_img, $original_filename.$type, 100);




    // create large crop first - then resize after as it should be the same aspect ratio
    // ====================================================================================
    // resize the original image to size of editor
    $lrg_resized_image = imagecreatetruecolor($img_w, $img_h);
    imagecopyresampled($lrg_resized_image, $source_image, 0, 0, 0, 0, $img_w, $img_h, $img_init_w, $img_init_h);

    // rotate the rezized image
    $rotated_image = imagerotate($lrg_resized_image, -$angle, 0);

    // find new width & height of rotated image
    $rotated_width = imagesx($rotated_image);
    $rotated_height = imagesy($rotated_image);

    // diff between rotated & original sizes
    $dx = $rotated_width - $img_w;
    $dy = $rotated_height - $img_h;

    // crop rotated image to fit into original rezized rectangle
    $cropped_rotated_image = imagecreatetruecolor($img_w, $img_h);
    imagecolortransparent($cropped_rotated_image, imagecolorallocate($cropped_rotated_image, 0, 0, 0));
    imagecopyresampled($cropped_rotated_image, $rotated_image, 0, 0, $dx / 2, $dy / 2, $img_w, $img_h, $img_w, $img_h);

    // crop image into selected area
    $final_image = imagecreatetruecolor($cropW, $cropH);
    imagecolortransparent($final_image, imagecolorallocate($final_image, 0, 0, 0));
    imagecopyresampled($final_image, $cropped_rotated_image, 0, 0, $imgX1, $imgY1, $cropW, $cropH, $cropW, $cropH);

    // finally output image
    imagejpeg($final_image, $cropped_lrg_filename.$type, $jpeg_quality);







    // now create smaller crop - same aspect ratio just smaller
    // ====================================================================================
    $sml_resized_image = imagecreatetruecolor($sml_resized_w, $sml_resized_h);
    imagecopyresampled($sml_resized_image, $final_image, 0, 0, 0, 0, $sml_resized_w, $sml_resized_h, $cropW, $cropH);
    // save it
    imagejpeg($sml_resized_image, $cropped_sml_filename.$type, $jpeg_quality);




    $response = array(
        'status' => 'success',
        'url' => $cropped_lrg_filename.$type
    );
}
echo json_encode($response);

更新

以下是两个示例图像,第一个是调整大小的版本(质量差,但输出为100%jpeg质量),第二个是原始图像。看看墙上的石头,很明显质量降低了。

裁剪

enter image description here

原始

enter image description here

2个回答

1
这里的问题在于imagerotate()函数。即使没有应用旋转,我的脚本也被告知要旋转图像。这导致了图像质量下降。我已经修改了脚本,改为如下读取;
$rotated_image = $angle == 0 ? $lrg_resized_image : imagerotate($lrg_resized_image, -$angle, 0);

这里仍有一个小的未知因素,不清楚为什么会出现这个问题,而之前却没有这个问题。此外,即使我旋转了一张图片,效果也不是很好,但对于这种情况来说,幸运的是可能并不需要旋转图片。

感谢Golden_flash帮助我调试


1
通过这篇文章学到了很多。 - Golden_flash

1
添加。
imageconvolution($original_img,array(array(-1,-1,-1),array(-1,16,-1),array(-1,-1,-1)),8,0);

在imagecopyresample之后,imagejpeg之前

使用Imageconvolution来锐化图像


这确实稍微增加了图像的清晰度,但仍然存在明显的图像退化。至少这让我知道它不仅仅是模糊的...图像失去了很多细节。我已经更新了我的问题以展示一个例子。该示例使用了您的微调,如果没有它,图像会更糟。 - Novocaine
图像质量下降的原因可能是由于使用imagecopyresampled进行大量复制,尝试在每次调用imagecopyresampled后添加imageconvolution。您能告诉我您通过代码想要实现什么,以便我们可以减少一些代码吗? - Golden_flash
在每个imagecopyresampled之后添加另外两个实例的imageconvolution后,生成的图像变得更糟了 - 所以这不是正确的方法。 - Novocaine
质量损失不是由于imagecopyresampled(),而是由于JPEG压缩。不幸的是,GD的压缩算法无法与Photoshop相媲美。根据这篇文章https://dev59.com/2HE95IYBdhLWcg3wXcnd,您应该将jpeg质量值设置为99。这会增加大小,但它提供了可比较的质量。现在尝试设置jpeg质量,因为您已经使用imageconvolution进行了锐化。 - Golden_flash
很奇怪,以前以85%上传的图像质量还不错。尝试了99%,但仍然一样糟糕。值得注意的是,同时上传的原始图像没有失去质量。此外,我尝试将原始图像的质量降低到85,几乎无法与未经处理的离线图像区分开来。裁剪/调整大小显然会破坏图像。 - Novocaine
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接