Help us understand the problem. What is going on with this article?

PHP: GDで画像のサイズ変更やサムネイル生成のやりかた

More than 3 years have passed since last update.

GDを使う動機のひとつとして、ImageMagicが使えないけどGDが入っているような環境で、画像のサイムネイルを作ることがありますが、APIが分かりにくく結構面倒な作業になります。

幸いにもPHPには、ImagineというGDやImageMagicのAPIを抽象化して画像を処理できるライブラリがあります。が、サムネイル生成にImagineでは元画像の解像度が大きくなればなるほど、メモリの消費量が増えていくという欠点があり、1000x1000のこの画像を500x500にリサイズするだけで13.75MB消費します。従って、解像度の高い画像のサムネイルの作成については、直にGDのライブラリを使ったほうがメモリの消費を30〜50%くらい抑えられます。

ImagineとGDのメモリ最大消費量

画像サイズ Imagine GD
1000x1000 13.75MB 6.00MB
1000x2000 27.25MB 10.25MB
1000x3000 40.50MB 15.00MB
1000x4000 54.25MB 19.50MB

画像のサイズを変形する関数

サムネイル作成のコードを一気に書く前に、画像のサイズを変形するだけの関数を実装しておくと便利です。この関数はアスペクト比の計算を気にしないので、実装時や改修時に混乱しにくて良いです。

/**
 * 画像のサイズを変形して保存する
 * @param string $srcPath
 * @param string $dstPath
 * @param int $width
 * @param int $height
 */
function transform_image_size($srcPath, $dstPath, $width, $height)
{
    list($originalWidth, $originalHeight, $type) = getimagesize($srcPath);
    switch ($type) {
        case IMAGETYPE_JPEG:
            $source = imagecreatefromjpeg($srcPath);
            break;
        case IMAGETYPE_PNG:
            $source = imagecreatefrompng($srcPath);
            break;
        case IMAGETYPE_GIF:
            $source = imagecreatefromgif($srcPath);
            break;
        default:
            throw new RuntimeException("サポートしていない画像形式です: $type");
    }

    $canvas = imagecreatetruecolor($width, $height);
    imagecopyresampled($canvas, $source, 0, 0, 0, 0, $width, $height, $originalWidth, $originalHeight);
    imagejpeg($canvas, $dstPath);
    imagedestroy($source);
    imagedestroy($canvas);
}

アスペクト比を計算するアルゴリズム

次にアスペクト比を計算するアルゴリズムを実装します。いろんなアルゴリズムがあると思いますが、ここで紹介するのは内接サイズを計算するアルゴリズムになります。

/**
 * 内接サイズを計算する
 * @param int $width
 * @param int $height
 * @param int $containerWidth
 * @param int $containerHeight
 * @return array
 */
function get_contain_size($width, $height, $containerWidth, $containerHeight)
{
    $ratio = $width / $height;
    $containerRatio = $containerWidth / $containerHeight;
    if ($ratio > $containerRatio) {
        return [$containerWidth, intval($containerWidth / $ratio)];
    } else {
        return [intval($containerHeight * $ratio), $containerHeight];
    }
}

こんな感じで、コンテナのサイズから、最適な内接サイズをタプルで返します。

php > var_dump(get_contain_size(500, 500, 100, 100));
array(2) {
  [0]=>
  int(100)
  [1]=>
  int(100)
}
php > var_dump(get_contain_size(640, 480, 100, 100));
array(2) {
  [0]=>
  int(100)
  [1]=>
  int(75)
}
php > var_dump(get_contain_size(50, 50, 100, 100));
array(2) {
  [0]=>
  int(100)
  [1]=>
  int(100)
}

サムネイルを作る関数を実装する

最後に、上の2つの関数を組み合わせてサムネイルを作成する関数を実装します。

/**
 * 画像のサムネイルを保存する
 * @param string $srcPath
 * @param string $dstPath
 * @param int $maxWidth
 * @param int $maxHeight
 */
function make_thumbnail($srcPath, $dstPath, $maxWidth, $maxHeight)
{
    list($originalWidth, $originalHeight) = getimagesize($srcPath);
    list($canvasWidth, $canvasHeight) = get_contain_size($originalWidth, $originalHeight, $maxWidth, $maxHeight);
    transform_image_size($srcPath, $dstPath, $canvasWidth, $canvasHeight);
}

おわり

GDを使って画像のサイズ変更・アスペクト比を維持したサイムネイル画像の生成方法を紹介しました。もうPHPで画像処理するのはやめたいです(´・ω・`)

suin
Qiita 4位/TypeScript入門書執筆中/TypeScripterのための座談会「YYTypeScript」主催/『実践ドメイン駆動設計』書籍邦訳レビュア/分報Slack考案/YYPHP主催/CodeIQマガジン執筆/株式会社クラフトマンソフトウェア創設/Web自動テスト「ShouldBee」の開発/TypeScript/DDD/OOP
https://yyts.connpass.com/
shouldbee
開発者向けテスト支援サービスShouldBeeを開発・運営するスタートアップ(onlab第8期)
http://shouldbee.at
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした