【问题标题】:PHP - Compress Image to Meet File Size LimitPHP - 压缩图像以满足文件大小限制
【发布时间】:2014-09-12 08:47:54
【问题描述】:

我必须上传符合最大宽度尺寸和最大文件大小的图像文件。

我有检查宽度大小并调整图像大小以满足最大图像宽度的代码。

但是,当我保存文件时,我可以设置质量

imagejpeg( $imgObject , 'resized/50.jpg' , 50 ); //save image and set quality

我想要做的是避免设置标准质量,因为提交的图像质量差异很大,并且可能一开始就很低。

在不超过最大文件大小限制的情况下,应将图像质量设置得尽可能高。

我唯一的解决方案是保存几个不同质量的图像版本,检查每个文件大小并选择最好的一个。这可行,但非常缓慢且过程密集。

关于如何做得更好的任何建议?

谢谢

【问题讨论】:

  • 如果您决定采用“多个版本”的路线,您可以创建一个简单的学习算法,根据先前结果的平均值在第一次迭代中选择维度。随着时间的推移,这将使算法更加高效。

标签: php image compression gd php-gd


【解决方案1】:

这是实现这一目标的一种方法。在确定文件大小低于指定的最大值之前,它不会保存文件。

$original_file = 'original.jpg';
$resized_file = 'resized.jpg';
$max_file_size = '30000'; // maximum file size, in bytes

$original_image = imagecreatefromjpeg($original_file);

$image_quality = 100;

do {
    $temp_stream = fopen('php://temp', 'w+');
    $saved = imagejpeg($original_image, $temp_stream, $image_quality--);
    rewind($temp_stream);
    $fstat = fstat($temp_stream);
    fclose($temp_stream);

    $file_size = $fstat['size'];
}
while (($file_size > $max_file_size) && ($image_quality >= 0));

if (-1 == $image_quality) {
    echo "Unable to get the file that small. Best I could do was $file_size bytes at image quality 0.\n";
}
else {
    echo "Successfully resized $original_file to $file_size bytes using image quality $image_quality. Resized file saved as $resized_file.\n";
    imagejpeg($original_image, $resized_file, $image_quality + 1);
}

在您成功找到理想的压缩比之前,无需写入任何文件。

此循环从 $image_quality 为 100 开始,然后向下计数,尝试在临时内存缓冲区中尝试每个新值。如果尝试 0 并且文件仍然太大,它会返回错误消息。否则,如果它达到一个有效的值,它会报告并使用该图像质量设置保存新文件。

如果您喜欢冒险,您可以实现一个二分搜索算法来更快地找到理想的压缩比,但是这个快速修复会给出相同的结果,尽管可能会慢一点。

【讨论】:

  • 上面的代码几乎可以工作。但是,您在最后一行错误地命名了变量。应该是imagejpeg($original_image, $resized_file, $image_quality + 1); 变量$new_image 不存在。
  • 哎呀,谢谢@AdamRehal - 很好。固定。
【解决方案2】:

没有。这是可能的最佳选择。

【讨论】:

    【解决方案3】:

    很遗憾,您无法估算最终文件大小,因为您无法访问 JPEG 编码器的内部状态。

    您可以做的一件事是压缩图像的较小版本并从那里推断。将宽度和高度减少四意味着计算机只需处理十六分之一的像素。这里的技巧是如何准备一个有代表性的测试图像。使用 imagecopyresampled() 或 imagecopyresized() 缩小图像将不起作用。它改变了太多的压缩特性。相反,您要做的是从原始图像中每隔四个 8x8 平铺复制一次:

    $width1 = imagesx($img1);
    $height1 = imagesy($img1);
    $width2 = floor($width1 / 32) * 8;
    $height2 = floor($height1 / 32) * 8;
    $img2 = imagecreatetruecolor($width2, $height2);
    for($x1 = 0, $x2 = 0; $x2 + 8 < $width2; $x1 += 32, $x2 += 8) {
        for($y1 = 0, $y2 = 0; $y2 + 8 < $height2; $y1 += 32, $y2 += 8) {
            imagecopy($img2, $img1, $x2, $y2, $x1, $y1, 8, 8);
        }
    }
    

    以各种质量级别压缩较小的图像并获得受尊重的文件大小。将它们乘以 16 应该可以很好地估计原始图像会得到什么。

    【讨论】:

    • 你有例子吗?