关于php切图问题引发的疑惑

有时候写代码挺好奇的,触及到了我的知识盲区。
我有一张jpg的图片,切图生成新的图片,新的图片和原图颜色并不是完全一致,我问过chatGPT也并没有很好的解决。但是我换成nodejs重写切图逻辑,新图和原图颜色就是完全一致的。
如果我要搞清楚这是怎么回事,我应该怎么去做呢?

这里提供代码,请大家指点一下。


<?php

$split = function ($sourceImage, $outputPath, $splitType, $overlapRatio = 0.2) {
  list($width, $height, $type, $attr) = getimagesize($sourceImage);
  $symbol = "w";
  if ($splitType == 1) {
          $sliceWidth = ceil($width / 4);
          $overlapWidth = (int)($sliceWidth * $overlapRatio);
          $sliceWidthWithOverlap = $sliceWidth + $overlapWidth;

          for ($i = 0; $i < 4; $i++) {
              if ($type == IMAGETYPE_JPEG) {
              $image = imagecreatefromjpeg($sourceImage);
              $ext = ".jpg";
              $create = "imagejpeg";
          } else {
              $image = imagecreatefrompng($sourceImage);
              $ext = ".png";
              $create = "imagepng";
          }
          if (!$image) {
            throw new Exception("Failed to load the image.");
          }
          $slice = imagecreatetruecolor($sliceWidthWithOverlap, $height);
          $srcX = $i == 0 ? $i * $sliceWidth : $i * $sliceWidth - $overlapWidth;
          imagecopyresampled(
              $slice,
              $image,
              0, 0, // 目标 x, y  $srcX, 0, // 源 x, y  $sliceWidthWithOverlap, $height, // 目标宽度, 目标高度
              $sliceWidthWithOverlap, $height // 源宽度, 源高度
          );
          $create($slice, "{$outputPath}{$symbol}_{$overlapWidth}_{$i}{$ext}");
          imagedestroy($slice);
          imagedestroy($image);
          }
     } else {
      $symbol = "h";
      $sliceHeight = ceil($height / 4);
      $overlapHeight = (int)($sliceHeight * $overlapRatio);
      $sliceHeightWithOverlap = $sliceHeight + $overlapHeight;

      for ($i = 0; $i < 4; $i++) {
          if ($type == IMAGETYPE_JPEG) {
              $image = imagecreatefromjpeg($sourceImage);
              $ext = ".jpg";
              $create = "imagejpeg";
          } else {
              $image = imagecreatefrompng($sourceImage);
              $ext = ".png";
              $create = "imagepng";
          }
          if (!$image) {
              throw new Exception("Failed to load the image.");
          }
          $slice = imagecreatetruecolor($width, $sliceHeightWithOverlap);
          $srcY = $i == 0 ? $i * $sliceHeight : $i * $sliceHeight - $overlapHeight;
          imagecopyresampled(
              $slice,
              $image,
              0, 0, // 目标 x, y  0, $srcY, // 源 x, y  $width, $sliceHeightWithOverlap, // 目标宽度, 目标高度
              $width, $sliceHeightWithOverlap // 源宽度, 源高度
          );
          $create($slice, "{$outputPath}{$symbol}_{$overlapHeight}_{$i}{$ext}");
          imagedestroy($slice);
          imagedestroy($image);
      }
 }};

这是nodejs代码


 //npm install sharp
const  fs  =  require('fs').promises;
const  path  =  require('path');
const  sharp  =  require('sharp');

