写真ギャラリーのソース解説① ~画像リサイズPHP~

先日公開した写真ギャラリーのソースコードを解説していきます。

まずは、第1回目ということで画像をリサイズするPHPについてです。

画像変換の拡張機能 (PHP GD) を使用しているので必ずインストールして下さい。
Xserverの場合は予めインストール済みです。

機能などの紹介は紹介の記事を見て下さい。



ソースコード

では、まずは、ソースコードを見ていきましょう。
ファイルをダウンロードする方はこちらを右クリックしてリンク先を保存して下さい。

<?php

$image_file = './default.jpg';
if (!empty($_GET['url'])) {
	$image_file = $_GET['url'];
}

list($new_width, $new_height) = getimagesize($image_file);
$exif = exif_read_data($image_file);
if($exif['Orientation'] == 6 || $exif['Orientation'] == 8){
	$buff       = $new_width;
	$new_width  = $new_height;
	$new_height = $buff;
}
if($new_width >= 1920 || $new_height >= 1920){
	$buff = $new_width / $new_height;
	$new_width  = 1920;
	$new_height = 1920 / $buff;
	if($new_height > 1920){
		$new_height = 1920;
		$new_width  = 1920 * $buff;
	}
}

$cache_name = 'notspecified';

// 幅か高さだけ指定されている場合は、正方形にする
if (!empty($_GET['width']) && !empty($_GET['height'])) {
	$new_width  = $_GET['width'];
	$new_height = $_GET['height'];
	$cache_name = $new_width.'x'.$new_height;
} else if(!empty($_GET['width']) && empty($_GET['height'])){
	$new_width  = $_GET['width'];
	$new_height = $new_width;
	$cache_name = $new_width.'x'.$new_height;
} else if(empty($_GET['width']) && !empty($_GET['height'])){
	$new_height = $_GET['height'];
	$new_width  = $new_height;
	$cache_name = $new_width.'x'.$new_height;
}

// jpegの圧縮品質
// 初期値は95で、サイズが720px以下は80
$quolity = 95;
if($new_width <= 720 || $new_height <= 720){
	$quolity = 80;
}

$file_type = strtolower(end(explode('.', $image_file)));

if($file_type === 'jpg' || $file_type === 'jpeg'){
	$original_image = ImageCreateFromJPEG($image_file);               // jpegファイルを読み込む
	$exif = exif_read_data($image_file);
	switch($exif['Orientation']){
		case 3:
			$original_image = imagerotate($original_image, 180, 0, 0);
		break;
		case 6:
			$original_image = imagerotate($original_image, 270, 0, 0);
		break;
		case 8:
			$original_image = imagerotate($original_image,  90, 0, 0);
		break;
	}
	$new_image      = ImageCreateTrueColor($new_width, $new_height);  // 背景画像作成
} else{
	// その他の形式の場合は何もしない
	return;
}

// 元画像のファイルサイズを取得
list($original_width, $original_height) = getimagesize($image_file);
if($exif['Orientation'] == 6 || $exif['Orientation'] == 8){
	$buff            = $original_width;
	$original_width  = $original_height;
	$original_height = $buff;
}

// 元画像のアスペクト比を計算
$aspect = $original_width / $original_height;

// リサイズ後の横幅を設定 (一旦、高さは無視)
if($original_width > $new_width){
	$resize_width = $new_width;
} else{
	$resize_width = $original_width;
}

// 高さをアスペクト比から計算
$resize_height = $resize_width / $aspect;
// 指定サイズよりも大きかったら横幅を再計算
if($resize_height > $new_height){
	$resize_height = $new_height;
	$resize_width  = $new_height * $aspect;
}

// 元画像から再サンプリング
ImageCopyResampled($new_image, $original_image          ,
                   (($new_width - $resize_width) / 2)   ,
                   (($new_height - $resize_height) / 2) ,
                   0, 0, $resize_width, $resize_height  ,
                   $original_width, $original_height)   ;

// 画像をブラウザに表示
$new_file_name = end(explode('./', $image_file));
$new_file_name = str_replace('/', '_', $new_file_name);
$new_file_name = str_replace('.', '-'.$cache_name.'.', $new_file_name);

if ($file_type === 'jpg' || $file_type === 'jpeg') {
	ImageJPEG($new_image, './cache/'.$new_file_name, $quolity);
	ImageJPEG($new_image, null, $quolity);
}

