私は最近、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
品質引数と同じものを使用してさらに圧縮することができます。なぜですか? 私はまた、次の違いについても暗闇の中にいます。
Imagick::setCompression()
/Imagick::setImageCompression()
そしてImagick::setCompressionQuality()
/Imagick::setImageCompressionQuality()
どちらを使用する必要があり、それらの違いは何ですか? また、最も重大な問題は 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 にリモート イメージを読み取らせる簡単な方法はありますか (ファイル ハンドルを開いたり閉じたりする必要はありません)。
誰かがこれらの質問のいずれかに光を当てることができれば、私は非常に感謝しています.
前もって感謝します!