Intervention Imageで画像リサイズ・トリミングを実装する

概要

PHPで画像を扱うアプリケーションでは、「アップロードされた画像をサムネイルにする」「表示サイズに合わせてリサイズする」「正方形に切り抜く」といった処理が頻繁に必要になります。PHP標準のGDやImagickを直接扱うこともできますが、APIが低レベルで記述量が多く、ドライバ差も意識しなければなりません。

Intervention Image は、GD / Imagick の差異を統一APIで吸収してくれるPHPライブラリです。本記事では現行の v4 を対象に、リサイズ・スケール・トリミングの実装をハンズオン形式で整理します。

環境

  • PHP 8.3 以上
  • Intervention Image 4.x
  • GD または Imagick 拡張のいずれか

v3 から v4 でも resize / scale / cover / crop といった基本APIはほぼ互換で、本記事のサンプルは v4 でもそのまま動きます。一方で v2 とはメソッド体系が大きく異なるため、本記事は v3 系で導入され v4 に引き継がれた記法で統一します。

インストール

composer require intervention/image

GD と Imagick のどちらをドライバに使うかは、インスタンス生成時に指定します。GDは導入が容易で軽量、Imagickは色再現や対応フォーマットで優れます。ここではGDを例にします。

基本:画像の読み込みと保存

ImageManager にドライバを渡してインスタンスを作り、read() で画像を読み込みます。この読み込み・保存の流れは v3 と v4 で変わりません。

<?php

use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;

$manager = new ImageManager(new Driver());

$image = $manager->read('path/to/source.jpg');

$image->save('path/to/output.jpg'); // 拡張子から形式を自動判定

リサイズ:resize と scale の違い

リサイズには用途の異なるメソッドが複数あります。まず押さえたいのが resize() と scale() の違いです。

resize:指定サイズに強制する

resize() は指定した幅・高さに「強制的に」合わせます。片方だけ指定することもできます。

// 幅300px・高さ200pxに変形(アスペクト比は崩れる)
$image->resize(300, 200);

// 幅だけ指定(高さは元のまま比率維持)
$image->resize(width: 300);

両方指定すると縦横比が無視されて画像が歪むため、サムネイル用途では後述のメソッドを使うことが多くなります。

scale:アスペクト比を保つ

scale() は縦横比を保ったまま、指定した枠に収まるよう縮小・拡大します。

$image->scale(width: 300);     // 比率維持で幅300pxに
$image->scale(300, 200);       // 300x200の枠に比率維持で収める

scaleDown:拡大はしない

小さい画像を引き伸ばしてぼかしたくない場合は scaleDown() を使います。指定サイズより小さい画像はそのまま維持されます。

// 幅が300pxを超える場合だけ縮小。それ以下ならそのまま
$image->scaleDown(width: 300);

トリミング:cover・crop・pad

「指定サイズちょうどに、かつ歪ませずに収めたい」ときはトリミングを併用します。

cover:枠を埋めるように切り抜く

cover() は、指定サイズを完全に埋めるようアスペクト比を保って拡大・縮小し、はみ出した部分を切り落とします。サムネイルやアイコンで最もよく使います。

$image->cover(300, 300);         // 300x300の正方形サムネイル(中央基準)
$image->cover(300, 300, 'top');  // 切り抜き基準位置を指定

crop:座標を指定して切り抜く

crop() は、幅・高さに加えてオフセット座標を指定して任意の領域を切り出します。トリミングUIから受け取った座標をそのまま渡す用途に向きます。

// 幅200・高さ150の領域を、左から50px・上から30pxの位置で切り出す
$image->crop(200, 150, 50, 30);

pad:余白を付けて収める

切り落としたくない場合は、枠に収めつつ余白を付ける pad() が使えます。

$image->pad(300, 300, 'ffffff'); // 足りない部分を背景色で埋める

実装例:Laravelでアップロード画像をサムネイル化する

実務で頻出するのは「アップロードされた画像を受け取り、オリジナルとサムネイルの2種類を保存する」処理です。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;

class ImageUploadController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'image' => ['required', 'image', 'max:5120'], // 5MBまで
        ]);

        $manager = new ImageManager(new Driver());
        $uploaded = $request->file('image');
        $filename = Str::uuid() . '.jpg';

        // オリジナル:長辺1600pxを超える場合だけ縮小
        $original = $manager->read($uploaded)
            ->scaleDown(1600, 1600)
            ->toJpeg(80);
        Storage::disk('public')->put("images/{$filename}", $original);

        // サムネイル:300x300の正方形に中央でトリミング
        $thumb = $manager->read($uploaded)
            ->cover(300, 300)
            ->toJpeg(80);
        Storage::disk('public')->put("thumbnails/{$filename}", $thumb);

        return response()->json([
            'original'  => Storage::url("images/{$filename}"),
            'thumbnail' => Storage::url("thumbnails/{$filename}"),
        ]);
    }
}

実装のポイントは3つです。

  • 同じアップロードファイルから2回 read() して、用途別に加工しています(1つのインスタンスを使い回すと前段の加工結果が残ります)。
  • toJpeg(80) でエンコードし、保存先は Storage に任せることで、ディスク管理をLaravel側に一元化しています。
  • オリジナルは scaleDown で「大きいときだけ縮小」。むやみに拡大しません。

Laravelプロジェクトでは、公式の image-laravel パッケージを併用すると、ドライバ設定をコンフィグに寄せたりファサード経由で呼び出したりできます。上記のように ImageManager を直接生成する書き方でも問題なく動きます。

確認方法

  • 横長・縦長の画像をアップロードし、サムネイルが歪まず正方形で中央トリミングされているか確認する。
  • 300pxより小さい画像で scaleDown の箇所が引き伸ばされていないか確認する。
  • 出力JPEGのファイルサイズが品質指定に見合っているか確認する。
  • スマホ撮影の縦画像(Exif向き情報)が倒れずに扱われているか確認する。

注意点

  • v2 と v3 以降で API が異なります。v2 の Image::make() や fit() は v3 で変わっています。さらに v4 では v2 互換の Image ファサードと mask() が削除され、decode() 系のメソッド(decodePath() / decodeBinary() など)が追加されました。既存記事を参照する際はバージョンに注意してください。
  • ドライバの差異。GDとImagickで出力が完全一致するとは限りません。色再現やアニメーションGIF対応が必要ならImagickを選びます。
  • メモリ。大きな画像はデコード時にピクセル数分のメモリを消費します。アップロードサイズを事前にバリデーションし、memory_limit と突き合わせておきます。
  • オリジナルを破壊しない。切り抜き後に別サイズが欲しくなることは多いため、オリジナルは保持し、派生画像は都度生成できる設計にしておくと後が楽になります。

まとめ

  • resize は強制変形、scale は比率維持、scaleDown は縮小のみ。
  • 「指定サイズちょうど・歪ませない」は cover、座標指定の切り出しは crop。
  • アップロード処理では同一ソースから複数回 read() し、用途別に加工する。
  • 基本APIは v3 から v4 でほぼ互換だが、v2 とは別物。記事やサンプルのバージョンに注意する。

統一APIのおかげで「どのドライバか」をほとんど意識せずに書けるのがIntervention Imageの強みです。サムネイル生成のような定番処理ほど、こうしたライブラリで短く・読みやすく書いておく価値があります。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。