// メモリを開放する
imagedestroy($new_image);
imagedestroy($original_image);

?>

解説

それでは上から順番に解説します。

元画像URLと変更後サイズの取得、修正

外部から本PHPを読み出すときにサーチ情報 (URLの?以降のクエリ情報) から元画像と変更後のサイズを取得していきます。

PHPでは特殊変数 “$_GET” に連想配列の形で保存されています。
従って、”$_GET[key]” で必要な情報を取得できます。
※keyは?以降の=の前に相当する文字列で、””か”で囲みます。

$image_file = './default.jpg';
if (!empty($_GET['url'])) {
	$image_file = $_GET['url'];
}

はじめに変数 “$image_file” にデフォルトの画像パスを代入します。
これは元画像URLが指定されない時に表示させたい画像のパスとします。

if文でurlが指定されている時は “$image_file” のパスを指定画像に変更します。

list($new_width, $new_height) = getimagesize($image_file);
$exif = exif_read_data($image_file);
if($exif['Orientation'] == 6 || $exif['Orientation'] == 8){
	$buff       = $new_width;
	$new_width  = $new_height;
	$new_height = $buff;
}
if($new_width >= 1920 || $new_height >= 1920){
	$buff = $new_width / $new_height;
	$new_width  = 1920;
	$new_height = 1920 / $buff;
	if($new_height > 1920){
		$new_height = 1920;
		$new_width  = 1920 * $buff;
	}
}

次に元画像のサイズを取得します。
getimagesizeメソッドで縦横寸法を取得するのですが、注意が必要な点があります。

それはEXIFで回転情報が90度若しくは-90度の時は表示されている縦横寸法と取得される寸法が逆になることです。
なので、その場合は縦横寸法を入れ替える操作が必要です。

回転情報はEXIFの ‘Orientation’ というキーで保存されています。
この値が6か8の時が縦横が逆になります。
(画像が反転している時の処理はしていません)

ちなみに、’Orientation’ の値と回転情報の関係は以下です。

‘Orientation’ の値 画像の回転情報
無し 回転しない
1 回転しない
2 左右反転
3 180度回転
4 上下反転
5 左90度回転して、更に上下反転
6 左90度回転
7 右90度回転して、更に上下反転
8 右90度回転

ここで、変更後の縦横寸法が指定されなかった場合の寸法に上限を設けます。
上記コードではフルHDの長辺寸法である1920pxを上限としてリサイズ後の寸法を一旦決めています。

// 幅か高さだけ指定されている場合は、正方形にする
if (!empty($_GET['width']) && !empty($_GET['height'])) {
	$new_width  = $_GET['width'];
	$new_height = $_GET['height'];
	$cache_name = $new_width.'x'.$new_height;
} else if(!empty($_GET['width']) && empty($_GET['height'])){
	$new_width  = $_GET['width'];
	$new_height = $new_width;
	$cache_name = $new_width.'x'.$new_height;
} else if(empty($_GET['width']) && !empty($_GET['height'])){
	$new_height = $_GET['height'];
	$new_width  = $new_height;
	$cache_name = $new_width.'x'.$new_height;
}

最後に変更後の寸法が指定された場合にはその寸法になるように変更します。
但し、縦若しくは横だけの寸法が指定された場合は正方形になるようにします。

この辺りの例外処理はご自分の用途に合わせて変更して下さい。

元画像を読み込む

続いて元画像をメモリ上に読み出します。

一応読み込む前に拡張子がjpeg形式であることを確認します。
explodeメソッドで画像パスを ‘.’ で分割し、endメソッドで最後の要素を取り出します。
strtolowerメソッドで小文字に変換して拡張子を取得できます。

$file_type = strtolower(end(explode('.', $image_file)));

if($file_type === 'jpg' || $file_type === 'jpeg'){
	$original_image = ImageCreateFromJPEG($image_file);               // jpegファイルを読み込む
	$exif = exif_read_data($image_file);
	switch($exif['Orientation']){
		case 3:
			$original_image = imagerotate($original_image, 180, 0, 0);
		break;
		case 6:
			$original_image = imagerotate($original_image, 270, 0, 0);
		break;
		case 8:
			$original_image = imagerotate($original_image,  90, 0, 0);
		break;
	}
	$new_image      = ImageCreateTrueColor($new_width, $new_height);  // 背景画像作成
} else{
	// その他の形式の場合は何もしない
	return;
}

