GDライブラリを用いたサムネイル生成

どんどん先延ばしにしていましたが、ようやくまとまった時間がとれたので公開します。

投稿された画像のサイズをいじるためのプログラムです。

PHPには画像編集用のライブラリ(GDライブラリ)が存在してるのでこれを使ってサムネイルを生成します。

具体的にはこんな感じ

「参照」でサイズ変更する画像を選択し、「リサイズ」を押してください。
JPEG,PNG,GIFに対応しています。(一応透過情報も保持するようにしています。)

つくったソースは以下の二つ

  • test.php
  • class.resize.php

test.php

<?php
require_once("class.resize.php");
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
<!--
	body{color:#ffffff;}
-->
</style>
</head>
<body bgcolor="#000000">

<form action="test.php" method="post" enctype="multipart/form-data">
	<input type="file" name="userfiles" />
    <input type="submit" value="リサイズ" />
</form>

<br /><br />

<?php
if(!$_FILES['userfiles']['size'] == 0){

	$updir = "./uploads/";
	$resizedir = "./resize/";

	$filename = $_FILES['userfiles']['name'];
	$filepath = "./uploads/".$_FILES['userfiles']['name'];	

	if(!move_uploaded_file($_FILES['userfiles']['tmp_name'],$filepath)){
		die("アップロードに失敗しました。");
	}

	$size = getimagesize($filepath);

	echo "元ファイル\n<br />\n width:".$size[0]."px \n <br> \n height:".$size[1]."px \n <br />\n";
	echo "<br />\n<img src=\"{$filepath}\" />\n<br />\n";

	//サムネイルのwidthを200pxとする

	$height = $size[1] * (200 / $size[0]);

	$resize = new Resize();
	$resize->img_resize($filename,$updir,200,
                         ceil($height),$resizedir,100);
	echo "<br />リサイズ後の画像<br />\n";

	$size = getimagesize($resizedir.$filename);

	echo "width:".$size[0]."px \n<br>\n height:".$size[1]."px\n<br />\n";
	echo "<img src=\"resize/{$filename}\" />\n";
}
?>

</body>
</html>

class.resize.php

<?php
class Resize{

	public function __construct(){}

	public function __set($prop,$val){
		echo "error : 存在しないプロパティ{$prop}に、値{$val}を設定しようとしています。\n";
	}

	public function __get($prop){
		echo "error : プロパティ{$prop}は存在しません\n";
	}

	public function __call($func,$params){
		echo "error : 存在しない関数{$func}が呼び出されました。\n";
		var_dump($params);
	}

	public function img_resize($filename,$path,$thumb_width,
					$thumb_height,$savedir,$quality){
    	$img_size = getimagesize($path.$filename);

		switch($img_size['mime']){
			//それぞれ元画像のリソースIDを返す
			case "image/jpeg":
			case "image/pjpeg":
				$img = @imagecreatefromjpeg($path.$filename);
				//リサイズ後の画像のリソースIDを返す
				$cp_img = imagecreatetruecolor($thumb_width,
									$thumb_height);
				break;

			case "image/gif":
				$img = @imagecreatefromgif($path.$filename);
				$cp_img = imagecreatetruecolor($thumb_width,
									$thumb_height);
				$scal_index = imagecolortransparent($img);

				if(!$scal_index>=0)break;//透過色がなければbreak

				//第二引数は色相近似の向上のためのディザon
				imagetruecolortopalette($cp_img, true, 256);
				$trnprt_color = imagecolorsforindex( $img, $scal_index);
				imagecolorset( $cp_img, 0, $trnprt_color['red'],
				$trnprt_color['green'],$trnprt_color['blue']);
				imagefill( $cp_img, 0, 0, 0 );
				imagecolortransparent( $cp_img, 0);
				break;

			case "image/png":
				$img = @imagecreatefrompng($path.$filename);
				$color_num = imagecolorstotal($img);
				$cp_img = imagecreatetruecolor($thumb_width,
									$thumb_height);
				$scal_index = imagecolortransparent($img);
				if(!$scal_index>=0)break;
				imagetruecolortopalette($cp_img, true, $color_num);
				break;

			default:
				return FALSE;
		}

		//imagecopyresized->返り値:bool , 第1引数のIDにリサイズしてコピーする。
		$res = imagecopyresampled(
			$cp_img,
			$img,
			0,
			0,
			0,
			0,
			$thumb_width,
			$thumb_height,
			$img_size[0],
			$img_size[1]
		);

		if(!$res)return FALSE;

		switch($img_size['mime']){
			//それぞれ元画像のリソースIDを返す
			case "image/jpeg":
			case "image/pjpeg":
				imagejpeg($cp_img,$savedir.$filename,$quality);
				break;
			case "image/gif":
				imagegif($cp_img,$savedir.$filename);
				break;
			case "image/png":
				$quality = ceil($quality * 0.09);
				imagepng($cp_img,$savedir.$filename,$quality);
				break;
		}
		//メモリ開放
		imagedestroy($cp_img);
		imagedestroy($img);
    }
}

?>

とこんな感じです。

test.php解説
まず1~3行目でサムネイル生成用のクラスを呼び出しています。
16~19行目までがサムネイルの元となる画像のアップロード用のformを生成しています。

24行目の条件分岐はファイルがPOSTされている場合に動くというものです。

25~34行目がファイルのアップロードです。
ファイルアップロードを行う関数、move_uploaded_fileは第一引数に現在のファイルパス、第二引数にアップロード先のファイルパスになります。
返り値は成功、および失敗をしめすBoolean値です。
基本的にPOSTで送られてきたファイルというのはサーバー内のテンポラリーファイル内に保存されます。
$_FILES['name']['tmp_name'] でアクセスできます。
ちなみにnameはformのinput type=”file”で設定したnameに相当します。

36行目のgetimagesizeで元のファイルの横幅、縦幅を獲得します。
getimagesizeの返り値は配列になっており キーは0,1,mimeの三つです。
0が横幅 1が縦幅 mimeがMIMETYPEになります。

43行目はサムネイル後のサイズを算出しています。
今回は横幅を200pxにあわせています。
この行の数式はアスペクト比を保持した縦幅の算出を行っています。

45行目でリサイズ用のクラスをインスタンス化しています。
46行目でリサイズクラスのサムネイルを作るための関数img_resize(ユーザー定義関数)を使ってサムネイルを生成しています。
引数は1.画像の名前 2.保存されているディレクトリのパス 3.サムネイルの横幅 4.サムネイルの縦幅 5.サムネイルの保存先ディレクトリのパス 6.画質(0~100 単位:%)

class.resize.php解説

6~17行目の__set , __get , __call で存在しないプロパティ、関数を呼び出したときの処理を書いています。

19行目のimg_resize関数がサムネイルを作成する関数になります。

23行目はファイルの種類をmimeから判定して、それぞれ種類に応じて処理を行っています。

25~26行目はjpeg画像の分岐ですが26行目のpjpegというのが曲者で、IEでプログレッシブJPGをアップロードしたときにこんなMIMEタイプがでてきます。 処理は同じなのでまとめています。

27行目でimagecreatefromjpeg関数で元画像を現すIDを取得します。
29行目がリサイズ後の画像を表現するIDの取得です。
(まだ現時点では真っ白の画像)

gif,pngはこれに加えて imagecolortransparent関数を用いて画像内の透過カラーが存在するかどうかを調べ、存在すればそれを元にリサイズ後の画像で透過を保持するように処理を行っています。

65行目はimagecopyresampled関数を用いた画像のリサイズです。
よく似た処理をする関数にimagecopyresized関数というものがありますが、256色までしか表現できないというものなのでimagecopyresamped関数を用いています。

そして80~92行目がリサイズ後のファイルの書き出しです。
imagejpeg関数の第3引数は画質の%ですが0~100なのでimg_resize関数の引数をそのまま使っておkです。

pngの方も書き出しの関数imagepngで第3引数が画質の値をとるのですが、これは0~9までの値です。
img_resize関数の画質の引数は0~100なので0.09倍して整数になるように切り上げする処理をいれています。

最後の95~96行目はメモリの開放です。
やたらめったにメモリ食う処理なので必ず開放してあげましょう。

3 Comments
1月 7, 2010 in PHP

3 Responses

  1. ブログ作ったと聞いて

    なんかイロイロやってんだねぇ

    手を出してみたいけど時間が・・・

  2. >kura

    忙しいと仕方ないものです。 僕も卒論で色々フラグがたってますが提出日までもがきまくります〜orz

  3. 青春18切符、いつ買いに行く??

Leave a Reply

Comments links could be nofollow free.

Using Gravatars in the comments - get your own and be recognized!

XHTML: These are some of the tags you can use: <a href=""> <b> <blockquote> <code> <em> <i> <strike> <strong>