15

私は最近、Imagick がカラー プロファイルをサポートできるため、GD よりも優れた品質の画像を生成できることを発見しました (詳細については、この質問/回答を参照してください)。そのため、代わりに Imagick クラスを使用するように GD ラッパーを移植しようとしています。現在の 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 出力に変換するときに、透明度を維持する効果があります。ただし、透明な PNG 画像を JPEG 形式に変換する場合は、透明なピクセルの色を白に設定する必要があります。これまでのところ、ImageMagick を使用して、すべての透明ピクセルを白に変換することしかできませんでしたが、出力形式がサポートしている場合、透明度を維持することはできません。


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 圧縮定数のリストは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)

たまたま 100 ~ 200 KB のサイズの GD PNG 出力が、代わりに Imagick で出力すると非常に太くなるため (2 MB 程度のサイズ)、これは厄介な問題です...

この問題に関する SOに関する質問がいくつか ありますが、外部アプリケーションに依存しない有効な解決策を見つけることができませんでした。これは ImageMagick では本当に不可能ですか?!


3 - 画像畳み込み

画像を少し鮮明にするために呼び出した GD 実装ではImageConvolution()、Imagick に画像を鮮明にするメソッドが組み込まれていることは知っていますが (まだ試してみる機会がありませんでした)、Imagick に関数に相当しImageConvolution()ます。


4 - カラー プロファイル

これは元の実装とは関係ありませんが、私もそれを正しくしたいと考えています。

Imagick / International Color Consortium sRGB カラー プロファイルを常にすべての画像に追加する必要がありますか? それとも、特定のカラー プロファイルがある (またはない) 場合にのみ追加する必要がありますか?

また、既存のカラー プロファイルを削除する必要がありますか?

これは幅広い質問かもしれませんが、カラー プロファイルに関する私の理解は非常に限られているため、これに関する一般的なガイダンスをいただければ幸いです。


5 - リモート画像を開く

ImageCreateFrom*GD は、関数を介して、または私が行っているようにfile_get_contents()組み合わせて使用​​して、リモート画像を開くことをネイティブにサポートしています。ImageCreateFromString()

Imagick は、ローカルの画像を開くか、ファイル ハンドルを開くことしかできないようです。Imagick にリモート イメージを読み取らせる簡単な方法はありますか (ファイル ハンドルを開いたり閉じたりする必要はありません)。


誰かがこれらの質問のいずれかに光を当てることができれば、私は非常に感謝しています.

前もって感謝します!

4

4 に答える 4

13

PNG 画像のサイズが大きくなる理由はいくつかありますが、最も明白な理由は、GM/IM が透明性を tRNS チャンク (基本的には PNG 画像のブール値の透明性) として伝達できないことです。残念ながら、GraphicsMagick と ImageMagick のメンテナーは、この機能をまだ実装していません。私は彼らとメールを交換したので、確かにこれを知っています。

あなたが外部ツールを使いたくないことはわかっていますが、そうしていると信じてください。Image/GraphicsMagick は、PNG 画像の圧縮が非常に苦手です。私が使用している解決策は、 GraphicsMagick を使用して画像を操作し、画像に透明なピクセルが含まれているかどうかを確認し、透明なピクセルが含まれている場合は画像で OptiPNG を実行することです。OptiPNG は、透過性を tRNS チャンクとして伝達できることを確認し、それに応じて動作します。実際には、Image/GraphicsMagick を使用した後、すべての PNG 画像に対して OptiPNG を実行する必要があります。ディザリングをオフにして、YUV カラー スペースを使用することで、スペースを節約することもできます。

GM が画像のサイズを IM よりも適切に縮小する場合、ImageMagick はデフォルトで 16 ビットを使用するのに対し、GM はデフォルトで画像を縮小するときに 8 ビットの色空間を使用することを知っておく必要があります。これが、イメージを 255 色以上の値に減色するときに、GM が IM よりもはるかに高速である理由です。おそらく、圧縮後の各画像の色数を確認して確認する必要があります。

于 2011-04-29T11:25:39.590 に答える
2

optipng (別の PNG コマンドライン ツール) を使用して、PNG ファイルのサイズを最適化できます。

于 2011-05-03T07:42:28.117 に答える
0

まだ回答が必要かどうかはわかりませんが、GD と Imagick をラップする画像処理ライブラリを書いているので、いくつかの問題に遭遇しました。

2 - 出力形式の圧縮 (つまり、JPEG と PNG)

ImageMagick は、「不可逆」形式である JPEG とは異なり、PNG は可逆形式であるという単純な事実のために、PNG の圧縮を提供しません。ImageMagick はこれを正しく取得したと言えます。

PNG で圧縮するオプションをドロップし、GD で imagepng の適切なデフォルトを指定するだけです。

3 - 画像畳み込み

getPixelIteratorを使用して各ピクセルをループし、手動で畳み込みを行うだけです。ウィキペディアには、これに関する疑似コードの良い記事があります。

5 - リモート画像を開く

画像を個別に開いて、Imagick に渡すことができます

$handle = fopen('http://example.com/foo.jpg', 'rb');
$img = new Imagick();
$img->readImageFile($handle);
于 2016-07-10T03:49:47.833 に答える