変数 “$original_image” にImageCreateFromJPEGメソッドで元画像を読み込みます。
元画像が回転している場合は、imagerotateメソッドで必要な回転角だけ回転させておきます。

また、このタイミングで変更後のサイズで黒い背景画像をImageCreateTrueColorメソッドを用いて作成して、画像IDを変数 $new_image に保存していおきます。

リサイズした時の画像サイズを計算

今見たら元画像の縦横寸法取得が2回になっていましたが、とりあえず解説なのでそのまま行きます。
最初の変更後サイズの計算時と共通化できるところはしてしまった方が良いでしょう。

// 元画像のファイルサイズを取得
list($original_width, $original_height) = getimagesize($image_file);
if($exif['Orientation'] == 6 || $exif['Orientation'] == 8){
	$buff            = $original_width;
	$original_width  = $original_height;
	$original_height = $buff;
}

// 元画像のアスペクト比を計算
$aspect = $original_width / $original_height;

// リサイズ後の横幅を設定 (一旦、高さは無視)
if($original_width > $new_width){
	$resize_width = $new_width;
} else{
	$resize_width = $original_width;
}

// 高さをアスペクト比から計算
$resize_height = $resize_width / $aspect;
// 指定サイズよりも大きかったら横幅を再計算
if($resize_height > $new_height){
	$resize_height = $new_height;
	$resize_width  = $new_height * $aspect;
}

ここでは変更後のサイズ内に収まるように寸法を計算します。
元画像の方が変更後よりも小さい場合は、拡大しません。
(余った領域は黒で塗りつぶし)

画像の書き出し

最後に画像を書き出して、メモリを開放して処理完了です。

// 元画像から再サンプリング
ImageCopyResampled($new_image, $original_image          ,
                   (($new_width - $resize_width) / 2)   ,
                   (($new_height - $resize_height) / 2) ,
                   0, 0, $resize_width, $resize_height  ,
                   $original_width, $original_height)   ;

// 画像をブラウザに表示
$new_file_name = end(explode('./', $image_file));
$new_file_name = str_replace('/', '_', $new_file_name);
$new_file_name = str_replace('.', '-'.$cache_name.'.', $new_file_name);

if ($file_type === 'jpg' || $file_type === 'jpeg') {
	ImageJPEG($new_image, './cache/'.$new_file_name, $quolity);
	ImageJPEG($new_image, null, $quolity);
}

// メモリを開放する
imagedestroy($new_image);
imagedestroy($original_image);

画像をリサイズして再サンプリングするにはImageCopyResampledメソッドを使います。
ImageCopyResampled(出力先の変数, コピー元の画像変数, 元画像の描画開始点X座標, 元画像の描画開始点Y座標, 元画像のコピー開始点X座標, 元画像のコピー開始点Y座標, 変更後の横寸法, 変更後の縦寸法, コピー元の横寸法, コピー元の縦寸法) と書きます。

出力先の変数には予め黒背景を作っておいた $new_image を指定して、その上に元画像をリサイズしながらコピーしてきます。

元画像の描画開始座標は変更後の画像の中央になるようにします。
トリミングはしないのでコピー開始座標は (0,0) です。
Instagramのようにトリミングする場合は開始座標を変更して下さい。

今回は一度作成したリサイズ画像をキャッシュとして保存しておくことにするのでそのファイル名を $new_file_name にパスやサイズをファイル名に含めるように変換して保存しておきます。

出来上がった画像をJPEG画像として書き出していきます。
これにはImageJPEGメソッドを用います。
ImageJPEG(生成した画像の変数, 出力ファイル, 圧縮品質) と書きます。

生成した画像は $new_image に保存されているのでそれを指定します。
出力ファイルが不要であれば “null” を指定すればブラウザに標準出力されます。
圧縮品質は0~100で100が最高画質です。
65以上であればそれほど気にならないレベルの画像になります。

ちなみにですが、圧縮品質が初期値 (75) で標準出力だけで良ければ ImageJPEG(生成した画像の変数) と省略して書くことができます。

一番最後に使用したメモリをimagedestroyで開放して完了です。

まとめ

以上、画像をリサイズするPHPのソースコード解説でした。

不具合やご質問等あればコメントやお問合せからお願いします。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

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