将PHP / GD包装器移植到Imagick的问题 [英] Issues porting PHP/GD wrapper to Imagick

查看:85
本文介绍了将PHP / GD包装器移植到Imagick的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近发现,与GD相比,Imagick可以支持颜色配置文件,从而生成质量更好的图像(请参阅此问题 / 回答更多细节),所以我试图移植我的GD包装器来使用Imagick类,我目前的GD实现如下所示:

I've recently discovered that Imagick can support color profiles and thus produce images of better quality compared to GD (see this question / answer for more details), so I'm trying to port my GD wrapper to use the Imagick class instead, my current GD implementation looks like this:

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类方法,这就是我得到的到目前为止:

I've been experimenting with the Imagick class methods and this is what I got so far:

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文档有点糟糕,我别无选择,只能尝试所有可用方法和参数的试错方法组合,这需要花费大量时间。

The basic functionality (crop / resize / watermark) is already supported, however, I'm still having some issues. Since the PHP Imagick documentation kinda sucks I've no other choice than to try a trial and error approach combination of all the available methods and arguments, which takes a lot of time.

我目前遇到的问题是:

在我的原始实现中,行:

In my original implementation, the lines:

ImageFill($image, 0, 0, IMG_COLOR_TRANSPARENT);
ImageSaveAlpha($image, true);
ImageAlphaBlending($image, true);

具有保留透明度。但是,如果尝试将透明PNG图像转换为JPEG格式,则透明像素的颜色应设置为白色。到目前为止,使用ImageMagick,我只能将所有透明像素转换为白色,但如果输出格式支持透明度,我无法保留透明度。

Have the effect of preserving the transparency when you are converting a transparent PNG image to a PNG output. If, however, you try to convert a transparent PNG image to a JPEG format, the transparent pixels should have their color set to white. So far, with ImageMagick, I've only been able to convert all transparent pixels to white, but I can't preserve the transparency if the output format supports it.

我的原始实现在PNG上使用9的压缩级别,在JPEG上使用90的质量:

My original implementation uses a compression level of 9 on PNGs and a quality of 90 on JPEGs:

foreach (array('gif' => 0, 'png' => 9, 'jpe?g' => 90) as $key => $value)

行:

$image->setImageCompression(Imagick::COMPRESSION_JPEG);
$image->setImageCompressionQuality($value);
$image->stripImage();

似乎压缩JPEG图像 - 然而GD可以使用相同的<$来压缩它c $ c> $ value 作为质量参数 - 为什么?关于以下两者之间的差异,我也处于黑暗中:

Seem to compress JPEG images - GD however, is able to compress it much more using the same $value as a quality argument - why? I'm also in the dark regarding the differences between:

  • Imagick::setCompression() / Imagick::setImageCompression() and
  • Imagick::setCompressionQuality() / Imagick::setImageCompressionQuality()

其中我应该使用哪一种,它们的区别是什么?此外,最关键的问题与PNG压缩有关, Imagick压缩列表常量似乎不支持PNG格式:

Which one should I use and what are their differences? Also, the most critical problem has to do with PNG compression, the list of Imagick compression constants seem to not support PNG formats:

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)

这是屁股中的一个油漆,因为GD PNG输出恰好大小为100-200 KB,如果输出则会变得非常胖改为使用Imagick(大小约为2 MB)......

This is being a paint in the ass, since a GD PNG output that happens to have a size of 100-200 KB gets extremely fatter if outputted with Imagick instead (size in the order of 2 MB)...

一对 问题 关于这个问题,但我找不到任何不依赖外部应用程序的工作解决方案。这对ImageMagick来说真的不可能吗?!

There are a couple of questions on SO regarding this issue, but I haven't been able to find any working solution that doesn't rely on external applications. Is this really impossible to do with ImageMagick?!

