我最近发现,Imagick可以支持颜色配置文件,从而生成质量更好的图像(有关详细信息,请参阅此question / answer),因此我尝试将我的GD包装器移植到使用Imagick类,而不是目前的GD实现如下:
function Image($input, $crop = null, $scale = null, $merge = null, $output = null, $sharp = true)
{
if (isset($input, $output) === true)
{
if (is_string($input) === true)
{
$input = @ImageCreateFromString(@file_get_contents($input));
}
if (is_resource($input) === true)
{
$size = array(ImageSX($input), ImageSY($input));
$crop = array_values(array_filter(explode('/', $crop), 'is_numeric'));
$scale = array_values(array_filter(explode('*', $scale), 'is_numeric'));
if (count($crop) == 2)
{
$crop = array($size[0] / $size[1], $crop[0] / $crop[1]);
if ($crop[0] > $crop[1])
{
$size[0] = round($size[1] * $crop[1]);
}
else if ($crop[0] < $crop[1])
{
$size[1] = round($size[0] / $crop[1]);
}
$crop = array(ImageSX($input) - $size[0], ImageSY($input) - $size[1]);
}
else
{
$crop = array(0, 0);
}
if (count($scale) >= 1)
{
if (empty($scale[0]) === true)
{
$scale[0] = round($scale[1] * $size[0] / $size[1]);
}
else if (empty($scale[1]) === true)
{
$scale[1] = round($scale[0] * $size[1] / $size[0]);
}
}
else
{
$scale = array($size[0], $size[1]);
}
$image = ImageCreateTrueColor($scale[0], $scale[1]);
if (is_resource($image) === true)
{
ImageFill($image, 0, 0, IMG_COLOR_TRANSPARENT);
ImageSaveAlpha($image, true);
ImageAlphaBlending($image, true);
if (ImageCopyResampled($image, $input, 0, 0, round($crop[0] / 2), round($crop[1] / 2), $scale[0], $scale[1], $size[0], $size[1]) === true)
{
$result = false;
if ((empty($sharp) !== true) && (is_array($matrix = array_fill(0, 9, -1)) === true))
{
array_splice($matrix, 4, 1, (is_int($sharp) === true) ? $sharp : 16);
if (function_exists('ImageConvolution') === true)
{
ImageConvolution($image, array_chunk($matrix, 3), array_sum($matrix), 0);
}
}
if ((isset($merge) === true) && (is_resource($merge = @ImageCreateFromString(@file_get_contents($merge))) === true))
{
ImageCopy($image, $merge, round(0.95 * $scale[0] - ImageSX($merge)), round(0.95 * $scale[1] - ImageSY($merge)), 0, 0, ImageSX($merge), ImageSY($merge));
}
foreach (array('gif' => 0, 'png' => 9, 'jpe?g' => 90) as $key => $value)
{
if (preg_match('~' . $key . '$~i', $output) > 0)
{
$type = str_replace('?', '', $key);
$output = preg_replace('~^[.]?' . $key . '$~i', '', $output);
if (empty($output) === true)
{
header('Content-Type: image/' . $type);
}
$result = call_user_func_array('Image' . $type, array($image, $output, $value));
}
}
return (empty($output) === true) ? $result : self::Chmod($output);
}
}
}
}
else if (count($result = @GetImageSize($input)) >= 2)
{
return array_map('intval', array_slice($result, 0, 2));
}
return false;
}
我一直在尝试使用Imagick类方法,这是我到目前为止所得到的:
function Imagick($input, $crop = null, $scale = null, $merge = null, $output = null, $sharp = true)
{
if (isset($input, $output) === true)
{
if (is_file($input) === true)
{
$input = new Imagick($input);
}
if (is_object($input) === true)
{
$size = array_values($input->getImageGeometry());
$crop = array_values(array_filter(explode('/', $crop), 'is_numeric'));
$scale = array_values(array_filter(explode('*', $scale), 'is_numeric'));
if (count($crop) == 2)
{
$crop = array($size[0] / $size[1], $crop[0] / $crop[1]);
if ($crop[0] > $crop[1])
{
$size[0] = round($size[1] * $crop[1]);
}
else if ($crop[0] < $crop[1])
{
$size[1] = round($size[0] / $crop[1]);
}
$crop = array($input->getImageWidth() - $size[0], $input->getImageHeight() - $size[1]);
}
else
{
$crop = array(0, 0);
}
if (count($scale) >= 1)
{
if (empty($scale[0]) === true)
{
$scale[0] = round($scale[1] * $size[0] / $size[1]);
}
else if (empty($scale[1]) === true)
{
$scale[1] = round($scale[0] * $size[1] / $size[0]);
}
}
else
{
$scale = array($size[0], $size[1]);
}
$image = new IMagick();
$image->newImage($scale[0], $scale[1], new ImagickPixel('white'));
$input->cropImage($size[0], $size[1], round($crop[0] / 2), round($crop[1] / 2));
$input->resizeImage($scale[0], $scale[1], Imagick::FILTER_LANCZOS, 1); // $image->scaleImage($scale[0], $scale[1]);
//if (in_array('icc', $image->getImageProfiles('*', false)) === true)
{
$version = preg_replace('~([^-]*).*~', '$1', ph()->Value($image->getVersion(), 'versionString'));
if (is_file($profile = sprintf('/usr/share/%s/config/sRGB.icm', str_replace(' ', '-', $version))) !== true)
{
$profile = 'http://www.color.org/sRGB_v4_ICC_preference.icc';
}
if ($input->profileImage('icc', file_get_contents($profile)) === true)
{
$input->setImageColorSpace(Imagick::COLORSPACE_SRGB);
}
}
$image->compositeImage($input, Imagick::COMPOSITE_OVER, 0, 0);
if ((isset($merge) === true) && (is_object($merge = new Imagick($merge)) === true))
{
$image->compositeImage($merge, Imagick::COMPOSITE_OVER, round(0.95 * $scale[0] - $merge->getImageWidth()), round(0.95 * $scale[1] - $merge->getImageHeight()));
}
foreach (array('gif' => 0, 'png' => 9, 'jpe?g' => 90) as $key => $value)
{
if (preg_match('~' . $key . '$~i', $output) > 0)
{
$type = str_replace('?', '', $key);
$output = preg_replace('~^[.]?' . $key . '$~i', '', $output);
if (empty($output) === true)
{
header('Content-Type: image/' . $type);
}
$image->setImageFormat($type);
if (strcmp('jpeg', $type) === 0)
{
$image->setImageCompression(Imagick::COMPRESSION_JPEG);
$image->setImageCompressionQuality($value);
$image->stripImage();
}
if (strlen($output) > 0)
{
$image->writeImage($output);
}
else
{
echo $image->getImageBlob();
}
}
}
return (empty($output) === true) ? $result : self::Chmod($output);
}
}
else if (count($result = @GetImageSize($input)) >= 2)
{
return array_map('intval', array_slice($result, 0, 2));
}
return false;
}
已经支持基本功能(裁剪/调整大小/水印),但是,我仍然遇到一些问题 . 由于PHP Imagick文档有点糟透,我别无选择,只能尝试所有可用方法和参数的试错方法组合,这需要花费大量时间 .
我目前的问题/疑虑是:
1 - 保持透明度
在我原来的实现中,行:
ImageFill($image, 0, 0, IMG_COLOR_TRANSPARENT);
ImageSaveAlpha($image, true);
ImageAlphaBlending($image, true);
将透明PNG图像转换为PNG输出时,会产生preserving the transparency的效果 . 但是,如果尝试将透明PNG图像转换为JPEG格式,则透明像素的颜色应设置为白色 . 到目前为止,使用ImageMagick,我只能convert all transparent pixels to white,但如果输出格式支持它,我无法保持透明度 .
2 - 压缩输出格式(即JPEG和PNG)
我的原始实现在PNG上使用压缩级别9,在JPEG上使用90的质量:
foreach (array('gif' => 0, 'png' => 9, 'jpe?g' => 90) as $key => $value)
线条:
$image->setImageCompression(Imagick::COMPRESSION_JPEG);
$image->setImageCompressionQuality($value);
$image->stripImage();
似乎压缩JPEG图像 - 然而,GD能够使用相同的 $value
作为质量参数来压缩它 - 为什么?关于以下两者之间的差异,我也处于黑暗中:
我应该使用哪一个,它们的区别是什么?此外,最关键的问题与PNG压缩有关,Imagick compression constants列表似乎不支持PNG格式:
imagick::COMPRESSION_UNDEFINED (integer)
imagick::COMPRESSION_NO (integer)
imagick::COMPRESSION_BZIP (integer)
imagick::COMPRESSION_FAX (integer)
imagick::COMPRESSION_GROUP4 (integer)
imagick::COMPRESSION_JPEG (integer)
imagick::COMPRESSION_JPEG2000 (integer)
imagick::COMPRESSION_LOSSLESSJPEG (integer)
imagick::COMPRESSION_LZW (integer)
imagick::COMPRESSION_RLE (integer)
imagick::COMPRESSION_ZIP (integer)
imagick::COMPRESSION_DXT1 (integer)
imagick::COMPRESSION_DXT3 (integer)
imagick::COMPRESSION_DXT5 (integer)
这是一个屁股画,因为如果用Imagick输出(尺寸大约2 MB),GD PNG输出恰好大小为100-200 KB会变得非常胖...
有a couple of questions on SO regarding this issue,但我还没有依靠外部应用程序 . 这对ImageMagick来说真的不可能吗?!
3 - 图像卷积
在GD实现中,我调用 ImageConvolution()
来锐化图像,我知道Imagick有内置的方法来锐化图像(我想知道Imagick是否有相当于 ImageConvolution()
的功能 .
4 - 颜色配置文件
这与原始实现无关,但我也想做到对 .
我是否应该始终将Imagick / International Color Consortium sRGB颜色配置文件添加到所有图像中?或者只有在有(或没有)特定颜色配置文件时才添加?
另外,我应该删除现有的颜色配置文件吗?
我知道这可能是一个广泛的问题,但我对颜色配置文件的理解非常有限,非常感谢对此的一些一般性指导 .
5 - 打开远程图像
GD本身支持通过 ImageCreateFrom*
函数打开远程图像,或者像我正在做的那样使用 file_get_contents()
和_675528 .
Imagick似乎只能打开本 Map 像或打开文件句柄 . 是否有任何直接的方法使Imagick读取远程图像(无需打开和关闭文件句柄)?
如果有人能够对这些问题有所了解,我将非常感激 .
提前致谢!
4 回答
PNG图像大小可能会增加的原因有很多,最明显的原因是GM / IM无法将透明度作为tRNS块传递(PNG图像基本上是布尔透明度) . 不幸的是,GraphicsMagick和ImageMagick的维护者尚未实现此功能 . 我和他们交换了电子邮件,所以我肯定知道这一点 .
我知道你不想使用外部工具,但请相信我 . Image / GraphicsMagick在压缩PNG图像方面非常糟糕 . 我使用的解决方案是,使用GraphicsMagick来操作图像,并检查图像是否包含透明像素,如果它确实包含透明像素,则在图像上运行OptiPNG . OptiPNG将看到透明度可以作为tRNS块传达并相应地采取行动 . 实际上你应该在使用Image / GraphicsMagick之后在所有PNG图像上运行OptiPNG,因为我发现你可以实现更大的压缩 . 您还可以通过关闭抖动和使用YUV颜色空间来节省空间 .
至于GM比IM更好地减小图像的大小,你应该知道GM默认使用8位色彩空间,而色彩还原图像,而ImageMagick默认使用16位 . 这就是为什么当颜色将图像减少到超过255种颜色时,GM比IM快得多 . 也许您应该在压缩后检查每个图像中的颜色数量以确认 .
您可以使用optipng(另一个PNG命令行工具)来优化PNG文件的大小 .
因为is not much support for ICM in browsers配置文件基本上是浪费带宽 . 因此,如果您的图像位于sRGB中,则可以安全地删除配置文件,否则最好将图像转换为sRGB并随后删除其配置文件 .
删除sRGB图像配置文件的原因是sRGB实际上是Internet,计算机和打印机上的标准,甚至Firefox也将sRGB颜色配置文件应用于未标记的图像 .
完全删除所有配置文件的另一个原因是,我计划将图像与嵌入的配置文件与其他无轮廓图像混合,例如, GIF图像,根据定义不能包含配置文件,您可以根据其嵌入的颜色空间渲染一些图像,使用a some other color profile渲染其他图像,这会导致您在具有嵌入式ICC配置文件的图像与之间看到边界的情况纯色背景颜色,与相同颜色背景颜色的其他无轮廓图像相邻 . 即使您设法获取页面上每个图像的配置文件,也有很多用户使用古老的ICC禁用浏览器 .
Bottom line: 颜色配置文件是邪恶的 . 只有在你真正需要的时候才使用它们 .
我所说的只有在您为最广泛的受众群体定位您的网站时才是正确的 . 否则YMMV .
我不确定你是否仍然需要一个答案,但我一直在编写一个包装GD和Imagick的图像处理库,所以我遇到了一些你的问题 .
2 - 压缩输出格式(即JPEG和PNG)
ImageMagick没有为PNG提供压缩,因为PNG是一种与JPEG不同的无损格式,这是一种“有损”格式 . 我会说ImageMagick正确地得到了这个 .
只需删除选项以在PNG中压缩,并为GD中的imagepng提供合理的默认设置 .
3 - 图像卷积
只需使用getPixelIterator循环每个像素并进行手动卷积 . 维基百科有一个很好的article与伪代码有关 .
5 - 打开远程图像
您可以单独打开图像并将其传递给Imagick