async function split(sourceImage, outputPath, splitType, overlapRatio  =  0.2) {

     try {
         const  imageBuffer  =  await  fs.readFile(sourceImage);
         const  metadata  =  await  sharp(imageBuffer).metadata();
         let  extension  =  path.extname(sourceImage);
         let  createSharpFunction;

         if (extension  ===  '.jpg'  ||  extension  ===  '.jpeg') {
             extension  =  '.jpg';
             createSharpFunction  =  sharp().toFile;
        } else  if (extension  ===  '.png') {
             extension  =  '.png';
             createSharpFunction  =  sharp().toFile;
        } else {
             throw  new  Error('Unsupported file format.');
        }

         const { width, height } =  metadata;
         let  sliceWidth, sliceHeight, overlapWidth, overlapHeight, sliceWidthWithOverlap, sliceHeightWithOverlap;
         let  symbol  =  'w';
         let  slices  =  4;

         if (splitType  ===  1) {

             sliceWidth  =  Math.ceil(width  /  slices);

             overlapWidth  =  Math.floor(sliceWidth  *  overlapRatio);

             sliceWidthWithOverlap  =  sliceWidth  +  overlapWidth;

        } else {

             symbol  =  'h';

             sliceHeight  =  Math.ceil(height  /  slices);

             overlapHeight  =  Math.floor(sliceHeight  *  overlapRatio);

             sliceHeightWithOverlap  =  sliceHeight  +  overlapHeight;

        }

     for (let  i  =  0; i  <  slices; i++) {

             let  croppedImage  =  sharp(imageBuffer);

             if (splitType  ===  1) {

             let  srcX  =  i  *  sliceWidth  - (i  >  0  ?  overlapWidth  * (i  -  1) :  0);

             let  endX  =  srcX  +  sliceWidthWithOverlap;

             if (endX  >  width) {
                 srcX  -=  endX  -  width;
                 sliceWidthWithOverlap  =  width  -  srcX;
            }

             await  croppedImage.extract({
                     left:  srcX,
                     top:  0,
                     width:  sliceWidthWithOverlap,
                     height:  height
            }).toFile(`${outputPath}${symbol}_${overlapWidth}_${i}${extension}`);

        } else {

             let  srcY  =  i  *  sliceHeight  - (i  >  0  ?  overlapHeight  * (i  -  1) :  0);

             let  endY  =  srcY  +  sliceHeightWithOverlap;

             if (endY  >  height) {

                 srcY  -=  endY  -  height;

                 sliceHeightWithOverlap  =  height  -  srcY;

            }

         await  croppedImage.extract({
                 left:  0,
                 top:  srcY,
                 width:  width,
                 height:  sliceHeightWithOverlap

            }).toFile(`${outputPath}${symbol}_${overlapHeight}_${i}${extension}`);
        }

    }
} catch (error) {

     console.error(`Error processing the image: ${error.message}`);

  }

}

split('img/height.jpg','img/',2).then(log=>{
     console.log(log);
})

关于php切图问题引发的疑惑

这里提供一张图片,大家可以复制代码下载图片在直接本地测试使用。

最后,非常感谢你花时间阅读,如果你有什么想法,欢迎讨论交流。

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 16

因为imagejpeg会压缩质量,第三个参数是质量值默认约为75

3个月前 评论
L学习不停 (楼主) 3个月前

我觉得你可以去找一些比较成熟的图片处理第三方包或者扩展来实现,没必要折腾原生

3个月前 评论
L学习不停 (楼主) 3个月前
忆往昔弹指间 (作者) 3个月前
L学习不停 (楼主) 3个月前

是修改过的图片发白吗

3个月前 评论
L学习不停 (楼主) 3个月前

jpg是有损压缩,这个应该是主要原因,尽量用专业的软件解决,比如imagemagick,用php去调用 imagemagick,不行的话应该是(1)要么参数没选对(2)要么图片本身有问题。必居其一。

3个月前 评论
L学习不停 (楼主) 3个月前
sanders

楼上的各位兄弟都讲了不少,我觉得主要是因为 jpeg 有损压缩导致,另外一个问题也需要楼主考虑一下,就是图片的分辨率。通过初步观察和最终下载求证,我发现楼主上传的图片分辨率是 300x300 这一般是打印图片的分辨率,网图分辨率一般在 72x72 也就是常说的300线和72线之分。建议将图片转换分辨率后再进行对比一下看看。

3个月前 评论
L学习不停 (楼主) 3个月前

如果是调用Imagick库的话,可以离线用命令行测试一下参数,验证一下。php仅仅是调用,应该不是php的事。

3个月前 评论
L学习不停 (楼主) 3个月前

本质上就是 libgd 这个库和 libvips 的差异,libgd 在处理图像时可能会造成质量损失,尤其是在缩放和压缩时。插值算法和颜色处理能力较有限,因此生成的图像质量通常不如 libvips。

libvips 提供更高质量的图像处理,支持多种高级插值算法,并能处理 16 位甚至 32 位的高色深图像。它还可以执行色彩管理、处理嵌入的 ICC 配置文件,确保输出图像的颜色准确。

PHP 也可以用 libvips 作为图片处理的库,具体参考 libvips/php-vips 这个仓库。

3个月前 评论
L学习不停 (楼主) 3个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!