在GD实现中,我调用 ImageConvolution()来锐化图像,我知道Imagick有内置的方法来锐化图像(我没有'我有机会尝试一下)但我想知道Imagick是否有相当于 ImageConvolution()的功能。

In the GD implementation I call ImageConvolution() to sharpen the image a bit, I know that Imagick has built-in methods to sharpen images (I haven't had the chance to try them out yet) but I'd like to know if Imagick has an equivalent of the ImageConvolution() function.

这与原始实现无关,但我也会喜欢搞定。

This is not related to the original implementation, but I would also like to get it right.

我是否应该始终将Imagick / International Color Consortium sRGB颜色配置文件添加到所有图像中?或者只有在有(或没有)特定颜色配置文件时才添加?

Should I always add the Imagick / International Color Consortium sRGB color profile to all images? Or should this only be added when there is (or isn't) a specific color profile?

另外,我应该删除现有的颜色配置文件吗?

Also, should I delete the existing color profiles?

我知道这可能是一个广泛的问题,但我对颜色配置文件的理解非常有限,对此的一些一般性指导将非常感激。

I understand that this may be a broad question but my understanding of color profiles is very limited and some general guidance on this would be very much appreciated.

GD本身支持通过<$ c $打开远程图像c> ImageCreateFrom * 函数,或者使用 file_get_contents()结合使用ImageCreateFromString()就像我在做的那样。

GD natively supports opening remote images, either via the ImageCreateFrom* functions, or using file_get_contents() in combination with ImageCreateFromString() like I am doing.

Imagick似乎只能打开本地图像或打开文件句柄。是否有任何直接的方法使Imagick读取远程图像(无需打开和关闭文件句柄)?

Imagick seems to only be able to open local images, or open file handles. Is there any straightforward way to make Imagick read remote images (without having to open and close file handles)?

如果有人我可能会对这些问题有所了解。我将非常感激。

If someone could shed some light into any of these questions I will be very grateful.

提前致谢!

推荐答案

为什么你的PNG图像的大小可能会增加有很多原因,你将遇到的最明显的原因是GM / IM无法将透明度传达为tRNS块(基本上是布尔透明度)对于PNG图像)。不幸的是,GraphicsMagick和ImageMagick的维护者尚未实现此功能。我和他们交换了电子邮件,所以我肯定知道这一点。

There are a number of reasons why your PNG images may be increasing in size, the most obvious one that you will run into is GM/IM's inability to convey transparency as a tRNS chunk (basically boolean transparency for PNG images). Unfortunately the maintainers of GraphicsMagick and ImageMagick have not implemented this feature yet. I exchanged emails with them so I know this for sure.

我知道你不想使用外部工具但是相信我你做的。 Image / GraphicsMagick在压缩PNG图像方面非常糟糕。我使用的解决方案是,使用GraphicsMagick来操作图像,并检查图像是否包含透明像素,如果它确实包含透明像素,则在图像上运行OptiPNG。 OptiPNG将看到透明度可以作为tRNS块传达并相应地采取行动。实际上你应该在使用Image / GraphicsMagick之后在所有PNG图像上运行OptiPNG,因为我发现你可以实现更大的压缩。您还可以通过关闭抖动和使用YUV色彩空间来节省空间。

I know you don't want to use external tools but trust me you do. Image/GraphicsMagick are really bad at compressing PNG images. The solution I am using is, use GraphicsMagick to manipulate the image and also check if the image contains transparent pixels, if it does contain transparent pixels then run OptiPNG on the image. OptiPNG will see that transparency can be conveyed as a tRNS chunk and act accordingly. Actually you should run OptiPNG on all PNG images after using Image/GraphicsMagick because I have found that you can achieve much greater compression. You can also save space by turning dithering off and by using the YUV color space.

至于GM比IM更好地缩小图像大小,你应该知道GM by默认情况下,在减少图像颜色时使用8位颜色空间,而默认情况下ImageMagick使用16位。这就是为什么当颜色将图像减少到超过255种颜色时,GM比IM快得多。也许您应该在压缩后检查每个图像中的颜色数量以确认。

As for GM reducing the size of images better than IM, you should know that GM by default uses an 8 bit color space when color reducing images while ImageMagick by default uses 16 bits. This is why GM is so much faster than IM when color reducing images to a value over 255 colors. Maybe you should check the number of colors in each image after compression to confirm.

这篇关于将PHP / GD包装器移植到Imagick的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