关于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);
})
这里提供一张图片,大家可以复制代码下载图片在直接本地测试使用。
最后,非常感谢你花时间阅读,如果你有什么想法,欢迎讨论交流。
因为imagejpeg会压缩质量,第三个参数是质量值默认约为75
我觉得你可以去找一些比较成熟的图片处理第三方包或者扩展来实现,没必要折腾原生
是修改过的图片发白吗
jpg是有损压缩,这个应该是主要原因,尽量用专业的软件解决,比如imagemagick,用php去调用 imagemagick,不行的话应该是(1)要么参数没选对(2)要么图片本身有问题。必居其一。
楼上的各位兄弟都讲了不少,我觉得主要是因为 jpeg 有损压缩导致,另外一个问题也需要楼主考虑一下,就是图片的分辨率。通过初步观察和最终下载求证,我发现楼主上传的图片分辨率是 300x300 这一般是打印图片的分辨率,网图分辨率一般在 72x72 也就是常说的300线和72线之分。建议将图片转换分辨率后再进行对比一下看看。
如果是调用Imagick库的话,可以离线用命令行测试一下参数,验证一下。php仅仅是调用,应该不是php的事。
本质上就是 libgd 这个库和 libvips 的差异,libgd 在处理图像时可能会造成质量损失,尤其是在缩放和压缩时。插值算法和颜色处理能力较有限,因此生成的图像质量通常不如 libvips。
libvips 提供更高质量的图像处理,支持多种高级插值算法,并能处理 16 位甚至 32 位的高色深图像。它还可以执行色彩管理、处理嵌入的 ICC 配置文件,确保输出图像的颜色准确。
PHP 也可以用 libvips 作为图片处理的库,具体参考 libvips/php-vips 这个仓库。